gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
196 lines • 6.85 kB
JavaScript
import { EARTH_RADIUS } from '../../space/planets/index.js';
import { angleFromKM, angleFromMeters, angleToKM, angleToMeters } from './angle.js';
import { pointNorm2, pointSub } from '../s2/point.js';
/** 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 chordAngFromAngle(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 chordAngFromLength2(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 chordAngFromS2Points(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, pointNorm2(pointSub(a, b)));
}
/**
* Return a chord angle of 90 degrees (a "right angle").
* @returns The right angle.
*/
export function chordAngRightAngle() {
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 chordAngStraightAngle() {
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 chordAngNegativeAngle() {
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 chordAngFastUpperBoundFrom(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 chordAngFromLength2(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 chordAngIsSpecial(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 chordAngToAngle(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 chordAngToMeters(cAngle) {
return angleToMeters(chordAngToAngle(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 chordAngFromMeters(meters, radius = EARTH_RADIUS) {
return chordAngFromAngle(angleFromMeters(meters, radius));
}
/**
* Convert to kilometers.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding number of kilometers.
*/
export function chordAngToKM(cAngle) {
return angleToKM(chordAngToAngle(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 chordAngFromKM(km, radius = EARTH_RADIUS) {
return chordAngFromAngle(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 chordAngSin(cAngle) {
return Math.sqrt(chordAngSin2(cAngle));
}
/**
* apply a cosine function on a ChordAngle
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding cos(S1Angle).
*/
export function chordAngCos(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 chordAngTan(cAngle) {
return chordAngSin(cAngle) / chordAngCos(cAngle);
}
/**
* Returns sin(a)^2, but computed more efficiently.
* @param cAngle - The ChordAngle to convert.
* @returns The corresponding sin(S1Angle)^2.
*/
export function chordAngSin2(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