source: osm/applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java@ 32007

Last change on this file since 32007 was 32007, checked in by wiktorn, 8 years ago

Clear the queue of JMapViewer when panning map and switch to 3-threaded
downloader on default.

See: #josm12396

  • Property svn:eol-style set to native
File size: 6.2 KB
Line 
1// License: GPL. For details, see Readme.txt file.
2package org.openstreetmap.gui.jmapviewer;
3
4import java.io.IOException;
5import java.io.InputStream;
6import java.net.HttpURLConnection;
7import java.net.URL;
8import java.net.URLConnection;
9import java.util.HashMap;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.concurrent.Executors;
13import java.util.concurrent.ThreadPoolExecutor;
14
15import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
16import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
17import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
18
19/**
20 * A {@link TileLoader} implementation that loads tiles from OSM.
21 *
22 * @author Jan Peter Stotz
23 */
24public class OsmTileLoader implements TileLoader {
25 private static final ThreadPoolExecutor jobDispatcher = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
26
27 private final class OsmTileJob implements TileJob {
28 private final Tile tile;
29 private InputStream input;
30 private boolean force;
31
32 private OsmTileJob(Tile tile) {
33 this.tile = tile;
34 }
35
36 @Override
37 public void run() {
38 synchronized (tile) {
39 if ((tile.isLoaded() && !tile.hasError()) || tile.isLoading())
40 return;
41 tile.loaded = false;
42 tile.error = false;
43 tile.loading = true;
44 }
45 try {
46 URLConnection conn = loadTileFromOsm(tile);
47 if (force) {
48 conn.setUseCaches(false);
49 }
50 loadTileMetadata(tile, conn);
51 if ("no-tile".equals(tile.getValue("tile-info"))) {
52 tile.setError("No tile at this zoom level");
53 } else {
54 input = conn.getInputStream();
55 try {
56 tile.loadImage(input);
57 } finally {
58 input.close();
59 input = null;
60 }
61 }
62 tile.setLoaded(true);
63 listener.tileLoadingFinished(tile, true);
64 } catch (Exception e) {
65 tile.setError(e.getMessage());
66 listener.tileLoadingFinished(tile, false);
67 if (input == null) {
68 try {
69 System.err.println("Failed loading " + tile.getUrl() +": "
70 +e.getClass() + ": " + e.getMessage());
71 } catch (IOException ioe) {
72 ioe.printStackTrace();
73 }
74 }
75 } finally {
76 tile.loading = false;
77 tile.setLoaded(true);
78 }
79 }
80
81 @Override
82 public Tile getTile() {
83 return tile;
84 }
85
86 @Override
87 public void submit() {
88 submit(false);
89 }
90
91 @Override
92 public void submit(boolean force) {
93 this.force = force;
94 jobDispatcher.execute(this);
95 }
96 }
97
98 /**
99 * Holds the HTTP headers. Insert e.g. User-Agent here when default should not be used.
100 */
101 public Map<String, String> headers = new HashMap<>();
102
103 public int timeoutConnect;
104 public int timeoutRead;
105
106 protected TileLoaderListener listener;
107
108 public OsmTileLoader(TileLoaderListener listener) {
109 this(listener, null);
110 }
111
112 public OsmTileLoader(TileLoaderListener listener, Map<String, String> headers) {
113 this.headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
114 if (headers != null) {
115 this.headers.putAll(headers);
116 }
117 this.listener = listener;
118 }
119
120 @Override
121 public TileJob createTileLoaderJob(final Tile tile) {
122 return new OsmTileJob(tile);
123 }
124
125 protected URLConnection loadTileFromOsm(Tile tile) throws IOException {
126 URL url;
127 url = new URL(tile.getUrl());
128 URLConnection urlConn = url.openConnection();
129 if (urlConn instanceof HttpURLConnection) {
130 prepareHttpUrlConnection((HttpURLConnection) urlConn);
131 }
132 return urlConn;
133 }
134
135 protected void loadTileMetadata(Tile tile, URLConnection urlConn) {
136 String str = urlConn.getHeaderField("X-VE-TILEMETA-CaptureDatesRange");
137 if (str != null) {
138 tile.putValue("capture-date", str);
139 }
140 str = urlConn.getHeaderField("X-VE-Tile-Info");
141 if (str != null) {
142 tile.putValue("tile-info", str);
143 }
144
145 Long lng = urlConn.getExpiration();
146 if (lng.equals(0L)) {
147 try {
148 str = urlConn.getHeaderField("Cache-Control");
149 if (str != null) {
150 for (String token: str.split(",")) {
151 if (token.startsWith("max-age=")) {
152 lng = Long.parseLong(token.substring(8)) * 1000 +
153 System.currentTimeMillis();
154 }
155 }
156 }
157 } catch (NumberFormatException e) {
158 // ignore malformed Cache-Control headers
159 if (JMapViewer.debug) {
160 System.err.println(e.getMessage());
161 }
162 }
163 }
164 if (!lng.equals(0L)) {
165 tile.putValue("expires", lng.toString());
166 }
167 }
168
169 protected void prepareHttpUrlConnection(HttpURLConnection urlConn) {
170 for (Entry<String, String> e : headers.entrySet()) {
171 urlConn.setRequestProperty(e.getKey(), e.getValue());
172 }
173 if (timeoutConnect != 0)
174 urlConn.setConnectTimeout(timeoutConnect);
175 if (timeoutRead != 0)
176 urlConn.setReadTimeout(timeoutRead);
177 }
178
179 @Override
180 public String toString() {
181 return getClass().getSimpleName();
182 }
183
184 @Override
185 public void cancelOutstandingTasks() {
186 jobDispatcher.getQueue().clear();
187 }
188
189 /**
190 * Sets the maximum number of concurrent connections the tile loader will do
191 * @param num number of conncurent connections
192 */
193 public static void setConcurrentConnections(int num) {
194 jobDispatcher.setMaximumPoolSize(num);
195 }
196}
Note: See TracBrowser for help on using the repository browser.