Ticket #17551: 17551-v2.patch

File 17551-v2.patch, 23.6 KB (added by GerdP, 5 years ago)
  • src/org/openstreetmap/josm/actions/DownloadAlongAction.java

     
    55
    66import java.awt.GraphicsEnvironment;
    77import java.awt.GridBagLayout;
     8import java.awt.event.ActionEvent;
    89import java.awt.geom.Area;
     10import java.awt.geom.Path2D;
     11import java.awt.geom.PathIterator;
    912import java.awt.geom.Rectangle2D;
    1013import java.util.ArrayList;
    1114import java.util.Collection;
     
    1720import javax.swing.JPanel;
    1821
    1922import org.openstreetmap.josm.actions.downloadtasks.DownloadTaskList;
     23import org.openstreetmap.josm.data.coor.LatLon;
    2024import org.openstreetmap.josm.gui.MainApplication;
    21 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     25import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     26import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongPanel;
     27import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    2228import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
    2329import org.openstreetmap.josm.tools.GBC;
     30import org.openstreetmap.josm.tools.Logging;
    2431import org.openstreetmap.josm.tools.Shortcut;
     32import org.openstreetmap.josm.tools.Utils;
    2533
    2634/**
    2735 * Abstract superclass of DownloadAlongTrackAction and DownloadAlongWayAction
     
    3038public abstract class DownloadAlongAction extends JosmAction {
    3139
    3240    /**
     41     * Sub classes must override this method.
     42     * @return the task to start or null if nothing to do
     43     */
     44    protected abstract PleaseWaitRunnable createTask();
     45
     46    /**
    3347     * Constructs a new {@code DownloadAlongAction}
    3448     * @param name the action's text as displayed in the menu
    3549     * @param iconName the filename of the icon to use
     
    97111     * @param osmDownload Set to true if OSM data should be downloaded
    98112     * @param gpxDownload Set to true if GPX data should be downloaded
    99113     * @param title the title string for the confirmation dialog
    100      * @param progressMonitor the progress monitor
    101114     */
    102     protected static void confirmAndDownloadAreas(Area a, double maxArea, boolean osmDownload, boolean gpxDownload, String title,
    103             ProgressMonitor progressMonitor) {
     115    protected static void confirmAndDownloadAreas(Area a, double maxArea, boolean osmDownload, boolean gpxDownload, String title) {
    104116        List<Rectangle2D> toDownload = new ArrayList<>();
    105117        addToDownload(a, a.getBounds(), toDownload, maxArea);
    106118        if (toDownload.isEmpty()) {
     
    118130        final Future<?> future = new DownloadTaskList().download(false, toDownload, osmDownload, gpxDownload, monitor);
    119131        waitFuture(future, monitor);
    120132    }
     133
     134    /**
     135     * Calculate list of points between two given points so that the distance between two consecutive points is below a limit.
     136     * @param p1 first point or null
     137     * @param p2 second point (must not be null)
     138     * @param bufferDist the maximum distance
     139     * @return a list of points with at least one point (p2) and maybe more.
     140     */
     141    protected static Collection<LatLon> calcBetweenPoints(LatLon p1, LatLon p2, double bufferDist) {
     142        ArrayList<LatLon> intermediateNodes = new ArrayList<>();
     143        intermediateNodes.add(p2);
     144        if (p1 != null && p2.greatCircleDistance(p1) > bufferDist) {
     145            Double d = p2.greatCircleDistance(p1) / bufferDist;
     146            int nbNodes = d.intValue();
     147            if (Logging.isDebugEnabled()) {
     148                Logging.debug(tr("{0} intermediate nodes to download.", nbNodes));
     149                Logging.debug(tr("between {0} {1} and {2} {3}", p2.lat(), p2.lon(), p1.lat(), p1.lon()));
     150            }
     151            double latStep = (p2.lat() - p1.lat()) / (nbNodes + 1);
     152            double lonStep = (p2.lon() - p1.lon()) / (nbNodes + 1);
     153            for (int i = 1; i <= nbNodes; i++) {
     154                LatLon intermediate = new LatLon(p1.lat() + i * latStep, p1.lon() + i * lonStep);
     155                intermediateNodes.add(intermediate);
     156                if (Logging.isTraceEnabled()) {
     157                    Logging.trace(tr("  adding {0} {1}", intermediate.lat(), intermediate.lon()));
     158                }
     159            }
     160        }
     161        return intermediateNodes;
     162    }
     163
     164    /**
     165     * Create task that downloads areas along the given path using the values specified in the panel.
     166     * @param alongPath the path along which the areas are to be downloaded
     167     * @param panel the panel that was displayed to the user and now contains his selections
     168     * @param confirmTitle the title to display in the confirmation panel
     169     * @return the task or null if canceled by user
     170     */
     171    protected PleaseWaitRunnable createCalcTask(Path2D alongPath, DownloadAlongPanel panel, String confirmTitle) {
     172        /*
     173         * Find the average latitude for the data we're contemplating, so we can know how many
     174         * metres per degree of longitude we have.
     175         */
     176        double latsum = 0;
     177        int latcnt = 0;
     178        final PathIterator pit = alongPath.getPathIterator(null);
     179        final double[] res = new double[6];
     180        while (!pit.isDone()) {
     181            int type = pit.currentSegment(res);
     182            if (type == PathIterator.SEG_LINETO || type == PathIterator.SEG_MOVETO) {
     183                latsum += res[1];
     184                latcnt++;
     185            }
     186            pit.next();
     187        }
     188        if (latcnt == 0) {
     189            return null;
     190        }
     191        final double avglat = latsum / latcnt;
     192        final double scale = Math.cos(Utils.toRadians(avglat));
     193
     194        /*
     195         * Compute buffer zone extents and maximum bounding box size. Note that the maximum we
     196         * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as
     197         * soon as you touch any built-up area, that kind of bounding box will download forever
     198         * and then stop because it has more than 50k nodes.
     199         */
     200        final double bufferDist = panel.getDistance();
     201        final double maxArea = panel.getArea() / 10000.0 / scale;
     202        final double bufferY = bufferDist / 100000.0;
     203        final double bufferX = bufferY / scale;
     204        final int totalTicks = latcnt;
     205        // guess if a progress bar might be useful.
     206        final boolean displayProgress = totalTicks > 20_000 && bufferY < 0.01;
     207
     208        class CalculateDownloadArea extends PleaseWaitRunnable {
     209
     210            private final Path2D downloadPath = new Path2D.Double();
     211            private boolean cancel;
     212            private int ticks;
     213            private final Rectangle2D r = new Rectangle2D.Double();
     214
     215            CalculateDownloadArea() {
     216                super(tr("Calculating Download Area"), displayProgress ? null : NullProgressMonitor.INSTANCE, false);
     217            }
     218
     219            @Override
     220            protected void cancel() {
     221                cancel = true;
     222            }
     223
     224            @Override
     225            protected void finish() {
     226                // Do nothing
     227            }
     228
     229            @Override
     230            protected void afterFinish() {
     231                if (cancel) {
     232                    return;
     233                }
     234                confirmAndDownloadAreas(new Area(downloadPath), maxArea, panel.isDownloadOsmData(), panel.isDownloadGpxData(),
     235                        confirmTitle);
     236            }
     237
     238            /**
     239             * increase tick count by one, report progress every 100 ticks
     240             */
     241            private void tick() {
     242                ticks++;
     243                if (ticks % 100 == 0) {
     244                    progressMonitor.worked(100);
     245                }
     246            }
     247
     248            /**
     249             * calculate area enclosing a single point
     250             */
     251            private void calcAreaForWayPoint(LatLon c) {
     252                r.setRect(c.lon() - bufferX, c.lat() - bufferY, 2 * bufferX, 2 * bufferY);
     253                downloadPath.append(r, false);
     254            }
     255
     256            @Override
     257            protected void realRun() {
     258                progressMonitor.setTicksCount(totalTicks);
     259                PathIterator pit = alongPath.getPathIterator(null);
     260                double[] res = new double[6];
     261                LatLon previous = null;
     262                while (!pit.isDone()) {
     263                    int type = pit.currentSegment(res);
     264                    LatLon c = new LatLon(res[1], res[0]);
     265                    if (type == PathIterator.SEG_LINETO) {
     266                        tick();
     267                        for (LatLon d : calcBetweenPoints(previous, c, bufferDist)) {
     268                            calcAreaForWayPoint(d);
     269                        }
     270                        previous = c;
     271                    } else if (type == PathIterator.SEG_MOVETO) {
     272                        previous = c;
     273                        tick();
     274                        calcAreaForWayPoint(c);
     275                    }
     276                    pit.next();
     277                }
     278            }
     279        }
     280
     281        return new CalculateDownloadArea();
     282    }
     283
     284    @Override
     285    public void actionPerformed(ActionEvent e) {
     286        PleaseWaitRunnable task = createTask();
     287        if (task != null) {
     288            MainApplication.worker.submit(task);
     289        }
     290    }
    121291}
  • src/org/openstreetmap/josm/actions/DownloadAlongWayAction.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.actions;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.awt.event.KeyEvent;
     7import java.awt.geom.Path2D;
     8import java.util.Collection;
     9
     10import javax.swing.JOptionPane;
     11
     12import org.openstreetmap.josm.data.osm.Node;
     13import org.openstreetmap.josm.data.osm.OsmPrimitive;
     14import org.openstreetmap.josm.data.osm.Way;
     15import org.openstreetmap.josm.gui.MainApplication;
     16import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     17import org.openstreetmap.josm.gui.help.HelpUtil;
     18import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongPanel;
     19import org.openstreetmap.josm.tools.Shortcut;
     20
     21/**
     22 * Calculate area around selected ways and split it into reasonable parts so
     23 * that they can be downloaded.
     24 *
     25 */
     26public class DownloadAlongWayAction extends DownloadAlongAction {
     27
     28    private static final String PREF_DOWNLOAD_ALONG_WAY_DISTANCE = "downloadAlongWay.distance";
     29    private static final String PREF_DOWNLOAD_ALONG_WAY_AREA = "downloadAlongWay.area";
     30
     31    private static final String PREF_DOWNLOAD_ALONG_WAY_OSM = "downloadAlongWay.download.osm";
     32    private static final String PREF_DOWNLOAD_ALONG_WAY_GPS = "downloadAlongWay.download.gps";
     33
     34    /**
     35     * Create new {@link DownloadAlongWayAction}.
     36     */
     37    public DownloadAlongWayAction() {
     38        super(tr("Download along..."), "download_along_way", tr("Download OSM data along the selected ways."),
     39                Shortcut.registerShortcut("tools:download_along", tr("Tool: {0}", tr("Download Along")),
     40                        KeyEvent.VK_D, Shortcut.ALT_SHIFT), true);
     41    }
     42
     43    @Override
     44    protected PleaseWaitRunnable createTask() {
     45        Collection<Way> selectedWays = getLayerManager().getEditDataSet().getSelectedWays();
     46
     47        if (selectedWays.isEmpty()) {
     48            JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Please select 1 or more ways to download along"));
     49            return null;
     50        }
     51
     52        final DownloadAlongPanel panel = new DownloadAlongPanel(
     53                PREF_DOWNLOAD_ALONG_WAY_OSM, PREF_DOWNLOAD_ALONG_WAY_GPS,
     54                PREF_DOWNLOAD_ALONG_WAY_DISTANCE, PREF_DOWNLOAD_ALONG_WAY_AREA, null);
     55
     56        if (0 != panel.showInDownloadDialog(tr("Download from OSM along selected ways"), HelpUtil.ht("/Tools/DownloadAlong"))) {
     57            return null;
     58        }
     59
     60        // Convert OSM ways to Path2D
     61        Path2D gpxPath = new Path2D.Double();
     62        for (Way way : selectedWays) {
     63            boolean first = true;
     64            for (Node p : way.getNodes()) {
     65                if (first) {
     66                    gpxPath.moveTo(p.lon(), p.lat());
     67                    first = false;
     68                } else {
     69                    gpxPath.lineTo(p.lon(), p.lat());
     70                }
     71            }
     72        }
     73        return createCalcTask(gpxPath, panel, tr("Download from OSM along selected ways"));
     74
     75    }
     76
     77    @Override
     78    protected void updateEnabledState() {
     79        if (getLayerManager().getEditDataSet() == null) {
     80            setEnabled(false);
     81        } else {
     82            updateEnabledState(getLayerManager().getEditDataSet().getSelected());
     83        }
     84    }
     85
     86    @Override
     87    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
     88        setEnabled(selection.stream().anyMatch(Way.class::isInstance));
     89    }
     90}
  • src/org/openstreetmap/josm/gui/MainMenu.java

     
    4242import org.openstreetmap.josm.actions.DialogsToggleAction;
    4343import org.openstreetmap.josm.actions.DistributeAction;
    4444import org.openstreetmap.josm.actions.DownloadAction;
     45import org.openstreetmap.josm.actions.DownloadAlongWayAction;
    4546import org.openstreetmap.josm.actions.DownloadNotesInViewAction;
    4647import org.openstreetmap.josm.actions.DownloadOsmInViewAction;
    4748import org.openstreetmap.josm.actions.DownloadPrimitiveAction;
    4849import org.openstreetmap.josm.actions.DownloadReferrersAction;
     50import org.openstreetmap.josm.actions.DrawBoundariesOfDownloadedDataAction;
    4951import org.openstreetmap.josm.actions.DuplicateAction;
    5052import org.openstreetmap.josm.actions.ExitAction;
    5153import org.openstreetmap.josm.actions.ExpertToggleAction;
     
    5254import org.openstreetmap.josm.actions.FollowLineAction;
    5355import org.openstreetmap.josm.actions.FullscreenToggleAction;
    5456import org.openstreetmap.josm.actions.GpxExportAction;
    55 import org.openstreetmap.josm.actions.DrawBoundariesOfDownloadedDataAction;
    5657import org.openstreetmap.josm.actions.HelpAction;
    5758import org.openstreetmap.josm.actions.HistoryInfoAction;
    5859import org.openstreetmap.josm.actions.HistoryInfoWebAction;
     
    281282    public final CreateMultipolygonAction createMultipolygon = new CreateMultipolygonAction(false);
    282283    /** Tools / Update multipolygon */
    283284    public final CreateMultipolygonAction updateMultipolygon = new CreateMultipolygonAction(true);
     285    /** Tools / Download along way */
     286    public final DownloadAlongWayAction downloadAlongWay = new DownloadAlongWayAction();
    284287
    285288    /* Selection menu */
    286289    /** Selection / Select All */
     
    815818        add(toolsMenu, joinAreas);
    816819        add(toolsMenu, createMultipolygon);
    817820        add(toolsMenu, updateMultipolygon);
     821        add(toolsMenu, downloadAlongWay);
    818822
    819823        // -- changeset manager toggle action
    820824        final JCheckBoxMenuItem mi = MainMenu.addWithCheckbox(windowMenu, changesetManager,
  • src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.event.ActionEvent;
    7 import java.awt.geom.Area;
    86import java.awt.geom.Path2D;
    9 import java.awt.geom.Rectangle2D;
    107
    118import org.openstreetmap.josm.actions.DownloadAlongAction;
    12 import org.openstreetmap.josm.data.coor.LatLon;
    139import org.openstreetmap.josm.data.gpx.GpxData;
    1410import org.openstreetmap.josm.data.gpx.GpxTrack;
    1511import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
    1612import org.openstreetmap.josm.data.gpx.WayPoint;
    17 import org.openstreetmap.josm.gui.MainApplication;
    1813import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    1914import org.openstreetmap.josm.gui.help.HelpUtil;
    20 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    21 import org.openstreetmap.josm.tools.Utils;
    2215
    2316/**
    2417 * Action that issues a series of download requests to the API, following the GPX track.
     
    5043        this.data = data;
    5144    }
    5245
    53     PleaseWaitRunnable createTask() {
     46    @Override
     47    protected PleaseWaitRunnable createTask() {
    5448        final DownloadAlongPanel panel = new DownloadAlongPanel(
    5549                PREF_DOWNLOAD_ALONG_TRACK_OSM, PREF_DOWNLOAD_ALONG_TRACK_GPS,
    5650                PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, PREF_DOWNLOAD_ALONG_TRACK_AREA, PREF_DOWNLOAD_ALONG_TRACK_NEAR);
     
    6155
    6256        final int near = panel.getNear();
    6357
    64         /*
    65          * Find the average latitude for the data we're contemplating, so we can know how many
    66          * metres per degree of longitude we have.
    67          */
    68         double latsum = 0;
    69         int latcnt = 0;
     58        // Convert the GPX data into a Path2D.
     59        Path2D gpxPath = new Path2D.Double();
    7060        if (near == NEAR_TRACK || near == NEAR_BOTH) {
    7161            for (GpxTrack trk : data.tracks) {
    7262                for (GpxTrackSegment segment : trk.getSegments()) {
     63                    boolean first = true;
    7364                    for (WayPoint p : segment.getWayPoints()) {
    74                         latsum += p.lat();
    75                         latcnt++;
     65                        if (first) {
     66                            gpxPath.moveTo(p.lon(), p.lat());
     67                            first = false;
     68                        } else {
     69                            gpxPath.lineTo(p.lon(), p.lat());
     70                        }
    7671                    }
    7772                }
    7873            }
     
    7974        }
    8075        if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {
    8176            for (WayPoint p : data.waypoints) {
    82                 latsum += p.getCoor().lat();
    83                 latcnt++;
     77                gpxPath.moveTo(p.lon(), p.lat());
     78                gpxPath.closePath();
    8479            }
    8580        }
    86         if (latcnt == 0) {
    87             return null;
    88         }
    89         double avglat = latsum / latcnt;
    90         double scale = Math.cos(Utils.toRadians(avglat));
    91         /*
    92          * Compute buffer zone extents and maximum bounding box size. Note that the maximum we
    93          * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as
    94          * soon as you touch any built-up area, that kind of bounding box will download forever
    95          * and then stop because it has more than 50k nodes.
    96          */
    97         final double bufferDist = panel.getDistance();
    98         final double maxArea = panel.getArea() / 10000.0 / scale;
    99         final double bufferY = bufferDist / 100000.0;
    100         final double bufferX = bufferY / scale;
    101         final int totalTicks = latcnt;
    102         // guess if a progress bar might be useful.
    103         final boolean displayProgress = totalTicks > 200_000 && bufferY < 0.01;
    104 
    105         class CalculateDownloadArea extends PleaseWaitRunnable {
    106 
    107             private final Path2D path = new Path2D.Double();
    108             private boolean cancel;
    109             private int ticks;
    110             private final Rectangle2D r = new Rectangle2D.Double();
    111 
    112             CalculateDownloadArea() {
    113                 super(tr("Calculating Download Area"), displayProgress ? null : NullProgressMonitor.INSTANCE, false);
    114             }
    115 
    116             @Override
    117             protected void cancel() {
    118                 cancel = true;
    119             }
    120 
    121             @Override
    122             protected void finish() {
    123                 // Do nothing
    124             }
    125 
    126             @Override
    127             protected void afterFinish() {
    128                 if (cancel) {
    129                     return;
    130                 }
    131                 confirmAndDownloadAreas(new Area(path), maxArea, panel.isDownloadOsmData(), panel.isDownloadGpxData(),
    132                         tr("Download from OSM along this track"), progressMonitor);
    133             }
    134 
    135             /**
    136              * increase tick count by one, report progress every 100 ticks
    137              */
    138             private void tick() {
    139                 ticks++;
    140                 if (ticks % 100 == 0) {
    141                     progressMonitor.worked(100);
    142                 }
    143             }
    144 
    145             /**
    146              * calculate area for single, given way point and return new LatLon if the
    147              * way point has been used to modify the area.
    148              */
    149             private LatLon calcAreaForWayPoint(WayPoint p, LatLon previous) {
    150                 tick();
    151                 LatLon c = p.getCoor();
    152                 if (previous == null || c.greatCircleDistance(previous) > bufferDist) {
    153                     // we add a buffer around the point.
    154                     r.setRect(c.lon() - bufferX, c.lat() - bufferY, 2 * bufferX, 2 * bufferY);
    155                     path.append(r, false);
    156                     return c;
    157                 }
    158                 return previous;
    159             }
    160 
    161             @Override
    162             protected void realRun() {
    163                 progressMonitor.setTicksCount(totalTicks);
    164                 /*
    165                  * Collect the combined area of all gpx points plus buffer zones around them. We ignore
    166                  * points that lie closer to the previous point than the given buffer size because
    167                  * otherwise this operation takes ages.
    168                  */
    169                 LatLon previous = null;
    170                 if (near == NEAR_TRACK || near == NEAR_BOTH) {
    171                     for (GpxTrack trk : data.tracks) {
    172                         for (GpxTrackSegment segment : trk.getSegments()) {
    173                             for (WayPoint p : segment.getWayPoints()) {
    174                                 if (cancel) {
    175                                     return;
    176                                 }
    177                                 previous = calcAreaForWayPoint(p, previous);
    178                             }
    179                         }
    180                     }
    181                 }
    182                 if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {
    183                     for (WayPoint p : data.waypoints) {
    184                         if (cancel) {
    185                             return;
    186                         }
    187                         previous = calcAreaForWayPoint(p, previous);
    188                     }
    189                 }
    190             }
    191         }
    192 
    193         return new CalculateDownloadArea();
     81        return createCalcTask(gpxPath, panel, tr("Download from OSM along this track"));
    19482    }
    195 
    196     @Override
    197     public void actionPerformed(ActionEvent e) {
    198         PleaseWaitRunnable task = createTask();
    199         if (task != null) {
    200             MainApplication.worker.submit(task);
    201         }
    202     }
    20383}
  • src/org/openstreetmap/josm/plugins/PluginHandler.java

     
    145145            new DeprecatedPlugin("kendzi3d_Improved_by_Andrei", tr("no longer required")),
    146146            new DeprecatedPlugin("videomapping", tr("no longer required")),
    147147            new DeprecatedPlugin("public_transport_layer", tr("replaced by new {0} plugin", "pt_assistant")),
    148             new DeprecatedPlugin("lakewalker", tr("replaced by new {0} plugin", "scanaerial"))
     148            new DeprecatedPlugin("lakewalker", tr("replaced by new {0} plugin", "scanaerial")),
     149            new DeprecatedPlugin("download_along", inCore)
    149150        );
    150151    }
    151152