s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
196 lines • 6.73 kB
JavaScript
import { EARTH_RADIUS } from '../../space/planets';
import { fromKM as angleFromKM, fromMeters as angleFromMeters, toKM as angleToKM, toMeters as angleToMeters, } from './angle';
import { norm2, sub } from '../s2/point';
/** The Maximum allowed squared chord length. */
export const K_MAX_LENGTH_2 = 4.0;
/**
* Conversion from an S1Angle. Angles outside the range [0, Pi] are handled
* as follows: Infinity() is mapped to Infinity(), negative angles are
* mapped to Negative(), and finite angles larger than Pi are mapped to
* Straight().
*
* Note that this operation is relatively expensive and should be avoided.
* To use S1ChordAngle effectively, you should structure your code so that
* input arguments are converted to S1ChordAngles at the beginning of your
* algorithm, and results are converted back to S1Angles only at the end.
*
* S1ChordAngles are represented by the squared chord length, which can
* range from 0 to 4. Infinity() uses an infinite squared length.
* @param angle - An angle in radians.
* @returns The corresponding ChordAngle.
*/
export function fromAngle(angle) {
const { sin, min, PI } = Math;
let length2_ = 0.0;
if (angle < 0.0) {
return -1;
}
else if (angle === Infinity) {
return Infinity;
}
else {
// The chord length is 2 * sin(angle / 2).
const length = 2.0 * sin(0.5 * min(PI, angle));
length2_ = length * length;
}
return length2_;
}
/**
* Construct an S1ChordAngle from the squared chord length. Note that the
* argument is automatically clamped to a maximum of 4.0 to handle possible
* roundoff errors. The argument must be non-negative.
* @param length2_ - The squared chord length.
* @returns The corresponding ChordAngle.
*/
export function fromLength2(length2_) {
return Math.min(K_MAX_LENGTH_2, length2_);
}
/**
* Construct the S1ChordAngle corresponding to the distance between the two
* given points. The points must be unit length.
* @param a - The first point.
* @param b - The second point.
* @returns The corresponding ChordAngle.
*/
export function fromS2Points(a, b) {
// The squared distance may slightly exceed 4.0 due to roundoff errors.
// The maximum error in the result is 2 * DBL_EPSILON * length2_.
return Math.min(K_MAX_LENGTH_2, norm2(sub(a, b)));
}
/**
* Return a chord angle of 90 degrees (a "right angle").
* @returns The right angle.
*/
export function rightAngle() {
return 2.0;
}
/**
* Return a chord angle of 180 degrees (a "straight angle"). This is the
* maximum finite chord angle.
* @returns The straight angle.
*/
export function straightAngle() {
return K_MAX_LENGTH_2;
}
/**
* Return a chord angle smaller than Zero(). The only valid operations on
* Negative() are comparisons, S1Angle conversions, and successor() /
* predecessor().
* @returns The negative angle.
*/
export function negativeAngle() {
return -1;
}
/**
* Construct an S1ChordAngle that is an upper bound on the given S1Angle.
* i.i. such that FastUpperBoundFrom(x).toAngle() >= x. Unlike the S1Angle
* constructor above, this method is very fast, and the bound is accurate to
* within 1% for distances up to about 3100km on the Earth's surface.
* @param angle - The S1Angle.
* @returns The corresponding S1ChordAngle.
*/
export function fastUpperBoundFrom(angle) {
// This method uses the distance along the surface of the sphere as an upper
// bound on the distance through the sphere's interior.
return fromLength2(angle * angle);
}
/**
* Convenience function to test if a ChordAngle is special.
* @param cAngle - The ChordAngle to test.
* @returns - true if the ChordAngle is special.
*/
export function isSpecial(cAngle) {
return cAngle < 0 || cAngle === Infinity;
}
/**
* Convert to an S1Angle.
* Infinity() is converted to S1Angle.Infinity(), and Negative() is
* converted to an unspecified negative S1Angle.
*
* Note that the conversion uses trigonometric functions and therefore
* should be avoided in inner loops.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding S1Angle.
*/
export function toAngle(cAngle) {
if (cAngle < 0)
return -1.0;
if (cAngle === Infinity)
return Infinity;
return 2 * Math.asin(0.5 * Math.sqrt(cAngle));
}
/**
* Convert to meters.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding number of meters.
*/
export function toMeters(cAngle) {
return angleToMeters(toAngle(cAngle));
}
/**
* Convert from meters.
* @param meters - distance in meters
* @param radius - radius of the planet (defaults to Earth's radius)
* @returns - the ChordAngle
*/
export function fromMeters(meters, radius = EARTH_RADIUS) {
return fromAngle(angleFromMeters(meters, radius));
}
/**
* Convert to kilometers.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding number of kilometers.
*/
export function toKM(cAngle) {
return angleToKM(toAngle(cAngle));
}
/**
* Convert from kilometers.
* @param km - distance in kilometers
* @param radius - radius of the planet (defaults to Earth's radius)
* @returns - the ChordAngle
*/
export function fromKM(km, radius = EARTH_RADIUS) {
return fromAngle(angleFromKM(km, radius));
}
// Trigonmetric functions. It is more accurate and efficient to call these
// rather than first converting to an S1Angle.
/**
* apply a sine function on a ChordAngle
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding sin(S1Angle).
*/
export function chordAngleSin(cAngle) {
return Math.sqrt(chordAngleSin2(cAngle));
}
/**
* apply a cosine function on a ChordAngle
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding cos(S1Angle).
*/
export function chordAngleCos(cAngle) {
// cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A)
return 1.0 - 0.5 * cAngle;
}
/**
* apply a tangent function on a ChordAngle
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding cos(S1Angle).
*/
export function chordAngleTan(cAngle) {
return chordAngleSin(cAngle) / chordAngleCos(cAngle);
}
/**
* Returns sin(a)^2, but computed more efficiently.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding sin(S1Angle)^2.
*/
export function chordAngleSin2(cAngle) {
// Let "a" be the (non-squared) chord length, and let A be the corresponding
// half-angle (a = 2*sin(A)). The formula below can be derived from:
// sin(2*A) = 2 * sin(A) * cos(A)
// cos^2(A) = 1 - sin^2(A)
// This is much faster than converting to an angle and computing its sine.
return cAngle * (1.0 - 0.25 * cAngle);
}
//# sourceMappingURL=chordAngle.js.map