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