001package org.openstreetmap.gui.jmapviewer;
002
003//License: GPL. Copyright 2008 by Jan Peter Stotz
004
005import java.io.IOException;
006import java.io.InputStream;
007import java.net.HttpURLConnection;
008import java.net.URL;
009import java.net.URLConnection;
010import java.util.HashMap;
011import java.util.Map;
012import java.util.Map.Entry;
013
014import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
015import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
016import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
017
018/**
019 * A {@link TileLoader} implementation that loads tiles from OSM.
020 *
021 * @author Jan Peter Stotz
022 */
023public class OsmTileLoader implements TileLoader {
024
025    /**
026     * Holds the HTTP headers. Insert e.g. User-Agent here when default should not be used.
027     */
028    public Map<String, String> headers = new HashMap<String, String>();
029
030    public int timeoutConnect = 0;
031    public int timeoutRead = 0;
032
033    protected TileLoaderListener listener;
034
035    public OsmTileLoader(TileLoaderListener listener) {
036        headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
037        this.listener = listener;
038    }
039
040    public TileJob createTileLoaderJob(final Tile tile) {
041        return new TileJob() {
042
043            InputStream input = null;
044
045            public void run() {
046                synchronized (tile) {
047                    if ((tile.isLoaded() && !tile.hasError()) || tile.isLoading())
048                        return;
049                    tile.loaded = false;
050                    tile.error = false;
051                    tile.loading = true;
052                }
053                try {
054                    URLConnection conn = loadTileFromOsm(tile);
055                    loadTileMetadata(tile, conn);
056                    if ("no-tile".equals(tile.getValue("tile-info"))) {
057                        tile.setError("No tile at this zoom level");
058                    } else {
059                        input = conn.getInputStream();
060                        try {
061                            tile.loadImage(input);
062                        } finally {
063                            input.close();
064                            input = null;
065                        }
066                    }
067                    tile.setLoaded(true);
068                    listener.tileLoadingFinished(tile, true);
069                } catch (Exception e) {
070                    tile.setError(e.getMessage());
071                    listener.tileLoadingFinished(tile, false);
072                    if (input == null) {
073                        try {
074                            System.err.println("Failed loading " + tile.getUrl() +": " + e.getMessage());
075                        } catch(IOException i) {
076                        }
077                    }
078                } finally {
079                    tile.loading = false;
080                    tile.setLoaded(true);
081                }
082            }
083
084            public Tile getTile() {
085                return tile;
086            }
087        };
088    }
089
090    protected URLConnection loadTileFromOsm(Tile tile) throws IOException {
091        URL url;
092        url = new URL(tile.getUrl());
093        URLConnection urlConn = url.openConnection();
094        if (urlConn instanceof HttpURLConnection) {
095            prepareHttpUrlConnection((HttpURLConnection)urlConn);
096        }
097        urlConn.setReadTimeout(30000); // 30 seconds read timeout
098        return urlConn;
099    }
100
101    protected void loadTileMetadata(Tile tile, URLConnection urlConn) {
102        String str = urlConn.getHeaderField("X-VE-TILEMETA-CaptureDatesRange");
103        if (str != null) {
104            tile.putValue("capture-date", str);
105        }
106        str = urlConn.getHeaderField("X-VE-Tile-Info");
107        if (str != null) {
108            tile.putValue("tile-info", str);
109        }
110    }
111
112    protected void prepareHttpUrlConnection(HttpURLConnection urlConn) {
113        for(Entry<String, String> e : headers.entrySet()) {
114            urlConn.setRequestProperty(e.getKey(), e.getValue());
115        }
116        if(timeoutConnect != 0)
117            urlConn.setConnectTimeout(timeoutConnect);
118        if(timeoutRead != 0)
119            urlConn.setReadTimeout(timeoutRead);
120    }
121
122    @Override
123    public String toString() {
124        return getClass().getSimpleName();
125    }
126
127}