s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
88 lines • 3.23 kB
JavaScript
import { lonLatToXYZ } from '../s2/coords';
import { degToRad, radToDeg } from '../util';
/**
* Converts an LonLat to the equivalent unit-length vector. Unnormalized
* values (see Normalize()) are wrapped around the sphere as would be expected
* based on their definition as spherical angles. So for example the
* following pairs yield equivalent points (modulo numerical error):
* (90.5, 10) =~ (89.5, -170)
* (a, b) =~ (a + 360 * n, b)
* The maximum error in the result is 1.5 * DBL_EPSILON. (This does not
* include the error of converting degrees, E5, E6, or E7 to radians.)
*
* Can be used just like an S2Point constructor. For example:
* S2Cap cap;
* cap.AddPoint(S2Point(latlon));
* @param ll - input LonLat
* @returns - equivalent unit-length vector 3D point
*/
export function toS2Point(ll) {
return lonLatToXYZ(ll[0], ll[1]);
}
/**
* Convert a direction vector (not necessarily unit length) to an LonLat.
* @param p - input direction vector
* @returns - LonLat
*/
export function fromS2Point(p) {
const { atan2, sqrt } = Math;
const [x, y, z] = p;
return [radToDeg(atan2(y, x)), radToDeg(atan2(z, sqrt(x * x + y * y)))];
}
/**
* @param ll - input LonLat
* @returns a lon-lat in radians
*/
export function toAngles(ll) {
return ll.map(degToRad);
}
/**
* @param ll - input lon-lat in degrees
* @returns - ensures lon in [-180, 180], lat in [-90, 90]
*/
export function normalize(ll) {
let [lon, lat] = ll;
// Normalize longitude using modulo
lon = ((((lon + 180) % 360) + 360) % 360) - 180;
// Clamp latitude between -90 and 90
lat = Math.max(-90, Math.min(90, lat));
return [lon, lat];
}
/**
* Returns the distance (measured along the surface of the sphere) to the
* given LonLat, implemented using the Haversine formula. This is
* equivalent to
*
* S1Angle(ToPoint(), o.ToPoint())
*
* except that this function is slightly faster, and is also somewhat less
* accurate for distances approaching 180 degrees (see s1angle.h for
* details). Both LngLats must be normalized.
* @param a - input LonLat
* @param b - input LonLat
* @returns - distance in radians
*/
export function getDistance(a, b) {
const { asin, sin, cos, sqrt, min } = Math;
// This implements the Haversine formula, which is numerically stable for
// small distances but only gets about 8 digits of precision for very large
// distances (e.g. antipodal points). Note that 8 digits is still accurate
// to within about 10cm for a sphere the size of the Earth.
//
// This could be fixed with another sin() and cos() below, but at that point
// you might as well just convert both arguments to S2Points and compute the
// distance that way (which gives about 15 digits of accuracy for all
// distances).
let [lonA, latA] = a;
let [lonB, latB] = b;
// conver all to radians
lonA = degToRad(lonA);
latA = degToRad(latA);
lonB = degToRad(lonB);
latB = degToRad(latB);
const dlat = sin(0.5 * (latB - latA));
const dlon = sin(0.5 * (lonB - lonA));
const x = dlat * dlat + dlon * dlon * cos(latA) * cos(latB);
return 2 * asin(sqrt(min(1, x)));
}
//# sourceMappingURL=index.js.map