Ticket #17131: GpxDistance_v8.patch

File GpxDistance_v8.patch, 17.6 KB (added by taylor.smock, 5 years ago)

Significantly improve performance by not rebuilding the same fake gpxlayer everytime -- it now stores the fake layer in the GeoImageLayer.

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

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.ArrayList;
     5import java.util.List;
     6import java.util.stream.Collectors;
     7
     8import org.openstreetmap.josm.data.coor.EastNorth;
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.data.osm.Node;
     11import org.openstreetmap.josm.data.osm.OsmPrimitive;
     12import org.openstreetmap.josm.data.osm.Relation;
     13import org.openstreetmap.josm.data.osm.Way;
     14import org.openstreetmap.josm.gui.MainApplication;
     15import org.openstreetmap.josm.gui.layer.GpxLayer;
     16import org.openstreetmap.josm.gui.layer.Layer;
     17import org.openstreetmap.josm.gui.layer.LayerManager;
     18import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
     19import org.openstreetmap.josm.tools.Geometry;
     20
     21/**
     22 * A class to find the distance between an OsmPrimitive and a GPX point
     23 *
     24 * @author Taylor Smock
     25 *
     26 */
     27public final class GpxDistance {
     28    private GpxDistance() {
     29        // This class should not be instantiated
     30    }
     31
     32    /**
     33     * @param p The OsmPrimitive that we are checking the maximum distance for
     34     * @param maximumDistance The maximum distance from a GPX point to the OsmPrimitive
     35     * @return true if the distance to the closest GPX point is lower than maximumDistance
     36     * @since xxx
     37     */
     38    public static boolean isCloseTo(OsmPrimitive p, double maximumDistance) {
     39        double distance = getLowestDistance(p);
     40        return (distance < maximumDistance);
     41    }
     42
     43    /**
     44     * Get the lowest distance from the layers to p
     45     * @param p OsmPrimitive from which to get the lowest distance to a GPX point
     46     * @return the lowest distance from any GpxLayer to p.
     47     * @since xxx
     48     */
     49    public static double getLowestDistance(OsmPrimitive p) {
     50        LayerManager layerManager = MainApplication.getLayerManager();
     51        List<Layer> layers = layerManager.getLayers();
     52        double distance = Double.MAX_VALUE;
     53        for (Layer layer : layers) {
     54            double tdistance = getLowestDistance(p, layer);
     55            if (tdistance < distance) distance = tdistance;
     56        }
     57        return distance;
     58    }
     59
     60    /**
     61     * Find the distance between a point and a layer of surveyed points
     62     * @param p OsmPrimitive from which to get the lowest distance to a GPX point
     63     * @param layer Layer from which to get the GPX points (currently, only GpxLayer is supported)
     64     * @return The shortest distance
     65     * @since xxx
     66     */
     67    public static double getLowestDistance(OsmPrimitive p, Layer layer) {
     68        if (layer instanceof GpxLayer || layer instanceof GeoImageLayer) {
     69            GpxData gpxData = null;
     70            if (layer instanceof GpxLayer) {
     71                GpxLayer gpxLayer = (GpxLayer) layer;
     72                gpxData = gpxLayer.data;
     73            } else if (layer instanceof GeoImageLayer) {
     74                GeoImageLayer tmp = (GeoImageLayer) layer;
     75                gpxData = tmp.getFauxGpxLayer().data;
     76            }
     77            if (gpxData == null) return Double.MAX_VALUE;
     78            List<WayPoint> trackPoints = gpxData.getTrackPoints().collect(Collectors.toList());
     79            double lowestDistance = Double.MAX_VALUE;
     80            for (WayPoint trackPoint : trackPoints) {
     81                double distance = getDistance(p, trackPoint);
     82                if (distance >= 0.0 && distance < lowestDistance) lowestDistance = distance;
     83            }
     84            return lowestDistance;
     85        }
     86        return Double.MAX_VALUE;
     87    }
     88
     89    /**
     90     * Get the distance between an object and a waypoint
     91     * @param p OsmPrimitive to get the distance to the WayPoint
     92     * @param waypoint WayPoint to get the distance from
     93     * @return The shortest distance between p and waypoint
     94     * @since xxx
     95     */
     96    public static double getDistance(OsmPrimitive p, WayPoint waypoint) {
     97        if (p instanceof Node) {
     98            return getDistanceNode((Node) p, waypoint);
     99        } else if (p instanceof Way) {
     100            return getDistanceWay((Way) p, waypoint);
     101        } else if (p instanceof Relation) {
     102            return getDistanceRelation((Relation) p, waypoint);
     103        }
     104        return Double.MAX_VALUE;
     105    }
     106
     107    /**
     108     * Get the shortest distance between a relation and a waypoint
     109     * @param relation Relation to get the distance from
     110     * @param waypoint WayPoint to get the distance to
     111     * @return The distance between the relation and the waypoint
     112     * @since xxx
     113     */
     114    public static double getDistanceRelation(Relation relation, WayPoint waypoint) {
     115        double shortestDistance = Double.MAX_VALUE;
     116        List<Node> nodes = new ArrayList<>(relation.getMemberPrimitives(Node.class));
     117        List<Way> ways = new ArrayList<>(relation.getMemberPrimitives(Way.class));
     118        List<Relation> relations = new ArrayList<>(relation.getMemberPrimitives(Relation.class));
     119        if (nodes.isEmpty() && ways.isEmpty() && relations.isEmpty()) return Double.MAX_VALUE;
     120        for (Relation nrelation : relations) {
     121            double distance = getDistanceRelation(nrelation, waypoint);
     122            if (distance < shortestDistance) shortestDistance = distance;
     123        }
     124        for (Way way : ways) {
     125            double distance = getDistanceWay(way, waypoint);
     126            if (distance < shortestDistance) shortestDistance = distance;
     127        }
     128        for (Node node : nodes) {
     129            double distance = getDistanceNode(node, waypoint);
     130            if (distance < shortestDistance) shortestDistance = distance;
     131        }
     132        return shortestDistance;
     133    }
     134
     135    /**
     136     * Get the shortest distance between a way and a waypoint
     137     * @param way Way to get the distance from
     138     * @param waypoint WayPoint to get the distance to
     139     * @return The distance between the way and the waypoint
     140     * @since xxx
     141     */
     142    public static double getDistanceWay(Way way, WayPoint waypoint) {
     143        double shortestDistance = Double.MAX_VALUE;
     144        if (way == null || waypoint == null) return shortestDistance;
     145        LatLon llwaypoint = waypoint.getCoor();
     146        EastNorth enwaypoint = new EastNorth(llwaypoint.getY(), llwaypoint.getX());
     147        for (int i = 0; i < way.getNodesCount() - 1; i++) {
     148            double distance = Double.MAX_VALUE;
     149            LatLon llfirst = way.getNode(i).getCoor();
     150            LatLon llsecond = way.getNode(i + 1).getCoor();
     151            EastNorth first = new EastNorth(llfirst.getY(), llfirst.getX());
     152            EastNorth second = new EastNorth(llsecond.getY(), llsecond.getX());
     153            if (first.isValid() && second.isValid()) {
     154                EastNorth closestPoint = Geometry.closestPointToSegment(first, second, enwaypoint);
     155                distance = llwaypoint.greatCircleDistance(new LatLon(closestPoint.getX(), closestPoint.getY()));
     156            } else if (first.isValid() && !second.isValid()) {
     157                distance = getDistanceEastNorth(first, waypoint);
     158            } else if (!first.isValid() && second.isValid()) {
     159                distance = getDistanceEastNorth(second, waypoint);
     160            } else if (!first.isValid() && !second.isValid()) {
     161                distance = Double.MAX_VALUE;
     162            }
     163            if (distance < shortestDistance) shortestDistance = distance;
     164
     165        }
     166        return shortestDistance;
     167    }
     168
     169    /**
     170     * Get the distance between a node and a waypoint
     171     * @param node Node to get the distance from
     172     * @param waypoint WayPoint to get the distance to
     173     * @return The distance between the two points
     174     * @since xxx
     175     */
     176    public static double getDistanceNode(Node node, WayPoint waypoint) {
     177        if (node == null) return Double.MAX_VALUE;
     178        return getDistanceLatLon(node.getCoor(), waypoint);
     179    }
     180
     181    /**
     182     * Get the distance between coordinates (provided by EastNorth) and a waypoint
     183     * @param en The EastNorth to get the distance to
     184     * @param waypoint WayPoint to get the distance to
     185     * @return The distance between the two points
     186     * @since xxx
     187     */
     188    public static double getDistanceEastNorth(EastNorth en, WayPoint waypoint) {
     189        if (en == null || !en.isValid()) return Double.MAX_VALUE;
     190        return getDistanceLatLon(new LatLon(en.getY(), en.getX()), waypoint);
     191    }
     192
     193    /**
     194     * Get the distance between coordinates (latitude longitude) and a waypoint
     195     * @param latlon LatLon to get the distance from
     196     * @param waypoint WayPoint to get the distance to
     197     * @return The distance between the two points
     198     * @since xxx
     199     */
     200    public static double getDistanceLatLon(LatLon latlon, WayPoint waypoint) {
     201        if (latlon == null || waypoint == null || waypoint.getCoor() == null) return Double.MAX_VALUE;
     202        return waypoint.getCoor().greatCircleDistance(latlon);
     203    }
     204}
  • src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

     
    4242import org.openstreetmap.josm.data.Bounds;
    4343import org.openstreetmap.josm.data.ImageData;
    4444import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener;
     45import org.openstreetmap.josm.data.gpx.GpxData;
     46import org.openstreetmap.josm.data.gpx.WayPoint;
    4547import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    4648import org.openstreetmap.josm.gui.MainApplication;
    4749import org.openstreetmap.josm.gui.MapFrame;
     
    7577
    7678    private final ImageData data;
    7779    GpxLayer gpxLayer;
     80    GpxLayer gpxFauxLayer;
    7881
    7982    private final Icon icon = ImageProvider.get("dialogs/geoimage/photo-marker");
    8083    private final Icon selectedIcon = ImageProvider.get("dialogs/geoimage/photo-marker-selected");
     
    924927        return gpxLayer;
    925928    }
    926929
     930    /**
     931     * Returns a faux GPX layer built from the images or the associated GPX layer
     932     * @return A faux GPX layer or the associated GPX layer
     933     * @since xxx
     934     */
     935    public GpxLayer getFauxGpxLayer() {
     936        if (gpxLayer != null) return getGpxLayer();
     937        if (gpxFauxLayer == null) {
     938            GpxData gpxData = new GpxData();
     939            List<ImageEntry> imageList = data.getImages();
     940            for (ImageEntry image : imageList) {
     941                WayPoint twaypoint = new WayPoint(image.getPos());
     942                gpxData.addWaypoint(twaypoint);
     943            }
     944            gpxFauxLayer = new GpxLayer(gpxData);
     945        }
     946        return gpxFauxLayer;
     947    }
     948
    927949    @Override
    928950    public void jumpToNextMarker() {
    929951        data.selectNextImage();
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java

     
    2424import java.util.zip.CRC32;
    2525
    2626import org.openstreetmap.josm.data.coor.LatLon;
     27import org.openstreetmap.josm.data.gpx.GpxDistance;
    2728import org.openstreetmap.josm.data.osm.IPrimitive;
    2829import org.openstreetmap.josm.data.osm.Node;
     30import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2931import org.openstreetmap.josm.data.osm.Way;
    3032import org.openstreetmap.josm.data.osm.search.SearchCompiler;
    3133import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match;
     
    519521        }
    520522
    521523        /**
     524         * Returns the lowest distance between the OSM object and a GPX point
     525         * <p>
     526         * @param env the environment
     527         * @return the distance between the object and the closest gpx point or {@code Double.MAX_VALUE}
     528         * @since xxx
     529         */
     530        public static double gpx_distance(final Environment env) { // NO_UCD (unused code)
     531            if (env.osm instanceof OsmPrimitive) {
     532                return GpxDistance.getLowestDistance((OsmPrimitive) env.osm);
     533            } else {
     534                return Double.MAX_VALUE;
     535            }
     536        }
     537
     538        /**
    522539         * Determines whether the object has a tag with the given key.
    523540         * @param env the environment
    524541         * @param key the OSM key
  • test/unit/org/openstreetmap/josm/data/gpx/GpxDistanceTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import static org.junit.Assert.assertEquals;
     5
     6import org.junit.Rule;
     7import org.junit.Test;
     8import org.openstreetmap.josm.data.coor.EastNorth;
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.data.osm.Node;
     11import org.openstreetmap.josm.data.osm.Way;
     12import org.openstreetmap.josm.testutils.JOSMTestRules;
     13
     14import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     15
     16/**
     17 * Unit tests for class {@link GpxDistance}.
     18 */
     19public class GpxDistanceTest {
     20
     21    /**
     22     * Setup test.
     23     */
     24    @Rule
     25    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     26    public JOSMTestRules test = new JOSMTestRules().projection();
     27
     28    @Test
     29    /**
     30     * Unit test of method {@link GpxDistance#getDistanceWay}.
     31     */
     32    public void testGetDistanceWay() {
     33        Node node1 = new Node();
     34        Node node2 = new Node();
     35        Way way = new Way();
     36        node1.setCoor(new LatLon(0, 0));
     37        node2.setCoor(new LatLon(0, 1));
     38        way.addNode(node1);
     39        way.addNode(node2);
     40
     41        WayPoint waypoint = new WayPoint(new LatLon(1, 0));
     42
     43        double distance = GpxDistance.getDistanceWay(null, waypoint);
     44        assertEquals(Double.MAX_VALUE, distance, 0.1);
     45
     46        distance = GpxDistance.getDistanceWay(way, null);
     47        assertEquals(Double.MAX_VALUE, distance, 0.1);
     48
     49        distance = GpxDistance.getDistanceWay(null, null);
     50        assertEquals(Double.MAX_VALUE, distance, 0.1);
     51
     52        distance = GpxDistance.getDistanceWay(way, waypoint);
     53        /* 111319.49077 uses the WGS84/NAD38/GRS80 model for
     54         * the distance between (0, 0) and (0, 1) */
     55        assertEquals(111319.49077, distance, 0.1);
     56    }
     57
     58    /**
     59     * Unit test of method {@link GpxDistance#getDistanceNode}.
     60     */
     61    @Test
     62    public void testGetDistanceNode() {
     63        double distance = GpxDistance.getDistanceNode(null, null);
     64        assertEquals(Double.MAX_VALUE, distance, 0.1);
     65
     66        Node node = new Node();
     67        node.setCoor(new LatLon(0, 0));
     68        distance = GpxDistance.getDistanceNode(node, null);
     69        assertEquals(Double.MAX_VALUE, distance, 0.1);
     70
     71        WayPoint waypoint = new WayPoint(new LatLon(0, 0));
     72        distance = GpxDistance.getDistanceNode(node, waypoint);
     73        assertEquals(0.0, distance, 0.0001); // should be zero delta
     74
     75        distance = GpxDistance.getDistanceNode(null, waypoint);
     76        assertEquals(Double.MAX_VALUE, distance, 0.1);
     77
     78        node.setCoor(new LatLon(1, 0));
     79        distance = GpxDistance.getDistanceNode(node, waypoint);
     80        /* 111319.49077 uses the WGS84/NAD38/GRS80 model for
     81         * the distance between (0, 0) and (0, 1) */
     82        assertEquals(111319.49077, distance, 0.1);
     83    }
     84
     85    /**
     86     * Unit test of method {@link GpxDistance#getDistanceEastNorth}.
     87     */
     88    @Test
     89    public void testGetDistanceEastNorth() {
     90        double distance = GpxDistance.getDistanceEastNorth(null, null);
     91        assertEquals(Double.MAX_VALUE, distance, 0.1);
     92
     93        EastNorth en = new EastNorth(0, 0);
     94        distance = GpxDistance.getDistanceEastNorth(en, null);
     95        assertEquals(Double.MAX_VALUE, distance, 0.1);
     96
     97        WayPoint waypoint = new WayPoint(new LatLon(0, 0));
     98        distance = GpxDistance.getDistanceEastNorth(en, waypoint);
     99        assertEquals(0.0, distance, 0.0001); // should be zero delta
     100
     101        distance = GpxDistance.getDistanceEastNorth(null, waypoint);
     102        assertEquals(Double.MAX_VALUE, distance, 0.1);
     103
     104        en = new EastNorth(0, 1);
     105        distance = GpxDistance.getDistanceEastNorth(en, waypoint);
     106        /* 111319.49077 uses the WGS84/NAD38/GRS80 model for
     107         * the distance between (0, 0) and (0, 1) */
     108        assertEquals(111319.49077, distance, 0.1);
     109    }
     110
     111
     112    /**
     113     * Unit test of method {@link GpxDistance#getDistanceLatLon}.
     114     */
     115    @Test
     116    public void testGetDistanceLatLon() {
     117        double distance = GpxDistance.getDistanceLatLon(null, null);
     118        assertEquals(Double.MAX_VALUE, distance, 0.1);
     119
     120        LatLon ll = new LatLon(0, 0);
     121        distance = GpxDistance.getDistanceLatLon(ll, null);
     122        assertEquals(Double.MAX_VALUE, distance, 0.1);
     123
     124        WayPoint waypoint = new WayPoint(ll);
     125        distance = GpxDistance.getDistanceLatLon(ll, waypoint);
     126        assertEquals(0.0, distance, 0.0001); // should be zero delta
     127
     128        distance = GpxDistance.getDistanceLatLon(null, waypoint);
     129        assertEquals(Double.MAX_VALUE, distance, 0.1);
     130
     131        ll = new LatLon(0, 1);
     132        distance = GpxDistance.getDistanceLatLon(ll, waypoint);
     133        /* 111319.49077 uses the WGS84/NAD38/GRS80 model for
     134         * the distance between (0, 0) and (0, 1) */
     135        assertEquals(111319.49077, distance, 0.1);
     136    }
     137}