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

Last change on this file since 7226 was 7149, checked in by simon04, 10 years ago

fix #10051 - Allow to load from Overpass via HTTPS

  • Property svn:eol-style set to native
File size: 12.5 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[153]2package org.openstreetmap.josm.actions.downloadtasks;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
[5691]7import java.net.URL;
[6524]8import java.util.ArrayList;
[1869]9import java.util.Collection;
[1465]10import java.util.concurrent.Future;
[5024]11import java.util.regex.Matcher;
12import java.util.regex.Pattern;
[153]13
14import org.openstreetmap.josm.Main;
[1465]15import org.openstreetmap.josm.data.Bounds;
[3066]16import org.openstreetmap.josm.data.coor.LatLon;
[153]17import org.openstreetmap.josm.data.osm.DataSet;
[1082]18import org.openstreetmap.josm.data.osm.DataSource;
[2477]19import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
[153]20import org.openstreetmap.josm.gui.PleaseWaitRunnable;
[1869]21import org.openstreetmap.josm.gui.layer.Layer;
[153]22import org.openstreetmap.josm.gui.layer.OsmDataLayer;
[5402]23import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
[1811]24import org.openstreetmap.josm.gui.progress.ProgressMonitor;
[153]25import org.openstreetmap.josm.io.BoundingBoxDownloader;
[1146]26import org.openstreetmap.josm.io.OsmServerLocationReader;
27import org.openstreetmap.josm.io.OsmServerReader;
[4310]28import org.openstreetmap.josm.io.OsmTransferCanceledException;
[1670]29import org.openstreetmap.josm.io.OsmTransferException;
[6524]30import org.openstreetmap.josm.tools.Utils;
[5782]31import org.xml.sax.SAXException;
[153]32
33/**
34 * Open the download dialog and download the data.
35 * Run in the worker thread.
36 */
[2322]37public class DownloadOsmTask extends AbstractDownloadTask {
[6069]38
[6920]39 protected static final String PATTERN_OSM_API_URL = "https?://.*/api/0.6/(map|nodes?|ways?|relations?|\\*).*";
[7149]40 protected static final String PATTERN_OVERPASS_API_URL = "https?://.*/interpreter\\?data=.*";
41 protected static final String PATTERN_OVERPASS_API_XAPI_URL = "https?://.*/xapi(\\?.*\\[@meta\\]|_meta\\?).*";
[6589]42 protected static final String PATTERN_EXTERNAL_OSM_FILE = "https?://.*/.*\\.osm";
[6069]43
[4523]44 protected Bounds currentBounds;
45 protected DataSet downloadedData;
46 protected DownloadTask downloadTask;
[6069]47
[4530]48 protected OsmDataLayer targetLayer;
[6069]49
[5024]50 protected String newLayerName = null;
[6069]51
[6031]52 @Override
53 public String[] getPatterns() {
54 if (this.getClass() == DownloadOsmTask.class) {
[6069]55 return new String[]{PATTERN_OSM_API_URL, PATTERN_OVERPASS_API_URL,
[6031]56 PATTERN_OVERPASS_API_XAPI_URL, PATTERN_EXTERNAL_OSM_FILE};
57 } else {
58 return super.getPatterns();
59 }
60 }
[1082]61
[6031]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
[4523]71 protected void rememberDownloadedData(DataSet ds) {
[2322]72 this.downloadedData = ds;
73 }
74
[5402]75 /**
76 * Replies the {@link DataSet} containing the downloaded OSM data.
77 * @return The {@link DataSet} containing the downloaded OSM data.
78 */
[2322]79 public DataSet getDownloadedData() {
80 return downloadedData;
81 }
82
[5097]83 @Override
[2328]84 public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
[5097]85 return download(new BoundingBoxDownloader(downloadArea), newLayer, downloadArea, progressMonitor);
86 }
[2414]87
[5402]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>
[6830]98 * Future&lt;?&gt; future = task.download(...);
[5402]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>
[6830]106 * final Future&lt;?&gt; future = task.download(...);
[5402]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 */
[5097]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);
[2322]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
[6588]137 /**
138 * This allows subclasses to perform operations on the URL before {@link #loadUrl} is performed.
139 */
140 protected String modifyUrlBeforeLoad(String url) {
[5782]141 return url;
142 }
[6069]143
[2322]144 /**
145 * Loads a given URL from the OSM Server
[5369]146 * @param new_layer True if the data should be saved to a new layer
147 * @param url The URL as String
[2322]148 */
[6031]149 @Override
[2322]150 public Future<?> loadUrl(boolean new_layer, String url, ProgressMonitor progressMonitor) {
[6588]151 url = modifyUrlBeforeLoad(url);
[2322]152 downloadTask = new DownloadTask(new_layer,
153 new OsmServerLocationReader(url),
154 progressMonitor);
[3064]155 currentBounds = null;
[5024]156 // Extract .osm filename from URL to set the new layer name
[5486]157 extractOsmFilename("https?://.*/(.*\\.osm)", url);
[2322]158 return Main.worker.submit(downloadTask);
159 }
[6069]160
[5345]161 protected final void extractOsmFilename(String pattern, String url) {
162 Matcher matcher = Pattern.compile(pattern).matcher(url);
163 newLayerName = matcher.matches() ? matcher.group(1) : null;
164 }
[6031]165
[4521]166 @Override
[2322]167 public void cancel() {
168 if (downloadTask != null) {
169 downloadTask.cancel();
170 }
171 }
172
[4530]173 protected class DownloadTask extends PleaseWaitRunnable {
[4523]174 protected OsmServerReader reader;
175 protected DataSet dataSet;
176 protected boolean newLayer;
[1647]177
[2322]178 public DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
[1811]179 super(tr("Downloading data"), progressMonitor, false);
[1169]180 this.reader = reader;
181 this.newLayer = newLayer;
182 }
[6069]183
[4530]184 protected DataSet parseDataSet() throws OsmTransferException {
185 return reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
186 }
[153]187
[1670]188 @Override public void realRun() throws IOException, SAXException, OsmTransferException {
[1879]189 try {
[2322]190 if (isCanceled())
191 return;
[4530]192 dataSet = parseDataSet();
[1879]193 } catch(Exception e) {
[2322]194 if (isCanceled()) {
[6248]195 Main.info(tr("Ignoring exception because download has been canceled. Exception was: {0}", e.toString()));
[1879]196 return;
197 }
[4310]198 if (e instanceof OsmTransferCanceledException) {
[2641]199 setCanceled(true);
200 return;
201 } else if (e instanceof OsmTransferException) {
[2322]202 rememberException(e);
[1879]203 } else {
[2322]204 rememberException(new OsmTransferException(e));
[1879]205 }
[2322]206 DownloadOsmTask.this.setFailed(true);
[1879]207 }
[1169]208 }
[153]209
[1810]210 protected OsmDataLayer getEditLayer() {
[2343]211 if (!Main.isDisplayingMapView()) return null;
[6336]212 return Main.main.getEditLayer();
[1810]213 }
214
[1869]215 protected int getNumDataLayers() {
216 int count = 0;
[2343]217 if (!Main.isDisplayingMapView()) return 0;
[1869]218 Collection<Layer> layers = Main.map.mapView.getAllLayers();
219 for (Layer layer : layers) {
220 if (layer instanceof OsmDataLayer) {
221 count++;
222 }
223 }
224 return count;
225 }
226
227 protected OsmDataLayer getFirstDataLayer() {
[2343]228 if (!Main.isDisplayingMapView()) return null;
[1895]229 Collection<Layer> layers = Main.map.mapView.getAllLayersAsList();
[1869]230 for (Layer layer : layers) {
231 if (layer instanceof OsmDataLayer)
232 return (OsmDataLayer) layer;
233 }
234 return null;
235 }
[6069]236
[5024]237 protected OsmDataLayer createNewLayer(String layerName) {
238 if (layerName == null || layerName.isEmpty()) {
239 layerName = OsmDataLayer.createNewName();
240 }
241 return new OsmDataLayer(dataSet, layerName, null);
242 }
[6069]243
[4523]244 protected OsmDataLayer createNewLayer() {
[5024]245 return createNewLayer(null);
[4523]246 }
[1869]247
[1169]248 @Override protected void finish() {
[2322]249 if (isFailed() || isCanceled())
[1879]250 return;
[1169]251 if (dataSet == null)
[1465]252 return; // user canceled download or error occurred
[1169]253 if (dataSet.allPrimitives().isEmpty()) {
[2322]254 rememberErrorMessage(tr("No data found in this area."));
[1082]255 // need to synthesize a download bounds lest the visual indication of downloaded
256 // area doesn't work
[3066]257 dataSet.dataSources.add(new DataSource(currentBounds != null ? currentBounds : new Bounds(new LatLon(0, 0)), "OpenStreetMap server"));
[1082]258 }
[3047]259
[1670]260 rememberDownloadedData(dataSet);
[1869]261 int numDataLayers = getNumDataLayers();
262 if (newLayer || numDataLayers == 0 || (numDataLayers > 1 && getEditLayer() == null)) {
263 // the user explicitly wants a new layer, we don't have any layer at all
264 // or it is not clear which layer to merge to
265 //
[5024]266 targetLayer = createNewLayer(newLayerName);
[3047]267 final boolean isDisplayingMapView = Main.isDisplayingMapView();
[3064]268
[4530]269 Main.main.addLayer(targetLayer);
[3047]270
271 // If the mapView is not there yet, we cannot calculate the bounds (see constructor of MapView).
272 // Otherwise jump to the current download.
273 if (isDisplayingMapView) {
[4530]274 computeBboxAndCenterScale();
[3047]275 }
[1670]276 } else {
[4530]277 targetLayer = getEditLayer();
278 if (targetLayer == null) {
279 targetLayer = getFirstDataLayer();
[1869]280 }
[4530]281 targetLayer.mergeFrom(dataSet);
282 computeBboxAndCenterScale();
283 targetLayer.onPostDownloadFromServer();
[1670]284 }
[1169]285 }
[6069]286
[4530]287 protected void computeBboxAndCenterScale() {
288 BoundingXYVisitor v = new BoundingXYVisitor();
289 if (currentBounds != null) {
290 v.visit(currentBounds);
291 } else {
292 v.computeBoundingBox(dataSet.getNodes());
293 }
294 Main.map.mapView.recalculateCenterScale(v);
295 }
[175]296
[1169]297 @Override protected void cancel() {
[2322]298 setCanceled(true);
[1670]299 if (reader != null) {
[1169]300 reader.cancel();
[1670]301 }
[1169]302 }
303 }
[5691]304
305 @Override
306 public String getConfirmationMessage(URL url) {
307 if (url != null) {
308 String urlString = url.toExternalForm();
309 if (urlString.matches(PATTERN_OSM_API_URL)) {
310 // TODO: proper i18n after stabilization
[7005]311 Collection<String> items = new ArrayList<>();
[6524]312 items.add(tr("OSM Server URL:") + " " + url.getHost());
313 items.add(tr("Command")+": "+url.getPath());
[5691]314 if (url.getQuery() != null) {
[6524]315 items.add(tr("Request details: {0}", url.getQuery().replaceAll(",\\s*", ", ")));
[5691]316 }
[6524]317 return Utils.joinAsHtmlUnorderedList(items);
[5691]318 }
319 // TODO: other APIs
320 }
321 return null;
322 }
[175]323}
Note: See TracBrowser for help on using the repository browser.