From 6af10d56dc117c68b23f50df44ca2686268af241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D5=A1=C9=A8=D5=BC=C9=A2=D3=84=D5=A1=D6=85=D5=BC=C9=A2?= Date: Mon, 28 Aug 2023 17:06:31 +0800 Subject: [PATCH] Release: 0.8.0 (#33) * FILES & FEATURES (#32) * Implemented #28 Calculating Polygon Area With Hole * Implemented Vincenty Formula for Geodesic Distance Calculation * Added CODE_OF_CONDUCT.md * Revised LICENSE --------- Co-authored-by: wingkwong * refactor: comments * docs: 0.8.0 CHANGELOG * chore: bump to 0.8.0 * refactor: revise comments --------- Co-authored-by: Ayoub Ali --- .gitignore | 3 +- CHANGELOG.md | 7 ++ LICENSE | 2 +- README.md | 47 +++++++++- doc/CLASS.md | 26 ++++-- doc/CODE_OF_CONDUCT.md | 44 ++++++++++ doc/METHODS.md | 24 +++-- example/example.dart | 37 ++++++++ example/main.dart | 34 +++++++- lib/src/core/core.dart | 2 + ...ination_point_by_distance_and_bearing.dart | 12 +-- lib/src/core/polygon_with_hole.dart | 37 ++++++++ .../core/vincenty_distance_calculation.dart | 87 +++++++++++++++++++ lib/src/geodesy.dart | 12 +++ lib/src/math/to_radian.dart | 9 ++ pubspec.yaml | 2 +- test/geodesy_test.dart | 44 ++++++++++ 17 files changed, 406 insertions(+), 23 deletions(-) create mode 100644 doc/CODE_OF_CONDUCT.md create mode 100644 lib/src/core/polygon_with_hole.dart create mode 100644 lib/src/core/vincenty_distance_calculation.dart create mode 100644 lib/src/math/to_radian.dart diff --git a/.gitignore b/.gitignore index ec3fc38..c05e403 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,5 @@ build/ test/.test_coverage.dart coverage/ coverage_badge.svg -!coverage/lcov.info \ No newline at end of file +!coverage/lcov.info +TODO.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8266ee2..278478c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## 0.8.0 + +- Implement Calculating Polygon Area With Hole +- Implement Vincenty Formula for Geodesic Distance Calculation +- Add CODE_OF_CONDUCT.md +- Revise LICENSE + ## 0.7.0 - Refactor geodesy diff --git a/LICENSE b/LICENSE index 8b23445..adbc35c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 +Copyright (c) 2023 աɨռɢӄաօռɢ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index df76cee..8a42ae5 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Please check out [here](example/main.dart) for more. ```dart import 'package:geodesy/geodesy.dart'; -void main(){ +void main() { final Geodesy geodesy = Geodesy(); // Calculate Bounding Box // Example central position (San Francisco) @@ -102,6 +102,26 @@ void main(){ 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 @@ -158,4 +178,29 @@ void main() { 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); ``` + +## Code of Conduct + +See [here](doc/CODE_OF_CONDUCT.md). + +## License + +See [here](./LICENSE). \ No newline at end of file diff --git a/doc/CLASS.md b/doc/CLASS.md index e8fbec0..e0eb32f 100644 --- a/doc/CLASS.md +++ b/doc/CLASS.md @@ -141,7 +141,7 @@ bool isGeoPointInPolygon(LatLng point, List polygon) ``` - `point` (LatLng): The point to check (latitude, longitude). -- `polygon` (List): A list of vertices defining the polygon. +- `polygon` (List``): A list of vertices defining the polygon. Returns `true` if the point is within the polygon, otherwise `false`. @@ -154,7 +154,7 @@ List pointsInRange(LatLng point, List pointsToCheck, num distanc ``` - `point` (LatLng): The center point (latitude, longitude). -- `pointsToCheck` (List): List of points to check against. +- `pointsToCheck` (List``): List of points to check against. - `distance` (num): The maximum distance in meters. Returns a list of `LatLng` points within the specified distance from the center point. @@ -184,7 +184,7 @@ Get the bounding rectangle for a polygon defined by its vertices. List getRectangleBounds(List polygonCoords) ``` -- `polygonCoords` (List): List of vertices defining the polygon. +- `polygonCoords` (List``): List of vertices defining the polygon. Returns a list of `LatLng` points representing the bounding rectangle's corners. @@ -209,7 +209,7 @@ Find the centroid of a polygon defined by its vertices. LatLng findPolygonCentroid(List polygon) ``` -- `polygon` (List): List of vertices defining the polygon. +- `polygon` (List``): List of vertices defining the polygon. Returns a `LatLng` object representing the centroid of the polygon. @@ -223,11 +223,25 @@ Calculate the intersection of List getPolygonIntersection(List polygon1, List polygon2) ``` -- `polygon1` (List): List of vertices defining the first polygon. -- `polygon2` (List): List of vertices defining the second polygon. +- `polygon1` (List``): List of vertices defining the first polygon. +- `polygon2` (List``): List of vertices defining the second polygon. Returns a list of `LatLng` points representing the intersection polygon. +### Vincenty formula for Geodesic Distance Calculation + +```dart + final double calculatedDistance = geodesy.vincentyDistance( + point1.latitude, point1.longitude, point2.latitude, point2.longitude); +``` + +### Calculate Area of Polygon with Hole + +```dart +final calculatedArea = + geodesy.calculatePolygonWithHolesArea(outerPolygon, holes); +``` + --- 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 new file mode 100644 index 0000000..5940a2b --- /dev/null +++ b/doc/CODE_OF_CONDUCT.md @@ -0,0 +1,44 @@ + +# Code of Conduct + +## Introduction + +This Code of Conduct outlines the expectations for behavior in the geodesy Dart library community. We are committed to providing a welcoming and inclusive environment for everyone, regardless of their background or beliefs. + +## Responsibilities + +All members of the geodesy Dart library community are expected to: + +* Be respectful of others. +* Be welcoming and inclusive. +* Be open to feedback. +* Be constructive in their criticism. +* Be mindful of their language and avoid using discriminatory or offensive language. +* Report any instances of unacceptable behavior to the repository maintainers. + +## Unacceptable Behavior + +The following behaviors are considered unacceptable in the geodesy Dart library community: + +* Harassment of any kind, including but not limited to: + * Verbal abuse + * Sexual harassment + * Threats or intimidation + * Stalking or following + * Unwanted physical contact +* Discrimination based on race, gender, sexual orientation, disability, or other personal characteristics +* Spamming or trolling +* Publishing private information about others without their consent +* Disrupting or interfering with the work of others + +## Reporting Unacceptable Behavior + +If you witness or experience unacceptable behavior, please report it to the repository maintainers immediately. You can do this by sending an email to wingkwong.code@gmail.com or opening an issue on the GitHub repository. + +## Enforcement + +The repository maintainers will investigate all reports of unacceptable behavior and take appropriate action. This may include warning the offender, suspending their access to the repository, or banning them from the community. + +## Contact Information + +If you have any questions about this Code of Conduct, please contact the repository maintainers at wingkwong.code@gmail.com. diff --git a/doc/METHODS.md b/doc/METHODS.md index 7f29a53..70e53e2 100644 --- a/doc/METHODS.md +++ b/doc/METHODS.md @@ -122,7 +122,7 @@ GeoPoints.isGeoPointInPolygon(l, polygon); ``` - `l` (LatLng): The point to check (latitude, longitude). -- `polygon` (List): A list of vertices defining the polygon. +- `polygon` (List``): A list of vertices defining the polygon. Returns `true` if the point is within the polygon, otherwise `false`. @@ -135,7 +135,7 @@ PointRange.pointInRange(point, pointsToCheck, distance); ``` - `point` (LatLng): The center point (latitude, longitude). -- `pointsToCheck` (List): List of points to check against. +- `pointsToCheck` (List``): List of points to check against. - `distance` (num): The maximum distance in meters. Returns a list of `LatLng` points within the specified distance from the center point. @@ -163,7 +163,7 @@ Get the bounding rectangle for a polygon defined by its vertices. RectangleBounds.getRectangleBounds(polygonCoords); ``` -- `polygonCoords` (List): List of vertices defining the polygon. +- `polygonCoords` (List``): List of vertices defining the polygon. Returns a list of `LatLng` points representing the bounding rectangle's corners. @@ -188,7 +188,7 @@ Find the centroid of a polygon defined by its vertices. PolygonCentroid.findPolygonCentroid(polygons); ``` -- `polygon` (List): List of vertices defining the polygon. +- `polygon` (List``): List of vertices defining the polygon. Returns a `LatLng` object representing the centroid of the polygon. @@ -202,11 +202,23 @@ Calculate the intersection of PolygonIntersection.getPolygonIntersection(polygon1, polygon2); ``` -- `polygon1` (List): List of vertices defining the first polygon. -- `polygon2` (List): List of vertices defining the second polygon. +- `polygon1` (List``): List of vertices defining the first polygon. +- `polygon2` (List``): List of vertices defining the second polygon. Returns a list of `LatLng` points representing the intersection polygon. +### Vincenty formula for Geodesic Distance Calculation + +```dart +VincentyDistance.vincentyDistance(lat1, lon1, lat2, lon2); +``` + +### Calculate Area of Polygon with Hole + +```dart +final area = Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); +``` + --- 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/example.dart b/example/example.dart index 3732677..fe8c1f8 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,4 +1,5 @@ import 'package:geodesy/geodesy.dart'; +import 'package:geodesy/src/core/polygon_with_hole.dart'; void main() { // Calculate Bounding Box @@ -48,4 +49,40 @@ void main() { for (final point in intersectionPoints) { print('Latitude: ${point.latitude}, Longitude: ${point.longitude}'); } + + // Vincenty formula for Geodesic Distance Calculation + final LatLng point1 = const LatLng(37.7749, -122.4194); // San Francisco + final LatLng point2 = const LatLng(34.0522, -118.2437); // Los Angeles + + final double calculatedDistance = VincentyDistance.vincentyDistance( + point1.latitude, + point1.longitude, + point2.latitude, + point2.longitude, + ); + + print( + '''Distance between San Francisco and Los Angeles: $calculatedDistance meters''', + ); + + // Define the outer polygon + 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 area = Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); + print("Area of polygon with holes: $area"); } diff --git a/example/main.dart b/example/main.dart index 8efe5e1..8edbbe0 100644 --- a/example/main.dart +++ b/example/main.dart @@ -1,5 +1,4 @@ import 'package:latlong2/latlong.dart'; - import 'package:geodesy/geodesy.dart' show Geodesy; void main() { @@ -130,4 +129,37 @@ void main() { for (final point in intersectionPoints) { print('Latitude: ${point.latitude}, Longitude: ${point.longitude}'); } + + // Vincenty formula for Geodesic Distance Calculation + final LatLng point1 = const LatLng(37.7749, -122.4194); // San Francisco + final LatLng point2 = const LatLng(34.0522, -118.2437); // Los Angeles + + final double calculatedDistance = geodesy.vincentyDistance( + point1.latitude, point1.longitude, point2.latitude, point2.longitude); + + print( + '''Distance between San Francisco and Los Angeles: + $calculatedDistance meters''', + ); + + // Define the outer polygon + 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 area = geodesy.calculatePolygonWithHolesArea(outerPolygon, holes); + print("Area of polygon with holes: $area"); } diff --git a/lib/src/core/core.dart b/lib/src/core/core.dart index 7b63b94..414cf4c 100644 --- a/lib/src/core/core.dart +++ b/lib/src/core/core.dart @@ -1,3 +1,4 @@ +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'; @@ -5,6 +6,7 @@ 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/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:latlong2/latlong.dart' hide pi; diff --git a/lib/src/core/destination_point_by_distance_and_bearing.dart b/lib/src/core/destination_point_by_distance_and_bearing.dart index 20abf2c..9d3912e 100644 --- a/lib/src/core/destination_point_by_distance_and_bearing.dart +++ b/lib/src/core/destination_point_by_distance_and_bearing.dart @@ -1,12 +1,12 @@ import 'core.dart'; -/// +/// DestinationPointByDistanceAndBearing 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. + /// 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. static LatLng destinationPointByDistanceAndBearing( LatLng l, num distance, num bearing, [num? radius]) { diff --git a/lib/src/core/polygon_with_hole.dart b/lib/src/core/polygon_with_hole.dart new file mode 100644 index 0000000..5d1c6fa --- /dev/null +++ b/lib/src/core/polygon_with_hole.dart @@ -0,0 +1,37 @@ +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. + static double calculatePolygonWithHolesArea( + List outerPolygon, List> holes) { + // Calculate and sum the areas of holes + final holeAreas = holes.map((hole) { + double area = 0.0; + for (int i = 0; i < hole.length; i++) { + final j = (i + 1) % hole.length; + area += (hole[i].longitude + hole[j].longitude) * + (hole[j].latitude - hole[i].latitude); + } + return area / 2.0; + }).toList(); + + // Calculate the area of the outer polygon + double outerArea = 0.0; + for (int i = 0; i < outerPolygon.length; i++) { + final j = (i + 1) % outerPolygon.length; + outerArea += (outerPolygon[i].longitude + outerPolygon[j].longitude) * + (outerPolygon[j].latitude - outerPolygon[i].latitude); + } + + // Subtract hole areas from the outer area to get the final area + final finalArea = + (outerArea / 2.0 - holeAreas.reduce((a, b) => a + b)).abs(); + + return finalArea; + } +} diff --git a/lib/src/core/vincenty_distance_calculation.dart b/lib/src/core/vincenty_distance_calculation.dart new file mode 100644 index 0000000..babb30e --- /dev/null +++ b/lib/src/core/vincenty_distance_calculation.dart @@ -0,0 +1,87 @@ +import 'core.dart'; + +/// Vincenty formula for Geodesic Distance Calculation +class VincentyDistance { + /// Calculate the geodesic distance between two points + /// on the surface of an ellipsoid, such as the Earth. + static double vincentyDistance( + double lat1, + double lon1, + double lat2, + double lon2, + ) { + final double a = 6378137.0; // WGS-84 semi-major axis in meters + final double f = 1 / 298.257223563; // WGS-84 flattening + final double b = (1 - f) * a; // WGS-84 semi-minor axis in meters + + final double phi1 = lat1.toRadians(); + final double phi2 = lat2.toRadians(); + final double deltaLambda = (lon2 - lon1).toRadians(); + + final double u1 = atan((1 - f) * tan(phi1)); + final double u2 = atan((1 - f) * tan(phi2)); + + final double sinU1 = sin(u1); + final double cosU1 = cos(u1); + final double sinU2 = sin(u2); + final double cosU2 = cos(u2); + + double lambda = deltaLambda; + double lambdaP; + double sinSigma; + double cosSigma; + double sigma; + double sinAlpha; + double cosSqAlpha; + double cos2SigmaM; + double C; + + do { + final double sinLambda = sin(lambda); + final double cosLambda = cos(lambda); + sinSigma = sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)); + + cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; + sigma = atan2(sinSigma, cosSigma); + + sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma; + cosSqAlpha = 1 - sinAlpha * sinAlpha; + cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; + + C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); + + lambdaP = lambda; + lambda = deltaLambda + + (1 - C) * + f * + sinAlpha * + (sigma + + C * + sinSigma * + (cos2SigmaM + + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); + } while ((lambda - lambdaP).abs() > 1e-12); + + final double uSq = cosSqAlpha * ((a * a - b * b) / (b * b)); + final double A = + 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); + final double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); + final double deltaSigma = B * + sinSigma * + (cos2SigmaM + + B / + 4 * + (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - + B / + 6 * + cos2SigmaM * + (-3 + 4 * sinSigma * sinSigma) * + (-3 + 4 * cos2SigmaM * cos2SigmaM))); + + final double distance = b * A * (sigma - deltaSigma); + + return distance; + } +} diff --git a/lib/src/geodesy.dart b/lib/src/geodesy.dart index b5e51b5..7b26c47 100644 --- a/lib/src/geodesy.dart +++ b/lib/src/geodesy.dart @@ -1,4 +1,5 @@ import 'package:geodesy/geodesy.dart'; +import 'package:geodesy/src/core/polygon_with_hole.dart'; /// The main Geodesy Class class Geodesy { @@ -87,4 +88,15 @@ class Geodesy { List polygon1, List polygon2) { return PolygonIntersection.getPolygonIntersection(polygon1, polygon2); } + + /// Vincenty formula for Geodesic Distance Calculation + double vincentyDistance(double lat1, double lon1, double lat2, double lon2) { + return VincentyDistance.vincentyDistance(lat1, lon1, lat2, lon2); + } + + /// Calculate the Area inside polygon Hole + double calculatePolygonWithHolesArea( + List outerPolygon, List> holes) { + return Polygon.calculatePolygonWithHolesArea(outerPolygon, holes); + } } diff --git a/lib/src/math/to_radian.dart b/lib/src/math/to_radian.dart new file mode 100644 index 0000000..1f7e48c --- /dev/null +++ b/lib/src/math/to_radian.dart @@ -0,0 +1,9 @@ +import '../core/core.dart'; + +/// Angle Conversion Extension +extension AngleConversion on double { + /// Convert the double value to Radian + double toRadians() { + return this * (pi / 180.0); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index acc9aeb..9fa8c1c 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.7.0 +version: 0.8.0 homepage: https://github.com/wingkwong/geodesy environment: diff --git a/test/geodesy_test.dart b/test/geodesy_test.dart index 3d104f7..baf1450 100644 --- a/test/geodesy_test.dart +++ b/test/geodesy_test.dart @@ -208,4 +208,48 @@ void main() { expect(intersectionPoints[1].latitude, 2); expect(intersectionPoints[1].longitude, 1); }); + + // Calculate Vincenty Distance + test('Calculate Vincenty distance', () { + // Distance between SF and LA in meters + final double expectedDistance = 559042.3365044123; + + final double calculatedDistance = geodesy.vincentyDistance( + 37.7749, -122.4194, // San Francisco + 34.0522, -118.2437, // Los Angeles + ); + + expect(calculatedDistance, closeTo(expectedDistance, 0.01)); + }); + + /// Calculate Area of Polygon with Hole + test('Calculate Area of Polygon with Hole', () { + // Define the outer polygon + 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), + ]; + + // Combine the outer polygon and holes + final holes = [hole1]; + + // Calculate the area of the polygon with the hole + final calculatedArea = + geodesy.calculatePolygonWithHolesArea(outerPolygon, holes); + + // Expected area of the polygon with the hole + final expectedArea = 0.75; // This value should match the actual calculation + + expect(calculatedArea, expectedArea); + }); }