source: josm/trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java@ 11784

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

fix #13922 - fix GPX and Note download area computation

  • Property svn:eol-style set to native
File size: 9.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.net.URL;
8import java.util.Optional;
9import java.util.concurrent.Future;
10import java.util.regex.Matcher;
11import java.util.regex.Pattern;
12import java.util.stream.Stream;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.data.Bounds;
16import org.openstreetmap.josm.data.Bounds.ParseMethod;
17import org.openstreetmap.josm.data.ProjectionBounds;
18import org.openstreetmap.josm.data.ViewportData;
19import org.openstreetmap.josm.data.gpx.GpxData;
20import org.openstreetmap.josm.gui.PleaseWaitRunnable;
21import org.openstreetmap.josm.gui.layer.GpxLayer;
22import org.openstreetmap.josm.gui.layer.Layer;
23import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
24import org.openstreetmap.josm.gui.progress.ProgressMonitor;
25import org.openstreetmap.josm.gui.progress.ProgressTaskId;
26import org.openstreetmap.josm.gui.progress.ProgressTaskIds;
27import org.openstreetmap.josm.io.BoundingBoxDownloader;
28import org.openstreetmap.josm.io.GpxImporter;
29import org.openstreetmap.josm.io.GpxImporter.GpxImporterData;
30import org.openstreetmap.josm.io.OsmServerLocationReader;
31import org.openstreetmap.josm.io.OsmServerReader;
32import org.openstreetmap.josm.io.OsmTransferException;
33import org.openstreetmap.josm.tools.CheckParameterUtil;
34import org.xml.sax.SAXException;
35
36/**
37 * Task allowing to download GPS data.
38 */
39public class DownloadGpsTask extends AbstractDownloadTask<GpxData> {
40
41 private DownloadTask downloadTask;
42 private GpxLayer gpxLayer;
43
44 private static final String PATTERN_TRACE_ID = "https?://.*(osm|openstreetmap).org/trace/\\p{Digit}+/data";
45 private static final String PATTERN_USER_TRACE_ID = "https?://.*(osm|openstreetmap).org/user/[^/]+/traces/(\\p{Digit}+)";
46 private static final String PATTERN_EDIT_TRACE_ID = "https?://.*(osm|openstreetmap).org/edit/?\\?gpx=(\\p{Digit}+)(#.*)?";
47
48 private static final String PATTERN_TRACKPOINTS_BBOX = "https?://.*/api/0.6/trackpoints\\?bbox=.*,.*,.*,.*";
49
50 private static final String PATTERN_EXTERNAL_GPX_SCRIPT = "https?://.*exportgpx.*";
51 private static final String PATTERN_EXTERNAL_GPX_FILE = "https?://.*/(.*\\.gpx)";
52
53 protected String newLayerName;
54
55 @Override
56 public String[] getPatterns() {
57 return new String[] {
58 PATTERN_EXTERNAL_GPX_FILE, PATTERN_EXTERNAL_GPX_SCRIPT,
59 PATTERN_TRACE_ID, PATTERN_USER_TRACE_ID, PATTERN_EDIT_TRACE_ID,
60 PATTERN_TRACKPOINTS_BBOX,
61 };
62 }
63
64 @Override
65 public String getTitle() {
66 return tr("Download GPS");
67 }
68
69 @Override
70 public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
71 downloadTask = new DownloadTask(newLayer,
72 new BoundingBoxDownloader(downloadArea), progressMonitor);
73 // We need submit instead of execute so we can wait for it to finish and get the error
74 // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
75 return Main.worker.submit(downloadTask);
76 }
77
78 @Override
79 public Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
80 CheckParameterUtil.ensureParameterNotNull(url, "url");
81 final Optional<String> mappedUrl = Stream.of(PATTERN_USER_TRACE_ID, PATTERN_EDIT_TRACE_ID)
82 .map(p -> Pattern.compile(p).matcher(url))
83 .filter(Matcher::matches)
84 .map(m -> "https://www.openstreetmap.org/trace/" + m.group(2) + "/data")
85 .findFirst();
86 if (mappedUrl.isPresent()) {
87 return loadUrl(newLayer, mappedUrl.get(), progressMonitor);
88 }
89 if (url.matches(PATTERN_TRACE_ID)
90 || url.matches(PATTERN_EXTERNAL_GPX_SCRIPT)
91 || url.matches(PATTERN_EXTERNAL_GPX_FILE)) {
92 downloadTask = new DownloadTask(newLayer,
93 new OsmServerLocationReader(url), progressMonitor);
94 // Extract .gpx filename from URL to set the new layer name
95 Matcher matcher = Pattern.compile(PATTERN_EXTERNAL_GPX_FILE).matcher(url);
96 newLayerName = matcher.matches() ? matcher.group(1) : null;
97 // We need submit instead of execute so we can wait for it to finish and get the error
98 // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
99 return Main.worker.submit(downloadTask);
100
101 } else if (url.matches(PATTERN_TRACKPOINTS_BBOX)) {
102 String[] table = url.split("\\?|=|&");
103 for (int i = 0; i < table.length; i++) {
104 if ("bbox".equals(table[i]) && i < table.length-1)
105 return download(newLayer, new Bounds(table[i+1], ",", ParseMethod.LEFT_BOTTOM_RIGHT_TOP), progressMonitor);
106 }
107 }
108 return null;
109 }
110
111 @Override
112 public void cancel() {
113 if (downloadTask != null) {
114 downloadTask.cancel();
115 }
116 }
117
118 @Override
119 public ProjectionBounds getDownloadProjectionBounds() {
120 return gpxLayer != null ? gpxLayer.getViewProjectionBounds() : null;
121 }
122
123 class DownloadTask extends PleaseWaitRunnable {
124 private final OsmServerReader reader;
125 private GpxData rawData;
126 private final boolean newLayer;
127
128 DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
129 super(tr("Downloading GPS data"), progressMonitor, false);
130 this.reader = reader;
131 this.newLayer = newLayer;
132 }
133
134 @Override
135 public void realRun() throws IOException, SAXException, OsmTransferException {
136 try {
137 if (isCanceled())
138 return;
139 rawData = reader.parseRawGps(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
140 } catch (OsmTransferException e) {
141 if (isCanceled())
142 return;
143 rememberException(e);
144 }
145 }
146
147 @Override
148 protected void finish() {
149 rememberDownloadedData(rawData);
150 if (rawData == null)
151 return;
152 String name = newLayerName != null ? newLayerName : tr("Downloaded GPX Data");
153
154 GpxImporterData layers = GpxImporter.loadLayers(rawData, reader.isGpxParsedProperly(), name,
155 tr("Markers from {0}", name));
156
157 gpxLayer = layers.getGpxLayer();
158 addOrMergeLayer(gpxLayer, findGpxMergeLayer());
159 addOrMergeLayer(layers.getMarkerLayer(), findMarkerMergeLayer(gpxLayer));
160
161 layers.getPostLayerTask().run();
162 }
163
164 private <L extends Layer> L addOrMergeLayer(L layer, L mergeLayer) {
165 if (layer == null) return null;
166 if (newLayer || mergeLayer == null) {
167 Main.getLayerManager().addLayer(layer, zoomAfterDownload);
168 return layer;
169 } else {
170 mergeLayer.mergeFrom(layer);
171 mergeLayer.invalidate();
172 if (Main.map != null && zoomAfterDownload && layer instanceof GpxLayer) {
173 Main.map.mapView.scheduleZoomTo(new ViewportData(layer.getViewProjectionBounds()));
174 }
175 return mergeLayer;
176 }
177 }
178
179 private GpxLayer findGpxMergeLayer() {
180 boolean merge = Main.pref.getBoolean("download.gps.mergeWithLocal", false);
181 Layer active = Main.getLayerManager().getActiveLayer();
182 if (active instanceof GpxLayer && (merge || ((GpxLayer) active).data.fromServer))
183 return (GpxLayer) active;
184 for (GpxLayer l : Main.getLayerManager().getLayersOfType(GpxLayer.class)) {
185 if (merge || l.data.fromServer)
186 return l;
187 }
188 return null;
189 }
190
191 private MarkerLayer findMarkerMergeLayer(GpxLayer fromLayer) {
192 for (MarkerLayer l : Main.getLayerManager().getLayersOfType(MarkerLayer.class)) {
193 if (fromLayer != null && l.fromLayer == fromLayer)
194 return l;
195 }
196 return null;
197 }
198
199 @Override
200 protected void cancel() {
201 setCanceled(true);
202 if (reader != null) {
203 reader.cancel();
204 }
205 }
206
207 @Override
208 public ProgressTaskId canRunInBackground() {
209 return ProgressTaskIds.DOWNLOAD_GPS;
210 }
211 }
212
213 @Override
214 public String getConfirmationMessage(URL url) {
215 // TODO
216 return null;
217 }
218
219 @Override
220 public boolean isSafeForRemotecontrolRequests() {
221 return true;
222 }
223
224 /**
225 * Determines if the given URL denotes an OSM gpx-related API call.
226 * @param url The url to check
227 * @return true if the url matches "Trace ID" API call or "Trackpoints bbox" API call, false otherwise
228 * @see GpxData#fromServer
229 * @since 5745
230 */
231 public static final boolean isFromServer(String url) {
232 return url != null && (url.matches(PATTERN_TRACE_ID) || url.matches(PATTERN_TRACKPOINTS_BBOX));
233 }
234}
Note: See TracBrowser for help on using the repository browser.