Select to view content in your preferred language

Finding midpoint of line

6926
2
Jump to solution
06-14-2012 03:04 AM
JoeHershman
MVP Alum
I am setting up something what I want to draw a line to the middle point of another line.  I have gone about a few approaches to finding that Midpoint none of which are returning the proper result.

  • Using GetCenter on the bounding envelope, works good with small lines, but for large lines works very poor as the envelope may not even be on the line

  • Trying to just grab the middle point (i.e., polyline.Paths[0][polyline.Paths[0].Count/2]).  Better because at least it is always on the line, but with long lines usually not near the center because a curved section will have so many points compared to a straight section.

I am stuck thinking I need to compute the length of the line and find the point closest to the center of that length.  Calling a GeometryService is out of the question because this needs to work quickly, responding to a user event immediately.  I am having fears of high school trigonometry as I think about calculating the length of each individual line segment and adding them all up.  Anyone have a simpler approach that I am missing?

Thanks
-Joe
Thanks,
-Joe
0 Kudos
1 Solution

Accepted Solutions
DominiqueBroux
Esri Frequent Contributor
I am afraid you can't avoid calculating the length of each individual line segment if you want to get the real middle point of a polyline:).

But once the length of a segment is calculated it's just a LINQ matter to aggregate for a multi paths polyline.

Below is an extension class allowing to get the midpoint of a polyline by code like:
var point = polyline.Midpoint();

It's based on the calculation of the length and the calculation of a linear position along the line (M = length/2 being a particular case).

That's a good LINQ exercise 🙂

public static class GeometryExtension {     public static MapPoint MidPoint(this Polyline polyline)     {         var length = polyline.Length();         var m = length / 2.0;         return polyline.LinearPosition(m);     }      // length of a multipath polyline     public static double Length(this Polyline polyline)     {         return polyline.Paths.Sum((Func<PointCollection, double>)Length);     }      // length of one path     private static double Length(this PointCollection points)     {         return points.Zip(points.Skip(1), Length).Sum();     }      // length of a segment     private static double Length(MapPoint point1, MapPoint point2)     {         return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));     }      // Get a M position on a multipath polyline     public static MapPoint LinearPosition(this Polyline polyline, double m)     {         PointCollection path1 = polyline.Paths.Where(path => DecrementTotal(path.Length(), ref m))             .DefaultIfEmpty(polyline.Paths.Last()) // for the case where m > polyline length             .First();         return path1.LinearPosition(m);     }      // Get a M position on a polyline     private static MapPoint LinearPosition(this IEnumerable<MapPoint> points, double m)     {         var seg = points.Zip(points.Skip(1), (pt1, pt2) => new Tuple<MapPoint, MapPoint>(pt1, pt2))             .Where(segment => DecrementTotal(Length(segment.Item1, segment.Item2), ref m))             .FirstOrDefault();         return seg == null ? points.Last() : seg.LinearPosition(m);     }      // Get a M position on a segment     private static MapPoint LinearPosition(this Tuple<MapPoint, MapPoint> segment, double m)     {         var ratio = m / Length(segment.Item1, segment.Item2);         return new MapPoint(segment.Item1.X + ratio * (segment.Item2.X - segment.Item1.X), segment.Item1.Y + ratio * (segment.Item2.Y - segment.Item1.Y));     }      private static bool DecrementTotal(double l, ref double cumul)     {         if (l >= cumul)             return true;         cumul -= l;         return false;     } }

View solution in original post

0 Kudos
2 Replies
DominiqueBroux
Esri Frequent Contributor
I am afraid you can't avoid calculating the length of each individual line segment if you want to get the real middle point of a polyline:).

But once the length of a segment is calculated it's just a LINQ matter to aggregate for a multi paths polyline.

Below is an extension class allowing to get the midpoint of a polyline by code like:
var point = polyline.Midpoint();

It's based on the calculation of the length and the calculation of a linear position along the line (M = length/2 being a particular case).

That's a good LINQ exercise 🙂

public static class GeometryExtension {     public static MapPoint MidPoint(this Polyline polyline)     {         var length = polyline.Length();         var m = length / 2.0;         return polyline.LinearPosition(m);     }      // length of a multipath polyline     public static double Length(this Polyline polyline)     {         return polyline.Paths.Sum((Func<PointCollection, double>)Length);     }      // length of one path     private static double Length(this PointCollection points)     {         return points.Zip(points.Skip(1), Length).Sum();     }      // length of a segment     private static double Length(MapPoint point1, MapPoint point2)     {         return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));     }      // Get a M position on a multipath polyline     public static MapPoint LinearPosition(this Polyline polyline, double m)     {         PointCollection path1 = polyline.Paths.Where(path => DecrementTotal(path.Length(), ref m))             .DefaultIfEmpty(polyline.Paths.Last()) // for the case where m > polyline length             .First();         return path1.LinearPosition(m);     }      // Get a M position on a polyline     private static MapPoint LinearPosition(this IEnumerable<MapPoint> points, double m)     {         var seg = points.Zip(points.Skip(1), (pt1, pt2) => new Tuple<MapPoint, MapPoint>(pt1, pt2))             .Where(segment => DecrementTotal(Length(segment.Item1, segment.Item2), ref m))             .FirstOrDefault();         return seg == null ? points.Last() : seg.LinearPosition(m);     }      // Get a M position on a segment     private static MapPoint LinearPosition(this Tuple<MapPoint, MapPoint> segment, double m)     {         var ratio = m / Length(segment.Item1, segment.Item2);         return new MapPoint(segment.Item1.X + ratio * (segment.Item2.X - segment.Item1.X), segment.Item1.Y + ratio * (segment.Item2.Y - segment.Item1.Y));     }      private static bool DecrementTotal(double l, ref double cumul)     {         if (l >= cumul)             return true;         cumul -= l;         return false;     } }
0 Kudos
JoeHershman
MVP Alum
Thanks, it may take me a bit to fully understand the Zip method calls, but this is just what I need.  I have an identify tool that works very similar to the desktop tool, so when they click on the result in the tree it displays the locator lines from the four sides and I wanted it to hit the midpoint on lines which was giving me troubles.  Always enjoy learning some more linq

much appreciated
-joe
Thanks,
-Joe
0 Kudos