Ticket #17388: 17388.4.patch

File 17388.4.patch, 18.6 KB (added by taylor.smock, 3 years ago)

Avoid calculating validity until isValid is called, at which point caching may occur.

  • src/org/openstreetmap/josm/data/coor/EastNorth.java

     
    1212
    1313    private static final long serialVersionUID = 1L;
    1414
     15    /** Set on valid when the east north is valid */
     16    private static final byte IS_VALID = 1;
     17    /** Set on valid when east north is not valid */
     18    private static final byte IS_INVALID = 2;
    1519    /**
     20     * If {@link #IS_VALID}, return {#code true}. If {@link #IS_INVALID}, return {@code false}.
     21     * Otherwise, run {@link #updateValid}. Default value is {@code 0}.
     22     */
     23    private byte valid;
     24
     25    /**
    1626     * A zero constant
    1727     */
    1828    public static final EastNorth ZERO = new EastNorth(0, 0);
     
    153163     * @return true if east and north are different from Double.NaN and not infinite
    154164     */
    155165    public boolean isValid() {
    156         return !Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y);
     166        /* Most of the time, EastNorth should be valid. When it isn't cached, and not IS_INVALID,
     167         * update and cache the validity of the east north. */
     168        return this.valid == IS_VALID || (this.valid == 0 && updateValid());
    157169    }
    158170
     171    private boolean updateValid() {
     172        if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y)) {
     173            this.valid = IS_VALID;
     174        } else {
     175            this.valid = IS_INVALID;
     176        }
     177        return this.valid == IS_VALID;
     178    }
     179
    159180    /**
    160181     * Returns an EastNorth representing the this EastNorth rotated around
    161182     * a given EastNorth by a given angle
  • src/org/openstreetmap/josm/data/gpx/GpxDistance.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
    4 import org.openstreetmap.josm.data.osm.Node;
     4import java.util.OptionalDouble;
     5
     6import org.openstreetmap.josm.data.coor.ILatLon;
     7import org.openstreetmap.josm.data.coor.LatLon;
     8import org.openstreetmap.josm.data.osm.BBox;
    59import org.openstreetmap.josm.data.osm.OsmPrimitive;
     10import org.openstreetmap.josm.data.projection.Ellipsoid;
     11import org.openstreetmap.josm.data.projection.ProjectionRegistry;
     12import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
    613import org.openstreetmap.josm.tools.Geometry;
    714
    815/**
     
    2330     * @return The shortest distance
    2431     */
    2532    public static double getLowestDistance(OsmPrimitive p, GpxData gpxData) {
    26         return gpxData.getTrackPoints()
    27                 .mapToDouble(tp -> Geometry.getDistance(p, new Node(tp.getCoor())))
    28                 .filter(x -> x >= 0)
    29                 .min().orElse(Double.MAX_VALUE);
     33        OptionalDouble min = gpxData.getTrackPoints().map(waypoint -> waypoint.getEastNorth(ProjectionRegistry.getProjection()))
     34                .mapToDouble(tp -> Geometry.getDistanceSq(p, tp))
     35                .filter(x -> x >= 0).min();
     36        return min.isPresent() ? Math.sqrt(min.getAsDouble()) : Double.MAX_VALUE;
    3037    }
     38
     39    /**
     40     * Find the distance between a point and a dataset of surveyed points
     41     * @param p OsmPrimitive from which to get the lowest distance to a GPX point
     42     * @param gpxData Data from which to get the GPX points
     43     * @param maxDistance The maximum distance to look (LatLon distance)
     44     * @return The shortest distance
     45     */
     46    public static double getLowestDistance(OsmPrimitive p, GpxData gpxData, double maxDistance) {
     47        BBox onlyInside = p.getBBox();
     48        Ellipsoid ellipsoid = WGS84Datum.INSTANCE.getEllipsoid();
     49        ILatLon centroid = p.getBBox().getCenter();
     50        // This probably isn't the best method, but this should work. TODO
     51        double dLat = maxDistance / (111132.954 - 559.822 * Math.cos(2 * centroid.lat()) + 1.175 * Math.cos(4 * centroid.lat()));
     52        // This is currently ~4x the actual.
     53        double dLong = maxDistance / (Math.PI * ellipsoid.a * Math.cos(centroid.lat())
     54                / (180 * Math.sqrt(1 - ellipsoid.e2 * Math.pow(Math.sin(centroid.lat()), 2))));
     55        dLong = dLong / 4;
     56        LatLon lowerRight = new LatLon(onlyInside.getBottomRightLat() - dLat, onlyInside.getBottomRightLon() + dLong);
     57        LatLon upperLeft = new LatLon(onlyInside.getTopLeftLat() + dLat, onlyInside.getTopLeftLon() - dLong);
     58        onlyInside.add(lowerRight);
     59        onlyInside.add(upperLeft);
     60        // TODO if GpxData ever implements OsmData, use the searchNodes method.
     61        OptionalDouble min = gpxData.getTrackPoints().filter(onlyInside::bounds)
     62                .map(waypoint -> waypoint.getEastNorth(ProjectionRegistry.getProjection()))
     63                .mapToDouble(tp -> Geometry.getDistanceSq(p, tp))
     64                .filter(x -> x >= 0).min();
     65        return min.isPresent() ? Math.sqrt(min.getAsDouble()) : Double.MAX_VALUE;
     66    }
    3167}
  • src/org/openstreetmap/josm/data/osm/BBox.java

     
    236236     * Tests, whether the Point {@code c} lies within the bbox.
    237237     * @param c point
    238238     * @return {@code true} if {@code c} lies within the bbox
     239     * @deprecated use {@link #bounds(ILatLon)} instead. Casting may be required. This will eventually become unnecessary.
    239240     */
     241    @Deprecated
    240242    public boolean bounds(LatLon c) {
     243        return bounds((ILatLon) c);
     244    }
     245
     246    /**
     247     * Tests, whether the Point {@code c} lies within the bbox.
     248     * @param c point
     249     * @return {@code true} if {@code c} lies within the bbox
     250     * @since xxx
     251     */
     252    public boolean bounds(ILatLon c) {
    241253        return xmin <= c.lon() && xmax >= c.lon()
    242             && ymin <= c.lat() && ymax >= c.lat();
     254                && ymin <= c.lat() && ymax >= c.lat();
    243255    }
    244256
    245257    /**
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java

     
    513513     * @param env the environment
    514514     * @return the distance between the object and the closest gpx point or {@code Double.MAX_VALUE}
    515515     * @since 14802
     516     * @deprecated Use {@link #gpx_distance(Environment, float)} instead. There is a major performance improvement.
    516517     */
     518    @Deprecated
    517519    public static double gpx_distance(final Environment env) { // NO_UCD (unused code)
    518520        if (env.osm instanceof OsmPrimitive) {
    519521            return MainApplication.getLayerManager().getAllGpxData().stream()
     
    524526    }
    525527
    526528    /**
     529     * Returns the lowest distance between the OSM object and a GPX point
     530     * <p>
     531     * @param env the environment
     532     * @param maxDistance The maximum distance to consider
     533     * @return the distance between the object and the closest gpx point or {@code Double.MAX_VALUE}
     534     * @since xxx
     535     */
     536    public static double gpx_distance(final Environment env, final float maxDistance) { // NO_UCD (unused code)
     537        if (env.osm instanceof OsmPrimitive) {
     538            return MainApplication.getLayerManager().getAllGpxData().stream()
     539                    .mapToDouble(gpx -> GpxDistance.getLowestDistance((OsmPrimitive) env.osm, gpx, maxDistance))
     540                    .min().orElse(Double.MAX_VALUE);
     541        }
     542        return Double.MAX_VALUE;
     543    }
     544
     545    /**
    527546     * Determines whether the object has a tag with the given key.
    528547     * @param env the environment
    529548     * @param key the OSM key
  • src/org/openstreetmap/josm/tools/Geometry.java

     
    13031303        double lowestDistance = Double.MAX_VALUE;
    13041304        TreeSet<T> closest = new TreeSet<>();
    13051305        for (T primitive : primitives) {
    1306             double distance = getDistance(osm, primitive);
     1306            double distance = getDistanceSq(osm, primitive);
    13071307            if (Double.isNaN(distance)) continue;
    13081308            int comp = Double.compare(distance, lowestDistance);
    13091309            if (comp < 0) {
     
    13671367        double furthestDistance = Double.NEGATIVE_INFINITY;
    13681368        TreeSet<T> furthest = new TreeSet<>();
    13691369        for (T primitive : primitives) {
    1370             double distance = getDistance(osm, primitive);
     1370            double distance = getDistanceSq(osm, primitive);
    13711371            if (Double.isNaN(distance)) continue;
    13721372            int comp = Double.compare(distance, furthestDistance);
    13731373            if (comp > 0) {
     
    13941394     * @since 15035
    13951395     */
    13961396    public static double getDistance(OsmPrimitive one, OsmPrimitive two) {
     1397        double distance = getDistanceSq(one, two);
     1398        return distance != Double.MAX_VALUE && !Double.isNaN(distance) ? Math.sqrt(distance) : Double.NaN;
     1399    }
     1400
     1401    /**
     1402     * Get the distance squared between different {@link OsmPrimitive}s
     1403     * @param one The primitive to get the distance from
     1404     * @param two The primitive to get the distance to
     1405     * @return The distance squared between the primitives in meters
     1406     * (or the unit of the current projection, see {@link Projection}).
     1407     * May return {@link Double#NaN} if one of the primitives is incomplete.
     1408     *
     1409     * Note: The complexity is O(n*m), where (n,m) are the number of child
     1410     * objects the {@link OsmPrimitive}s have.
     1411     * @since xxx
     1412     */
     1413    public static double getDistanceSq(OsmPrimitive one, OsmPrimitive two) {
    13971414        double rValue = Double.MAX_VALUE;
    13981415        if (one == null || two == null || one.isIncomplete()
    13991416                || two.isIncomplete()) return Double.NaN;
    14001417        if (one instanceof Node && two instanceof Node) {
    1401             rValue = ((Node) one).getCoor().greatCircleDistance(((Node) two).getCoor());
     1418            rValue = Math.pow(((Node) one).getCoor().greatCircleDistance(((Node) two).getCoor()), 2);
    14021419        } else if (one instanceof Node && two instanceof Way) {
    1403             rValue = getDistanceWayNode((Way) two, (Node) one);
     1420            rValue = getDistanceWayEastNorthSq((Way) two, ((Node) one).getEastNorth());
    14041421        } else if (one instanceof Way && two instanceof Node) {
    1405             rValue = getDistanceWayNode((Way) one, (Node) two);
     1422            rValue = getDistanceWayEastNorthSq((Way) one, ((Node) two).getEastNorth());
    14061423        } else if (one instanceof Way && two instanceof Way) {
    1407             rValue = getDistanceWayWay((Way) one, (Way) two);
     1424            rValue = getDistanceWayWaySq((Way) one, (Way) two);
    14081425        } else if (one instanceof Relation && !(two instanceof Relation)) {
    14091426            for (OsmPrimitive osmPrimitive: ((Relation) one).getMemberPrimitives()) {
    1410                 double currentDistance = getDistance(osmPrimitive, two);
     1427                double currentDistance = getDistanceSq(osmPrimitive, two);
    14111428                if (currentDistance < rValue) rValue = currentDistance;
    14121429            }
    14131430        } else if (!(one instanceof Relation) && two instanceof Relation) {
    1414             rValue = getDistance(two, one);
     1431            rValue = getDistanceSq(two, one);
    14151432        } else if (one instanceof Relation && two instanceof Relation) {
    14161433            for (OsmPrimitive osmPrimitive1 : ((Relation) one).getMemberPrimitives()) {
    14171434                for (OsmPrimitive osmPrimitive2 : ((Relation) two).getMemberPrimitives()) {
    1418                     double currentDistance = getDistance(osmPrimitive1, osmPrimitive2);
     1435                    double currentDistance = getDistanceSq(osmPrimitive1, osmPrimitive2);
    14191436                    if (currentDistance < rValue) rValue = currentDistance;
    14201437                }
    14211438            }
    14221439        }
    1423         return rValue != Double.MAX_VALUE ? rValue : Double.NaN;
     1440        return rValue;
    14241441    }
    14251442
    14261443    /**
     1444     * Get the distance between a {@link OsmPrimitive} and a position
     1445     * @param one The primitive to get the distance from
     1446     * @param two The EastNorth to get the distance to
     1447     * @return The distance between the objects in meters
     1448     * (or the unit of the current projection, see {@link Projection}).
     1449     * May return {@link Double#NaN} if one of the objects is incomplete.
     1450     *
     1451     * Note: The complexity is O(n*m), where (n,m) are the number of child
     1452     * objects the {@link OsmPrimitive} have.
     1453     * @since xxx
     1454     */
     1455    public static double getDistanceSq(OsmPrimitive one, EastNorth two) {
     1456        double rValue = Double.MAX_VALUE;
     1457        if (one == null || two == null || one.isIncomplete()
     1458                || !two.isValid()) return Double.NaN;
     1459        if (one instanceof Node) {
     1460            rValue = two.distanceSq(((Node) one).getEastNorth());
     1461        } else if (one instanceof Way) {
     1462            rValue = getDistanceWayEastNorthSq((Way) one, two);
     1463        } else if (one instanceof Relation) {
     1464            for (OsmPrimitive osmPrimitive: ((Relation) one).getMemberPrimitives()) {
     1465                double currentDistance = getDistanceSq(osmPrimitive, two);
     1466                if (currentDistance < rValue) rValue = currentDistance;
     1467            }
     1468        }
     1469        return rValue;
     1470    }
     1471
     1472    /**
    14271473     * Get the distance between a way and a node
    14281474     * @param way The way to get the distance from
    14291475     * @param node The node to get the distance to
     
    14351481    public static double getDistanceWayNode(Way way, Node node) {
    14361482        if (way == null || node == null || way.getNodesCount() < 2 || !node.isLatLonKnown())
    14371483            return Double.NaN;
     1484        double distanceSq = getDistanceWayEastNorthSq(way, node.getEastNorth());
     1485        return distanceSq != Double.MAX_VALUE && !Double.isNaN(distanceSq) ? Math.sqrt(distanceSq) : distanceSq;
     1486    }
    14381487
     1488    /**
     1489     * Get the distance between a way and a node squared
     1490     * @param way The way to get the distance from
     1491     * @param eastNorth The EastNorth to get the distance to
     1492     * @return The distance between the {@code way} and the {@code eastnorth} in
     1493     * meters (or the unit of the current projection, see {@link Projection}).
     1494     * May return {@link Double#NaN} if the primitives are incomplete.
     1495     * @since xxx
     1496     */
     1497    public static double getDistanceWayEastNorthSq(Way way, EastNorth eastNorth) {
     1498        if (way == null || eastNorth == null || way.getNodesCount() < 2 || !eastNorth.isValid())
     1499            return Double.NaN;
     1500
    14391501        double smallest = Double.MAX_VALUE;
    1440         EastNorth en0 = node.getEastNorth();
    14411502        // go through the nodes as if they were paired
    14421503        Iterator<Node> iter = way.getNodes().iterator();
    14431504        EastNorth en1 = iter.next().getEastNorth();
    14441505        while (iter.hasNext()) {
    14451506            EastNorth en2 = iter.next().getEastNorth();
    1446             double distance = getSegmentNodeDistSq(en1, en2, en0);
     1507            double distance = getSegmentNodeDistSq(en1, en2, eastNorth);
    14471508            if (distance < smallest)
    14481509                smallest = distance;
    14491510            en1 = en2;
    14501511        }
    1451         return smallest != Double.MAX_VALUE ? Math.sqrt(smallest) : Double.NaN;
     1512        return smallest != Double.MAX_VALUE ? smallest : Double.NaN;
    14521513    }
    14531514
    14541515    /**
     
    14921553     * @since 15035
    14931554     */
    14941555    public static double getDistanceWayWay(Way w1, Way w2) {
     1556        double distance = getDistanceWayWaySq(w1, w2);
     1557        return distance != Double.MAX_VALUE && !Double.isNaN(distance) ? Math.sqrt(distance) : Double.NaN;
     1558    }
     1559
     1560    /**
     1561     *
     1562     * Get the distance squared between different ways. Iterates over the nodes of the ways, complexity is O(n*m)
     1563     * (n,m giving the number of nodes)
     1564     * @param w1 The first {@link Way}
     1565     * @param w2 The second {@link Way}
     1566     * @return The shortest distance squared between the ways in meters
     1567     * (or the unit of the current projection, see {@link Projection}).
     1568     * May return {@link Double#NaN}.
     1569     * @since xxx
     1570     */
     1571    public static double getDistanceWayWaySq(Way w1, Way w2) {
    14951572        if (w1 == null || w2 == null || w1.getNodesCount() < 2 || w2.getNodesCount() < 2)
    14961573            return Double.NaN;
    14971574        double rValue = Double.MAX_VALUE;
     
    15031580            Node w2N1 = iter2.next();
    15041581            while (iter2.hasNext()) {
    15051582                Node w2N2 = iter2.next();
    1506                 double distance = getDistanceSegmentSegment(w1N1, w1N2, w2N1, w2N2);
     1583                double distance = getDistanceSegmentSegmentSq(w1N1, w1N2, w2N1, w2N2);
    15071584                if (distance < rValue)
    15081585                    rValue = distance;
    15091586                w2N1 = w2N2;
     
    15381615     * @since 15035
    15391616     */
    15401617    public static double getDistanceSegmentSegment(Node ws1Node1, Node ws1Node2, Node ws2Node1, Node ws2Node2) {
     1618        double distance = getDistanceSegmentSegmentSq(ws1Node1, ws1Node2, ws2Node1, ws2Node2);
     1619        return distance != Double.MAX_VALUE && !Double.isNaN(distance) ? Math.sqrt(distance) : Double.NaN;
     1620    }
     1621
     1622    /**
     1623     * Get the distance squared between different {@link WaySegment}s
     1624     * @param ws1Node1 The first node of the first WaySegment
     1625     * @param ws1Node2 The second node of the second WaySegment
     1626     * @param ws2Node1 The first node of the second WaySegment
     1627     * @param ws2Node2 The second node of the second WaySegment
     1628     * @return The distance squared between the two {@link WaySegment}s in meters
     1629     * (or the unit of the current projection, see {@link Projection}).
     1630     * May return {@link Double#NaN} or {@link Double#MAX_VALUE}.
     1631     * @since xxx
     1632     */
     1633    public static double getDistanceSegmentSegmentSq(Node ws1Node1, Node ws1Node2, Node ws2Node1, Node ws2Node2) {
    15411634        EastNorth enWs1Node1 = ws1Node1.getEastNorth();
    15421635        EastNorth enWs1Node2 = ws1Node2.getEastNorth();
    15431636        EastNorth enWs2Node1 = ws2Node1.getEastNorth();
     
    15511644        double dist2sq = getSegmentNodeDistSq(enWs1Node1, enWs1Node2, enWs2Node2);
    15521645        double dist3sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node1);
    15531646        double dist4sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node2);
    1554         double smallest = Math.min(Math.min(dist1sq, dist2sq), Math.min(dist3sq, dist4sq));
    1555         return smallest != Double.MAX_VALUE ? Math.sqrt(smallest) : Double.NaN;
     1647        return Math.min(Math.min(dist1sq, dist2sq), Math.min(dist3sq, dist4sq));
    15561648    }
    15571649
    15581650    /**