-
Notifications
You must be signed in to change notification settings - Fork 944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow optional ellipsoid definition to be passed to distance allowing for calculation types other than great circle #2476
base: master
Are you sure you want to change the base?
Changes from 3 commits
3f3f203
e6dfbd8
13d093c
c51158c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,33 @@ | ||
import { Point } from "geojson"; | ||
import { getCoord } from "@turf/invariant"; | ||
import { radiansToLength, degreesToRadians, Coord, Units } from "@turf/helpers"; | ||
|
||
import type { Coord, Datum, Units } from "@turf/helpers"; | ||
import { | ||
datums, | ||
radiansToLength, | ||
degreesToRadians, | ||
convertLength, | ||
} from "@turf/helpers"; | ||
import LatLon from "geodesy/latlon-ellipsoidal-vincenty.js"; | ||
//http://en.wikipedia.org/wiki/Haversine_formula | ||
//http://www.movable-type.co.uk/scripts/latlong.html | ||
|
||
/** | ||
* Calculates the distance between two {@link Point|points} in degrees, radians, miles, or kilometers. | ||
* This uses the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula) to account for global curvature. | ||
* Calculates the distance between two {@link Point|points}. | ||
* If a specific datum is passed, uses a geodesic ellipsoid calculation. | ||
* If no datum is passed, performs a great circle calculation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you think about keeping the ", using the Haversine formula". That seems to be a level of detail that people request having. |
||
* | ||
* @name distance | ||
* @param {Coord | Point} from origin point or coordinate | ||
* @param {Coord | Point} to destination point or coordinate | ||
* @param {Object} [options={}] Optional parameters | ||
* @param {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers | ||
* @returns {number} distance between the two points | ||
* @param {Datum} [options.datum=datums.WGS84] datum listed in {@link Helpers} | ||
* @returns {number} distance between the two points in chosen units | ||
* @example | ||
* var from = turf.point([-75.343, 39.984]); | ||
* var to = turf.point([-75.534, 39.123]); | ||
* var options = {units: 'miles'}; | ||
* | ||
* var options = {units: 'miles'}; | ||
* var distance = turf.distance(from, to, options); | ||
* | ||
* //addToMap | ||
|
@@ -28,6 +36,46 @@ import { radiansToLength, degreesToRadians, Coord, Units } from "@turf/helpers"; | |
* to.properties.distance = distance; | ||
*/ | ||
function distance( | ||
from: Coord | Point, | ||
to: Coord | Point, | ||
options: { | ||
units?: Units; | ||
datum?: Datum; | ||
} = {} | ||
) { | ||
if (options?.datum) { | ||
smallsaucepan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return geodesicEllipsoidDistance(from, to, { | ||
datum: options.datum, // Must pass a datum option | ||
...options, | ||
}); | ||
} else { | ||
return greatCircleDistance(from, to, options); | ||
} | ||
} | ||
|
||
/** | ||
* Calculates the distance between two {@link Point|points} in degrees, radians, miles, or kilometers. | ||
* Performs a great circle calculation using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula) to account for global curvature. | ||
* | ||
* @name greatCircleDistance | ||
* @param {Coord | Point} from origin point or coordinate | ||
* @param {Coord | Point} to destination point or coordinate | ||
* @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.greatCircleDistance(from, to, options); | ||
* | ||
* //addToMap | ||
* var addToMap = [from, to]; | ||
* from.properties.distance = distance; | ||
* to.properties.distance = distance; | ||
*/ | ||
function greatCircleDistance( | ||
from: Coord | Point, | ||
to: Coord | Point, | ||
options: { | ||
|
@@ -51,4 +99,51 @@ function distance( | |
); | ||
} | ||
|
||
/** | ||
* Calculates the distance between two {@link Point|points}. | ||
* Performs a geodesic ellipsoid calculation using [Vincenty's formulae](https://en.wikipedia.org/wiki/Vincenty%27s_formulae) to account for speroidal curvature. | ||
* | ||
* @name geodesicEllipsoidDistance | ||
* @param {Coord | Point} from origin point or coordinate | ||
* @param {Coord | Point} to destination point or coordinate | ||
* @param {Object} [options={}] Optional parameters | ||
* @param {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers | ||
* @param {Datum} [options.datum=datums.WGS84] datum listed in {@link Helpers} | ||
* @returns {number} distance between the two points in chosen units | ||
* @example | ||
* var from = turf.point([-75.343, 39.984]); | ||
* var to = turf.point([-75.534, 39.123]); | ||
* var options = {units: 'miles', datum: datums.WGS84}; | ||
* | ||
* var distance = turf.geodesicEllipsoidDistance(from, to, options); | ||
* | ||
* //addToMap | ||
* var addToMap = [from, to]; | ||
* from.properties.distance = distance; | ||
* to.properties.distance = distance; | ||
*/ | ||
function geodesicEllipsoidDistance( | ||
from: Coord | Point, | ||
to: Coord | Point, | ||
options: { | ||
units?: Units; | ||
datum: Datum; | ||
} = { datum: datums.WGS84 } | ||
) { | ||
const fromCoord = getCoord(from); | ||
const toCoord = getCoord(to); | ||
|
||
const datum = options.datum; | ||
|
||
const fromLatLon = new LatLon(fromCoord[1], fromCoord[0]); | ||
// datum on from point sets the tone. | ||
fromLatLon.datum = datum; | ||
const toLatLon = new LatLon(toCoord[1], toCoord[0]); | ||
|
||
const meters = fromLatLon.distanceTo(toLatLon); | ||
// geodesy lib result is in meters | ||
return convertLength(meters, "meters", options.units); | ||
} | ||
|
||
export { greatCircleDistance, geodesicEllipsoidDistance }; | ||
export default distance; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ import { | |
Position, | ||
GeoJsonProperties, | ||
} from "geojson"; | ||
import type { Datum } from "geodesy"; | ||
import LatLonEllipsoidalVincenty from "geodesy/latlon-ellipsoidal-vincenty.js"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing this module isn't exported out of the top-level of the geodesy library. |
||
|
||
import { Id } from "./lib/geojson"; | ||
export * from "./lib/geojson"; | ||
|
@@ -115,6 +117,19 @@ export const areaFactors: Record<AreaUnits, number> = { | |
yards: 1.195990046, | ||
}; | ||
|
||
/** | ||
* Common datum and ellipsoid definitions. Re-export verbatim from geodesy. | ||
* | ||
* @memberof helpers | ||
* @type {Object} | ||
*/ | ||
const datums = LatLonEllipsoidalVincenty.datums; | ||
export { datums }; | ||
Comment on lines
+126
to
+127
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed this geodesy module defines only one datum, WGS84.
https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal.js So this would export just the one datum to users via @turf/helpers for use in the distance function correct? And to use it, the user would need to know to import this datum and pass it. Does this feel like the right interface for the users? Do they need this low level of access? Is the intention of exposing the datum objects to be able to offer more datums via https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal-datum.js? What do you think about just accepting a Alternatively, can you see how the documentation will be improved alongside this PR to tell users how to access/use a datum object? I can see the param doc at least points at the Helpers module. |
||
|
||
// Re-export type from geodesy so clients don't need to refer directly to | ||
// geodesy types. | ||
export type { Datum }; | ||
|
||
/** | ||
* Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are technically correct here in that if the user passes any valid datum object, that the code as it is now will run
geodesicEllipsoidDistance
. But that doesn't seem clean/clear. Technically, I could possibly make up a datum and it will still run that one function right? But why? It seems like it would be better to accept the one expected ellipsoid datum, and throw an error otherwise. Or see my comment below about exposing just a string parameter to specify the datum.