Ticket #17388: GpxDistance_speed_v2.patch

File GpxDistance_speed_v2.patch, 5.8 KB (added by taylor.smock, 7 years ago)

Working patch (VERY SLOW), but it does cut down on the iterations significantly in many cases (I don't know where the slowdown is).

  • src/org/openstreetmap/josm/data/gpx/GpxDistance.java

     
    22package org.openstreetmap.josm.data.gpx;
    33
    44import java.util.ArrayList;
     5import java.util.Collection;
     6import java.util.HashMap;
     7import java.util.HashSet;
     8import java.util.Iterator;
    59import java.util.List;
    610
     11import org.openstreetmap.josm.data.Bounds;
    712import org.openstreetmap.josm.data.coor.EastNorth;
    813import org.openstreetmap.josm.data.coor.LatLon;
    914import org.openstreetmap.josm.data.osm.Node;
     
    3035     * @return The shortest distance
    3136     */
    3237    public static double getLowestDistance(OsmPrimitive p, GpxData gpxData) {
     38        if (gpxData.getTrackCount() > 1) {
     39            gpxData = getRelevantData(p, gpxData);
     40        }
    3341        return gpxData.getTrackPoints()
    3442                .mapToDouble(tp -> getDistance(p, tp))
    3543                .filter(x -> x >= 0)
     
    3745    }
    3846
    3947    /**
     48     * Get tracks that are relevant to an OsmPrimitive
     49     * @param p OsmPrimitive that we need relevant tracks for
     50     * @param gpxData Data that has the tracks
     51     * @return {@code HashSet} of GpxTracks that include the primitive (or are
     52     * the closest) or overlap with the the closest tracks.
     53     *
     54     * @since xxx
     55     */
     56    public static GpxData getRelevantData(OsmPrimitive p, GpxData gpxData) {
     57        HashSet<GpxTrack> relevantTracks = new HashSet<>();
     58        Collection<GpxTrack> tracks = gpxData.getTracks();
     59        HashMap<GpxTrack, Double> trackDistances = new HashMap<>();
     60        for (GpxTrack track : tracks) {
     61            Bounds bounds = track.getBounds();
     62            double distance = getMinDistanceBounds(p, bounds);
     63            trackDistances.put(track, distance);
     64        }
     65        Iterator<GpxTrack> trackIterator = trackDistances.keySet().iterator();
     66        double minValue = trackDistances.values().stream().min(Double::compare).get();
     67        double furthestDistance = Double.MIN_VALUE;
     68        while (trackIterator.hasNext()) {
     69            GpxTrack track = trackIterator.next();
     70            if (trackDistances.getOrDefault(trackDistances, Double.MAX_VALUE).compareTo(minValue) == 0) {
     71                relevantTracks.add(track);
     72                double mdistance = getMaxDistanceBounds(p, track.getBounds());
     73                if (mdistance > furthestDistance) furthestDistance = mdistance;
     74            }
     75        }
     76        trackDistances.entrySet().removeIf((track) -> relevantTracks.contains(track));
     77        trackIterator = trackDistances.keySet().iterator();
     78        while (trackIterator.hasNext()) {
     79            GpxTrack track = trackIterator.next();
     80            double mdistance = getMinDistanceBounds(p, track.getBounds());
     81            if (mdistance < furthestDistance) {
     82                relevantTracks.add(track);
     83            }
     84        }
     85        GpxData rData = new GpxData();
     86        relevantTracks.forEach(track -> rData.addTrack(track));
     87        return rData;
     88    }
     89
     90    /**
    4091     * Get the distance between an object and a waypoint
    4192     * @param p OsmPrimitive to get the distance to the WayPoint
    4293     * @param waypoint WayPoint to get the distance from
     
    145196        if (latlon == null || waypoint == null || waypoint.getCoor() == null) return Double.MAX_VALUE;
    146197        return waypoint.getCoor().greatCircleDistance(latlon);
    147198    }
     199
     200    /**
     201     * @param p OsmPrimitive to get the lowest distance to its centroid
     202     * @param bounds The bounds with which we are getting the distance to
     203     * @return The distance to the bounds (0 if the OsmPrimitive is inside the bounds)
     204     */
     205    public static double getMinDistanceBounds(OsmPrimitive p, Bounds bounds) {
     206        return getDistanceBounds(p, bounds, true);
     207    }
     208
     209    /**
     210     * @param p OsmPrimitive to get the furthest distance to its centroid
     211     * @param bounds The bounds with which we are getting the distance to
     212     * @return The furthest distance to the bounds
     213     */
     214    public static double getMaxDistanceBounds(OsmPrimitive p, Bounds bounds) {
     215        return getDistanceBounds(p, bounds, false);
     216    }
     217
     218    private static double getDistanceBounds(OsmPrimitive p, Bounds bounds, boolean closest) {
     219        // If the primitive is within the bounds, return 0
     220        if (bounds.toBBox().intersects(p.getBBox()) && closest) return 0;
     221
     222        // The order the corners are added is important! When connected in order, they _must_ form a rectangle.
     223        ArrayList<Node> corners = new ArrayList<>();
     224        corners.add(new Node(bounds.getMax()));
     225        corners.add(new Node(new LatLon(bounds.getMaxLat(), bounds.getMinLon())));
     226        corners.add(new Node(bounds.getMin()));
     227        corners.add(new Node(new LatLon(bounds.getMinLat(), bounds.getMaxLon())));
     228
     229        ArrayList<Way> ways = new ArrayList<>();
     230        for (int i = 0; i < corners.size(); i++) {
     231            ways.add(new Way());
     232            ways.get(i).addNode(corners.get(i));
     233            // Doing % corners.size() avoids requiring an if statement to avoid overflow (which _is_ expected)
     234            ways.get(i).addNode(corners.get((i + 1) % corners.size()));
     235        }
     236        double distance;
     237        if (closest) distance = Double.MAX_VALUE;
     238        else distance = Double.MIN_VALUE;
     239        for (Way way : ways) {
     240            // Not the best of ideas, but there isn't a method implemented for two ways
     241            double tdistance = getDistance(way, new WayPoint(p.getBBox().getCenter()));
     242            if ((closest && tdistance < distance) || (!closest && tdistance > distance)) {
     243                distance = tdistance;
     244            }
     245        }
     246        return distance;
     247    }
    148248}