diff --git a/CHANGELOG.md b/CHANGELOG.md index e52487d..d188495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +## 0.10.0 + +- Geodesy Refactoring +- Implement Geodesic Measurements + - Calculate the length of a polyline formed by connecting multiple points + - Calculate the area of a polygon defined by a set of points using Shoelace formula +- Implement Intersection and Projection + - Calculate intersection points of two geodesic lines + - Project a point onto a geodesic line + ## 0.9.0 - Implement DistanceCalculations: EquirectangularApproximation, and SphericalLawOfCosines diff --git a/README.md b/README.md index 8156c01..3e3b850 100644 --- a/README.md +++ b/README.md @@ -40,163 +40,18 @@ The Geodesy class provides a collection of methods for performing various geodet Please see the details [here](doc/CLASS.md). +### Example + +Please check out [here](example/class_example.dart). + ## Static Methods Static methods are available without using Geodesy instance. Please see the details [here](doc/METHODS.md). +### Example -## Example - Geodesy Class - -Please check out [here](example/main.dart) for more. - -```dart -import 'package:geodesy/geodesy.dart'; - -void main() { - final Geodesy geodesy = Geodesy(); - // Calculate Bounding Box - // Example central position (San Francisco) - final centerPoint = const LatLng(37.7749, -122.4194); - // Example distance in kilometers - final distanceInKm = 1.0; - - final boundingBox = geodesy.calculateBoundingBox(centerPoint, distanceInKm); - - 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}'); - } -} - -// Calculate Area -final outerPolygon = [ - const LatLng(0.0, 0.0), - const LatLng(0.0, 1.0), - const LatLng(1.0, 1.0), - const LatLng(1.0, 0.0), -]; - -// Define a hole within the outer polygon -final hole1 = [ - const LatLng(0.25, 0.25), - const LatLng(0.25, 0.75), - const LatLng(0.75, 0.75), - const LatLng(0.75, 0.25), -]; - -final holes = [hole1]; -final calculatedArea = - geodesy.calculatePolygonWithHolesArea(outerPolygon, holes); -``` - -## Example Static Methods - -```dart -import 'package:geodesy/geodesy.dart'; - -void main() { - // Calculate Bounding Box - // Example central position (San Francisco) - final centerPoint = const LatLng(37.7749, -122.4194); - // Example distance in kilometers - final distanceInKm = 1.0; - // Static Method - final boundingBox = - BoundingBox.calculateBoundingBox(centerPoint, distanceInKm); - - 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) - ]; - // Static Method - final LatLng centroid = PolygonCentroid.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), - ]; - // Static Method - final List intersectionPoints = - PolygonIntersection.getPolygonIntersection(polygon1, polygon2); - - print('Intersection Points:'); - for (final point in intersectionPoints) { - print('Latitude: ${point.latitude}, Longitude: ${point.longitude}'); - } -} -// Static Method -final outerPolygon = [ - const LatLng(0.0, 0.0), - const LatLng(0.0, 1.0), - const LatLng(1.0, 1.0), - const LatLng(1.0, 0.0), - ]; - -final hole1 = [ - const LatLng(0.25, 0.25), - const LatLng(0.25, 0.75), - const LatLng(0.75, 0.75), - const LatLng(0.75, 0.25), -]; - -final holes = [hole1]; -final area = Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); -``` - +Please check out [here](example/static_example.dart). ## Code of Conduct See [here](doc/CODE_OF_CONDUCT.md). diff --git a/analysis_options.yaml b/analysis_options.yaml index de8fe33..54d8381 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,7 +7,6 @@ analyzer: missing_required_param: error invalid_use_of_protected_member: error dead_code: info - sdk_version_async_exported_from_core: ignore linter: rules: - always_declare_return_types @@ -16,7 +15,6 @@ linter: - one_member_abstracts - recursive_getters - null_closures - - lines_longer_than_80_chars - prefer_conditional_assignment - prefer_const_constructors - prefer_final_fields @@ -24,4 +22,4 @@ linter: - prefer_is_empty - prefer_is_not_empty - prefer_null_aware_operators - - public_member_api_docs \ No newline at end of file + - public_member_api_docs diff --git a/doc/CLASS.md b/doc/CLASS.md index 8143845..4d631b4 100644 --- a/doc/CLASS.md +++ b/doc/CLASS.md @@ -271,6 +271,25 @@ LatLng midPointBetweenTwoPoints = geodesy.calculateMidpoint(bgPoint1, pFPoint2); List arcPoints = geodesy.calculatePointsAlongGreatCircle(startPoint, endPoint, numPoints); ``` +### PolyLine Length by multiple Points + +```dart +double length = PolyLine.calculatePolyLineLength(polyLinePoints); +``` + +### Polygon Area Calculation using Shoelace formula + +```dart + double polygonArea = geodesy.calculatePolygonArea(polygonPoints); +``` + +### Intersection points of two geodesic lines + +```dart + LatLng? intersection = geodesy.calculateGeodesicLineIntersection( + start1, end1, start2, end2); +``` + --- This `Geodesy` class provides a comprehensive set of methods for performing various geodetic calculations and operations. You can use these methods to calculate distances, bearings, intersections, and more based on geographical coordinates. diff --git a/doc/CODE_OF_CONDUCT.md b/doc/CODE_OF_CONDUCT.md index 5940a2b..115fafe 100644 --- a/doc/CODE_OF_CONDUCT.md +++ b/doc/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # Code of Conduct ## Introduction diff --git a/doc/METHODS.md b/doc/METHODS.md index a3cfc7a..f174276 100644 --- a/doc/METHODS.md +++ b/doc/METHODS.md @@ -254,6 +254,25 @@ List arcPoints = GreatCirclePoint.calculatePointsAlongGreatCircle( startPoint, endPoint, numPoints); ``` +### PolyLine Length by multiple Points + +```dart +double length = PolyLine.calculatePolyLineLength(polyLinePoints); +``` + +### Polygon Area Calculation using Shoelace formula + +```dart + double polygonArea = PolygonArea.calculatePolygonArea(polygonPoints); +``` + +### Intersection points of two geodesic lines + +```dart + LatLng? intersection = GeodesicLines.calculateGeodesicLineIntersection( + start1, end1, start2, end2); +``` + --- This `Geodesy` provides a comprehensive set of methods for performing various geodetic calculations and operations. You can use these methods to calculate distances, bearings, intersections, and more based on geographical coordinates. diff --git a/example/main.dart b/example/class_example.dart similarity index 79% rename from example/main.dart rename to example/class_example.dart index b3f80b0..48e3780 100644 --- a/example/main.dart +++ b/example/class_example.dart @@ -216,4 +216,62 @@ void main() { for (var point in arcPoints) { print('${point.latitude}, ${point.longitude}'); } + + /// PolyLine Length Calculation + // Create a list of LatLng points representing your polyLine + List polyLinePoints = [ + const LatLng(42.345, -71.098), // Add your points here + const LatLng(42.355, -71.108), + const LatLng(42.365, -71.118), + // Add more points as needed + ]; + + // Calculate the length of the polyLine + double length = geodesy.calculatePolyLineLength(polyLinePoints); + + print('Length of the polyLine: $length meters'); + + /// Polygon Area by Using Shoelace formula + // Create a list of LatLng points representing your polygon + List polygonPoints = [ + const LatLng(42.345, -71.098), // Add your points here + const LatLng(42.355, -71.108), + const LatLng(42.365, -71.118), + // Add more points as needed + ]; + + // Calculate the area of the polygon + double polygonArea = geodesy.calculatePolygonArea(polygonPoints); + + print('Area of the polygon: $polygonArea square meters'); + + /// Calculate intersection points of two geodesic lines + // Example geodesic lines + LatLng start1 = const LatLng(42.345, -71.098); + LatLng end1 = const LatLng(42.355, -71.108); + LatLng start2 = const LatLng(42.355, -71.108); + LatLng end2 = const LatLng(42.365, -71.118); + + // Calculate intersection point + LatLng? intersection = + geodesy.calculateGeodesicLineIntersection(start1, end1, start2, end2); + + if (intersection != null) { + print(''''Intersection point: Latitude ${intersection.latitude}, + Longitude ${intersection.longitude}'''); + } else { + print('No intersection found.'); + } + + /// Project a point onto a geodesic line + // Example geodesic line and point + LatLng start = const LatLng(42.345, -71.098); + LatLng end = const LatLng(42.355, -71.108); + LatLng point = const LatLng(42.350, -71.103); + + // Project the point onto the geodesic line + LatLng projection = geodesy.projectPointOntoGeodesicLine(point, start, end); + + print('''Projected Point: Latitude ${projection.latitude}, + Longitude ${projection.longitude}'''); } diff --git a/example/example.dart b/example/static_example.dart similarity index 67% rename from example/example.dart rename to example/static_example.dart index 91e8676..7e80343 100644 --- a/example/example.dart +++ b/example/static_example.dart @@ -1,8 +1,10 @@ import 'package:geodesy/geodesy.dart'; +import 'package:geodesy/src/core/GeodesicMeasurements/polygon_area_by_points.dart'; +import 'package:geodesy/src/core/GeodesicMeasurements/polyline_length_by_multiple_points.dart'; import 'package:geodesy/src/core/GeodeticPointManipulation/calculate_destination_point.dart'; import 'package:geodesy/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart'; import 'package:geodesy/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart'; -import 'package:geodesy/src/core/polygon_with_hole.dart'; +import 'package:geodesy/src/core/IntersectionAndProjection/geodesic_lines.dart'; void main() { // Calculate Bounding Box @@ -86,7 +88,7 @@ void main() { final holes = [hole1]; - final area = Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); + final area = PolygonHole.calculatePolygonWithHolesArea(outerPolygon, holes); print("Area of polygon with holes: $area"); // Equirectangular approximation Calculation @@ -144,4 +146,63 @@ void main() { for (var point in arcPoints) { print('${point.latitude}, ${point.longitude}'); } + + /// PolyLine Length + // Create a list of LatLng points representing your polyLine + List polyLinePoints = [ + const LatLng(42.345, -71.098), // Add your points here + const LatLng(42.355, -71.108), + const LatLng(42.365, -71.118), + // Add more points as needed + ]; + + // Calculate the length of the polyLine + double length = PolyLine.calculatePolyLineLength(polyLinePoints); + + print('Length of the polyLine: $length meters'); + + /// Polygon Area by Using Shoelace formula + // Create a list of LatLng points representing your polygon + List polygonPoints = [ + const LatLng(42.345, -71.098), // Add your points here + const LatLng(42.355, -71.108), + const LatLng(42.365, -71.118), + // Add more points as needed + ]; + + // Calculate the area of the polygon + double polygonArea = PolygonArea.calculatePolygonArea(polygonPoints); + + print('Area of the polygon: $polygonArea square meters'); + + /// Calculate intersection points of two geodesic lines + // Example geodesic lines + LatLng start1 = const LatLng(42.345, -71.098); + LatLng end1 = const LatLng(42.355, -71.108); + LatLng start2 = const LatLng(42.355, -71.108); + LatLng end2 = const LatLng(42.365, -71.118); + + // Calculate intersection point + LatLng? intersection = GeodesicLines.calculateGeodesicLineIntersection( + start1, end1, start2, end2); + + if (intersection != null) { + print(''''Intersection point: Latitude ${intersection.latitude}, + Longitude ${intersection.longitude}'''); + } else { + print('No intersection found.'); + } + + /// Project a point onto a geodesic line + // Example geodesic line and point + LatLng start = const LatLng(42.345, -71.098); + LatLng end = const LatLng(42.355, -71.108); + LatLng point = const LatLng(42.350, -71.103); + + // Project the point onto the geodesic line + LatLng projection = + GeodesicLines.projectPointOntoGeodesicLine(point, start, end); + + print('''Projected Point: Latitude ${projection.latitude}, + Longitude ${projection.longitude}'''); } diff --git a/lib/geodesy.dart b/lib/geodesy.dart index 3d8ac01..524d75b 100644 --- a/lib/geodesy.dart +++ b/lib/geodesy.dart @@ -1,3 +1,9 @@ export 'src/geodesy.dart'; export 'package:latlong2/latlong.dart' hide pi; export 'package:geodesy/src/core/core.dart'; +export 'package:geodesy/src/core/GeodesicMeasurements/polygon_area_by_points.dart'; +export 'package:geodesy/src/core/GeodesicMeasurements/polyline_length_by_multiple_points.dart'; +export 'package:geodesy/src/core/GeodeticPointManipulation/calculate_destination_point.dart'; +export 'package:geodesy/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart'; +export 'package:geodesy/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart'; +export 'package:geodesy/src/core/IntersectionAndProjection/geodesic_lines.dart'; diff --git a/lib/src/core/DistanceCalculations/cross_track_distance_to.dart b/lib/src/core/DistanceCalculations/cross_track_distance_to.dart new file mode 100644 index 0000000..f7fa499 --- /dev/null +++ b/lib/src/core/DistanceCalculations/cross_track_distance_to.dart @@ -0,0 +1,55 @@ +import '../core.dart'; + +/// Track Distance class for calculating the signed distance between a +/// geographical point and the great circle defined by a start and end point. +class TrackDistance { + /// Calculate the signed distance from a geographic point to the great circle + /// defined by the start and end points, also known as cross-track distance. + /// + /// The cross-track distance is the shortest distance from the point `l1` to + /// + /// the great circle defined by the line between `start` and `end`. + /// It can be negative or positive depending on whether the point is to + /// the left or right of the path. + /// + /// [l1] - The geographic point for which to calculate the cross-track + /// distance. + /// + /// [start] - The starting point of the great circle path. + /// + /// [end] - The ending point of the great circle path. + /// + /// [radius] - (Optional) The radius of the Earth in meters. If not provided, + /// it defaults to the mean radius of the Earth + /// (approximately 6371 kilometers). + /// + /// Returns the cross-track distance in meters. A positive value indicates + /// + /// that the point `l1` is to the right of the great circle path, + /// and a negative value + /// indicates that it is to the left. + static num crossTrackDistanceTo(LatLng l1, LatLng start, LatLng end, + [num? radius]) { + // Calculate the radius of the Earth if not provided. + final R = radius ?? 6371e3; + + // Calculate the distance between start and l1 in radians. + final distStartL1 = GeoPoints.distanceBetweenTwoGeoPoints(start, l1, R) / R; + + // Calculate the initial bearing from start to l1 in radians. + final bearingStartL1 = degToRadian( + BearingBetweenTwoGeoPoints.bearingBetweenTwoGeoPoints(start, l1) + .toDouble()); + + // Calculate the initial bearing from start to end in radians. + final bearingStartEnd = degToRadian( + BearingBetweenTwoGeoPoints.bearingBetweenTwoGeoPoints(start, end) + .toDouble()); + + // Calculate the cross-track distance using the spherical law of sines. + final x = asin(sin(distStartL1) * sin(bearingStartL1 - bearingStartEnd)); + + // Return the cross-track distance in meters. + return x * R; + } +} diff --git a/lib/src/core/DistanceCalculations/equirectangular_distance.dart b/lib/src/core/DistanceCalculations/equirectangular_distance.dart index db01354..0dc657f 100644 --- a/lib/src/core/DistanceCalculations/equirectangular_distance.dart +++ b/lib/src/core/DistanceCalculations/equirectangular_distance.dart @@ -1,23 +1,40 @@ import '../core.dart'; -/// Equirectangular approximation Calculation +/// A Equi- Rectangular Approximation class for calculating the distance +/// between two geographical points +/// using the Equirectangular approximation formula. class EquirectangularApproximation { - /// EquirectangularDistance function takes two LatLng objects representing the - /// latitude and longitude coordinates of two points. It calculates the - /// distance between the points using the Equirectangular - /// approximation formula and the Earth's radius. + /// Calculates the distance between two geographic points using the + /// Equirectangular approximation formula and the Earth's radius. + /// + /// The Equirectangular approximation formula is a simple method to estimate + /// the distance between two points on the Earth's surface, assuming a + /// perfect sphere. While not as accurate as more complex methods, it is + /// computationally efficient and suitable for shorter distances. + /// + /// [point1] - The first LatLng object representing the latitude and longitude + /// coordinates of the first point. + /// + /// [point2] - The second LatLng object representing the latitude and + /// longitude coordinates of the second point. + /// + /// Returns the distance between the two points in kilometers, calculated + /// using the Equirectangular approximation formula. static double equirectangularDistance(LatLng point1, LatLng point2) { - final double radius = 6371.0; // Earth's radius in kilometers + // Earth's radius in kilometers. + final double radius = 6371.0; + // Extract latitude and longitude values from the LatLng objects. double lat1 = point1.latitude; double lon1 = point1.longitude; double lat2 = point2.latitude; double lon2 = point2.longitude; + // Calculate the differences in longitude and latitude. double x = (lon2 - lon1) * (radius * cos((lat1 + lat2) / 2)); double y = (lat2 - lat1) * radius; - // Calculate distance using Pythagorean theorem + // Calculate the distance using the Pythagorean theorem. double distance = sqrt(x * x + y * y); return distance; diff --git a/lib/src/core/DistanceCalculations/points_in_range.dart b/lib/src/core/DistanceCalculations/points_in_range.dart new file mode 100644 index 0000000..fdd750e --- /dev/null +++ b/lib/src/core/DistanceCalculations/points_in_range.dart @@ -0,0 +1,33 @@ +import '../core.dart'; + +/// A Point Range class for working with geographical points and ranges. +class PointRange { + /// Get a list of [LatLng] points within a specified distance from a given point. + /// + /// This function takes a central point, a list of points to check, and a distance + /// (in meters) to determine which points from the list fall within the specified + /// range. + /// + /// [point] - The central [LatLng] point from which the distance is measured. + /// + /// [pointsToCheck] - A list of [LatLng] points to be checked for proximity to the + /// central point. + /// + /// [distance] - The maximum distance (in meters) within which points are considered + /// to be within range of the central point. + /// + /// Returns a list of [LatLng] points that are within the specified distance from + /// the central point. + static List pointInRange( + LatLng point, List pointsToCheck, num distance) { + final geoFencedPoints = []; + + for (final LatLng p in pointsToCheck) { + if (GeoPoints.distanceBetweenTwoGeoPoints(point, p) <= distance) { + geoFencedPoints.add(p); + } + } + + return geoFencedPoints; + } +} diff --git a/lib/src/core/DistanceCalculations/spherical_law_of_cosines_distance.dart b/lib/src/core/DistanceCalculations/spherical_law_of_cosines_distance.dart index 9519f90..9c6436d 100644 --- a/lib/src/core/DistanceCalculations/spherical_law_of_cosines_distance.dart +++ b/lib/src/core/DistanceCalculations/spherical_law_of_cosines_distance.dart @@ -1,21 +1,37 @@ import '../core.dart'; -/// Calculate Spherical Law Of Cosines Distance +/// A Spherical Law Of Cosines class for calculating the distance between two geographical points +/// using the Spherical Law of Cosines formula. class SphericalLawOfCosines { - /// This function takes two LatLng objects representing the latitude and - /// longitude coordinates of two points. It calculates the distance - /// between the points using the Spherical Law of Cosines formula - /// and the Earth's radius. + /// Calculates the distance between two geographic points using the + /// Spherical Law of Cosines formula and the Earth's radius. + /// + /// The Spherical Law of Cosines formula provides a more accurate + /// method for calculating distances between two points on the Earth's + /// surface, taking into account the curvature of the Earth. + /// + /// [point1] - The first LatLng object representing the latitude and + /// longitude coordinates of the first point. + /// + /// [point2] - The second LatLng object representing the latitude and + /// longitude coordinates of the second point. + /// + /// Returns the distance between the two points in kilometers, calculated + /// using the Spherical Law of Cosines formula. static double sphericalLawOfCosinesDistance(LatLng point1, LatLng point2) { - final double radius = 6371.0; // Earth's radius in kilometers + // Earth's radius in kilometers. + final double radius = 6371.0; + // Convert latitude and longitude values to radians. double lat1 = (point1.latitude).toRadians(); double lon1 = (point1.longitude).toRadians(); double lat2 = (point2.latitude).toRadians(); double lon2 = (point2.longitude).toRadians(); + // Calculate the difference in longitude. double deltaLon = lon2 - lon1; + // Calculate the distance using the Spherical Law of Cosines formula. double distance = acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(deltaLon)) * radius; diff --git a/lib/src/core/vincenty_distance_calculation.dart b/lib/src/core/DistanceCalculations/vincenty_distance_calculation.dart similarity index 74% rename from lib/src/core/vincenty_distance_calculation.dart rename to lib/src/core/DistanceCalculations/vincenty_distance_calculation.dart index 0da7d17..63afcad 100644 --- a/lib/src/core/vincenty_distance_calculation.dart +++ b/lib/src/core/DistanceCalculations/vincenty_distance_calculation.dart @@ -1,11 +1,24 @@ -import 'core.dart'; +import '../core.dart'; -/// Vincenty formula for Geodesic Distance Calculation +/// Vincenty Distance class for calculating geodesic distances between two points on +/// the surface of an ellipsoid using the Vincenty formula. class VincentyDistance { - /// Calculate the distance between two points on the surface of an ellipsoid. - /// It's calculate geodesic distances, which are the shortest distances - /// between two points on the curved surface of an - /// ellipsoid (such as the Earth). + /// Calculates the geodesic distance between two points on the surface of an + /// ellipsoid using the Vincenty formula. + /// + /// The Vincenty formula provides a highly accurate method for calculating + /// geodesic distances, which are the shortest distances between two points + /// on the curved surface of an ellipsoid (such as the Earth). + /// + /// [lat1] - The latitude of the first point in decimal degrees. + /// + /// [lon1] - The longitude of the first point in decimal degrees. + /// + /// [lat2] - The latitude of the second point in decimal degrees. + /// + /// [lon2] - The longitude of the second point in decimal degrees. + /// + /// Returns the geodesic distance between the two points in meters. static double vincentyDistance( double lat1, double lon1, diff --git a/lib/src/core/get_rectangle_bounds.dart b/lib/src/core/GeoBounds/get_rectangle_bounds.dart similarity index 60% rename from lib/src/core/get_rectangle_bounds.dart rename to lib/src/core/GeoBounds/get_rectangle_bounds.dart index 179826a..b5c9215 100644 --- a/lib/src/core/get_rectangle_bounds.dart +++ b/lib/src/core/GeoBounds/get_rectangle_bounds.dart @@ -1,8 +1,22 @@ -import 'core.dart'; +import '../core.dart'; -/// +/// Rectangle Bounds class for calculating the bounding rectangle of a set of LatLng points. class RectangleBounds { - /// GetRectangleBounds + /// Calculates the bounding rectangle (four corner points) that encloses a list + /// of LatLng coordinates. + /// + /// [polygonCoords] - A list of LatLng coordinates to calculate the bounding + /// rectangle for. + /// + /// Returns a list of LatLng points representing the corners of the bounding + /// rectangle, ordered in the following sequence: + /// + /// 1. Bottom-left corner + /// 2. Bottom-right corner + /// 3. Top-right corner + /// 4. Top-left corner + /// + /// The bounding rectangle encompasses all the points provided in the input list. static List getRectangleBounds(List polygonCoords) { num minLatitude = double.infinity.toDouble(); num maxLatitude = double.negativeInfinity.toDouble(); diff --git a/lib/src/core/bounding_box.dart b/lib/src/core/GeoSpatial/bounding_box.dart similarity index 53% rename from lib/src/core/bounding_box.dart rename to lib/src/core/GeoSpatial/bounding_box.dart index bca2380..b0c3431 100644 --- a/lib/src/core/bounding_box.dart +++ b/lib/src/core/GeoSpatial/bounding_box.dart @@ -1,8 +1,17 @@ -import 'core.dart'; +import '../core.dart'; -/// +/// A BoundingBox class for calculating bounding boxes and checking if a point is within a bounding box. class BoundingBox { - /// Calculate Bounding Box per distance in Kilometers + /// Calculates the bounding box (rectangle) around a center point with a specified + /// distance in kilometers. + /// + /// [centerPoint] - The LatLng coordinates of the center point. + /// + /// [distanceInKm] - The distance in kilometers from the center point to each side + /// of the bounding box. + /// + /// Returns a list of two LatLng points representing the top-left and bottom-right + /// corners of the bounding box. static List calculateBoundingBox( LatLng centerPoint, num distanceInKm) { // Earth's radius in kilometers @@ -21,7 +30,16 @@ class BoundingBox { return [topLeft, bottomRight]; } - /// Check if a given geo point is in the bounding box + /// Checks if a given geo point is inside the bounding box defined by the top-left + /// and bottom-right corners. + /// + /// [l] - The LatLng coordinates of the point to check. + /// + /// [topLeft] - The LatLng coordinates of the top-left corner of the bounding box. + /// + /// [bottomRight] - The LatLng coordinates of the bottom-right corner of the bounding box. + /// + /// Returns true if the point is within the bounding box; otherwise, returns false. static bool isGeoPointInBoundingBox( LatLng l, LatLng topLeft, LatLng bottomRight) { return (bottomRight.latitude <= l.latitude && diff --git a/lib/src/core/GeodesicMeasurements/polygon_area_by_points.dart b/lib/src/core/GeodesicMeasurements/polygon_area_by_points.dart new file mode 100644 index 0000000..4c77426 --- /dev/null +++ b/lib/src/core/GeodesicMeasurements/polygon_area_by_points.dart @@ -0,0 +1,35 @@ +import '../core.dart'; + +/// A Polygon Area class for calculating the area of a polygon using the Shoelace formula. +class PolygonArea { + /// Calculates the area of a polygon defined by a list of LatLng points. + /// + /// The function checks if the number of points is less than 3, in which case + /// it returns 0 because a polygon must have at least 3 points to form an area. + /// It then iterates through the points and uses the Shoelace formula to calculate + /// the signed area of the polygon. The result is the absolute value of the + /// calculated area, divided by 2, which gives the final area in square meters. + /// + /// [polygonPoints] - A list of LatLng points representing the vertices of the polygon. + /// + /// Returns the area of the polygon in square meters. + static double calculatePolygonArea(List polygonPoints) { + if (polygonPoints.length < 3) { + // A polygon must have at least 3 points to form an area. + return 0.0; + } + + double area = 0.0; + + for (int i = 0; i < polygonPoints.length; i++) { + final LatLng currentPoint = polygonPoints[i]; + final LatLng nextPoint = polygonPoints[(i + 1) % polygonPoints.length]; + + area += (currentPoint.latitude + nextPoint.latitude) * + (currentPoint.longitude - nextPoint.longitude); + } + + area = area.abs() / 2.0; + return area; + } +} diff --git a/lib/src/core/GeodesicMeasurements/polyline_length_by_multiple_points.dart b/lib/src/core/GeodesicMeasurements/polyline_length_by_multiple_points.dart new file mode 100644 index 0000000..41d2d7e --- /dev/null +++ b/lib/src/core/GeodesicMeasurements/polyline_length_by_multiple_points.dart @@ -0,0 +1,23 @@ +import '../core.dart'; + +/// A PolyLine class for calculating the length of a polyline formed by connecting +/// multiple points. +class PolyLine { + /// Calculates the length of a polyline by summing the distances between + /// consecutive points using the Haversine distance formula. + /// + /// [polyLinePoints] - A list of LatLng points representing the vertices of the + /// polyline, ordered in the sequence they are connected. + /// + /// Returns the total length of the polyline in meters. + static double calculatePolyLineLength(List polyLinePoints) { + final Distance distance = const Distance(); + double length = 0.0; + + for (int i = 0; i < polyLinePoints.length - 1; i++) { + length += distance(polyLinePoints[i], polyLinePoints[i + 1]); + } + + return length; + } +} diff --git a/lib/src/core/GeodeticPointManipulation/calculate_destination_point.dart b/lib/src/core/GeodeticPointManipulation/calculate_destination_point.dart index a172bd6..6b2e700 100644 --- a/lib/src/core/GeodeticPointManipulation/calculate_destination_point.dart +++ b/lib/src/core/GeodeticPointManipulation/calculate_destination_point.dart @@ -1,14 +1,23 @@ import '../core.dart'; -/// Geodetic Point Manipulation - Rhumb Line Destination Formula - +/// A Destination Point class for calculating destination points on the Earth's surface +/// using the Rhumb Line Destination formula. class DestinationPoint { - /// Calculate the destination point given an initial point - /// (latitude and longitude), a bearing (azimuth), and a distance, - /// you can use the formula known as the "Direct Rhumb Line" formula or - /// "Rhumb Line Destination" formula. - /// This formula provides an approximate destination point when traveling - /// along a constant compass direction. + /// Calculates the destination point on the Earth's surface given an initial point + /// (latitude and longitude), a bearing (azimuth) in degrees, and a distance in + /// kilometers. This formula provides an approximate destination point when + /// traveling along a constant compass direction. + /// + /// [initialPoint] - The initial LatLng point representing the starting location. + /// + /// [bearingDegrees] - The bearing or azimuth angle in degrees, indicating the + /// compass direction from the initial point. + /// + /// [distanceKm] - The distance in kilometers to travel along the constant bearing + /// direction from the initial point to the destination point. + /// + /// Returns a new LatLng object representing the destination point's latitude and + /// longitude coordinates. static LatLng calculateDestinationPoint( LatLng initialPoint, double bearingDegrees, double distanceKm) { final double radius = 6371.0; // Earth's radius in kilometers diff --git a/lib/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart b/lib/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart index 01315d0..0c30274 100644 --- a/lib/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart +++ b/lib/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart @@ -1,17 +1,28 @@ import '../core.dart'; -/// Geodetic Point Manipulation - Calculate Point Along Great Circle +/// A Great Circle Point class for calculating points along the great circle arc +/// connecting two LatLng points. class GreatCirclePoint { - /// calculates a list of points along the great circle arc connecting - /// the startPoint and endPoint. - /// It uses linear interpolation to calculate the intermediate points - /// at equally spaced intervals along the arc. + /// Calculates a list of points along the great circle arc connecting + /// the [startPoint] and [endPoint]. + /// + /// It uses linear interpolation to calculate intermediate points at equally + /// spaced intervals along the arc. + /// + /// [startPoint] - The starting LatLng point of the great circle arc. + /// + /// [endPoint] - The ending LatLng point of the great circle arc. + /// + /// [numPoints] - The number of points to calculate along the arc, including + /// the start and end points. + /// + /// Returns a list of LatLng points representing points along the great circle arc. static List calculatePointsAlongGreatCircle( LatLng startPoint, LatLng endPoint, int numPoints) { final List points = []; - final double startLat = - startPoint.latitude * (pi / 180.0); // Convert degrees to radians + // Convert degrees to radians + final double startLat = startPoint.latitude * (pi / 180.0); final double startLon = startPoint.longitude * (pi / 180.0); final double endLat = endPoint.latitude * (pi / 180.0); final double endLon = endPoint.longitude * (pi / 180.0); @@ -23,7 +34,8 @@ class GreatCirclePoint { double lat = startLat * (1 - t) + endLat * t; double lon = startLon * (1 - t) + endLon * t; - lat = lat * (180.0 / pi); // Convert radians to degrees + // Convert radians back to degrees + lat = lat * (180.0 / pi); lon = lon * (180.0 / pi); points.add(LatLng(lat, lon)); diff --git a/lib/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart b/lib/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart index 7727138..2754706 100644 --- a/lib/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart +++ b/lib/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart @@ -1,9 +1,17 @@ import '../core.dart'; -/// Geodetic Point Manipulation - Midpoint between two points +/// A MidPoint Between Two Points class for calculating the midpoint between two points on the +/// Earth's surface +/// specified by their latitude and longitude. class MidPointBetweenTwoPoints { - /// Calculate the midpoint between two points on the Earth's surface - /// (specified by their latitude and longitude), + /// Calculates the midpoint between two points on the Earth's surface specified + /// by their latitude and longitude coordinates. + /// + /// [point1] - The first LatLng point. + /// + /// [point2] - The second LatLng point. + /// + /// Returns a LatLng object representing the midpoint between the two input points. static LatLng calculateMidpoint(LatLng point1, LatLng point2) { double lat1 = point1.latitude * (pi / 180.0); // Convert degrees to radians double lon1 = point1.longitude * (pi / 180.0); @@ -13,9 +21,10 @@ class MidPointBetweenTwoPoints { double midLat = (lat1 + lat2) / 2; double midLon = (lon1 + lon2) / 2; - midLon = (midLon + pi) % (2 * pi) - pi; // Normalize to -π to π radians + // Normalize longitude to the range of -π to π radians + midLon = (midLon + pi) % (2 * pi) - pi; - return LatLng(midLat * (180.0 / pi), - midLon * (180.0 / pi)); // Convert radians to degrees + // Convert radians back to degrees + return LatLng(midLat * (180.0 / pi), midLon * (180.0 / pi)); } } diff --git a/lib/src/core/GeographicBearing/bearing_between_two_geo_points.dart b/lib/src/core/GeographicBearing/bearing_between_two_geo_points.dart new file mode 100644 index 0000000..1a8506e --- /dev/null +++ b/lib/src/core/GeographicBearing/bearing_between_two_geo_points.dart @@ -0,0 +1,43 @@ +import '../core.dart'; + +/// A Bearing Between Two GeoPoints class for calculating bearings between two geographical points +/// represented by their latitude and longitude coordinates. +class BearingBetweenTwoGeoPoints { + /// Calculates the initial bearing (azimuth) from point [l1] to point [l2]. + /// + /// [l1] - The LatLng coordinates of the starting point. + /// + /// [l2] - The LatLng coordinates of the ending point. + /// + /// Returns the initial bearing in degrees, where 0 degrees indicates + /// a northward direction and 90 degrees indicates an eastward direction. + static num bearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { + final l1LatRadians = degToRadian(l1.latitude); + final l2LatRadians = degToRadian(l2.latitude); + final lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); + + final y = sin(lngRadiansDiff) * cos(l2LatRadians); + final x = cos(l1LatRadians) * sin(l2LatRadians) - + sin(l1LatRadians) * cos(l2LatRadians) * cos(lngRadiansDiff); + + final radians = atan2(y, x); + final degrees = radianToDeg(radians); + + return (degrees + 360) % 360; + } + + /// Calculates the final bearing (azimuth) from point [l1] to point [l2]. + /// + /// The final bearing is the initial bearing when traveling in the reverse + /// direction from point [l2] to point [l1]. + /// + /// [l1] - The LatLng coordinates of the starting point. + /// + /// [l2] - The LatLng coordinates of the ending point. + /// + /// Returns the final bearing in degrees, where 0 degrees indicates + /// a northward direction and 90 degrees indicates an eastward direction. + static num finalBearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { + return (bearingBetweenTwoGeoPoints(l2, l1) + 180) % 360; + } +} diff --git a/lib/src/core/destination_point_by_distance_and_bearing.dart b/lib/src/core/GeographicBearing/destination_point_by_distance_and_bearing.dart similarity index 52% rename from lib/src/core/destination_point_by_distance_and_bearing.dart rename to lib/src/core/GeographicBearing/destination_point_by_distance_and_bearing.dart index e044aff..b512601 100644 --- a/lib/src/core/destination_point_by_distance_and_bearing.dart +++ b/lib/src/core/GeographicBearing/destination_point_by_distance_and_bearing.dart @@ -1,12 +1,22 @@ -import 'core.dart'; +import '../core.dart'; -/// DestinationPointByDistanceAndBearing +/// A DistanceAndBearing class for calculating destination points based on a starting point, +/// a distance, a direction (bearing), and optionally a radius. class DistanceAndBearing { - /// DestinationPointByDistanceAndBearing: - /// This code takes a starting point, a distance, a direction (bearing), and - /// optionally a radius, and calculates the coordinates of a new point based - /// on these inputs, using trigonometric calculations and - /// the Haversine formula for spherical geometry. + /// Calculates the coordinates of a destination point based on a starting point, + /// a distance, a direction (bearing), and optionally a radius. + /// + /// [l] - The LatLng coordinates of the starting point. + /// + /// [distance] - The distance in meters to travel from the starting point. + /// + /// [bearing] - The direction or bearing in degrees (0 to 360) from the starting + /// point (0 degrees points north, 90 degrees points east, etc.). + /// + /// [radius] - The radius of the Earth or the sphere being considered. It is + /// an optional parameter and defaults to the Earth's radius (6371e3 meters). + /// + /// Returns a new LatLng object representing the coordinates of the destination point. static LatLng destinationPointByDistanceAndBearing( LatLng l, num distance, num bearing, [num? radius]) { @@ -31,7 +41,7 @@ class DistanceAndBearing { final x = cosAngularDistanceRadius - sinLatRadians * sinLatRadians2; final num lngRadians2 = lngRadians + atan2(y, x); - return LatLng(radianToDeg(latRadians2 as double), - (radianToDeg(lngRadians2 as double) + 540) % 360 - 180); + return LatLng(radianToDeg(latRadians2.toDouble()), + (radianToDeg(lngRadians2.toDouble()) + 540.0) % 360.0 - 180.0); } } diff --git a/lib/src/core/IntersectionAndProjection/geodesic_lines.dart b/lib/src/core/IntersectionAndProjection/geodesic_lines.dart new file mode 100644 index 0000000..6ad6270 --- /dev/null +++ b/lib/src/core/IntersectionAndProjection/geodesic_lines.dart @@ -0,0 +1,139 @@ +import '../core.dart'; + +/// A Geodesic Lines class for calculating geodesic lines, intersections, and projections. +class GeodesicLines { + /// Calculates the intersection point of two geodesic lines defined by their + /// starting and ending points. + /// + /// [start1] - The LatLng coordinates of the starting point of the first line. + /// + /// [end1] - The LatLng coordinates of the ending point of the first line. + /// + /// [start2] - The LatLng coordinates of the starting point of the second line. + /// + /// [end2] - The LatLng coordinates of the ending point of the second line. + /// + /// Returns the intersection point as a LatLng object, or null if no intersection + /// is found. + static LatLng? calculateGeodesicLineIntersection( + LatLng start1, LatLng end1, LatLng start2, LatLng end2) { + const double earthRadius = 6371000; // Earth's radius in meters + + final Distance distance = const Distance(); + final num bearing13 = initialBearing(start1, end1); + final num bearing23 = initialBearing(start2, end2); + final num lat1 = (start1.latitude).toRadians(); + final num lon1 = (start1.longitude).toRadians(); + final num lat2 = (start2.latitude).toRadians(); + final num lon2 = (start2.longitude).toRadians(); + final num lat3 = (end2.latitude).toRadians(); + + final num dLon = lon2 - lon1; + + final num dist12 = distance(start1, start2); + if (dist12 == 0) { + return null; // The two starting points are the same. + } + + final num alpha1 = bearing13; + final num alpha2 = bearing23; + final num alpha3 = acos(cos(dist12 / earthRadius) / + (cos(lat1) * cos(lat2) * sin(alpha1) * sin(alpha2) * sin(dLon))); + final num dist13 = earthRadius * alpha3; + final num bearingA = asin(sin(alpha1) * sin(dist13 / earthRadius)); + final num bearingB = asin(sin(alpha2) * sin(dist13 / earthRadius)); + + final num alpha4 = acos(-cos(dist13 / earthRadius) / + (cos(lat1) * cos(lat3) * sin(bearingA) * sin(bearingB))); + final num dist14 = earthRadius * alpha4; + + if (dist14.isNaN) { + return null; // No intersection found. + } + + final num lat4 = atan2( + sin(lat1) * cos(dist14 / earthRadius) * sin(bearingA), + cos(lat1) * sin(dist14 / earthRadius) * sin(bearingA) * cos(bearing13) - + sin(lat1) * + cos(dist14 / earthRadius) * + cos(bearingA) * + sin(bearing13) + + cos(lat1) * + cos(dist14 / earthRadius) * + cos(bearingA) * + cos(bearing13)); + + final num lon4 = lon1 + + atan2( + sin(bearing13) * sin(dist14 / earthRadius) * sin(bearingA), + cos(dist14 / earthRadius) - + sin(lat1) * sin(lat4) - + cos(lat1) * cos(lat4) * cos(bearingA)); + return LatLng((lat4).toDegrees(), (lon4).toDegrees()); + } + + /// Projects a given point onto a geodesic line defined by two endpoints. + /// + /// [point] - The LatLng coordinates of the point to project. + /// + /// [start] - The LatLng coordinates of the starting point of the geodesic line. + /// + /// [end] - The LatLng coordinates of the ending point of the geodesic line. + /// + /// Returns the projected point as a LatLng object. + static LatLng projectPointOntoGeodesicLine( + LatLng point, LatLng start, LatLng end) { + const double earthRadius = 6371000; // Earth's radius in meters + + final Distance distance = const Distance(); + + // Calculate the initial bearing from start to end + final double initialBearingValue = initialBearing(start, end); + + // Calculate the distance between start and the given point + final double distanceToStart = distance(start, point); + + // Calculate the angular distance in radians + final double angularDistance = distanceToStart / earthRadius; + + // Calculate the final bearing using the initial bearing and + // angular distance + final double finalBearing = + asin(sin(angularDistance) * sin(initialBearingValue)); + + // Calculate the destination point (projection) on the line + final double lat1 = (start.latitude).toRadians(); + final double lon1 = (start.longitude).toRadians(); + final double lat2 = asin(sin(lat1) * cos(angularDistance) + + cos(lat1) * sin(angularDistance) * cos(finalBearing)); + final double lon2 = lon1 + + atan2(sin(finalBearing) * sin(angularDistance) * cos(lat1), + cos(angularDistance) - sin(lat1) * sin(lat2)); + + return LatLng((lat2).toDegrees(), (lon2).toDegrees()); + } + + /// Calculates the initial bearing (azimuth) from one point to another. + /// + /// [start] - The LatLng coordinates of the starting point. + /// + /// [end] - The LatLng coordinates of the ending point. + /// + /// Returns the initial bearing in degrees, where 0 degrees indicates + /// a northward direction and 90 degrees indicates an eastward direction. + static double initialBearing(LatLng start, LatLng end) { + final double lat1 = (start.latitude).toRadians(); + final double lon1 = (start.longitude).toRadians(); + final double lat2 = (end.latitude).toRadians(); + final double lon2 = (end.longitude).toRadians(); + + final double dLon = lon2 - lon1; + + final double y = sin(dLon) * cos(lat2); + final double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon); + final double initialBearings = atan2(y, x); + + // Convert initial bearing from radians to degrees + return (initialBearings).toDegrees(); + } +} diff --git a/lib/src/core/find_polygon_centroid.dart b/lib/src/core/Polygon/find_polygon_centroid.dart similarity index 71% rename from lib/src/core/find_polygon_centroid.dart rename to lib/src/core/Polygon/find_polygon_centroid.dart index 358e34c..b764938 100644 --- a/lib/src/core/find_polygon_centroid.dart +++ b/lib/src/core/Polygon/find_polygon_centroid.dart @@ -1,8 +1,12 @@ -import 'core.dart'; +import '../core.dart'; -/// +/// A Polygon Centroid class for finding the centroid of polygons. class PolygonCentroid { - /// Finds the centroid of polygons + /// Finds the centroid of a polygon defined by a list of LatLng points. + /// + /// [polygons] - A list of LatLng coordinates representing the vertices of the polygon. + /// + /// Returns the centroid of the polygon as a LatLng object. static LatLng findPolygonCentroid(List polygons) { num x = 0; num y = 0; diff --git a/lib/src/core/get_polygon_intersection.dart b/lib/src/core/Polygon/get_polygon_intersection.dart similarity index 68% rename from lib/src/core/get_polygon_intersection.dart rename to lib/src/core/Polygon/get_polygon_intersection.dart index 239a016..c87669f 100644 --- a/lib/src/core/get_polygon_intersection.dart +++ b/lib/src/core/Polygon/get_polygon_intersection.dart @@ -1,8 +1,14 @@ -import 'core.dart'; +import '../core.dart'; -/// +/// A PolygonIntersection class for finding the intersection points of two polygons. class PolygonIntersection { - ///Get Polygon Intersection + /// Get the intersection points of two polygons. + /// + /// [polygon1] - A list of LatLng coordinates representing the vertices of the first polygon. + /// [polygon2] - A list of LatLng coordinates representing the vertices of the second polygon. + /// + /// Returns a list of LatLng points representing the intersection points of the two polygons. + static List getPolygonIntersection( List polygon1, List polygon2) { final List intersectionPoints = []; @@ -27,6 +33,14 @@ class PolygonIntersection { return intersectionPoints; } + /// Calculate the intersection point of two line segments defined by their endpoints. + /// + /// [start1] - The starting point of the first line segment. + /// [end1] - The ending point of the first line segment. + /// [start2] - The starting point of the second line segment. + /// [end2] - The ending point of the second line segment. + /// + /// Returns the intersection point as a LatLng object if it exists; otherwise, returns null. static LatLng? _getLineIntersection( LatLng start1, LatLng end1, LatLng start2, LatLng end2) { final num x1 = start1.latitude; @@ -61,6 +75,14 @@ class PolygonIntersection { } } + /// Check if a point lies on a line segment defined by its endpoints. + /// + /// [point] - The point to check. + /// [lineStart] - The starting point of the line segment. + /// [lineEnd] - The ending point of the line segment. + /// + /// Returns true if the point lies on the line segment; otherwise, returns false. + static bool _isPointOnLine(LatLng point, LatLng lineStart, LatLng lineEnd) { final minX = lineStart.latitude < lineEnd.latitude ? lineStart.latitude diff --git a/lib/src/core/polygon_with_hole.dart b/lib/src/core/Polygon/polygon_with_hole.dart similarity index 68% rename from lib/src/core/polygon_with_hole.dart rename to lib/src/core/Polygon/polygon_with_hole.dart index 5d1c6fa..33eff4b 100644 --- a/lib/src/core/polygon_with_hole.dart +++ b/lib/src/core/Polygon/polygon_with_hole.dart @@ -1,12 +1,13 @@ -import 'core.dart'; +import '../core.dart'; -/// Polygon -class Polygon { - /// In this code, the calculatePolygonWithHolesArea function takes the - /// outerPolygon (a list of LatLng points) and a list of holes - /// (each hole represented as a list of LatLng points). - /// It calculates the area using the given polygon and holes and - /// returns the final area. +/// A Polygon Hole class for calculating the area of a polygon with holes. +class PolygonHole { + /// Calculate the area of a polygon with holes. + /// + /// [outerPolygon] - A list of LatLng coordinates representing the vertices of the outer polygon. + /// [holes] - A list of holes, where each hole is represented as a list of LatLng coordinates. + /// + /// Returns the area of the polygon with holes in square meters. static double calculatePolygonWithHolesArea( List outerPolygon, List> holes) { // Calculate and sum the areas of holes diff --git a/lib/src/core/bearing_between_two_geo_points.dart b/lib/src/core/bearing_between_two_geo_points.dart deleted file mode 100644 index 26ff6fb..0000000 --- a/lib/src/core/bearing_between_two_geo_points.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'core.dart'; - -/// -class BearingBetweenTwoGeoPoints { - /// Calculate the bearing from point l1 to point l2 - static num bearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { - final l1LatRadians = degToRadian(l1.latitude); - final l2LatRadians = degToRadian(l2.latitude); - final lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); - - final y = sin(lngRadiansDiff) * cos(l2LatRadians); - final x = cos(l1LatRadians) * sin(l2LatRadians) - - sin(l1LatRadians) * cos(l2LatRadians) * cos(lngRadiansDiff); - - final radians = atan2(y, x); - final degrees = radianToDeg(radians); - - return (degrees + 360) % 360; - } - - /// - static num finalBearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { - return (bearingBetweenTwoGeoPoints(l2, l1) + 180) % 360; - } -} diff --git a/lib/src/core/core.dart b/lib/src/core/core.dart index a5cbd76..a65d861 100644 --- a/lib/src/core/core.dart +++ b/lib/src/core/core.dart @@ -1,15 +1,17 @@ export 'package:geodesy/src/math/to_radian.dart'; -export 'package:geodesy/src/core/bearing_between_two_geo_points.dart'; -export 'package:geodesy/src/core/bounding_box.dart'; -export 'package:geodesy/src/core/points_in_range.dart'; -export 'package:geodesy/src/core/get_rectangle_bounds.dart'; -export 'package:geodesy/src/core/get_polygon_intersection.dart'; -export 'package:geodesy/src/core/find_polygon_centroid.dart'; +export 'package:geodesy/src/core/GeographicBearing/bearing_between_two_geo_points.dart'; +export 'package:geodesy/src/core/GeoSpatial/bounding_box.dart'; +export 'package:geodesy/src/core/DistanceCalculations/points_in_range.dart'; +export 'package:geodesy/src/core/GeoBounds/get_rectangle_bounds.dart'; export 'package:geodesy/src/core/DistanceCalculations/geo_points.dart'; -export 'package:geodesy/src/core/vincenty_distance_calculation.dart'; -export 'package:geodesy/src/core/destination_point_by_distance_and_bearing.dart'; -export 'package:geodesy/src/core/cross_track_distance_to.dart'; +export 'package:geodesy/src/core/DistanceCalculations/vincenty_distance_calculation.dart'; +export 'package:geodesy/src/core/GeographicBearing/destination_point_by_distance_and_bearing.dart'; +export 'package:geodesy/src/core/DistanceCalculations/cross_track_distance_to.dart'; +export 'package:geodesy/src/math/to_degrees.dart'; export 'package:geodesy/src/core/DistanceCalculations/equirectangular_distance.dart'; +export 'package:geodesy/src/core/Polygon/find_polygon_centroid.dart'; +export 'package:geodesy/src/core/Polygon/get_polygon_intersection.dart'; +export 'package:geodesy/src/core/Polygon/polygon_with_hole.dart'; export 'package:geodesy/src/core/DistanceCalculations/spherical_law_of_cosines_distance.dart'; export 'package:latlong2/latlong.dart' hide pi; export 'dart:math'; diff --git a/lib/src/core/cross_track_distance_to.dart b/lib/src/core/cross_track_distance_to.dart deleted file mode 100644 index 418d889..0000000 --- a/lib/src/core/cross_track_distance_to.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'core.dart'; - -/// -class TrackDistance { - /// Calculate signed distance from a geo point - /// to create circle with start and end points - static num crossTrackDistanceTo(LatLng l1, LatLng start, LatLng end, - [num? radius]) { - final R = radius ?? 6371e3; - - final distStartL1 = GeoPoints.distanceBetweenTwoGeoPoints(start, l1, R) / R; - final bearingStartL1 = degToRadian( - BearingBetweenTwoGeoPoints.bearingBetweenTwoGeoPoints(start, l1) - as double); - final bearingStartEnd = degToRadian( - BearingBetweenTwoGeoPoints.bearingBetweenTwoGeoPoints(start, end) - as double); - - final x = asin(sin(distStartL1) * sin(bearingStartL1 - bearingStartEnd)); - - return x * R; - } -} diff --git a/lib/src/core/points_in_range.dart b/lib/src/core/points_in_range.dart deleted file mode 100644 index 75a6031..0000000 --- a/lib/src/core/points_in_range.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'core.dart'; - -/// -class PointRange { - /// Get a list of [LatLng] points within a distance from - /// a given point - /// Distance is in meters - static List pointInRange( - LatLng point, List pointsToCheck, num distance) { - final geoFencedPoints = []; - for (final LatLng p in pointsToCheck) { - if (GeoPoints.distanceBetweenTwoGeoPoints(point, p) <= distance) { - geoFencedPoints.add(p); - } - } - return geoFencedPoints; - } -} diff --git a/lib/src/geodesy.dart b/lib/src/geodesy.dart index adf3700..e9fe042 100644 --- a/lib/src/geodesy.dart +++ b/lib/src/geodesy.dart @@ -1,12 +1,14 @@ import 'package:geodesy/geodesy.dart'; -import 'package:geodesy/src/core/GeodeticPointManipulation/calculate_destination_point.dart'; -import 'package:geodesy/src/core/GeodeticPointManipulation/calculate_points_along_great_circle.dart'; -import 'package:geodesy/src/core/GeodeticPointManipulation/mid_point_between_two_points.dart'; -import 'package:geodesy/src/core/polygon_with_hole.dart'; -/// The main Geodesy Class +///Geodesy class that acts as a wrapper for various geodetic calculations. This class provides a +/// wide range of geo-spatial functionalities, from calculating distances and bearings to working +/// with polygons, points, and lines. This can be a valuable utility for geo-spatial applications class Geodesy { - /// Calculate a destination point given the distance and bearing + /// Calculate a destination point given the starting point, distance, bearing, and optional radius. + /// + /// Given a starting point [l], a distance in meters [distance], a bearing in degrees [bearing], and an optional radius in meters [radius], this function calculates the destination point on the Earth's surface. + /// The [radius] parameter allows you to specify a different radius than the Earth's default radius of approximately 6,371,000 meters. + /// Returns a [LatLng] object representing the destination point. LatLng destinationPointByDistanceAndBearing( LatLng l, num distance, num bearing, [num? radius]) { @@ -14,121 +16,191 @@ class Geodesy { l, distance, bearing, radius); } - /// Calculate the midpoint between teo geo points + /// Calculate the midpoint between two geographic points. + /// + /// Given two geographic points [l1] and [l2], this function calculates and returns the midpoint between these two points. LatLng midPointBetweenTwoGeoPoints(LatLng l1, LatLng l2) { return GeoPoints.midPointBetweenTwoGeoPoints(l1, l2); } - /// Calculate the geo point of intersection of two given paths + /// Calculate the geographic point of intersection of two paths. + /// + /// Given two geographic points [l1] and [l2], and two bearings [b1] and [b2], this function calculates and returns the point of intersection between the paths defined by these points and bearings. + /// Returns a [LatLng] object representing the intersection point or `null` if no intersection is found. LatLng? intersectionByPaths(LatLng l1, LatLng l2, num b1, num b2) { return GeoPoints.intersectionOfTwoGeoPointsByPaths(l1, l2, b1, b2); } - /// calculate the distance in meters between two geo points + /// Calculate the distance in meters between two geographic points. + /// + /// Given two geographic points [l1] and [l2], and an optional radius in meters [radius], this function calculates and returns the distance between these two points on the Earth's surface. + /// The [radius] parameter allows you to specify a different radius than the Earth's default radius of approximately 6,371,000 meters. num distanceBetweenTwoGeoPoints(LatLng l1, LatLng l2, [num? radius]) { return GeoPoints.distanceBetweenTwoGeoPoints(l1, l2, radius); } - /// calculate the bearing from point l1 to point l2 + /// Calculate the initial bearing from point [l1] to point [l2]. + /// + /// Given two geographic points [l1] and [l2], this function calculates and returns the initial bearing from point [l1] to point [l2] in degrees. num bearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { return BearingBetweenTwoGeoPoints.bearingBetweenTwoGeoPoints(l1, l2); } - /// calculate the final bearing from point l1 to point l2 + /// Calculate the final bearing from point [l1] to point [l2]. + /// + /// Given two geographic points [l1] and [l2], this function calculates and returns the final bearing from point [l1] to point [l2] in degrees. num finalBearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { return BearingBetweenTwoGeoPoints.finalBearingBetweenTwoGeoPoints(l1, l2); } - /// calculate signed distance from a geo point - /// to create circle with start and end points + /// Calculate the signed distance from a geographic point [l1] to create a circle with start [start] and end [end] points. + /// + /// Given a geographic point [l1], and start [start] and end [end] points, this function calculates and returns the signed distance in meters from point [l1] to create a circle with the specified start and end points. num crossTrackDistanceTo(LatLng l1, LatLng start, LatLng end, [num? radius]) { return TrackDistance.crossTrackDistanceTo(l1, start, end); } - /// check if a given geo point is in the bounding box + /// Check if a given geographic point [l] is within a bounding box defined by the top-left and bottom-right corners. + /// + /// Given a geographic point [l] and the top-left [topLeft] and bottom-right [bottomRight] corners of a bounding box, this function checks if the point is within the specified bounding box. bool isGeoPointInBoundingBox(LatLng l, LatLng topLeft, LatLng bottomRight) { return BoundingBox.isGeoPointInBoundingBox(l, topLeft, bottomRight); } - /// check if a given geo point is in the a polygon - /// using even-odd rule algorithm + /// Check if a given geographic point [l] is within a polygon defined by a list of [polygon] points using the even-odd rule algorithm. + /// + /// Given a geographic point [l] and a list of [polygon] points representing a polygon, this function checks if the point is inside the specified polygon using the even-odd rule algorithm. bool isGeoPointInPolygon(LatLng l, List polygon) { return GeoPoints.isGeoPointInPolygon(l, polygon); } - /// Get a list of [LatLng] points within a distance from - /// a given point - /// Distance is in meters + /// Get a list of [LatLng] points within a specified distance from a given point. + /// + /// Given a geographic point [point], a list of [pointsToCheck], and a distance in meters [distance], this function returns a list of [LatLng] points from [pointsToCheck] that are within the specified distance from the given point. List pointsInRange( LatLng point, List pointsToCheck, num distance) { return PointRange.pointInRange(point, pointsToCheck, distance); } - /// great-circle distance between two points using the Haversine formula + /// Calculate the great-circle distance between two geographic points using the Haversine formula. + /// + /// Given the latitude and longitude of two points [lat1], [lon1], [lat2], and [lon2], this function calculates and returns the great-circle distance between these two points in meters using the Haversine formula. num greatCircleDistanceBetweenTwoGeoPoints( num lat1, num lon1, num lat2, num lon2) { return GeoPoints.greatCircleDistanceBetweenTwoGeoPoints( lat1, lon1, lat2, lon2); } - /// GetRectangleBounds + /// Calculate the bounds of a rectangle that encloses a polygon defined by a list of [polygonCoords] points. + /// + /// Given a list of [polygonCoords] representing the vertices of a polygon, this function calculates and returns the bounds of the smallest rectangle that encloses the polygon as a list of two [LatLng] points: the top-left and bottom-right corners of the rectangle. List getRectangleBounds(List polygonCoords) { return RectangleBounds.getRectangleBounds(polygonCoords); } - /// Bounding Box per distance in Kilometers + /// Calculate a bounding box around a center point with a specified distance in kilometers. + /// + /// Given a center point [centerPoint] and a distance in kilometers [distanceInKm], this function calculates and returns the bounding box around the center point. List calculateBoundingBox(LatLng centerPoint, num distanceInKm) { return BoundingBox.calculateBoundingBox(centerPoint, distanceInKm); } - /// finds the centroid of polygons + /// Find the centroid of a polygon defined by a list of [polygons] points. + /// + /// Given a list of [polygons] points representing the vertices of a polygon, this function calculates and returns the centroid of the polygon as a [LatLng] object. LatLng findPolygonCentroid(List polygons) { return PolygonCentroid.findPolygonCentroid(polygons); } - /// Get Polygon Intersection + /// Calculate the intersection points of two polygons represented by lists of [polygon1] and [polygon2] points. + /// + /// Given two lists of [polygon1] and [polygon2] points representing the vertices of two polygons, this function calculates and returns a list of [LatLng] intersection points between the two polygons. List getPolygonIntersection( List polygon1, List polygon2) { return PolygonIntersection.getPolygonIntersection(polygon1, polygon2); } - /// Vincenty formula for Geodesic Distance Calculation + /// Calculate the Vincenty formula for geodesic distance calculation between two geographic points. + /// + /// Given the latitude and longitude of two points [lat1], [lon1], [lat2], and [lon2], this function calculates and returns the geodesic distance between these two points using the Vincenty formula. double vincentyDistance(double lat1, double lon1, double lat2, double lon2) { return VincentyDistance.vincentyDistance(lat1, lon1, lat2, lon2); } - /// Calculate the Area inside polygon Hole + /// Calculate the area of a polygon with holes. + /// + /// Given an outer polygon defined by a list of [outerPolygon] points and a list of [holes] (each hole represented as a list of LatLng points), this function calculates and returns the area of the polygon with holes. double calculatePolygonWithHolesArea( List outerPolygon, List> holes) { - return Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); + return PolygonHole.calculatePolygonWithHolesArea(outerPolygon, holes); } - /// Geodetic Point Manipulation - Rhumb Line Destination Formula + /// Calculate a destination point given an initial point, bearing in degrees, and distance in kilometers. + /// + /// Given an initial point [initialPoint], a bearing in degrees [bearingDegrees], and a distance in kilometers [distanceKm], this function calculates and returns the destination point on the Earth's surface. LatLng calculateDestinationPoint( LatLng initialPoint, double bearingDegrees, double distanceKm) { return DestinationPoint.calculateDestinationPoint( initialPoint, bearingDegrees, distanceKm); } - /// Geodetic Point Manipulation - Calculate Point Along Great Circle + /// Calculate a list of points along a great circle arc connecting two points. + /// + /// Given two points [startPoint] and [endPoint] and the number of intermediate points to calculate [numPoints], this function calculates and returns a list of [LatLng] points along the great circle arc connecting the two input points. The intermediate points are equally spaced along the arc. List calculatePointsAlongGreatCircle( LatLng startPoint, LatLng endPoint, int numPoints) { return GreatCirclePoint.calculatePointsAlongGreatCircle( startPoint, endPoint, numPoints); } - /// Geodetic Point Manipulation - Midpoint between two points + /// Calculate the midpoint between two geographic points. + /// + /// Given two geographic points [point1] and [point2], this function calculates and returns the midpoint between these two points as a [LatLng] object. LatLng calculateMidpoint(LatLng point1, LatLng point2) { return MidPointBetweenTwoPoints.calculateMidpoint(point1, point2); } - /// Equirectangular approximation Calculation + /// Calculate the equirectangular approximation distance between two geographic points. + /// + /// Given two geographic points [point1] and [point2], this function calculates and returns the equirectangular approximation distance between these two points in meters. double equirectangularDistance(LatLng point1, LatLng point2) { return EquirectangularApproximation.equirectangularDistance(point1, point2); } - /// Calculate Spherical Law Of Cosines Distance + /// Calculate the Spherical Law of Cosines distance between two geographic points. + /// + /// Given two geographic points [point1] and [point2], this function calculates and returns the Spherical Law of Cosines distance between these two points in meters. double sphericalLawOfCosinesDistance(LatLng point1, LatLng point2) { return SphericalLawOfCosines.sphericalLawOfCosinesDistance(point1, point2); } + + /// Calculate the length of a polyline formed by connecting multiple points. + /// + /// Given a list of [polyLinePoints] representing the vertices of a polyline, this function calculates and returns the length of the polyline in meters. + double calculatePolyLineLength(List polyLinePoints) { + return PolyLine.calculatePolyLineLength(polyLinePoints); + } + + /// Calculate the area of a polygon defined by a list of [polygonPoints] points. + /// + /// Given a list of [polygonPoints] representing the vertices of a polygon, this function calculates and returns the area of the polygon. + double calculatePolygonArea(List polygonPoints) { + return PolygonArea.calculatePolygonArea(polygonPoints); + } + + /// Calculate the intersection points of two geodesic lines defined by start and end points. + /// + /// Given four geographic points [start1], [end1], [start2], and [end2] representing the start and end points of two geodesic lines, this function calculates and returns the intersection point of these two lines as a [LatLng] object. If no intersection is found, it returns `null`. + LatLng? calculateGeodesicLineIntersection( + LatLng start1, LatLng end1, LatLng start2, LatLng end2) { + return GeodesicLines.calculateGeodesicLineIntersection( + start1, end1, start2, end2); + } + + /// Project a point onto a geodesic line. + /// + /// Given a geographic point [point], and start [start] and end [end] points defining a geodesic line, this function calculates and returns the projection (closest point) of the given point onto the geodesic line as a [LatLng] object. + LatLng projectPointOntoGeodesicLine(LatLng point, LatLng start, LatLng end) { + return GeodesicLines.projectPointOntoGeodesicLine(point, start, end); + } } diff --git a/lib/src/math/to_degrees.dart b/lib/src/math/to_degrees.dart new file mode 100644 index 0000000..5d2cf57 --- /dev/null +++ b/lib/src/math/to_degrees.dart @@ -0,0 +1,9 @@ +import '../core/core.dart'; + +/// Degree Conversion Extension +extension DegreeConversion on num { + /// Convert Value to Degree + double toDegrees() { + return this.toDouble() * (180.0 / pi); + } +} diff --git a/lib/src/math/to_radian.dart b/lib/src/math/to_radian.dart index f356aaa..8ee5c5f 100644 --- a/lib/src/math/to_radian.dart +++ b/lib/src/math/to_radian.dart @@ -4,6 +4,6 @@ import '../core/core.dart'; extension RadianConversion on num { /// Convert the double value to Radian double toRadians() { - return this * (pi / 180.0); + return this.toDouble() * (pi / 180.0); } } diff --git a/pubspec.yaml b/pubspec.yaml index 96ea83a..520b392 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.9.0 +version: 0.10.0 homepage: https://github.com/wingkwong/geodesy environment: diff --git a/test/geodesy_test.dart b/test/geodesy_test.dart index f7412ab..7b255c3 100644 --- a/test/geodesy_test.dart +++ b/test/geodesy_test.dart @@ -344,4 +344,71 @@ void main() { closeTo(expectedPoints[i].longitude, tolerance)); } }); + + test('Calculate the area of a square', () { + // Create a square with four vertices + List squarePoints = [ + const LatLng(0.0, 0.0), + const LatLng(0.0, 1.0), + const LatLng(1.0, 1.0), + const LatLng(1.0, 0.0), + ]; + + // Calculate the area of the square + double area = geodesy.calculatePolygonArea(squarePoints); + + // The area of a square with side length 1 is 1 square meter + expect(area, equals(1.0)); + }); + + test('Calculate the length of a straight line in meters', () { + // Create a straight line with two points + List linePoints = [ + const LatLng(0.0, 0.0), + const LatLng(0.0, 1.0), + ]; + + // Calculate the length of the line in meters + double length = geodesy.calculatePolyLineLength(linePoints); + + // The length of a straight line from (0,0) to (0,1) in meters + // is 111319.0 meters + expect(length, equals(111319.0)); + }); + + /// + test('Lines are parallel and do not intersect', () { + // Create two parallel lines with no intersection + LatLng start1 = const LatLng(0.0, 0.0); + LatLng end1 = const LatLng(1.0, 0.0); + LatLng start2 = const LatLng(0.0, 1.0); + LatLng end2 = const LatLng(1.0, 1.0); + + // Calculate intersection point + LatLng? intersection = + geodesy.calculateGeodesicLineIntersection(start1, end1, start2, end2); + + // There should be no intersection + expect(intersection, isNull); + }); + + test('Project a point onto a geodesic line', () { + // Define the geodesic line + LatLng start = const LatLng(42.345, -71.098); + LatLng end = const LatLng(42.355, -71.108); + + // Define the point to be projected + LatLng point = const LatLng(42.350, -71.103); + + // Calculate the expected projected point manually (e.g., using a GIS tool) + LatLng expectedProjection = + const LatLng(42.3512233054802, -71.09799913598741); + + // Project the point onto the geodesic line + LatLng projection = geodesy.projectPointOntoGeodesicLine(point, start, end); + + // Check if the projected point is close enough to the expected result + expect(projection.latitude, closeTo(expectedProjection.latitude, 0.0001)); + expect(projection.longitude, closeTo(expectedProjection.longitude, 0.0001)); + }); }