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 }