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.progress.ProgressMonitor;
019    import org.openstreetmap.josm.gui.progress.ProgressTaskId;
020    import org.openstreetmap.josm.gui.progress.ProgressTaskIds;
021    import org.openstreetmap.josm.io.BoundingBoxDownloader;
022    import org.openstreetmap.josm.io.OsmServerLocationReader;
023    import org.openstreetmap.josm.io.OsmServerReader;
024    import org.openstreetmap.josm.io.OsmTransferException;
025    import org.xml.sax.SAXException;
026    
027    public class DownloadGpsTask extends AbstractDownloadTask {
028    
029        private DownloadTask downloadTask;
030    
031        private static final String PATTERN_TRACE_ID = "http://.*openstreetmap.org/trace/\\p{Digit}+/data";
032    
033        private static final String PATTERN_TRACKPOINTS_BBOX = "http://.*/api/0.6/trackpoints\\?bbox=.*,.*,.*,.*";
034    
035        private static final String PATTERN_EXTERNAL_GPX_SCRIPT = "http://.*exportgpx.*";
036        private static final String PATTERN_EXTERNAL_GPX_FILE = "http://.*/(.*\\.gpx)";
037    
038        protected String newLayerName = null;
039    
040        public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
041            downloadTask = new DownloadTask(newLayer,
042                    new BoundingBoxDownloader(downloadArea), progressMonitor);
043            // We need submit instead of execute so we can wait for it to finish and get the error
044            // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
045            return Main.worker.submit(downloadTask);
046        }
047    
048        public Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
049            if (url != null && (url.matches(PATTERN_TRACE_ID) || url.matches(PATTERN_EXTERNAL_GPX_SCRIPT) || url.matches(PATTERN_EXTERNAL_GPX_FILE))) {
050                downloadTask = new DownloadTask(newLayer,
051                        new OsmServerLocationReader(url), progressMonitor);
052                // Extract .gpx filename from URL to set the new layer name
053                Matcher matcher = Pattern.compile(PATTERN_EXTERNAL_GPX_FILE).matcher(url);
054                newLayerName = matcher.matches() ? matcher.group(1) : null;
055                // We need submit instead of execute so we can wait for it to finish and get the error
056                // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
057                return Main.worker.submit(downloadTask);
058    
059            } else if (url != null && url.matches(PATTERN_TRACKPOINTS_BBOX)) {
060                String[] table = url.split("\\?|=|&");
061                for (int i = 0; i<table.length; i++) {
062                    if (table[i].equals("bbox") && i<table.length-1 )
063                        return download(newLayer, new Bounds(table[i+1], ",", ParseMethod.LEFT_BOTTOM_RIGHT_TOP), progressMonitor);
064                }
065            }
066            return null;
067        }
068    
069        /* (non-Javadoc)
070         * @see org.openstreetmap.josm.actions.downloadtasks.DownloadTask#acceptsUrl(java.lang.String)
071         */
072        @Override
073        public boolean acceptsUrl(String url) {
074            return url != null && (url.matches(PATTERN_TRACE_ID) || url.matches(PATTERN_TRACKPOINTS_BBOX)
075                    || url.matches(PATTERN_EXTERNAL_GPX_SCRIPT) || url.matches(PATTERN_EXTERNAL_GPX_FILE));
076        }
077    
078        public void cancel() {
079            if (downloadTask != null) {
080                downloadTask.cancel();
081            }
082        }
083    
084        class DownloadTask extends PleaseWaitRunnable {
085            private OsmServerReader reader;
086            private GpxData rawData;
087            private final boolean newLayer;
088    
089            public DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
090                super(tr("Downloading GPS data"));
091                this.reader = reader;
092                this.newLayer = newLayer;
093            }
094    
095            @Override public void realRun() throws IOException, SAXException, OsmTransferException {
096                try {
097                    if (isCanceled())
098                        return;
099                    rawData = reader.parseRawGps(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
100                } catch(Exception e) {
101                    if (isCanceled())
102                        return;
103                    if (e instanceof OsmTransferException) {
104                        rememberException(e);
105                    } else {
106                        rememberException(new OsmTransferException(e));
107                    }
108                }
109            }
110    
111            @Override protected void finish() {
112                if (isCanceled() || isFailed())
113                    return;
114                if (rawData == null)
115                    return;
116                String name = newLayerName != null ? newLayerName : tr("Downloaded GPX Data");
117                GpxLayer layer = new GpxLayer(rawData, name);
118                Layer x = findMergeLayer();
119                if (newLayer || x == null) {
120                    Main.main.addLayer(layer);
121                } else {
122                    x.mergeFrom(layer);
123                    Main.map.repaint();
124                }
125            }
126    
127            private Layer findMergeLayer() {
128                boolean merge = Main.pref.getBoolean("download.gps.mergeWithLocal", false);
129                if (!Main.isDisplayingMapView())
130                    return null;
131                Layer active = Main.map.mapView.getActiveLayer();
132                if (active != null && active instanceof GpxLayer && (merge || ((GpxLayer)active).data.fromServer))
133                    return active;
134                for (Layer l : Main.map.mapView.getAllLayers()) {
135                    if (l instanceof GpxLayer &&  (merge || ((GpxLayer)l).data.fromServer))
136                        return l;
137                }
138                return null;
139            }
140    
141            @Override protected void cancel() {
142                setCanceled(true);
143                if (reader != null) {
144                    reader.cancel();
145                }
146            }
147    
148            @Override
149            public ProgressTaskId canRunInBackground() {
150                return ProgressTaskIds.DOWNLOAD_GPS;
151            }
152        }
153    }