UNPKG

s2-tools

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

196 lines 6.73 kB
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