// https://en.wikipedia.org/wiki/Rhumb_line import { convertLength, earthRadius } from "@turf/helpers"; import { getCoord } from "@turf/invariant"; /** * Calculates the distance along a rhumb line between two {@link Point|points} in degrees, radians, * miles, or kilometers. * * @name rhumbDistance * @param {Coord} from origin point * @param {Coord} to destination point * @param {Object} [options] Optional parameters * @param {string} [options.units="kilometers"] can be degrees, radians, miles, or kilometers * @returns {number} distance between the two points * @example * var from = turf.point([-75.343, 39.984]); * var to = turf.point([-75.534, 39.123]); * var options = {units: 'miles'}; * * var distance = turf.rhumbDistance(from, to, options); * * //addToMap * var addToMap = [from, to]; * from.properties.distance = distance; * to.properties.distance = distance; */ function rhumbDistance(from, to, options) { if (options === void 0) { options = {}; } var origin = getCoord(from); var destination = getCoord(to); // compensate the crossing of the 180th meridian (https://macwright.org/2016/09/26/the-180th-meridian.html) // solution from https://github.com/mapbox/mapbox-gl-js/issues/3250#issuecomment-294887678 destination[0] += destination[0] - origin[0] > 180 ? -360 : origin[0] - destination[0] > 180 ? 360 : 0; var distanceInMeters = calculateRhumbDistance(origin, destination); var distance = convertLength(distanceInMeters, "meters", options.units); return distance; } /** * Returns the distance travelling from ‘this’ point to destination point along a rhumb line. * Adapted from Geodesy: https://github.com/chrisveness/geodesy/blob/master/latlon-spherical.js * * @private * @param {Array} origin point. * @param {Array} destination point. * @param {number} [radius=6371e3] - (Mean) radius of earth (defaults to radius in metres). * @returns {number} Distance in km between this point and destination point (same units as radius). * * @example * var p1 = new LatLon(51.127, 1.338); * var p2 = new LatLon(50.964, 1.853); * var d = p1.distanceTo(p2); // 40.31 km */ function calculateRhumbDistance(origin, destination, radius) { // φ => phi // λ => lambda // ψ => psi // Δ => Delta // δ => delta // θ => theta radius = radius === undefined ? earthRadius : Number(radius); // see www.edwilliams.org/avform.htm#Rhumb var R = radius; var phi1 = (origin[1] * Math.PI) / 180; var phi2 = (destination[1] * Math.PI) / 180; var DeltaPhi = phi2 - phi1; var DeltaLambda = (Math.abs(destination[0] - origin[0]) * Math.PI) / 180; // if dLon over 180° take shorter rhumb line across the anti-meridian: if (DeltaLambda > Math.PI) { DeltaLambda -= 2 * Math.PI; } // on Mercator projection, longitude distances shrink by latitude; q is the 'stretch factor' // q becomes ill-conditioned along E-W line (0/0); use empirical tolerance to avoid it var DeltaPsi = Math.log(Math.tan(phi2 / 2 + Math.PI / 4) / Math.tan(phi1 / 2 + Math.PI / 4)); var q = Math.abs(DeltaPsi) > 10e-12 ? DeltaPhi / DeltaPsi : Math.cos(phi1); // distance is pythagoras on 'stretched' Mercator projection var delta = Math.sqrt(DeltaPhi * DeltaPhi + q * q * DeltaLambda * DeltaLambda); // angular distance in radians var dist = delta * R; return dist; } export default rhumbDistance;