/// <summary> /// Gets the distance (in km) from Point A to Point B (both A and B must lie on a polyline defined by a list of points). /// If B is before A, the distance is negative! /// </summary> /// <param name="A">First point</param> /// <param name="B">Second point</param> /// <param name="points">List of Points that define the polyline</param> /// <returns></returns> public static double GetDistanceOnPolyline(PointLatLng A, PointLatLng B, List<PointLatLng> points) { double dist = 0d; //If both points are the same return 0! if (A == B) return dist; bool foundA = false; bool foundB = false; double distP1toA, distP2toA, distP1toB, distP2toB, distP1toP2; for (int i = 0; i < points.Count - 1; i++) { var p1 = points[i]; var p2 = points[i + 1]; bool AisBetween = GMapUtils.IsPointOnLineSegment(p1, p2, A, out distP1toA, out distP2toA, out distP1toP2); if (AisBetween) foundA = true; bool BisBetween = GMapUtils.IsPointOnLineSegment(p1, p2, B, out distP1toB, out distP2toB, out distP1toP2); if (BisBetween) foundB = true; if (AisBetween && BisBetween) { dist = GMapProviders.EmptyProvider.Projection.GetDistance(A, B); if (distP1toB < distP1toA) dist *= -1d; return dist; } //Point A was found, but not B: if (AisBetween && !foundB) dist += distP2toA; else if (foundA && !AisBetween && !foundB) dist += distP1toP2; else if (foundA && !AisBetween && BisBetween) { dist += distP1toB; break; } //Point B was found, but not A: else if (BisBetween && !foundA) dist += distP2toB; else if (foundB && !BisBetween && !foundA) dist += distP1toP2; else if (foundB && !BisBetween && AisBetween) { dist += distP1toA; dist *= -1d; break; } } if (!foundA || !foundB) throw new Exception("Point A or Point B (or both) are not on the polyline!"); return dist; }
/// <summary> /// Returns the point that lies on the polyline and is closest to a given point. /// </summary> /// <param name="points">A List of Vectors/Points that define the polyline</param> /// <param name="pos">The given point</param> /// <returns>Closest point that is on the polyline.</returns> public static PointLatLng GetClosestPointOnPolyline(List<PointLatLng> points, PointLatLng pos) { SortedDictionary<double, Vector> distances = new SortedDictionary<double, Vector>(); Vector v0 = new Vector(pos.Lng, pos.Lat); for (int i = 0; i < points.Count - 1; i++) { double dist; Vector v1 = new Vector(points[i].Lng, points[i].Lat); Vector v2 = new Vector(points[i + 1].Lng, points[i + 1].Lat); var point = GMapUtils.GetClosestPointOnLinesegment(v1, v2, v0, out dist); if (!distances.ContainsKey(dist)) distances.Add(dist, point); } return new PointLatLng(distances.First().Value.Y, distances.First().Value.X); }
/// <summary> /// Returns a point that lies on the polyline defined by the point list, and is a certain distance (in km!) from point A (that must also lie on the polyline). /// If the point would lie outside of the polyline, the last point of the polyline is returned! /// If the distance is negative, the new point lies BEFORE the given point! /// </summary> /// <param name="A">The Start point</param> /// <param name="dist">The distance from the start point in km</param> /// <param name="points">The points defining the polyline</param> /// <returns></returns> public static PointLatLng GetDistantPointOnPolyline(PointLatLng A, double distance, List<PointLatLng> points) { if (distance == 0d) return A; PointLatLng newPoint = new PointLatLng(); double distP1toA, distP2toA, distP1toP2; double fooDist = 0d, lastDist = 0d, newDist = 0d; if (distance > 0d) { for (int i = 0; i < points.Count - 1; i++) { var p1 = points[i]; var p2 = points[i + 1]; if (GMapUtils.IsPointOnLineSegment(p1, p2, A, out distP1toA, out distP2toA, out distP1toP2)) { fooDist = distP2toA; while (i < points.Count - 2 && fooDist < distance) { i++; p1 = points[i]; p2 = points[i + 1]; lastDist = GMapProviders.EmptyProvider.Projection.GetDistance(p1, p2); fooDist += lastDist; } if (fooDist < distance) return points.Last(); double bearing = GMapProviders.EmptyProvider.Projection.GetBearing(p1, p2); //Case 1: Last Distance is greater than 0 (the while loop ran at least one time) if (lastDist > 0d) { newDist = lastDist - (fooDist - distance); newPoint = GMapUtils.GetClosestPointOnPolyline(points, GMapUtils.GetPointLatLngFromStartPoint(p1, bearing, newDist)); } //Case 2: Last Distance is 0, the while loop was not called: else { newPoint = GMapUtils.GetClosestPointOnPolyline(points, GMapUtils.GetPointLatLngFromStartPoint(A, bearing, distance)); } break; } } } else { for (int i = 0; i < points.Count - 1; i++) { var p1 = points[i]; var p2 = points[i + 1]; if (GMapUtils.IsPointOnLineSegment(p1, p2, A, out distP1toA, out distP2toA, out distP1toP2)) { fooDist = distP1toA; while (i > 0 && fooDist < Math.Abs(distance)) { i--; p1 = points[i]; p2 = points[i + 1]; lastDist = GMapProviders.EmptyProvider.Projection.GetDistance(p1, p2); fooDist += lastDist; } if (fooDist < Math.Abs(distance)) return points.First(); double bearing = GMapProviders.EmptyProvider.Projection.GetBearing(p2, p1); //Case 1: Last Distance is greater than 0 (the while loop ran at least one time) if (lastDist > 0d) { newDist = lastDist - (fooDist - Math.Abs(distance)); newPoint = GMapUtils.GetClosestPointOnPolyline(points, GMapUtils.GetPointLatLngFromStartPoint(p2, bearing, newDist)); } //Case 2: Last Distance is 0, the while loop was not called: else newPoint = GMapUtils.GetClosestPointOnPolyline(points, GMapUtils.GetPointLatLngFromStartPoint(A, bearing, Math.Abs(distance))); break; } } } return newPoint; }