001 // License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.actions.downloadtasks;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.io.IOException;
007 import java.util.concurrent.Future;
008 import java.util.regex.Matcher;
009 import java.util.regex.Pattern;
010
011 import org.openstreetmap.josm.Main;
012 import org.openstreetmap.josm.data.Bounds;
013 import org.openstreetmap.josm.data.Bounds.ParseMethod;
014 import org.openstreetmap.josm.data.gpx.GpxData;
015 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
016 import org.openstreetmap.josm.gui.layer.GpxLayer;
017 import org.openstreetmap.josm.gui.layer.Layer;
018 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
019 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
020 import org.openstreetmap.josm.gui.progress.ProgressTaskId;
021 import org.openstreetmap.josm.gui.progress.ProgressTaskIds;
022 import org.openstreetmap.josm.io.BoundingBoxDownloader;
023 import org.openstreetmap.josm.io.GpxImporter;
024 import org.openstreetmap.josm.io.GpxImporter.GpxImporterData;
025 import org.openstreetmap.josm.io.OsmServerLocationReader;
026 import org.openstreetmap.josm.io.OsmServerReader;
027 import org.openstreetmap.josm.io.OsmTransferException;
028 import org.xml.sax.SAXException;
029
030 /**
031 * Task allowing to download GPS data.
032 */
033 public class DownloadGpsTask extends AbstractDownloadTask {
034
035 private DownloadTask downloadTask;
036
037 private static final String PATTERN_TRACE_ID = "http://.*openstreetmap.org/trace/\\p{Digit}+/data";
038
039 private static final String PATTERN_TRACKPOINTS_BBOX = "http://.*/api/0.6/trackpoints\\?bbox=.*,.*,.*,.*";
040
041 private static final String PATTERN_EXTERNAL_GPX_SCRIPT = "https?://.*exportgpx.*";
042 private static final String PATTERN_EXTERNAL_GPX_FILE = "https?://.*/(.*\\.gpx)";
043
044 protected String newLayerName = null;
045
046 public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
047 downloadTask = new DownloadTask(newLayer,
048 new BoundingBoxDownloader(downloadArea), progressMonitor);
049 // We need submit instead of execute so we can wait for it to finish and get the error
050 // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
051 return Main.worker.submit(downloadTask);
052 }
053
054 public Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
055 if (url != null && (url.matches(PATTERN_TRACE_ID) || url.matches(PATTERN_EXTERNAL_GPX_SCRIPT) || url.matches(PATTERN_EXTERNAL_GPX_FILE))) {
056 downloadTask = new DownloadTask(newLayer,
057 new OsmServerLocationReader(url), progressMonitor);
058 // Extract .gpx filename from URL to set the new layer name
059 Matcher matcher = Pattern.compile(PATTERN_EXTERNAL_GPX_FILE).matcher(url);
060 newLayerName = matcher.matches() ? matcher.group(1) : null;
061 // We need submit instead of execute so we can wait for it to finish and get the error
062 // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
063 return Main.worker.submit(downloadTask);
064
065 } else if (url != null && url.matches(PATTERN_TRACKPOINTS_BBOX)) {
066 String[] table = url.split("\\?|=|&");
067 for (int i = 0; i<table.length; i++) {
068 if (table[i].equals("bbox") && i<table.length-1 )
069 return download(newLayer, new Bounds(table[i+1], ",", ParseMethod.LEFT_BOTTOM_RIGHT_TOP), progressMonitor);
070 }
071 }
072 return null;
073 }
074
075 /* (non-Javadoc)
076 * @see org.openstreetmap.josm.actions.downloadtasks.DownloadTask#acceptsUrl(java.lang.String)
077 */
078 @Override
079 public boolean acceptsUrl(String url) {
080 return url != null && (url.matches(PATTERN_TRACE_ID) || url.matches(PATTERN_TRACKPOINTS_BBOX)
081 || url.matches(PATTERN_EXTERNAL_GPX_SCRIPT) || url.matches(PATTERN_EXTERNAL_GPX_FILE));
082 }
083
084 public void cancel() {
085 if (downloadTask != null) {
086 downloadTask.cancel();
087 }
088 }
089
090 class DownloadTask extends PleaseWaitRunnable {
091 private OsmServerReader reader;
092 private GpxData rawData;
093 private final boolean newLayer;
094
095 public DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
096 super(tr("Downloading GPS data"));
097 this.reader = reader;
098 this.newLayer = newLayer;
099 }
100
101 @Override public void realRun() throws IOException, SAXException, OsmTransferException {
102 try {
103 if (isCanceled())
104 return;
105 rawData = reader.parseRawGps(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
106 } catch(Exception e) {
107 if (isCanceled())
108 return;
109 if (e instanceof OsmTransferException) {
110 rememberException(e);
111 } else {
112 rememberException(new OsmTransferException(e));
113 }
114 }
115 }
116
117 @Override protected void finish() {
118 if (isCanceled() || isFailed())
119 return;
120 if (rawData == null)
121 return;
122 String name = newLayerName != null ? newLayerName : tr("Downloaded GPX Data");
123
124 GpxImporterData layers = GpxImporter.loadLayers(rawData, reader.isGpxParsedProperly(), name, tr("Markers from {0}", name));
125
126 GpxLayer gpxLayer = addOrMergeLayer(layers.getGpxLayer(), findGpxMergeLayer());
127 addOrMergeLayer(layers.getMarkerLayer(), findMarkerMergeLayer(gpxLayer));
128
129 layers.getPostLayerTask().run();
130 }
131
132 private <L extends Layer> L addOrMergeLayer(L layer, L mergeLayer) {
133 if (layer == null) return null;
134 if (newLayer || mergeLayer == null) {
135 Main.main.addLayer(layer);
136 return layer;
137 } else {
138 mergeLayer.mergeFrom(layer);
139 Main.map.repaint();
140 return mergeLayer;
141 }
142 }
143
144 private GpxLayer findGpxMergeLayer() {
145 if (!Main.isDisplayingMapView())
146 return null;
147 boolean merge = Main.pref.getBoolean("download.gps.mergeWithLocal", false);
148 Layer active = Main.map.mapView.getActiveLayer();
149 if (active != null && active instanceof GpxLayer && (merge || ((GpxLayer)active).data.fromServer))
150 return (GpxLayer) active;
151 for (GpxLayer l : Main.map.mapView.getLayersOfType(GpxLayer.class)) {
152 if (merge || l.data.fromServer)
153 return l;
154 }
155 return null;
156 }
157
158 private MarkerLayer findMarkerMergeLayer(GpxLayer fromLayer) {
159 if (!Main.isDisplayingMapView())
160 return null;
161 for (MarkerLayer l : Main.map.mapView.getLayersOfType(MarkerLayer.class)) {
162 if (fromLayer != null && l.fromLayer == fromLayer)
163 return l;
164 }
165 return null;
166 }
167
168 @Override protected void cancel() {
169 setCanceled(true);
170 if (reader != null) {
171 reader.cancel();
172 }
173 }
174
175 @Override
176 public ProgressTaskId canRunInBackground() {
177 return ProgressTaskIds.DOWNLOAD_GPS;
178 }
179 }
180 }