diff --git a/CHANGELOG.md b/CHANGELOG.md index 1892955..d1a563c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ # CHANGELOG +## 0.6.0 + +- Add findPolygonCentroid +- Add getPolygonIntersection + ## 0.5.0 -- greatCircleDistanceBetweenTwoGeoPoints -- getRectangleBounds -- calculateBoundingBox +- Add greatCircleDistanceBetweenTwoGeoPoints +- Add getRectangleBounds +- Add calculateBoundingBox ## 0.4.2 - Removed pedantic diff --git a/README.md b/README.md index fe34c29..fb29b5f 100644 --- a/README.md +++ b/README.md @@ -148,18 +148,18 @@ Similar to PolygonEnvelop in Python List rectangleBounds = geodesy.getRectangleBounds(polygonCoords); ``` -### Great-circle distance between two points using the Haversine formula +### greatCircleDistanceBetweenTwoGeoPoints(num lat1, num lng1, num lat2, num lng2) -Calculate the Great-Circle Distance between two Geo points +Calculate the Great-Circle Distance between two Geo points using the Haversine formula ```dart num latitude1 = 37.7749; - num longitude1 = -122.4194; - num latitude2 = 37.3382; - num longitude2 = -121.8863; +num longitude1 = -122.4194; +num latitude2 = 37.3382; +num longitude2 = -121.8863; - num greatCircleDistance = geodesy.greatCircleDistanceBetweenTwoGeoPoints( - latitude1, longitude1, latitude2, longitude2); +num greatCircleDistance = geodesy.greatCircleDistanceBetweenTwoGeoPoints( + latitude1, longitude1, latitude2, longitude2); ``` ### calculateBoundingBox(LatLng centerPoint, num distanceInKm) @@ -174,3 +174,43 @@ Given the Latitude and Longitude and distance in kilometers it calculate the bou final boundingBox = geodesy.calculateBoundingBox(centerPoint, distanceInKm); ``` + +## findPolygonCentroid(List polygon) + +```dart + List polygon = [ + const LatLng(0, 0), + const LatLng(4, 0), + const LatLng(4, 4), + const LatLng(0, 4) + ]; + + LatLng centroid = geodesy.findPolygonCentroid(polygon); + print("Centroid: ${centroid.latitude}, ${centroid.longitude}"); +``` + +## getPolygonIntersection(List polygon1, List polygon2) + +```dart +final List polygon1 = [ + const LatLng(0, 0), + const LatLng(0, 2), + const LatLng(2, 2), + const LatLng(2, 0), + ]; + + final List polygon2 = [ + const LatLng(1, 1), + const LatLng(1, 3), + const LatLng(3, 3), + const LatLng(3, 1), + ]; + + final List intersectionPoints = + geodesy.getPolygonIntersection(polygon1, polygon2); + + print('Intersection Points:'); + for (final point in intersectionPoints) { + print('Latitude: ${point.latitude}, Longitude: ${point.longitude}'); + } +``` diff --git a/analysis_options.yaml b/analysis_options.yaml index b31d1f1..de8fe33 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,9 +2,6 @@ include: package:lints/core.yaml analyzer: - strong-mode: - implicit-casts: false - implicit-dynamic: false errors: missing_return: error missing_required_param: error diff --git a/example/main.dart b/example/main.dart index 0e6abf4..6b3c7bd 100644 --- a/example/main.dart +++ b/example/main.dart @@ -96,4 +96,40 @@ void main() async { print('[calculateBoundingBox]: '); print(' > Top Left: ${boundingBox[0]}'); print(' > Bottom Right: ${boundingBox[1]}'); + + // Polygon Centroid + List polygon = [ + const LatLng(0, 0), + const LatLng(4, 0), + const LatLng(4, 4), + const LatLng(0, 4) + ]; + + LatLng centroid = geodesy.findPolygonCentroid(polygon); + + print("Centroid: ${centroid.latitude}, ${centroid.longitude}"); + + // Polygon Intersection + + final List polygon1 = [ + const LatLng(0, 0), + const LatLng(0, 2), + const LatLng(2, 2), + const LatLng(2, 0), + ]; + + final List polygon2 = [ + const LatLng(1, 1), + const LatLng(1, 3), + const LatLng(3, 3), + const LatLng(3, 1), + ]; + + final List intersectionPoints = + geodesy.getPolygonIntersection(polygon1, polygon2); + + print('Intersection Points:'); + for (final point in intersectionPoints) { + print('Latitude: ${point.latitude}, Longitude: ${point.longitude}'); + } } diff --git a/lib/src/geodesy.dart b/lib/src/geodesy.dart index db021df..cc71289 100644 --- a/lib/src/geodesy.dart +++ b/lib/src/geodesy.dart @@ -250,7 +250,6 @@ class Geodesy { } /// GetRectangleBounds - List getRectangleBounds(List polygonCoords) { num minLatitude = double.infinity.toDouble(); num maxLatitude = double.negativeInfinity.toDouble(); @@ -300,4 +299,113 @@ class Geodesy { LatLng(bottomLat.toDouble(), rightLng.toDouble()); return [topLeft, bottomRight]; } + + /// finds the centroid of polygons + LatLng findPolygonCentroid(List polygons) { + num x = 0; + num y = 0; + num signedArea = 0; + + num vertexCount = polygons.length; + + for (int i = 0; i < vertexCount; i++) { + final LatLng currentVertex = polygons[i]; + final LatLng nextVertex = polygons[(i + 1) % vertexCount.toInt()]; + + num a = currentVertex.longitude * nextVertex.latitude - + nextVertex.longitude * currentVertex.latitude; + signedArea += a; + x += (currentVertex.longitude + nextVertex.longitude) * a; + y += (currentVertex.latitude + nextVertex.latitude) * a; + } + + signedArea *= 0.5; + x /= (6 * signedArea); + y /= (6 * signedArea); + + // Return the centroid as LatLng object + return LatLng( + y.toDouble(), + x.toDouble(), + ); + } + + /// Polygon Intersection + List getPolygonIntersection( + List polygon1, List polygon2) { + final List intersectionPoints = []; + + for (int i = 0; i < polygon1.length; i++) { + final int j = (i + 1) % polygon1.length; + final LatLng edge1Start = polygon1[i]; + final LatLng edge1End = polygon1[j]; + + for (int k = 0; k < polygon2.length; k++) { + final int l = (k + 1) % polygon2.length; + final LatLng edge2Start = polygon2[k]; + final LatLng edge2End = polygon2[l]; + + final LatLng? intersection = + _getLineIntersection(edge1Start, edge1End, edge2Start, edge2End); + if (intersection != null) { + intersectionPoints.add(intersection); + } + } + } + return intersectionPoints; + } + + LatLng? _getLineIntersection( + LatLng start1, LatLng end1, LatLng start2, LatLng end2) { + final num x1 = start1.latitude; + final num y1 = start1.longitude; + final num x2 = end1.latitude; + final num y2 = end1.longitude; + final num x3 = start2.latitude; + final num y3 = start2.longitude; + final num x4 = end2.latitude; + final num y4 = end2.longitude; + + final num denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (denominator == 0) { + return null; // Lines are parallel or coincident + } + + final num intersectionX = + ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / + denominator; + final num intersectionY = + ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / + denominator; + + final LatLng intersection = + LatLng(intersectionX.toDouble(), intersectionY.toDouble()); + + if (_isPointOnLine(intersection, start1, end1) && + _isPointOnLine(intersection, start2, end2)) { + return intersection; + } else { + return null; + } + } + + bool _isPointOnLine(LatLng point, LatLng lineStart, LatLng lineEnd) { + final minX = lineStart.latitude < lineEnd.latitude + ? lineStart.latitude + : lineEnd.latitude; + final maxX = lineStart.latitude > lineEnd.latitude + ? lineStart.latitude + : lineEnd.latitude; + final minY = lineStart.longitude < lineEnd.longitude + ? lineStart.longitude + : lineEnd.longitude; + final maxY = lineStart.longitude > lineEnd.longitude + ? lineStart.longitude + : lineEnd.longitude; + + return point.latitude >= minX && + point.latitude <= maxX && + point.longitude >= minY && + point.longitude <= maxY; + } } diff --git a/pubspec.yaml b/pubspec.yaml index 4beb360..0a24943 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: geodesy description: A Dart library for geodesic and trigonometric calculations working with points and paths -version: 0.5.0 +version: 0.6.0 homepage: https://github.com/wingkwong/geodesy environment: diff --git a/test/geodesy_test.dart b/test/geodesy_test.dart index 3bbd27c..06a8268 100644 --- a/test/geodesy_test.dart +++ b/test/geodesy_test.dart @@ -164,4 +164,48 @@ void main() { }); }); +// Polygon Centroid + + test('findPolygonCentroid calculates the centroid correctly', () { + // Create a test polygon + List polygon = [ + const LatLng(0, 0), + const LatLng(0, 4), + const LatLng(4, 4), + const LatLng(4, 0), + ]; + + // Calculate the centroid + LatLng centroid = geodesy.findPolygonCentroid(polygon); + + // Verify the centroid coordinates + expect(centroid.latitude, equals(2.0)); + expect(centroid.longitude, equals(2.0)); + }); + + // Polygon Intersection + test('Intersection of two polygons', () { + final polygon1 = [ + const LatLng(0, 0), + const LatLng(0, 2), + const LatLng(2, 2), + const LatLng(2, 0), + ]; + + final polygon2 = [ + const LatLng(1, 1), + const LatLng(1, 3), + const LatLng(3, 3), + const LatLng(3, 1), + ]; + + final intersectionPoints = + geodesy.getPolygonIntersection(polygon1, polygon2); + + expect(intersectionPoints.length, 2); + expect(intersectionPoints[0].latitude, 1); + expect(intersectionPoints[0].longitude, 2); + expect(intersectionPoints[1].latitude, 2); + expect(intersectionPoints[1].longitude, 1); + }); }