geometry_utils.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import 'dart:math';
  2. import 'package:latlong2/latlong.dart';
  3. class Point3D {
  4. final num x;
  5. final num y;
  6. final num z;
  7. static const _earthRadiusInMeter = 6371 * 1000;
  8. const Point3D({required this.x, required this.y, required this.z});
  9. // From https://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
  10. // (because I forgot basic geometry...)
  11. factory Point3D.fromLatLng(LatLng point) {
  12. final latInRadian = point.latitude * pi / 180;
  13. final longInRadian = point.longitude * pi / 180;
  14. return Point3D(
  15. x: _earthRadiusInMeter * cos(latInRadian) * cos(longInRadian),
  16. y: _earthRadiusInMeter * cos(latInRadian) * sin(longInRadian),
  17. z: _earthRadiusInMeter * sin(latInRadian),
  18. );
  19. }
  20. Point3D operator +(Point3D other) {
  21. return Point3D(x: x + other.x, y: y + other.y, z: z + other.z);
  22. }
  23. Point3D operator -(Point3D other) {
  24. return Point3D(x: x - other.x, y: y - other.y, z: z - other.z);
  25. }
  26. Point3D operator *(num value) {
  27. return Point3D(x: x * value, y: y * value, z: z * value);
  28. }
  29. }
  30. class Segment {
  31. final LatLng start;
  32. final LatLng end;
  33. const Segment({required this.start, required this.end});
  34. }
  35. // Implementation found from StackOverflow answer https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
  36. class DistanceUtils {
  37. /// Distance from [point] to [segment] in meter
  38. static num distToSegment(LatLng point, Segment segment) {
  39. final p = Point3D.fromLatLng(point);
  40. final v = Point3D.fromLatLng(segment.start);
  41. final w = Point3D.fromLatLng(segment.end);
  42. final segmentLength = _sqrDistBetweenPoints(v, w);
  43. // Case v == w
  44. if (segmentLength == 0) {
  45. return sqrt(_sqrDistBetweenPoints(p, v));
  46. }
  47. // If dotProduct is between 0 and 1, then the projected point is on the segment
  48. // and we take the distance between p and the projected point
  49. // Else, we take the distance between p and the nearer end
  50. final t = max(0, min(1, _dotProduct(p - v, w - v) / segmentLength));
  51. final projection = v + (w - v) * t;
  52. return sqrt(_sqrDistBetweenPoints(p, projection));
  53. }
  54. /// Distance between [point1] and [point2] in meter
  55. static num distBetweenTwoPoints(LatLng point1, LatLng point2) {
  56. final v = Point3D.fromLatLng(point1);
  57. final w = Point3D.fromLatLng(point2);
  58. return sqrt(_sqrDistBetweenPoints(v, w));
  59. }
  60. /// If parameter is between 0 and 1, the projected point is on the segment
  61. ///
  62. /// If parameter is greater than 1, the projected point is after the end of the segment
  63. ///
  64. /// If parameter is lesser than 0, the projected point is before the start of the segment
  65. static num getParameterFromProjection(LatLng point, Segment segment) {
  66. final p = Point3D.fromLatLng(point);
  67. final v = Point3D.fromLatLng(segment.start);
  68. final w = Point3D.fromLatLng(segment.end);
  69. final segmentLength = _sqrDistBetweenPoints(v, w);
  70. // Case v == w
  71. if (segmentLength == 0) {
  72. return 0;
  73. }
  74. return _dotProduct(p - v, w - v) / segmentLength;
  75. }
  76. static num _sqr(num x) {
  77. return x * x;
  78. }
  79. static num _sqrDistBetweenPoints(Point3D v, Point3D w) {
  80. return _sqr(v.x - w.x) + _sqr(v.y - w.y) + _sqr(v.z - w.z);
  81. }
  82. static num _dotProduct(Point3D v, Point3D w) {
  83. return (v.x * w.x) + (v.y * w.y) + (v.z * w.z);
  84. }
  85. }