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

Last change on this file since 11186 was 11124, checked in by simon04, 8 years ago

see #13749 - Enhance supported URLs for downloading GPS data

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