001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer.gpx; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.awt.geom.Area; 008import java.awt.geom.Rectangle2D; 009 010import org.openstreetmap.josm.Main; 011import org.openstreetmap.josm.actions.DownloadAlongAction; 012import org.openstreetmap.josm.data.coor.LatLon; 013import org.openstreetmap.josm.data.gpx.GpxData; 014import org.openstreetmap.josm.data.gpx.GpxTrack; 015import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 016import org.openstreetmap.josm.data.gpx.WayPoint; 017import org.openstreetmap.josm.gui.PleaseWaitRunnable; 018import org.openstreetmap.josm.gui.help.HelpUtil; 019import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 020 021/** 022 * Action that issues a series of download requests to the API, following the GPX track. 023 * 024 * @author fred 025 */ 026public class DownloadAlongTrackAction extends DownloadAlongAction { 027 028 private static final int NEAR_TRACK = 0; 029 private static final int NEAR_WAYPOINTS = 1; 030 private static final int NEAR_BOTH = 2; 031 032 private static final String PREF_DOWNLOAD_ALONG_TRACK_OSM = "downloadAlongTrack.download.osm"; 033 private static final String PREF_DOWNLOAD_ALONG_TRACK_GPS = "downloadAlongTrack.download.gps"; 034 035 private static final String PREF_DOWNLOAD_ALONG_TRACK_DISTANCE = "downloadAlongTrack.distance"; 036 private static final String PREF_DOWNLOAD_ALONG_TRACK_AREA = "downloadAlongTrack.area"; 037 private static final String PREF_DOWNLOAD_ALONG_TRACK_NEAR = "downloadAlongTrack.near"; 038 039 private final transient GpxData data; 040 041 /** 042 * Constructs a new {@code DownloadAlongTrackAction} 043 * @param data The GPX data used to download along 044 */ 045 public DownloadAlongTrackAction(GpxData data) { 046 super(tr("Download from OSM along this track"), "downloadalongtrack", null, null, false); 047 this.data = data; 048 } 049 050 @Override 051 public void actionPerformed(ActionEvent e) { 052 053 final DownloadAlongPanel panel = new DownloadAlongPanel( 054 PREF_DOWNLOAD_ALONG_TRACK_OSM, PREF_DOWNLOAD_ALONG_TRACK_GPS, 055 PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, PREF_DOWNLOAD_ALONG_TRACK_AREA, PREF_DOWNLOAD_ALONG_TRACK_NEAR); 056 057 if (0 != panel.showInDownloadDialog(tr("Download from OSM along this track"), HelpUtil.ht("/Action/DownloadAlongTrack"))) { 058 return; 059 } 060 061 final int near = panel.getNear(); 062 063 /* 064 * Find the average latitude for the data we're contemplating, so we can know how many 065 * metres per degree of longitude we have. 066 */ 067 double latsum = 0; 068 int latcnt = 0; 069 if (near == NEAR_TRACK || near == NEAR_BOTH) { 070 for (GpxTrack trk : data.tracks) { 071 for (GpxTrackSegment segment : trk.getSegments()) { 072 for (WayPoint p : segment.getWayPoints()) { 073 latsum += p.getCoor().lat(); 074 latcnt++; 075 } 076 } 077 } 078 } 079 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) { 080 for (WayPoint p : data.waypoints) { 081 latsum += p.getCoor().lat(); 082 latcnt++; 083 } 084 } 085 double avglat = latsum / latcnt; 086 double scale = Math.cos(Math.toRadians(avglat)); 087 /* 088 * Compute buffer zone extents and maximum bounding box size. Note that the maximum we 089 * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as 090 * soon as you touch any built-up area, that kind of bounding box will download forever 091 * and then stop because it has more than 50k nodes. 092 */ 093 final double buffer_dist = panel.getDistance(); 094 final double max_area = panel.getArea() / 10000.0 / scale; 095 final double buffer_y = buffer_dist / 100000.0; 096 final double buffer_x = buffer_y / scale; 097 final int totalTicks = latcnt; 098 // guess if a progress bar might be useful. 099 final boolean displayProgress = totalTicks > 2000 && buffer_y < 0.01; 100 101 class CalculateDownloadArea extends PleaseWaitRunnable { 102 103 private final Area a = new Area(); 104 private boolean cancel; 105 private int ticks; 106 private final Rectangle2D r = new Rectangle2D.Double(); 107 108 CalculateDownloadArea() { 109 super(tr("Calculating Download Area"), displayProgress ? null : NullProgressMonitor.INSTANCE, false); 110 } 111 112 @Override 113 protected void cancel() { 114 cancel = true; 115 } 116 117 @Override 118 protected void finish() { 119 } 120 121 @Override 122 protected void afterFinish() { 123 if (cancel) { 124 return; 125 } 126 confirmAndDownloadAreas(a, max_area, panel.isDownloadOsmData(), panel.isDownloadGpxData(), 127 tr("Download from OSM along this track"), progressMonitor); 128 } 129 130 /** 131 * increase tick count by one, report progress every 100 ticks 132 */ 133 private void tick() { 134 ticks++; 135 if (ticks % 100 == 0) { 136 progressMonitor.worked(100); 137 } 138 } 139 140 /** 141 * calculate area for single, given way point and return new LatLon if the 142 * way point has been used to modify the area. 143 */ 144 private LatLon calcAreaForWayPoint(WayPoint p, LatLon previous) { 145 tick(); 146 LatLon c = p.getCoor(); 147 if (previous == null || c.greatCircleDistance(previous) > buffer_dist) { 148 // we add a buffer around the point. 149 r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y); 150 a.add(new Area(r)); 151 return c; 152 } 153 return previous; 154 } 155 156 @Override 157 protected void realRun() { 158 progressMonitor.setTicksCount(totalTicks); 159 /* 160 * Collect the combined area of all gpx points plus buffer zones around them. We ignore 161 * points that lie closer to the previous point than the given buffer size because 162 * otherwise this operation takes ages. 163 */ 164 LatLon previous = null; 165 if (near == NEAR_TRACK || near == NEAR_BOTH) { 166 for (GpxTrack trk : data.tracks) { 167 for (GpxTrackSegment segment : trk.getSegments()) { 168 for (WayPoint p : segment.getWayPoints()) { 169 if (cancel) { 170 return; 171 } 172 previous = calcAreaForWayPoint(p, previous); 173 } 174 } 175 } 176 } 177 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) { 178 for (WayPoint p : data.waypoints) { 179 if (cancel) { 180 return; 181 } 182 previous = calcAreaForWayPoint(p, previous); 183 } 184 } 185 } 186 } 187 188 Main.worker.submit(new CalculateDownloadArea()); 189 } 190}