index.js 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // https://en.wikipedia.org/wiki/Rhumb_line
  2. import { convertLength, degreesToRadians, earthRadius, point, } from "@turf/helpers";
  3. import { getCoord } from "@turf/invariant";
  4. /**
  5. * Returns the destination {@link Point} having travelled the given distance along a Rhumb line from the
  6. * origin Point with the (varant) given bearing.
  7. *
  8. * @name rhumbDestination
  9. * @param {Coord} origin starting point
  10. * @param {number} distance distance from the starting point
  11. * @param {number} bearing varant bearing angle ranging from -180 to 180 degrees from north
  12. * @param {Object} [options={}] Optional parameters
  13. * @param {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers
  14. * @param {Object} [options.properties={}] translate properties to destination point
  15. * @returns {Feature<Point>} Destination point.
  16. * @example
  17. * var pt = turf.point([-75.343, 39.984], {"marker-color": "F00"});
  18. * var distance = 50;
  19. * var bearing = 90;
  20. * var options = {units: 'miles'};
  21. *
  22. * var destination = turf.rhumbDestination(pt, distance, bearing, options);
  23. *
  24. * //addToMap
  25. * var addToMap = [pt, destination]
  26. * destination.properties['marker-color'] = '#00F';
  27. */
  28. function rhumbDestination(origin, distance, bearing, options) {
  29. if (options === void 0) { options = {}; }
  30. var wasNegativeDistance = distance < 0;
  31. var distanceInMeters = convertLength(Math.abs(distance), options.units, "meters");
  32. if (wasNegativeDistance)
  33. distanceInMeters = -Math.abs(distanceInMeters);
  34. var coords = getCoord(origin);
  35. var destination = calculateRhumbDestination(coords, distanceInMeters, bearing);
  36. // compensate the crossing of the 180th meridian (https://macwright.org/2016/09/26/the-180th-meridian.html)
  37. // solution from https://github.com/mapbox/mapbox-gl-js/issues/3250#issuecomment-294887678
  38. destination[0] +=
  39. destination[0] - coords[0] > 180
  40. ? -360
  41. : coords[0] - destination[0] > 180
  42. ? 360
  43. : 0;
  44. return point(destination, options.properties);
  45. }
  46. /**
  47. * Returns the destination point having travelled along a rhumb line from origin point the given
  48. * distance on the given bearing.
  49. * Adapted from Geodesy: http://www.movable-type.co.uk/scripts/latlong.html#rhumblines
  50. *
  51. * @private
  52. * @param {Array<number>} origin - point
  53. * @param {number} distance - Distance travelled, in same units as earth radius (default: metres).
  54. * @param {number} bearing - Bearing in degrees from north.
  55. * @param {number} [radius=6371e3] - (Mean) radius of earth (defaults to radius in metres).
  56. * @returns {Array<number>} Destination point.
  57. */
  58. function calculateRhumbDestination(origin, distance, bearing, radius) {
  59. // φ => phi
  60. // λ => lambda
  61. // ψ => psi
  62. // Δ => Delta
  63. // δ => delta
  64. // θ => theta
  65. radius = radius === undefined ? earthRadius : Number(radius);
  66. var delta = distance / radius; // angular distance in radians
  67. var lambda1 = (origin[0] * Math.PI) / 180; // to radians, but without normalize to 𝜋
  68. var phi1 = degreesToRadians(origin[1]);
  69. var theta = degreesToRadians(bearing);
  70. var DeltaPhi = delta * Math.cos(theta);
  71. var phi2 = phi1 + DeltaPhi;
  72. // check for some daft bugger going past the pole, normalise latitude if so
  73. if (Math.abs(phi2) > Math.PI / 2) {
  74. phi2 = phi2 > 0 ? Math.PI - phi2 : -Math.PI - phi2;
  75. }
  76. var DeltaPsi = Math.log(Math.tan(phi2 / 2 + Math.PI / 4) / Math.tan(phi1 / 2 + Math.PI / 4));
  77. // E-W course becomes ill-conditioned with 0/0
  78. var q = Math.abs(DeltaPsi) > 10e-12 ? DeltaPhi / DeltaPsi : Math.cos(phi1);
  79. var DeltaLambda = (delta * Math.sin(theta)) / q;
  80. var lambda2 = lambda1 + DeltaLambda;
  81. return [
  82. (((lambda2 * 180) / Math.PI + 540) % 360) - 180,
  83. (phi2 * 180) / Math.PI,
  84. ]; // normalise to −180..+180°
  85. }
  86. export default rhumbDestination;