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