@@ -127,6 +127,32 @@ public static double distance(Line line) {
127127 return distance (line .startPoint (), line .endPoint ());
128128 }
129129
130+ /**
131+ * Get the distance in meters between two points in degrees using the
132+ * Haversine formula
133+ *
134+ * @param point1
135+ * point 1
136+ * @param point2
137+ * point 2
138+ * @return distance in meters
139+ * @since 2.2.2
140+ */
141+ public static double distanceHaversine (Point point1 , Point point2 ) {
142+ double lat1 = point1 .getY ();
143+ double lon1 = point1 .getX ();
144+ double lat2 = point2 .getY ();
145+ double lon2 = point2 .getX ();
146+ double diffLat = degreesToRadians (lat2 - lat1 );
147+ double diffLon = degreesToRadians (lon2 - lon1 );
148+ double a = Math .sin (diffLat / 2 ) * Math .sin (diffLat / 2 )
149+ + Math .cos (degreesToRadians (lat1 ))
150+ * Math .cos (degreesToRadians (lat2 ))
151+ * Math .sin (diffLon / 2 ) * Math .sin (diffLon / 2 );
152+ double c = 2 * Math .atan2 (Math .sqrt (a ), Math .sqrt (1 - a ));
153+ return GeometryConstants .EARTH_RADIUS * c ;
154+ }
155+
130156 /**
131157 * Get the bearing heading in degrees between two points in degrees
132158 *
@@ -147,6 +173,52 @@ public static double bearing(Point point1, Point point2) {
147173 return (radiansToDegrees (Math .atan2 (y , x )) + 360 ) % 360 ;
148174 }
149175
176+ /**
177+ * Get the geodesic midpoint in degrees between two points in degrees
178+ *
179+ * @param point1
180+ * point 1
181+ * @param point2
182+ * point 2
183+ * @return geodesic midpoint in degrees
184+ * @since 2.2.2
185+ */
186+ public static Point geodesicMidpoint (Point point1 , Point point2 ) {
187+ Point point1Radians = degreesToRadians (point1 );
188+ Point point2Radians = degreesToRadians (point2 );
189+ Point midpointRadians = geodesicMidpointRadians (point1Radians ,
190+ point2Radians );
191+ return radiansToDegrees (midpointRadians );
192+ }
193+
194+ /**
195+ * Get the geodesic midpoint in radians between two points in radians
196+ *
197+ * @param point1
198+ * point 1
199+ * @param point2
200+ * point 2
201+ * @return geodesic midpoint in radians
202+ * @since 2.2.2
203+ */
204+ public static Point geodesicMidpointRadians (Point point1 , Point point2 ) {
205+
206+ double xDiff = point2 .getX () - point1 .getX ();
207+ double y1 = point1 .getY ();
208+ double y2 = point2 .getY ();
209+ double x1 = point1 .getX ();
210+
211+ double bx = Math .cos (y2 ) * Math .cos (xDiff );
212+ double by = Math .cos (y2 ) * Math .sin (xDiff );
213+
214+ double y = Math .atan2 (Math .sin (y1 ) + Math .sin (y2 ),
215+ Math .sqrt ((Math .cos (y1 ) + bx ) * (Math .cos (y1 ) + bx ) + by * by ));
216+ double x = x1 + Math .atan2 (by , Math .cos (y1 ) + bx );
217+ Point midpoint = new Point (x , y );
218+
219+ return midpoint ;
220+ }
221+
150222 /**
151223 * Get the bearing heading in degrees between line end points in degrees
152224 *
@@ -238,6 +310,34 @@ public static double radiansToDegrees(double radians) {
238310 return radians * GeometryConstants .RADIANS_TO_DEGREES ;
239311 }
240312
313+ /**
314+ * Convert point in degrees to radians
315+ *
316+ * @param point
317+ * point in degrees
318+ * @return point in radians
319+ * @since 2.2.2
320+ */
321+ public static Point degreesToRadians (Point point ) {
322+ double x = degreesToRadians (point .getX ());
323+ double y = degreesToRadians (point .getY ());
324+ return new Point (x , y );
325+ }
326+
327+ /**
328+ * Convert point in radians to degrees
329+ *
330+ * @param point
331+ * point in radians
332+ * @return point in degrees
333+ * @since 2.2.2
334+ */
335+ public static Point radiansToDegrees (Point point ) {
336+ double x = radiansToDegrees (point .getX ());
337+ double y = radiansToDegrees (point .getY ());
338+ return new Point (x , y );
339+ }
340+
241341 /**
242342 * Get the centroid point of a 2 dimensional representation of the Geometry
243343 * (balancing point of a 2d cutout of the geometry). Only the x and y
@@ -883,6 +983,127 @@ private static List<Point> simplifyPoints(List<Point> points,
883983 return result ;
884984 }
885985
986+ /**
987+ * Create a geodesic path of a line string in degrees with a max distance
988+ * between any two path points
989+ *
990+ * @param lineString
991+ * line string in degrees
992+ * @param maxDistance
993+ * max distance allowed between path points
994+ * @return geodesic path of points
995+ * @since 2.2.2
996+ */
997+ public static List <Point > geodesicPath (LineString lineString ,
998+ double maxDistance ) {
999+ return geodesicPath (lineString .getPoints (), maxDistance );
1000+ }
1001+
1002+ /**
1003+ * Create a geodesic path of a points in degrees with a max distance between
1004+ * any two path points
1005+ *
1006+ * @param points
1007+ * points in degrees
1008+ * @param maxDistance
1009+ * max distance allowed between path points
1010+ * @return geodesic path of points
1011+ * @since 2.2.2
1012+ */
1013+ public static List <Point > geodesicPath (List <Point > points ,
1014+ double maxDistance ) {
1015+ List <Point > path = new ArrayList <>();
1016+ if (!points .isEmpty ()) {
1017+ path .add (points .get (0 ));
1018+ for (int i = 0 ; i < points .size () - 1 ; i ++) {
1019+ List <Point > subPath = geodesicPath (points .get (i ),
1020+ points .get (i + 1 ), maxDistance );
1021+ path .addAll (subPath .subList (1 , subPath .size ()));
1022+ }
1023+ }
1024+ return path ;
1025+ }
1026+
1027+ /**
1028+ * Create a geodesic path between the two points in degrees with a max
1029+ * distance between any two path points
1030+ *
1031+ * @param point1
1032+ * point 1
1033+ * @param point2
1034+ * point 2
1035+ * @param maxDistance
1036+ * max distance allowed between path points
1037+ * @return geodesic path of points
1038+ * @since 2.2.2
1039+ */
1040+ public static List <Point > geodesicPath (Point point1 , Point point2 ,
1041+ double maxDistance ) {
1042+ List <Point > path = new ArrayList <>();
1043+ path .add (point1 );
1044+ geodesicPath (point1 , point2 , maxDistance , path );
1045+ path .add (point2 );
1046+ return path ;
1047+ }
1048+
1049+ /**
1050+ * Populate a geodesic path between the two points in degrees with a max
1051+ * distance between any two path points
1052+ *
1053+ * @param point1
1054+ * point 1
1055+ * @param point2
1056+ * point 2
1057+ * @param maxDistance
1058+ * max distance allowed between path points
1059+ * @param path
1060+ * geodesic path of points
1061+ */
1062+ private static void geodesicPath (Point point1 , Point point2 ,
1063+ double maxDistance , List <Point > path ) {
1064+ double distance = distanceHaversine (point1 , point2 );
1065+ if (distance > maxDistance ) {
1066+ Point midpoint = geodesicMidpoint (point1 , point2 );
1067+ geodesicPath (point1 , midpoint , maxDistance , path );
1068+ path .add (midpoint );
1069+ geodesicPath (midpoint , point2 , maxDistance , path );
1070+ }
1071+ }
1072+
1073+ /**
1074+ * Expand the vertical bounds of a geometry envelope in degrees by including
1075+ * geodesic bounds
1076+ *
1077+ * @param envelope
1078+ * geometry envelope in degrees
1079+ * @return geodesic expanded geometry envelope in degrees
1080+ * @since 2.2.2
1081+ */
1082+ public static GeometryEnvelope geodesicEnvelope (GeometryEnvelope envelope ) {
1083+ GeometryEnvelope geodesic = envelope .copy ();
1084+ if (envelope .getMinY () < 0 ) {
1085+ Point left = envelope .getBottomLeft ();
1086+ Point right = envelope .getBottomRight ();
1087+ Point midpoint = geodesicMidpoint (left , right );
1088+ double y = midpoint .getY ();
1089+ if (y < geodesic .getMinY ()) {
1090+ geodesic .setMinY (y );
1091+ }
1092+
1093+ }
1094+ if (envelope .getMaxY () > 0 ) {
1095+ Point left = envelope .getTopLeft ();
1096+ Point right = envelope .getTopRight ();
1097+ Point midpoint = geodesicMidpoint (left , right );
1098+ double y = midpoint .getY ();
1099+ if (y > geodesic .getMaxY ()) {
1100+ geodesic .setMaxY (y );
1101+ }
1102+
1103+ }
1104+ return geodesic ;
1105+ }
1106+
8861107 /**
8871108 * Calculate the perpendicular distance between the point and the line
8881109 * represented by the start and end points. Points should be in a meters
0 commit comments