source: josm/trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java@ 6380

Last change on this file since 6380 was 6380, checked in by Don-vip, 10 years ago

update license/copyright information

  • Property svn:eol-style set to native
File size: 13.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.downloadtasks;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.io.UnsupportedEncodingException;
8import java.net.URL;
9import java.net.URLEncoder;
10import java.util.Collection;
11import java.util.concurrent.Future;
12import java.util.regex.Matcher;
13import java.util.regex.Pattern;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.Bounds;
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.data.osm.DataSet;
19import org.openstreetmap.josm.data.osm.DataSource;
20import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
21import org.openstreetmap.josm.gui.PleaseWaitRunnable;
22import org.openstreetmap.josm.gui.layer.Layer;
23import org.openstreetmap.josm.gui.layer.OsmDataLayer;
24import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
26import org.openstreetmap.josm.io.BoundingBoxDownloader;
27import org.openstreetmap.josm.io.OsmServerLocationReader;
28import org.openstreetmap.josm.io.OsmServerReader;
29import org.openstreetmap.josm.io.OsmTransferCanceledException;
30import org.openstreetmap.josm.io.OsmTransferException;
31import org.xml.sax.SAXException;
32
33/**
34 * Open the download dialog and download the data.
35 * Run in the worker thread.
36 */
37public class DownloadOsmTask extends AbstractDownloadTask {
38
39 private static final String PATTERN_OSM_API_URL = "http://.*/api/0.6/(map|nodes?|ways?|relations?|\\*).*";
40 private static final String PATTERN_OVERPASS_API_URL = "http://.*/interpreter\\?data=.*";
41 private static final String PATTERN_OVERPASS_API_XAPI_URL = "http://.*/xapi(\\?.*\\[@meta\\]|_meta\\?).*";
42 private static final String PATTERN_EXTERNAL_OSM_FILE = "https?://.*/.*\\.osm";
43
44 protected Bounds currentBounds;
45 protected DataSet downloadedData;
46 protected DownloadTask downloadTask;
47
48 protected OsmDataLayer targetLayer;
49
50 protected String newLayerName = null;
51
52 @Override
53 public String[] getPatterns() {
54 if (this.getClass() == DownloadOsmTask.class) {
55 return new String[]{PATTERN_OSM_API_URL, PATTERN_OVERPASS_API_URL,
56 PATTERN_OVERPASS_API_XAPI_URL, PATTERN_EXTERNAL_OSM_FILE};
57 } else {
58 return super.getPatterns();
59 }
60 }
61
62 @Override
63 public String getTitle() {
64 if (this.getClass() == DownloadOsmTask.class) {
65 return tr("Download OSM");
66 } else {
67 return super.getTitle();
68 }
69 }
70
71 protected void rememberDownloadedData(DataSet ds) {
72 this.downloadedData = ds;
73 }
74
75 /**
76 * Replies the {@link DataSet} containing the downloaded OSM data.
77 * @return The {@link DataSet} containing the downloaded OSM data.
78 */
79 public DataSet getDownloadedData() {
80 return downloadedData;
81 }
82
83 @Override
84 public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
85 return download(new BoundingBoxDownloader(downloadArea), newLayer, downloadArea, progressMonitor);
86 }
87
88 /**
89 * Asynchronously launches the download task for a given bounding box.
90 *
91 * Set <code>progressMonitor</code> to null, if the task should create, open, and close a progress monitor.
92 * Set progressMonitor to {@link NullProgressMonitor#INSTANCE} if progress information is to
93 * be discarded.
94 *
95 * You can wait for the asynchronous download task to finish by synchronizing on the returned
96 * {@link Future}, but make sure not to freeze up JOSM. Example:
97 * <pre>
98 * Future<?> future = task.download(...);
99 * // DON'T run this on the Swing EDT or JOSM will freeze
100 * future.get(); // waits for the dowload task to complete
101 * </pre>
102 *
103 * The following example uses a pattern which is better suited if a task is launched from
104 * the Swing EDT:
105 * <pre>
106 * final Future<?> future = task.download(...);
107 * Runnable runAfterTask = new Runnable() {
108 * public void run() {
109 * // this is not strictly necessary because of the type of executor service
110 * // Main.worker is initialized with, but it doesn't harm either
111 * //
112 * future.get(); // wait for the download task to complete
113 * doSomethingAfterTheTaskCompleted();
114 * }
115 * }
116 * Main.worker.submit(runAfterTask);
117 * </pre>
118 * @param reader the reader used to parse OSM data (see {@link OsmServerReader#parseOsm})
119 * @param newLayer true, if the data is to be downloaded into a new layer. If false, the task
120 * selects one of the existing layers as download layer, preferably the active layer.
121 * @param downloadArea the area to download
122 * @param progressMonitor the progressMonitor
123 * @return the future representing the asynchronous task
124 */
125 public Future<?> download(OsmServerReader reader, boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
126 return download(new DownloadTask(newLayer, reader, progressMonitor), downloadArea);
127 }
128
129 protected Future<?> download(DownloadTask downloadTask, Bounds downloadArea) {
130 this.downloadTask = downloadTask;
131 this.currentBounds = new Bounds(downloadArea);
132 // We need submit instead of execute so we can wait for it to finish and get the error
133 // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
134 return Main.worker.submit(downloadTask);
135 }
136
137 protected final String encodePartialUrl(String url, String safePart) {
138 if (url != null && safePart != null) {
139 int pos = url.indexOf(safePart);
140 if (pos > -1) {
141 pos += safePart.length();
142 try {
143 return url.substring(0, pos) + URLEncoder.encode(url.substring(pos), "UTF-8").replaceAll("\\+", "%20");
144 } catch (UnsupportedEncodingException e) {
145 e.printStackTrace();
146 }
147 }
148 }
149 return url;
150 }
151
152 /**
153 * Loads a given URL from the OSM Server
154 * @param new_layer True if the data should be saved to a new layer
155 * @param url The URL as String
156 */
157 @Override
158 public Future<?> loadUrl(boolean new_layer, String url, ProgressMonitor progressMonitor) {
159 if (url.matches(PATTERN_OVERPASS_API_URL)) {
160 url = encodePartialUrl(url, "/interpreter?data="); // encode only the part after the = sign
161
162 } else if (url.matches(PATTERN_OVERPASS_API_XAPI_URL)) {
163 url = encodePartialUrl(url, "/xapi?"); // encode only the part after the ? sign
164 }
165 downloadTask = new DownloadTask(new_layer,
166 new OsmServerLocationReader(url),
167 progressMonitor);
168 currentBounds = null;
169 // Extract .osm filename from URL to set the new layer name
170 extractOsmFilename("https?://.*/(.*\\.osm)", url);
171 return Main.worker.submit(downloadTask);
172 }
173
174 protected final void extractOsmFilename(String pattern, String url) {
175 Matcher matcher = Pattern.compile(pattern).matcher(url);
176 newLayerName = matcher.matches() ? matcher.group(1) : null;
177 }
178
179 @Override
180 public void cancel() {
181 if (downloadTask != null) {
182 downloadTask.cancel();
183 }
184 }
185
186 protected class DownloadTask extends PleaseWaitRunnable {
187 protected OsmServerReader reader;
188 protected DataSet dataSet;
189 protected boolean newLayer;
190
191 public DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
192 super(tr("Downloading data"), progressMonitor, false);
193 this.reader = reader;
194 this.newLayer = newLayer;
195 }
196
197 protected DataSet parseDataSet() throws OsmTransferException {
198 return reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
199 }
200
201 @Override public void realRun() throws IOException, SAXException, OsmTransferException {
202 try {
203 if (isCanceled())
204 return;
205 dataSet = parseDataSet();
206 } catch(Exception e) {
207 if (isCanceled()) {
208 Main.info(tr("Ignoring exception because download has been canceled. Exception was: {0}", e.toString()));
209 return;
210 }
211 if (e instanceof OsmTransferCanceledException) {
212 setCanceled(true);
213 return;
214 } else if (e instanceof OsmTransferException) {
215 rememberException(e);
216 } else {
217 rememberException(new OsmTransferException(e));
218 }
219 DownloadOsmTask.this.setFailed(true);
220 }
221 }
222
223 protected OsmDataLayer getEditLayer() {
224 if (!Main.isDisplayingMapView()) return null;
225 return Main.main.getEditLayer();
226 }
227
228 protected int getNumDataLayers() {
229 int count = 0;
230 if (!Main.isDisplayingMapView()) return 0;
231 Collection<Layer> layers = Main.map.mapView.getAllLayers();
232 for (Layer layer : layers) {
233 if (layer instanceof OsmDataLayer) {
234 count++;
235 }
236 }
237 return count;
238 }
239
240 protected OsmDataLayer getFirstDataLayer() {
241 if (!Main.isDisplayingMapView()) return null;
242 Collection<Layer> layers = Main.map.mapView.getAllLayersAsList();
243 for (Layer layer : layers) {
244 if (layer instanceof OsmDataLayer)
245 return (OsmDataLayer) layer;
246 }
247 return null;
248 }
249
250 protected OsmDataLayer createNewLayer(String layerName) {
251 if (layerName == null || layerName.isEmpty()) {
252 layerName = OsmDataLayer.createNewName();
253 }
254 return new OsmDataLayer(dataSet, layerName, null);
255 }
256
257 protected OsmDataLayer createNewLayer() {
258 return createNewLayer(null);
259 }
260
261 @Override protected void finish() {
262 if (isFailed() || isCanceled())
263 return;
264 if (dataSet == null)
265 return; // user canceled download or error occurred
266 if (dataSet.allPrimitives().isEmpty()) {
267 rememberErrorMessage(tr("No data found in this area."));
268 // need to synthesize a download bounds lest the visual indication of downloaded
269 // area doesn't work
270 dataSet.dataSources.add(new DataSource(currentBounds != null ? currentBounds : new Bounds(new LatLon(0, 0)), "OpenStreetMap server"));
271 }
272
273 rememberDownloadedData(dataSet);
274 int numDataLayers = getNumDataLayers();
275 if (newLayer || numDataLayers == 0 || (numDataLayers > 1 && getEditLayer() == null)) {
276 // the user explicitly wants a new layer, we don't have any layer at all
277 // or it is not clear which layer to merge to
278 //
279 targetLayer = createNewLayer(newLayerName);
280 final boolean isDisplayingMapView = Main.isDisplayingMapView();
281
282 Main.main.addLayer(targetLayer);
283
284 // If the mapView is not there yet, we cannot calculate the bounds (see constructor of MapView).
285 // Otherwise jump to the current download.
286 if (isDisplayingMapView) {
287 computeBboxAndCenterScale();
288 }
289 } else {
290 targetLayer = getEditLayer();
291 if (targetLayer == null) {
292 targetLayer = getFirstDataLayer();
293 }
294 targetLayer.mergeFrom(dataSet);
295 computeBboxAndCenterScale();
296 targetLayer.onPostDownloadFromServer();
297 }
298 }
299
300 protected void computeBboxAndCenterScale() {
301 BoundingXYVisitor v = new BoundingXYVisitor();
302 if (currentBounds != null) {
303 v.visit(currentBounds);
304 } else {
305 v.computeBoundingBox(dataSet.getNodes());
306 }
307 Main.map.mapView.recalculateCenterScale(v);
308 }
309
310 @Override protected void cancel() {
311 setCanceled(true);
312 if (reader != null) {
313 reader.cancel();
314 }
315 }
316 }
317
318 @Override
319 public String getConfirmationMessage(URL url) {
320 if (url != null) {
321 String urlString = url.toExternalForm();
322 if (urlString.matches(PATTERN_OSM_API_URL)) {
323 // TODO: proper i18n after stabilization
324 String message = "<ul><li>"+tr("OSM Server URL:") + " " + url.getHost() + "</li><li>" +
325 tr("Command")+": "+url.getPath()+"</li>";
326 if (url.getQuery() != null) {
327 message += "<li>" + tr("Request details: {0}", url.getQuery().replaceAll(",\\s*", ", ")) + "</li>";
328 }
329 message += "</ul>";
330 return message;
331 }
332 // TODO: other APIs
333 }
334 return null;
335 }
336}
Note: See TracBrowser for help on using the repository browser.