// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.layer.gpx; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.event.ActionEvent; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.DownloadAlongAction; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.gpx.GpxData; import org.openstreetmap.josm.data.gpx.GpxTrack; import org.openstreetmap.josm.data.gpx.GpxTrackSegment; import org.openstreetmap.josm.data.gpx.WayPoint; import org.openstreetmap.josm.gui.PleaseWaitRunnable; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.gui.progress.NullProgressMonitor; /** * Action that issues a series of download requests to the API, following the GPX track. * * @author fred * @since 5715 */ public class DownloadAlongTrackAction extends DownloadAlongAction { private static final int NEAR_TRACK = 0; private static final int NEAR_WAYPOINTS = 1; private static final int NEAR_BOTH = 2; private static final String PREF_DOWNLOAD_ALONG_TRACK_OSM = "downloadAlongTrack.download.osm"; private static final String PREF_DOWNLOAD_ALONG_TRACK_GPS = "downloadAlongTrack.download.gps"; private static final String PREF_DOWNLOAD_ALONG_TRACK_DISTANCE = "downloadAlongTrack.distance"; private static final String PREF_DOWNLOAD_ALONG_TRACK_AREA = "downloadAlongTrack.area"; private static final String PREF_DOWNLOAD_ALONG_TRACK_NEAR = "downloadAlongTrack.near"; private final transient GpxData data; /** * Constructs a new {@code DownloadAlongTrackAction} * @param data The GPX data used to download along */ public DownloadAlongTrackAction(GpxData data) { super(tr("Download from OSM along this track"), "downloadalongtrack", null, null, false); this.data = data; } PleaseWaitRunnable createTask() { final DownloadAlongPanel panel = new DownloadAlongPanel( PREF_DOWNLOAD_ALONG_TRACK_OSM, PREF_DOWNLOAD_ALONG_TRACK_GPS, PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, PREF_DOWNLOAD_ALONG_TRACK_AREA, PREF_DOWNLOAD_ALONG_TRACK_NEAR); if (0 != panel.showInDownloadDialog(tr("Download from OSM along this track"), HelpUtil.ht("/Action/DownloadAlongTrack"))) { return null; } final int near = panel.getNear(); /* * Find the average latitude for the data we're contemplating, so we can know how many * metres per degree of longitude we have. */ double latsum = 0; int latcnt = 0; if (near == NEAR_TRACK || near == NEAR_BOTH) { for (GpxTrack trk : data.tracks) { for (GpxTrackSegment segment : trk.getSegments()) { for (WayPoint p : segment.getWayPoints()) { latsum += p.getCoor().lat(); latcnt++; } } } } if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) { for (WayPoint p : data.waypoints) { latsum += p.getCoor().lat(); latcnt++; } } if (latcnt == 0) { return null; } double avglat = latsum / latcnt; double scale = Math.cos(Math.toRadians(avglat)); /* * Compute buffer zone extents and maximum bounding box size. Note that the maximum we * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as * soon as you touch any built-up area, that kind of bounding box will download forever * and then stop because it has more than 50k nodes. */ final double bufferDist = panel.getDistance(); final double maxArea = panel.getArea() / 10000.0 / scale; final double bufferY = bufferDist / 100000.0; final double bufferX = bufferY / scale; final int totalTicks = latcnt; // guess if a progress bar might be useful. final boolean displayProgress = totalTicks > 2000 && bufferY < 0.01; class CalculateDownloadArea extends PleaseWaitRunnable { private final Area a = new Area(); private boolean cancel; private int ticks; private final Rectangle2D r = new Rectangle2D.Double(); CalculateDownloadArea() { super(tr("Calculating Download Area"), displayProgress ? null : NullProgressMonitor.INSTANCE, false); } @Override protected void cancel() { cancel = true; } @Override protected void finish() { // Do nothing } @Override protected void afterFinish() { if (cancel) { return; } confirmAndDownloadAreas(a, maxArea, panel.isDownloadOsmData(), panel.isDownloadGpxData(), tr("Download from OSM along this track"), progressMonitor); } /** * increase tick count by one, report progress every 100 ticks */ private void tick() { ticks++; if (ticks % 100 == 0) { progressMonitor.worked(100); } } /** * calculate area for single, given way point and return new LatLon if the * way point has been used to modify the area. */ private LatLon calcAreaForWayPoint(WayPoint p, LatLon previous) { tick(); LatLon c = p.getCoor(); if (previous == null || c.greatCircleDistance(previous) > bufferDist) { // we add a buffer around the point. r.setRect(c.lon() - bufferX, c.lat() - bufferY, 2 * bufferX, 2 * bufferY); a.add(new Area(r)); return c; } return previous; } @Override protected void realRun() { progressMonitor.setTicksCount(totalTicks); /* * Collect the combined area of all gpx points plus buffer zones around them. We ignore * points that lie closer to the previous point than the given buffer size because * otherwise this operation takes ages. */ LatLon previous = null; if (near == NEAR_TRACK || near == NEAR_BOTH) { for (GpxTrack trk : data.tracks) { for (GpxTrackSegment segment : trk.getSegments()) { for (WayPoint p : segment.getWayPoints()) { if (cancel) { return; } previous = calcAreaForWayPoint(p, previous); } } } } if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) { for (WayPoint p : data.waypoints) { if (cancel) { return; } previous = calcAreaForWayPoint(p, previous); } } } } return new CalculateDownloadArea(); } @Override public void actionPerformed(ActionEvent e) { PleaseWaitRunnable task = createTask(); if (task != null) { Main.worker.submit(task); } } }