Changeset 15035 in josm for trunk/src/org


Ignore:
Timestamp:
2019-05-02T06:55:14+02:00 (6 years ago)
Author:
GerdP
Message:

fix #17616: Add new functions to Geometry.java to find distances between primitives
Patch 17616_v12.patch by taylor.smock

Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxDistance.java

    r14802 r15035  
    3232    public static double getLowestDistance(OsmPrimitive p, GpxData gpxData) {
    3333        return gpxData.getTrackPoints()
    34                 .mapToDouble(tp -> getDistance(p, tp))
     34                .mapToDouble(tp -> Geometry.getDistance(p, new Node(tp.getCoor())))
    3535                .filter(x -> x >= 0)
    3636                .min().orElse(Double.MAX_VALUE);
     
    4242     * @param waypoint WayPoint to get the distance from
    4343     * @return The shortest distance between p and waypoint
     44     * @deprecated Use {@code Geometry.getDistance(p, new Node(waypoint.getCoor()))}
     45     * instead
    4446     */
     47    @Deprecated
    4548    public static double getDistance(OsmPrimitive p, WayPoint waypoint) {
    46         if (p instanceof Node) {
    47             return getDistanceNode((Node) p, waypoint);
    48         } else if (p instanceof Way) {
    49             return getDistanceWay((Way) p, waypoint);
    50         } else if (p instanceof Relation) {
    51             return getDistanceRelation((Relation) p, waypoint);
    52         }
    53         return Double.MAX_VALUE;
     49        return Geometry.getDistance(p, new Node(waypoint.getCoor()));
    5450    }
    5551
     
    5955     * @param waypoint WayPoint to get the distance to
    6056     * @return The distance between the relation and the waypoint
     57     * @deprecated Use {@code Geometry.getDistance(relation, new Node(waypoint.getCoor()))}
     58     * instead
    6159     */
     60    @Deprecated
    6261    public static double getDistanceRelation(Relation relation, WayPoint waypoint) {
    6362        double shortestDistance = Double.MAX_VALUE;
     
    8685     * @param waypoint WayPoint to get the distance to
    8786     * @return The distance between the way and the waypoint
     87     * @deprecated Use {@code Geometry.getDistanceWayNode(way, new Node(waypoint.getCoor()))} instead
    8888     */
     89    @Deprecated
    8990    public static double getDistanceWay(Way way, WayPoint waypoint) {
    90         double shortestDistance = Double.MAX_VALUE;
    91         if (way == null || waypoint == null) return shortestDistance;
    92         LatLon llwaypoint = waypoint.getCoor();
    93         EastNorth enwaypoint = new EastNorth(llwaypoint.getY(), llwaypoint.getX());
    94         for (int i = 0; i < way.getNodesCount() - 1; i++) {
    95             double distance = Double.MAX_VALUE;
    96             LatLon llfirst = way.getNode(i).getCoor();
    97             LatLon llsecond = way.getNode(i + 1).getCoor();
    98             EastNorth first = new EastNorth(llfirst.getY(), llfirst.getX());
    99             EastNorth second = new EastNorth(llsecond.getY(), llsecond.getX());
    100             if (first.isValid() && second.isValid()) {
    101                 EastNorth closestPoint = Geometry.closestPointToSegment(first, second, enwaypoint);
    102                 distance = llwaypoint.greatCircleDistance(new LatLon(closestPoint.getX(), closestPoint.getY()));
    103             } else if (first.isValid() && !second.isValid()) {
    104                 distance = getDistanceEastNorth(first, waypoint);
    105             } else if (!first.isValid() && second.isValid()) {
    106                 distance = getDistanceEastNorth(second, waypoint);
    107             } else if (!first.isValid() && !second.isValid()) {
    108                 distance = Double.MAX_VALUE;
    109             }
    110             if (distance < shortestDistance) shortestDistance = distance;
    111 
    112         }
    113         return shortestDistance;
     91        if (way == null || waypoint == null) return Double.MAX_VALUE;
     92        return Geometry.getDistanceWayNode(way, new Node(waypoint.getCoor()));
    11493    }
    11594
     
    11998     * @param waypoint WayPoint to get the distance to
    12099     * @return The distance between the two points
     100     * @deprecated Use {@code Geometry.getDistance(node, new Node(waypoint.getCoor()))}
     101     * instead
    121102     */
     103    @Deprecated
    122104    public static double getDistanceNode(Node node, WayPoint waypoint) {
    123         if (node == null) return Double.MAX_VALUE;
    124         return getDistanceLatLon(node.getCoor(), waypoint);
     105        if (node == null || waypoint == null) return Double.MAX_VALUE;
     106        return Geometry.getDistance(node, new Node(waypoint.getCoor()));
    125107    }
    126108
     
    130112     * @param waypoint WayPoint to get the distance to
    131113     * @return The distance between the two points
     114     * @deprecated Use {@code Geometry.getDistance(new Node(en), new Node(waypoint.getCoor()))} instead
    132115     */
     116    @Deprecated
    133117    public static double getDistanceEastNorth(EastNorth en, WayPoint waypoint) {
    134         if (en == null || !en.isValid()) return Double.MAX_VALUE;
    135         return getDistanceLatLon(new LatLon(en.getY(), en.getX()), waypoint);
     118        if (en == null || waypoint == null) return Double.MAX_VALUE;
     119        return Geometry.getDistance(new Node(en), new Node(waypoint.getCoor()));
    136120    }
    137121
     
    141125     * @param waypoint WayPoint to get the distance to
    142126     * @return The distance between the two points
     127     * @deprecated Use {@code Geometry.getDistance(new Node(latlon), new Node(waypoint.getCoor()))} instead
    143128     */
     129    @Deprecated
    144130    public static double getDistanceLatLon(LatLon latlon, WayPoint waypoint) {
    145131        if (latlon == null || waypoint == null || waypoint.getCoor() == null) return Double.MAX_VALUE;
    146         return waypoint.getCoor().greatCircleDistance(latlon);
     132        return Geometry.getDistance(new Node(latlon), new Node(waypoint.getCoor()));
    147133    }
    148134}
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r15021 r15035  
    1010import java.math.MathContext;
    1111import java.util.ArrayList;
     12import java.util.Collection;
    1213import java.util.Collections;
    1314import java.util.Comparator;
     15import java.util.Iterator;
    1416import java.util.LinkedHashSet;
    1517import java.util.List;
    1618import java.util.Set;
     19import java.util.TreeSet;
    1720import java.util.function.Predicate;
    1821import java.util.stream.Collectors;
     
    3134import org.openstreetmap.josm.data.osm.Node;
    3235import org.openstreetmap.josm.data.osm.NodePositionComparator;
     36import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3337import org.openstreetmap.josm.data.osm.Relation;
    3438import org.openstreetmap.josm.data.osm.Way;
     39import org.openstreetmap.josm.data.osm.WaySegment;
    3540import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
    3641import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
     
    11031108        return new AreaAndPerimeter(Math.abs(area) / 2, perimeter);
    11041109    }
     1110
     1111    /**
     1112     * Get the closest primitive to {@code osm} from the collection of
     1113     * OsmPrimitive {@code primitives}
     1114     *
     1115     * The {@code primitives} should be fully downloaded to ensure accuracy.
     1116     *
     1117     * Note: The complexity of this method is O(n*m), where n is the number of
     1118     * children {@code osm} has plus 1, m is the number of children the
     1119     * collection of primitives have plus the number of primitives in the
     1120     * collection.
     1121     *
     1122     * @param <T> The return type of the primitive
     1123     * @param osm The primitive to get the distances from
     1124     * @param primitives The collection of primitives to get the distance to
     1125     * @return The closest {@link OsmPrimitive}. This is not determinative.
     1126     * To get all primitives that share the same distance, use
     1127     * {@link Geometry#getClosestPrimitives}.
     1128     * @since 15035
     1129     */
     1130    public static <T extends OsmPrimitive> T getClosestPrimitive(OsmPrimitive osm, Collection<T> primitives) {
     1131        Collection<T> collection = getClosestPrimitives(osm, primitives);
     1132        return collection.iterator().next();
     1133    }
     1134
     1135    /**
     1136     * Get the closest primitives to {@code osm} from the collection of
     1137     * OsmPrimitive {@code primitives}
     1138     *
     1139     * The {@code primitives} should be fully downloaded to ensure accuracy.
     1140     *
     1141     * Note: The complexity of this method is O(n*m), where n is the number of
     1142     * children {@code osm} has plus 1, m is the number of children the
     1143     * collection of primitives have plus the number of primitives in the
     1144     * collection.
     1145     *
     1146     * @param <T> The return type of the primitive
     1147     * @param osm The primitive to get the distances from
     1148     * @param primitives The collection of primitives to get the distance to
     1149     * @return The closest {@link OsmPrimitive}s. May be empty.
     1150     * @since 15035
     1151     */
     1152    public static <T extends OsmPrimitive> Collection<T> getClosestPrimitives(OsmPrimitive osm, Collection<T> primitives) {
     1153        double lowestDistance = Double.MAX_VALUE;
     1154        TreeSet<T> closest = new TreeSet<>();
     1155        for (T primitive : primitives) {
     1156            double distance = getDistance(osm, primitive);
     1157            if (Double.isNaN(distance)) continue;
     1158            if (distance < lowestDistance) {
     1159                closest.clear();
     1160                lowestDistance = distance;
     1161                closest.add(primitive);
     1162            } else if (distance == lowestDistance) {
     1163                closest.add(primitive);
     1164            }
     1165        }
     1166        return closest;
     1167    }
     1168
     1169    /**
     1170     * Get the furthest primitive to {@code osm} from the collection of
     1171     * OsmPrimitive {@code primitives}
     1172     *
     1173     * The {@code primitives} should be fully downloaded to ensure accuracy.
     1174     *
     1175     * It does NOT give the furthest primitive based off of the furthest
     1176     * part of that primitive
     1177     *
     1178     * Note: The complexity of this method is O(n*m), where n is the number of
     1179     * children {@code osm} has plus 1, m is the number of children the
     1180     * collection of primitives have plus the number of primitives in the
     1181     * collection.
     1182     *
     1183     * @param <T> The return type of the primitive
     1184     * @param osm The primitive to get the distances from
     1185     * @param primitives The collection of primitives to get the distance to
     1186     * @return The furthest {@link OsmPrimitive}.  This is not determinative.
     1187     * To get all primitives that share the same distance, use
     1188     * {@link Geometry#getFurthestPrimitives}
     1189     * @since 15035
     1190     */
     1191    public static <T extends OsmPrimitive> T getFurthestPrimitive(OsmPrimitive osm, Collection<T> primitives) {
     1192        return getFurthestPrimitives(osm, primitives).iterator().next();
     1193    }
     1194
     1195    /**
     1196     * Get the furthest primitives to {@code osm} from the collection of
     1197     * OsmPrimitive {@code primitives}
     1198     *
     1199     * The {@code primitives} should be fully downloaded to ensure accuracy.
     1200     *
     1201     * It does NOT give the furthest primitive based off of the furthest
     1202     * part of that primitive
     1203     *
     1204     * Note: The complexity of this method is O(n*m), where n is the number of
     1205     * children {@code osm} has plus 1, m is the number of children the
     1206     * collection of primitives have plus the number of primitives in the
     1207     * collection.
     1208     *
     1209     * @param <T> The return type of the primitive
     1210     * @param osm The primitive to get the distances from
     1211     * @param primitives The collection of primitives to get the distance to
     1212     * @return The furthest {@link OsmPrimitive}s. It may return an empty collection.
     1213     * @since 15035
     1214     */
     1215    public static <T extends OsmPrimitive> Collection<T> getFurthestPrimitives(OsmPrimitive osm, Collection<T> primitives) {
     1216        double furthestDistance = Double.NEGATIVE_INFINITY;
     1217        TreeSet<T> furthest = new TreeSet<>();
     1218        for (T primitive : primitives) {
     1219            double distance = getDistance(osm, primitive);
     1220            if (Double.isNaN(distance)) continue;
     1221            if (distance > furthestDistance) {
     1222                furthest.clear();
     1223                furthestDistance = distance;
     1224                furthest.add(primitive);
     1225            } else if (distance == furthestDistance) {
     1226                furthest.add(primitive);
     1227            }
     1228        }
     1229        return furthest;
     1230    }
     1231
     1232    /**
     1233     * Get the distance between different {@link OsmPrimitive}s
     1234     * @param one The primitive to get the distance from
     1235     * @param two The primitive to get the distance to
     1236     * @return The distance between the primitives in meters
     1237     * (or the unit of the current projection, see {@link Projection}).
     1238     * May return {@link Double#NaN} if one of the primitives is incomplete.
     1239     *
     1240     * Note: The complexity is O(n*m), where (n,m) are the number of child
     1241     * objects the {@link OsmPrimitive}s have.
     1242     * @since 15035
     1243     */
     1244    public static double getDistance(OsmPrimitive one, OsmPrimitive two) {
     1245        double rValue = Double.MAX_VALUE;
     1246        if (one == null || two == null || one.isIncomplete()
     1247                || two.isIncomplete()) return Double.NaN;
     1248        if (one instanceof Node && two instanceof Node) {
     1249            rValue = ((Node) one).getCoor().greatCircleDistance(((Node) two).getCoor());
     1250        } else if (one instanceof Node && two instanceof Way) {
     1251            rValue = getDistanceWayNode((Way) two, (Node) one);
     1252        } else if (one instanceof Way && two instanceof Node) {
     1253            rValue = getDistanceWayNode((Way) one, (Node) two);
     1254        } else if (one instanceof Way && two instanceof Way) {
     1255            rValue = getDistanceWayWay((Way) one, (Way) two);
     1256        } else if (one instanceof Relation && !(two instanceof Relation)) {
     1257            for (OsmPrimitive osmPrimitive: ((Relation) one).getMemberPrimitives()) {
     1258                double currentDistance = getDistance(osmPrimitive, two);
     1259                if (currentDistance < rValue) rValue = currentDistance;
     1260            }
     1261        } else if (!(one instanceof Relation) && two instanceof Relation) {
     1262            rValue = getDistance(two, one);
     1263        } else if (one instanceof Relation && two instanceof Relation) {
     1264            for (OsmPrimitive osmPrimitive1 : ((Relation) one).getMemberPrimitives()) {
     1265                for (OsmPrimitive osmPrimitive2 : ((Relation) two).getMemberPrimitives()) {
     1266                    double currentDistance = getDistance(osmPrimitive1, osmPrimitive2);
     1267                    if (currentDistance < rValue) rValue = currentDistance;
     1268                }
     1269            }
     1270        }
     1271        return rValue != Double.MAX_VALUE ? rValue : Double.NaN;
     1272    }
     1273
     1274    /**
     1275     * Get the distance between a way and a node
     1276     * @param way The way to get the distance from
     1277     * @param node The node to get the distance to
     1278     * @return The distance between the {@code way} and the {@code node} in
     1279     * meters (or the unit of the current projection, see {@link Projection}).
     1280     * May return {@link Double#NaN} if the primitives are incomplete.
     1281     * @since 15035
     1282     */
     1283    public static double getDistanceWayNode(Way way, Node node) {
     1284        if (way == null || node == null || way.getNodesCount() < 2 || !node.isLatLonKnown())
     1285            return Double.NaN;
     1286
     1287        double smallest = Double.MAX_VALUE;
     1288        EastNorth en0 = node.getEastNorth();
     1289        // go through the nodes as if they were paired
     1290        Iterator<Node> iter = way.getNodes().iterator();
     1291        EastNorth en1 = iter.next().getEastNorth();
     1292        while (iter.hasNext()) {
     1293            EastNorth en2 = iter.next().getEastNorth();
     1294            double distance = getSegmentNodeDistSq(en1, en2, en0);
     1295            if (distance < smallest)
     1296                smallest = distance;
     1297            en1 = en2;
     1298        }
     1299        return smallest != Double.MAX_VALUE ? Math.sqrt(smallest) : Double.NaN;
     1300    }
     1301
     1302    /**
     1303     * Get the closest {@link WaySegment} from a way to a primitive.
     1304     * @param way The {@link Way} to get the distance from and the {@link WaySegment}
     1305     * @param primitive The {@link OsmPrimitive} to get the distance to
     1306     * @return The {@link WaySegment} that is closest to {@code primitive} from {@code way}.
     1307     * If there are multiple {@link WaySegment}s with the same distance, the last
     1308     * {@link WaySegment} with the same distance will be returned.
     1309     * May return {@code null} if the way has fewer than two nodes or one
     1310     * of the primitives is incomplete.
     1311     * @since 15035
     1312     */
     1313    public static WaySegment getClosestWaySegment(Way way, OsmPrimitive primitive) {
     1314        if (way == null || primitive == null || way.isIncomplete()
     1315                || primitive.isIncomplete()) return null;
     1316        double lowestDistance = Double.MAX_VALUE;
     1317        Pair<Node, Node> closestNodes = null;
     1318        for (Pair<Node, Node> nodes : way.getNodePairs(false)) {
     1319            Way tWay = new Way();
     1320            tWay.addNode(nodes.a);
     1321            tWay.addNode(nodes.b);
     1322            double distance = getDistance(tWay, primitive);
     1323            if (distance < lowestDistance) {
     1324                lowestDistance = distance;
     1325                closestNodes = nodes;
     1326            }
     1327        }
     1328        if (closestNodes == null) return null;
     1329        return lowestDistance != Double.MAX_VALUE ? WaySegment.forNodePair(way, closestNodes.a, closestNodes.b) : null;
     1330    }
     1331
     1332    /**
     1333     * Get the distance between different ways. Iterates over the nodes of the ways, complexity is O(n*m)
     1334     * (n,m giving the number of nodes)
     1335     * @param w1 The first {@link Way}
     1336     * @param w2 The second {@link Way}
     1337     * @return The shortest distance between the ways in meters
     1338     * (or the unit of the current projection, see {@link Projection}).
     1339     * May return {@link Double#NaN}.
     1340     * @since 15035
     1341     */
     1342    public static double getDistanceWayWay(Way w1, Way w2) {
     1343        if (w1 == null || w2 == null || w1.getNodesCount() < 2 || w2.getNodesCount() < 2)
     1344            return Double.NaN;
     1345        double rValue = Double.MAX_VALUE;
     1346        Iterator<Node> iter1 = w1.getNodes().iterator();
     1347        Node w1N1 = iter1.next();
     1348        while (iter1.hasNext()) {
     1349            Node w1N2 = iter1.next();
     1350            Iterator<Node> iter2 = w2.getNodes().iterator();
     1351            Node w2N1 = iter2.next();
     1352            while (iter2.hasNext()) {
     1353                Node w2N2 = iter2.next();
     1354                double distance = getDistanceSegmentSegment(w1N1, w1N2, w2N1, w2N2);
     1355                if (distance < rValue)
     1356                    rValue = distance;
     1357                w2N1 = w2N2;
     1358            }
     1359            w1N1 = w1N2;
     1360        }
     1361        return rValue != Double.MAX_VALUE ? rValue : Double.NaN;
     1362    }
     1363
     1364    /**
     1365     * Get the distance between different {@link WaySegment}s
     1366     * @param ws1 A {@link WaySegment}
     1367     * @param ws2 A {@link WaySegment}
     1368     * @return The distance between the two {@link WaySegment}s in meters
     1369     * (or the unit of the current projection, see {@link Projection}).
     1370     * May return {@link Double#NaN}.
     1371     * @since 15035
     1372     */
     1373    public static double getDistanceSegmentSegment(WaySegment ws1, WaySegment ws2) {
     1374        return getDistanceSegmentSegment(ws1.getFirstNode(), ws1.getSecondNode(), ws2.getFirstNode(), ws2.getSecondNode());
     1375    }
     1376
     1377    /**
     1378     * Get the distance between different {@link WaySegment}s
     1379     * @param ws1Node1 The first node of the first WaySegment
     1380     * @param ws1Node2 The second node of the second WaySegment
     1381     * @param ws2Node1 The first node of the second WaySegment
     1382     * @param ws2Node2 The second node of the second WaySegment
     1383     * @return The distance between the two {@link WaySegment}s in meters
     1384     * (or the unit of the current projection, see {@link Projection}).
     1385     * May return {@link Double#NaN}.
     1386     * @since 15035
     1387     */
     1388    public static double getDistanceSegmentSegment(Node ws1Node1, Node ws1Node2, Node ws2Node1, Node ws2Node2) {
     1389        EastNorth enWs1Node1 = ws1Node1.getEastNorth();
     1390        EastNorth enWs1Node2 = ws1Node2.getEastNorth();
     1391        EastNorth enWs2Node1 = ws2Node1.getEastNorth();
     1392        EastNorth enWs2Node2 = ws2Node2.getEastNorth();
     1393        if (enWs1Node1 == null || enWs1Node2 == null || enWs2Node1 == null || enWs2Node2 == null)
     1394            return Double.NaN;
     1395        if (getSegmentSegmentIntersection(enWs1Node1, enWs1Node2, enWs2Node1, enWs2Node2) != null)
     1396            return 0;
     1397
     1398        double dist1sq = getSegmentNodeDistSq(enWs1Node1, enWs1Node2, enWs2Node1);
     1399        double dist2sq = getSegmentNodeDistSq(enWs1Node1, enWs1Node2, enWs2Node2);
     1400        double dist3sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node1);
     1401        double dist4sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node2);
     1402        double smallest = Math.min(Math.min(dist1sq, dist2sq), Math.min(dist3sq, dist4sq));
     1403        return smallest != Double.MAX_VALUE ? Math.sqrt(smallest) : Double.NaN;
     1404    }
     1405
     1406    /**
     1407     * Calculate closest distance between a line segment s1-s2 and a point p
     1408     * @param s1 start of segment
     1409     * @param s2 end of segment
     1410     * @param p the point
     1411     * @return the square of the euclidean distance from p to the closest point on the segment
     1412     */
     1413    private static double getSegmentNodeDistSq(EastNorth s1, EastNorth s2, EastNorth p) {
     1414        EastNorth c1 = closestPointTo(s1, s2, p, true);
     1415        return c1.distanceSq(p);
     1416    }
    11051417}
Note: See TracChangeset for help on using the changeset viewer.