diff --git a/README.md b/README.md index 06d6080..eb953c4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A Dart library for implementing geodesic and trigonometric calculations based on ## Getting Started -### Add the following line in your pubspec file +### Add the following line in your `pubspec.yml` file ```dart geodesy: @@ -23,39 +23,39 @@ import 'package:geodesy/geodesy.dart'; ### Geodesy() ```dart -Geodesy geodesy = Geodesy(); +final Geodesy geodesy = Geodesy(); ``` ### LatLng(double latitude, double longitude) ```dart -LatLng l = LatLng(22.308, 114.1716); +final LatLng l = LatLng(22.308, 114.1716); ``` ## Methods ### destinationPointByDistanceAndBearing(LatLng l, num distance, num bearing, [num radius]) -Calculate a destination point given the distance and bearing. If raduis is not specified, Earth radius will be used. +Calculate a destination point given the distance and bearing. If radius is not specified, Earth radius will be used. ```dart -LatLng distinationPoint = geodesy.destinationPointByDistanceAndBearing(l3, 2400, 420.2); +final LatLng destinationPoint = geodesy.destinationPointByDistanceAndBearing(l3, 2400, 420.2); ``` ### midPointBetweenTwoGeoPoints(LatLng l1, LatLng l2) -Calcuate the midpoint bewteen teo geo points. +Calculate the midpoint between teo geo points. ```dart -LatLng midpoint = geodesy.midPointBetweenTwoGeoPoints(l1, l2); +final LatLng midpoint = geodesy.midPointBetweenTwoGeoPoints(l1, l2); ``` -### distanceBetweenTwoGeoPoints(LatLng l1, LatLng l2, [num radius]) +### distanceBetweenTwoGeoPoints(LatLng l1, LatLng l2, [num radius]) -Calculate the distance in meters between two geo points. If raduis is not specified, Earth radius will be used. +Calculate the distance in meters between two geo points. If radius is not specified, Earth radius will be used. ```dart -num distance = geodesy.distanceBetweenTwoGeoPoints(l1, l2); +final num distance = geodesy.distanceBetweenTwoGeoPoints(l1, l2); ``` ### bearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) @@ -63,7 +63,7 @@ num distance = geodesy.distanceBetweenTwoGeoPoints(l1, l2); Calculate the bearing from point l1 to point l2. ```dart -num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); +final num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); ``` ### finalBearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) @@ -71,7 +71,7 @@ num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); Calculate the final bearing from point l1 to point l2. ```dart -num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); +final num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); ``` ### degreesToRadians(num degrees) @@ -79,7 +79,7 @@ num finalBearing = geodesy.finalBearingBetweenTwoGeoPoints(l1, l2); Convert degrees to radians ```dart -num l1LatRadians = degreesToRadians(l1.lat); +final num l1LatRadians = degreesToRadians(l1.lat); ``` ### radiansToDegrees(num radians) @@ -87,7 +87,7 @@ num l1LatRadians = degreesToRadians(l1.lat); Convert degrees to radians ```dart -num degrees = radiansToDegrees(latRadians); +final num degrees = radiansToDegrees(latRadians); ``` ### isGeoPointInBoundingBox(LatLng l, LatLng topLeft, LatLng bottomRight) @@ -95,7 +95,7 @@ num degrees = radiansToDegrees(latRadians); Check if a given geo point is in the bounding box ```dart -bool inBoundingBox = geodesy.isGeoPointInBoundingBox(l1, l2, l3); +final bool inBoundingBox = geodesy.isGeoPointInBoundingBox(l1, l2, l3); ``` ### intersectionByPaths(LatLng l1, LatLng l2, num b1, num b2) @@ -103,15 +103,15 @@ bool inBoundingBox = geodesy.isGeoPointInBoundingBox(l1, l2, l3); Calculate the geo point of intersection of two given paths ```dart -LatLng intersectionByPaths = geodesy.intersectionByPaths(l1, l2, b1, b2); +final LatLng intersectionByPaths = geodesy.intersectionByPaths(l1, l2, b1, b2); ``` ### crossTrackDistanceTo(LatLng l1, LatLng start, LatLng end, [num radius]) -Calculate signed distance from a geo point to greate circle with start and end points +Calculate signed distance from a geo point to create circle with start and end points ```dart -num distanceToGreatCircle = geodesy.crossTrackDistanceTo(l1, l2, l3); +final num distanceToGreatCircle = geodesy.crossTrackDistanceTo(l1, l2, l3); ``` ### isGeoPointInPolygon(LatLng l, List polygon) @@ -119,7 +119,7 @@ num distanceToGreatCircle = geodesy.crossTrackDistanceTo(l1, l2, l3); Check if a given geo point is in the a polygon using even-odd rule algorithm ```dart -bool isGeoPointInPolygon = geodesy.isGeoPointInPolygon(l, poly); +final bool isGeoPointInPolygon = geodesy.isGeoPointInPolygon(l, poly); ``` ### pointsInRange(LatLng point, List pointsToCheck, num distance) @@ -130,6 +130,5 @@ Get a list of points within a distance in meters from a given point final point = LatLng(51.0, 0); final pointsToCheck = [/* points here */]; final distance = 10000; -List geofencedPoints = geodesy.pointsInRange(point, pointsToCheck, distance); +List geoFencedPoints = geodesy.pointsInRange(point, pointsToCheck, distance); ``` - \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index fb21034..b31d1f1 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,5 @@ -include: package:pedantic/analysis_options.yaml +# include: package:pedantic/analysis_options.yaml +include: package:lints/core.yaml analyzer: strong-mode: @@ -21,7 +22,6 @@ linter: - lines_longer_than_80_chars - prefer_conditional_assignment - prefer_const_constructors - - prefer_equal_for_default_values - prefer_final_fields - unnecessary_new - prefer_is_empty diff --git a/lib/src/geodesy.dart b/lib/src/geodesy.dart index 0d4a907..aa20486 100644 --- a/lib/src/geodesy.dart +++ b/lib/src/geodesy.dart @@ -11,46 +11,45 @@ class Geodesy { LatLng l, num distance, num bearing, [num? radius]) { radius = radius ?? _RADIUS; + final num angularDistanceRadius = distance / radius; + final num bearingRadians = degToRadian(bearing as double); - num angularDistanceRadius = distance / radius; - num bearingRadians = degToRadian(bearing as double); + final num latRadians = degToRadian(l.latitude); + final num lngRadians = degToRadian(l.longitude); - num latRadians = degToRadian(l.latitude); - num lngRadians = degToRadian(l.longitude); + final num sinLatRadians = math.sin(latRadians); + final num cosLatRadians = math.cos(latRadians); + final num sinAngularDistanceRadius = math.sin(angularDistanceRadius); + final num cosAngularDistanceRadius = math.cos(angularDistanceRadius); + final num sinBearingRadians = math.sin(bearingRadians); + final num cosBearingRadians = math.cos(bearingRadians); - num sinLatRadians = math.sin(latRadians); - num cosLatRadians = math.cos(latRadians); - num sinAngularDistanceRadius = math.sin(angularDistanceRadius); - num cosAngularDistanceRadius = math.cos(angularDistanceRadius); - num sinBearingRadians = math.sin(bearingRadians); - num cosBearingRadians = math.cos(bearingRadians); - - var sinLatRadians2 = sinLatRadians * cosAngularDistanceRadius + + final sinLatRadians2 = sinLatRadians * cosAngularDistanceRadius + cosLatRadians * sinAngularDistanceRadius * cosBearingRadians; - num latRadians2 = math.asin(sinLatRadians2); - var y = sinBearingRadians * sinAngularDistanceRadius * cosLatRadians; - var x = cosAngularDistanceRadius - sinLatRadians * sinLatRadians2; - num lngRadians2 = lngRadians + math.atan2(y, x); + final num latRadians2 = math.asin(sinLatRadians2); + final y = sinBearingRadians * sinAngularDistanceRadius * cosLatRadians; + final x = cosAngularDistanceRadius - sinLatRadians * sinLatRadians2; + final num lngRadians2 = lngRadians + math.atan2(y, x); + return LatLng(radianToDeg(latRadians2 as double), (radianToDeg(lngRadians2 as double) + 540) % 360 - 180); } - /// calcuate the midpoint bewteen teo geo points + /// calculate the midpoint between teo geo points LatLng midPointBetweenTwoGeoPoints(LatLng l1, LatLng l2) { - num l1LatRadians = degToRadian(l1.latitude); - num l1LngRadians = degToRadian(l1.longitude); - num l2LatRadians = degToRadian(l2.latitude); - num lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); - - num vectorX = math.cos(l2LatRadians) * math.cos(lngRadiansDiff); - num vectorY = math.cos(l2LatRadians) * math.sin(lngRadiansDiff); - - num x = math.sqrt((math.cos(l1LatRadians) + vectorX) * - (math.cos(l1LatRadians) + vectorX) + - vectorY * vectorY); - num y = math.sin(l1LatRadians) + math.sin(l2LatRadians); - num latRadians = math.atan2(y, x); - num lngRadians = + final num l1LatRadians = degToRadian(l1.latitude); + final num l1LngRadians = degToRadian(l1.longitude); + final num l2LatRadians = degToRadian(l2.latitude); + final num lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); + + final num vectorX = math.cos(l2LatRadians) * math.cos(lngRadiansDiff); + final num vectorY = math.cos(l2LatRadians) * math.sin(lngRadiansDiff); + + final num x = math.sqrt( + math.pow((math.cos(l1LatRadians) + vectorX), 2) + math.pow(vectorY, 2)); + final num y = math.sin(l1LatRadians) + math.sin(l2LatRadians); + final num latRadians = math.atan2(y, x); + final num lngRadians = l1LngRadians + math.atan2(vectorY, math.cos(l1LatRadians) + vectorX); return LatLng(radianToDeg(latRadians as double), @@ -59,56 +58,62 @@ class Geodesy { /// calculate the geo point of intersection of two given paths LatLng? intersectionByPaths(LatLng l1, LatLng l2, num b1, num b2) { - num l1LatRadians = degToRadian(l1.latitude); - num l1LngRadians = degToRadian(l1.longitude); - num l2LatRadians = degToRadian(l2.latitude); - num l2LngRadians = degToRadian(l2.longitude); - num b1Radians = degToRadian(b1 as double); - num b2Radians = degToRadian(b2 as double); - var latRadiansDiff = l2LatRadians - l1LatRadians; - var lngRadiansDiff = l2LngRadians - l1LngRadians; - - num angularDistance = 2 * + final num l1LatRadians = degToRadian(l1.latitude); + final num l1LngRadians = degToRadian(l1.longitude); + final num l2LatRadians = degToRadian(l2.latitude); + final num l2LngRadians = degToRadian(l2.longitude); + final num b1Radians = degToRadian(b1 as double); + final num b2Radians = degToRadian(b2 as double); + + final latRadiansDiff = l2LatRadians - l1LatRadians; + final lngRadiansDiff = l2LngRadians - l1LngRadians; + + final num angularDistance = 2 * math.asin(math.sqrt( math.sin(latRadiansDiff / 2) * math.sin(latRadiansDiff / 2) + math.cos(l1LatRadians) * math.cos(l2LatRadians) * math.sin(lngRadiansDiff / 2) * math.sin(lngRadiansDiff / 2))); + if (angularDistance == 0) return null; num initBearingX = math.acos((math.sin(l2LatRadians) - math.sin(l1LatRadians) * math.cos(angularDistance)) / (math.sin(angularDistance) * math.cos(l1LatRadians))); if (initBearingX.isNaN) initBearingX = 0; + num initBearingY = math.acos((math.sin(l1LatRadians) - math.sin(l2LatRadians) * math.cos(angularDistance)) / (math.sin(angularDistance) * math.cos(l2LatRadians))); - var finalBearingX = math.sin(l2LngRadians - l1LngRadians) > 0 + final finalBearingX = math.sin(l2LngRadians - l1LngRadians) > 0 ? initBearingX : 2 * _PI - initBearingX; - var finalBearingY = math.sin(l2LngRadians - l1LngRadians) > 0 + final finalBearingY = math.sin(l2LngRadians - l1LngRadians) > 0 ? 2 * _PI - initBearingY : initBearingY; - var angle1 = b1Radians - finalBearingX; - var angle2 = finalBearingY - b2Radians; + final angle1 = b1Radians - finalBearingX; + final angle2 = finalBearingY - b2Radians; if (math.sin(angle1) == 0 && math.sin(angle2) == 0) return null; if (math.sin(angle1) * math.sin(angle2) < 0) return null; - num angle3 = math.acos(-math.cos(angle1) * math.cos(angle2) + + final num angle3 = math.acos(-math.cos(angle1) * math.cos(angle2) + math.sin(angle1) * math.sin(angle2) * math.cos(angularDistance)); - num dst13 = math.atan2( + final num dst13 = math.atan2( math.sin(angularDistance) * math.sin(angle1) * math.sin(angle2), math.cos(angle2) + math.cos(angle1) * math.cos(angle3)); - num lat3 = math.asin(math.sin(l1LatRadians) * math.cos(dst13) + + + final num lat3 = math.asin(math.sin(l1LatRadians) * math.cos(dst13) + math.cos(l1LatRadians) * math.sin(dst13) * math.cos(b1Radians)); - num lngRadiansDiff13 = math.atan2( + + final num lngRadiansDiff13 = math.atan2( math.sin(b1Radians) * math.sin(dst13) * math.cos(l1LatRadians), math.cos(dst13) - math.sin(l1LatRadians) * math.sin(lat3)); - var l3LngRadians = l1LngRadians + lngRadiansDiff13; + + final l3LngRadians = l1LngRadians + lngRadiansDiff13; return LatLng(radianToDeg(lat3 as double), (radianToDeg(l3LngRadians as double) + 540) % 360 - 180); @@ -116,39 +121,42 @@ class Geodesy { /// calculate the distance in meters between two geo points num distanceBetweenTwoGeoPoints(LatLng l1, LatLng l2, [num? radius]) { - radius = radius ?? _RADIUS; - var R = radius; - num l1LatRadians = degToRadian(l1.latitude); - num l1LngRadians = degToRadian(l1.longitude); - num l2LatRadians = degToRadian(l2.latitude); - num l2LngRadians = degToRadian(l2.longitude); - var latRadiansDiff = l2LatRadians - l1LatRadians; - var lngRadiansDiff = l2LngRadians - l1LngRadians; - - num a = math.sin(latRadiansDiff / 2) * math.sin(latRadiansDiff / 2) + + final R = radius ?? _RADIUS; + final num l1LatRadians = degToRadian(l1.latitude); + final num l1LngRadians = degToRadian(l1.longitude); + final num l2LatRadians = degToRadian(l2.latitude); + final num l2LngRadians = degToRadian(l2.longitude); + + final latRadiansDiff = l2LatRadians - l1LatRadians; + final lngRadiansDiff = l2LngRadians - l1LngRadians; + + final num a = math.sin(latRadiansDiff / 2) * math.sin(latRadiansDiff / 2) + math.cos(l1LatRadians) * math.cos(l2LatRadians) * math.sin(lngRadiansDiff / 2) * math.sin(lngRadiansDiff / 2); - num c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); - var distance = R * c; + final num c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); + final distance = R * c; return distance; } /// calculate the bearing from point l1 to point l2 num bearingBetweenTwoGeoPoints(LatLng l1, LatLng l2) { - num l1LatRadians = degToRadian(l1.latitude); - num l2LatRadians = degToRadian(l2.latitude); - num lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); - num y = math.sin(lngRadiansDiff) * math.cos(l2LatRadians); - num x = math.cos(l1LatRadians) * math.sin(l2LatRadians) - + final l1LatRadians = degToRadian(l1.latitude); + final l2LatRadians = degToRadian(l2.latitude); + final lngRadiansDiff = degToRadian(l2.longitude - l1.longitude); + + final y = math.sin(lngRadiansDiff) * math.cos(l2LatRadians); + final x = math.cos(l1LatRadians) * math.sin(l2LatRadians) - math.sin(l1LatRadians) * math.cos(l2LatRadians) * math.cos(lngRadiansDiff); - num radians = math.atan2(y, x); - return (radianToDeg(radians as double) + 360) % 360; + final radians = math.atan2(y, x); + final degrees = radianToDeg(radians); + + return (degrees + 360) % 360; } /// calculate the final bearing from point l1 to point l2 @@ -157,30 +165,28 @@ class Geodesy { } /// calculate signed distance from a geo point - /// to greate circle with start and end points + /// to create circle with start and end points num crossTrackDistanceTo(LatLng l1, LatLng start, LatLng end, [num? radius]) { - var R = radius ?? _RADIUS; + final R = radius ?? _RADIUS; - num distStartL1 = distanceBetweenTwoGeoPoints(start, l1, R) / R; - num radiansStartL1 = + final distStartL1 = distanceBetweenTwoGeoPoints(start, l1, R) / R; + final bearingStartL1 = degToRadian(bearingBetweenTwoGeoPoints(start, l1) as double); - num radiansEndL1 = + final bearingStartEnd = degToRadian(bearingBetweenTwoGeoPoints(start, end) as double); - num x = math - .asin(math.sin(distStartL1) * math.sin(radiansStartL1 - radiansEndL1)); + final x = math.asin( + math.sin(distStartL1) * math.sin(bearingStartL1 - bearingStartEnd)); return x * R; } /// check if a given geo point is in the bounding box bool isGeoPointInBoundingBox(LatLng l, LatLng topLeft, LatLng bottomRight) { - return bottomRight.latitude <= l.latitude && - l.latitude <= topLeft.latitude && - topLeft.longitude <= l.longitude && - l.longitude <= bottomRight.longitude - ? true - : false; + return (bottomRight.latitude <= l.latitude && + l.latitude <= topLeft.latitude) && + (topLeft.longitude <= l.longitude && + l.longitude <= bottomRight.longitude); } /// check if a given geo point is in the a polygon @@ -189,29 +195,35 @@ class Geodesy { var isInPolygon = false; for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { - if ((((polygon[i].latitude <= l.latitude) && - (l.latitude < polygon[j].latitude)) || - ((polygon[j].latitude <= l.latitude) && - (l.latitude < polygon[i].latitude))) && - (l.longitude < - (polygon[j].longitude - polygon[i].longitude) * - (l.latitude - polygon[i].latitude) / - (polygon[j].latitude - polygon[i].latitude) + - polygon[i].longitude)) isInPolygon = !isInPolygon; + final vertexI = polygon[i]; + final vertexJ = polygon[j]; + + final aboveLatitude = + (vertexI.latitude <= l.latitude) && (l.latitude < vertexJ.latitude); + final belowLatitude = + (vertexJ.latitude <= l.latitude) && (l.latitude < vertexI.latitude); + final withinLongitude = l.longitude < + (vertexJ.longitude - vertexI.longitude) * + (l.latitude - vertexI.latitude) / + (vertexJ.latitude - vertexI.latitude) + + vertexI.longitude; + + if ((aboveLatitude || belowLatitude) && withinLongitude) { + isInPolygon = !isInPolygon; + } } + return isInPolygon; } /// Get a list of [LatLng] points within a distance from /// a given point - /// /// Distance is in meters List pointsInRange( LatLng point, List pointsToCheck, num distance) { final geoFencedPoints = []; for (final p in pointsToCheck) { - final distanceFromCenter = distanceBetweenTwoGeoPoints(point, p); - if (distanceFromCenter <= distance) { + if (distanceBetweenTwoGeoPoints(point, p) <= distance) { geoFencedPoints.add(p); } } diff --git a/pubspec.yaml b/pubspec.yaml index 681c103..db28fa4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: latlong2: ^0.9.0 - pedantic: ^1.11.0 dev_dependencies: + lints: ^2.1.1 test: ^1.16.8 diff --git a/test/geodesy_test.dart b/test/geodesy_test.dart index fe33287..386e8ee 100644 --- a/test/geodesy_test.dart +++ b/test/geodesy_test.dart @@ -2,7 +2,7 @@ import 'package:test/test.dart'; import 'package:geodesy/geodesy.dart'; void main() { - var geodesy = Geodesy(); + final geodesy = Geodesy(); test('distanceBetweenTwoGeoPoints', () async { final l1 = const LatLng(50.06638889, 5.71472222); @@ -93,9 +93,9 @@ void main() { final pointInRange = geodesy.destinationPointByDistanceAndBearing( point, distance - 10, 420.0); final pointsToCheck = [pointInRange, pointNotInRange]; - final geofencedPoints = + final geoFencedPoints = geodesy.pointsInRange(point, pointsToCheck, distance); - expect((geofencedPoints.contains(pointInRange)), true); - expect((geofencedPoints.contains(pointNotInRange)), false); + expect((geoFencedPoints.contains(pointInRange)), true); + expect((geoFencedPoints.contains(pointNotInRange)), false); }); }