001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.gpx; 003 004import java.util.ArrayList; 005import java.util.List; 006 007import org.openstreetmap.josm.data.coor.EastNorth; 008import org.openstreetmap.josm.data.coor.LatLon; 009import org.openstreetmap.josm.data.osm.Node; 010import org.openstreetmap.josm.data.osm.OsmPrimitive; 011import org.openstreetmap.josm.data.osm.Relation; 012import org.openstreetmap.josm.data.osm.Way; 013import org.openstreetmap.josm.tools.Geometry; 014 015/** 016 * A class to find the distance between an {@link OsmPrimitive} and a GPX point. 017 * 018 * @author Taylor Smock 019 * @since 14802 020 */ 021public final class GpxDistance { 022 private GpxDistance() { 023 // This class should not be instantiated 024 } 025 026 /** 027 * Find the distance between a point and a dataset of surveyed points 028 * @param p OsmPrimitive from which to get the lowest distance to a GPX point 029 * @param gpxData Data from which to get the GPX points 030 * @return The shortest distance 031 */ 032 public static double getLowestDistance(OsmPrimitive p, GpxData gpxData) { 033 return gpxData.getTrackPoints() 034 .mapToDouble(tp -> getDistance(p, tp)) 035 .filter(x -> x >= 0) 036 .min().orElse(Double.MAX_VALUE); 037 } 038 039 /** 040 * Get the distance between an object and a waypoint 041 * @param p OsmPrimitive to get the distance to the WayPoint 042 * @param waypoint WayPoint to get the distance from 043 * @return The shortest distance between p and waypoint 044 */ 045 public static double getDistance(OsmPrimitive p, WayPoint waypoint) { 046 if (p instanceof Node) { 047 return getDistanceNode((Node) p, waypoint); 048 } else if (p instanceof Way) { 049 return getDistanceWay((Way) p, waypoint); 050 } else if (p instanceof Relation) { 051 return getDistanceRelation((Relation) p, waypoint); 052 } 053 return Double.MAX_VALUE; 054 } 055 056 /** 057 * Get the shortest distance between a relation and a waypoint 058 * @param relation Relation to get the distance from 059 * @param waypoint WayPoint to get the distance to 060 * @return The distance between the relation and the waypoint 061 */ 062 public static double getDistanceRelation(Relation relation, WayPoint waypoint) { 063 double shortestDistance = Double.MAX_VALUE; 064 List<Node> nodes = new ArrayList<>(relation.getMemberPrimitives(Node.class)); 065 List<Way> ways = new ArrayList<>(relation.getMemberPrimitives(Way.class)); 066 List<Relation> relations = new ArrayList<>(relation.getMemberPrimitives(Relation.class)); 067 if (nodes.isEmpty() && ways.isEmpty() && relations.isEmpty()) return Double.MAX_VALUE; 068 for (Relation nrelation : relations) { 069 double distance = getDistanceRelation(nrelation, waypoint); 070 if (distance < shortestDistance) shortestDistance = distance; 071 } 072 for (Way way : ways) { 073 double distance = getDistanceWay(way, waypoint); 074 if (distance < shortestDistance) shortestDistance = distance; 075 } 076 for (Node node : nodes) { 077 double distance = getDistanceNode(node, waypoint); 078 if (distance < shortestDistance) shortestDistance = distance; 079 } 080 return shortestDistance; 081 } 082 083 /** 084 * Get the shortest distance between a way and a waypoint 085 * @param way Way to get the distance from 086 * @param waypoint WayPoint to get the distance to 087 * @return The distance between the way and the waypoint 088 */ 089 public static double getDistanceWay(Way way, WayPoint waypoint) { 090 double shortestDistance = Double.MAX_VALUE; 091 if (way == null || waypoint == null) return shortestDistance; 092 LatLon llwaypoint = waypoint.getCoor(); 093 EastNorth enwaypoint = new EastNorth(llwaypoint.getY(), llwaypoint.getX()); 094 for (int i = 0; i < way.getNodesCount() - 1; i++) { 095 double distance = Double.MAX_VALUE; 096 LatLon llfirst = way.getNode(i).getCoor(); 097 LatLon llsecond = way.getNode(i + 1).getCoor(); 098 EastNorth first = new EastNorth(llfirst.getY(), llfirst.getX()); 099 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; 114 } 115 116 /** 117 * Get the distance between a node and a waypoint 118 * @param node Node to get the distance from 119 * @param waypoint WayPoint to get the distance to 120 * @return The distance between the two points 121 */ 122 public static double getDistanceNode(Node node, WayPoint waypoint) { 123 if (node == null) return Double.MAX_VALUE; 124 return getDistanceLatLon(node.getCoor(), waypoint); 125 } 126 127 /** 128 * Get the distance between coordinates (provided by EastNorth) and a waypoint 129 * @param en The EastNorth to get the distance to 130 * @param waypoint WayPoint to get the distance to 131 * @return The distance between the two points 132 */ 133 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); 136 } 137 138 /** 139 * Get the distance between coordinates (latitude longitude) and a waypoint 140 * @param latlon LatLon to get the distance from 141 * @param waypoint WayPoint to get the distance to 142 * @return The distance between the two points 143 */ 144 public static double getDistanceLatLon(LatLon latlon, WayPoint waypoint) { 145 if (latlon == null || waypoint == null || waypoint.getCoor() == null) return Double.MAX_VALUE; 146 return waypoint.getCoor().greatCircleDistance(latlon); 147 } 148}