// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugin.download_along; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.Set; import javax.swing.JOptionPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.DownloadAlongAction; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongPanel; import org.openstreetmap.josm.gui.progress.NullProgressMonitor; import org.openstreetmap.josm.tools.Shortcut; class DownloadAlongWayAction extends DownloadAlongAction { private static final String PREF_DOWNLOAD_ALONG_WAY_DISTANCE = "downloadAlongWay.distance"; private static final String PREF_DOWNLOAD_ALONG_WAY_AREA = "downloadAlongWay.area"; private static final String PREF_DOWNLOAD_ALONG_WAY_OSM = "downloadAlongWay.download.osm"; private static final String PREF_DOWNLOAD_ALONG_WAY_GPS = "downloadAlongWay.download.gps"; DownloadAlongWayAction() { super(tr("Download along..."), "download_along", tr("Download OSM data along the selected ways."), Shortcut.registerShortcut("tools:download_along", tr("Tool: {0}", tr("Download Along")), KeyEvent.VK_D, Shortcut.ALT_SHIFT), true); } @Override public void actionPerformed(ActionEvent e) { Set<Way> selectedWays = OsmPrimitive.getFilteredSet(getLayerManager().getEditDataSet().getSelected(), Way.class); if (selectedWays.isEmpty()) { JOptionPane.showMessageDialog(Main.parent, tr("Please select 1 or more ways to download along")); return; } final DownloadAlongPanel panel = new DownloadAlongPanel( PREF_DOWNLOAD_ALONG_WAY_OSM, PREF_DOWNLOAD_ALONG_WAY_GPS, PREF_DOWNLOAD_ALONG_WAY_DISTANCE, PREF_DOWNLOAD_ALONG_WAY_AREA, null); if (0 != panel.showInDownloadDialog(tr("Download from OSM along selected ways"), HelpUtil.ht("/Tools/DownloadAlong"))) { return; } /* * 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; for (Way way : selectedWays) { for (Node n : way.getNodes()) { latsum += n.getCoor().lat(); latcnt++; } } 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. */ double buffer_dist = panel.getDistance(); double buffer_y = buffer_dist / 100000.0; double buffer_x = buffer_y / scale; double max_area = panel.getArea() / 10000.0 / scale; Area a = new Area(); Rectangle2D r = new Rectangle2D.Double(); /* * 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; for (Way way : selectedWays) { for (Node p : way.getNodes()) { LatLon c = p.getCoor(); ArrayList<LatLon> intermediateNodes = new ArrayList<>(); if (previous != null && c.greatCircleDistance(previous) > buffer_dist) { Double d = c.greatCircleDistance(previous) / buffer_dist; int nbNodes = d.intValue(); Main.info(tr("{0} intermediate nodes to download.", nbNodes)); Main.info(tr("between {0} {1} and {2} {3}", c.lat(), c.lon(), previous.lat(), previous.lon())); for (int i = 1; i < nbNodes; i++) { intermediateNodes.add(new LatLon(previous.lat() + (i * (c.lat() - previous.lat()) / (nbNodes + 1)), previous.lon() + (i * (c.lon() - previous.lon()) / (nbNodes + 1)))); Main.info(tr(" adding {0} {1}", previous.lat() + (i * (c.lat() - previous.lat()) / (nbNodes + 1)), previous.lon() + (i * (c.lon() - previous.lon()) / (nbNodes + 1)))); } } intermediateNodes.add(c); for (LatLon d : intermediateNodes) { if (previous == null || d.greatCircleDistance(previous) > buffer_dist) { // we add a buffer around the point. r.setRect(d.lon() - buffer_x, d.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y); a.add(new Area(r)); previous = d; } } previous = c; } } confirmAndDownloadAreas(a, max_area, panel.isDownloadOsmData(), panel.isDownloadGpxData(), tr("Download from OSM along selected ways"), NullProgressMonitor.INSTANCE); } @Override protected void updateEnabledState() { if (getLayerManager().getEditDataSet() == null) { setEnabled(false); } else { updateEnabledState(getLayerManager().getEditDataSet().getSelected()); } } @Override protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { setEnabled(selection.stream().anyMatch(Way.class::isInstance)); } }