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() +": " 074 +e.getClass() + ": " + e.getMessage()); 075 } catch (IOException ioe) { 076 ioe.printStackTrace(); 077 } 078 } 079 } finally { 080 tile.loading = false; 081 tile.setLoaded(true); 082 } 083 } 084 085 public Tile getTile() { 086 return tile; 087 } 088 }; 089 } 090 091 protected URLConnection loadTileFromOsm(Tile tile) throws IOException { 092 URL url; 093 url = new URL(tile.getUrl()); 094 URLConnection urlConn = url.openConnection(); 095 if (urlConn instanceof HttpURLConnection) { 096 prepareHttpUrlConnection((HttpURLConnection)urlConn); 097 } 098 urlConn.setReadTimeout(30000); // 30 seconds read timeout 099 return urlConn; 100 } 101 102 protected void loadTileMetadata(Tile tile, URLConnection urlConn) { 103 String str = urlConn.getHeaderField("X-VE-TILEMETA-CaptureDatesRange"); 104 if (str != null) { 105 tile.putValue("capture-date", str); 106 } 107 str = urlConn.getHeaderField("X-VE-Tile-Info"); 108 if (str != null) { 109 tile.putValue("tile-info", str); 110 } 111 } 112 113 protected void prepareHttpUrlConnection(HttpURLConnection urlConn) { 114 for(Entry<String, String> e : headers.entrySet()) { 115 urlConn.setRequestProperty(e.getKey(), e.getValue()); 116 } 117 if(timeoutConnect != 0) 118 urlConn.setConnectTimeout(timeoutConnect); 119 if(timeoutRead != 0) 120 urlConn.setReadTimeout(timeoutRead); 121 } 122 123 @Override 124 public String toString() { 125 return getClass().getSimpleName(); 126 } 127 128}