UNPKG

s2-tools

Version:

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

502 lines 13.2 kB
/* eslint-disable no-loss-of-precision */ import { EPSLN, HALF_PI, SPI, TWO_PI } from './constants'; /** * Returns the arc hyperbolic cosine of x. * @param x - input * @returns - acosh(x) */ export function acosh(x) { return 2 * Math.log(Math.sqrt((x + 1) / 2) + Math.sqrt((x - 1) / 2)); } /** * Returns an adjusted latitude * @param x - input * @returns - the adjusted latitude */ export function adjustLat(x) { return Math.abs(x) < HALF_PI ? x : x - sign(x) * Math.PI; } /** * Returns an adjusted longitude * @param x - input * @returns - the adjusted longitude */ export function adjustLon(x) { return Math.abs(x) <= SPI ? x : x - sign(x) * TWO_PI; } /** * Returns an adjusted zone relative to the input zone and longitude * @param zone - the input zone * @param lon - the input longitude * @returns - the adjusted zone */ export function adjustZone(zone, lon) { zone = zone ?? Math.floor(((adjustLon(lon) + Math.PI) * 30) / Math.PI) + 1; if (zone < 0) { return 0; } else if (zone > 60) { return 60; } else { return zone; } } /** * Returns the arc hyperbolic sine of x. * @param x - input * @returns - asinh(x) */ export function asinh(x) { const s = x >= 0 ? 1 : -1; return s * Math.log(Math.abs(x) + Math.sqrt(x * x + 1)); } /** * Returns the arc hyperbolic tangent of x. * @param x - input * @returns - asinhy(x) */ export function asinhy(x) { let y = Math.abs(x); y = log1py(y * (1 + y / (hypot(1, y) + 1))); return x < 0 ? -y : y; } /** * Returns the absolute value of the arc sine of x. * @param x - input * @returns - asinz(x) */ export function asinz(x) { if (Math.abs(x) > 1) x = x > 1 ? 1 : -1; return Math.asin(x); } /** * Returns the complex form of coefficients * @param pp - array of coefficients * @param arg_r - input * @param arg_i - input * @returns the complex result as a 2D array */ export function clensCmplx(pp, arg_r, arg_i) { const sin_arg_r = Math.sin(arg_r); const cos_arg_r = Math.cos(arg_r); const sinh_arg_i = sinh(arg_i); const cosh_arg_i = cosh(arg_i); let r = 2 * cos_arg_r * cosh_arg_i; let i = -2 * sin_arg_r * sinh_arg_i; let j = pp.length - 1; let hr = pp[j]; let hi1 = 0; let hr1 = 0; let hi = 0; let hr2; let hi2; while (--j >= 0) { hr2 = hr1; hi2 = hi1; hr1 = hr; hi1 = hi; hr = -hr2 + r * hr1 - i * hi1 + pp[j]; hi = -hi2 + i * hr1 + r * hi1; } r = sin_arg_r * cosh_arg_i; i = cos_arg_r * sinh_arg_i; return [r * hr - i * hi, r * hi + i * hr]; } /** * Returns the complex form of coefficients * @param pp - array of coefficients * @param arg_r - input * @returns the resultant compex number */ export function clens(pp, arg_r) { const r = 2 * Math.cos(arg_r); let i = pp.length - 1; let hr1 = pp[i]; let hr2 = 0; let hr = 0; while (--i >= 0) { hr = -hr2 + r * hr1 + pp[i]; hr2 = hr1; hr1 = hr; } return Math.sin(arg_r) * hr; } /** * Returns the hyperbolic cosine of x. * @param x - input * @returns - cosh(x) */ export function cosh(x) { let r = Math.exp(x); r = (r + 1 / r) / 2; return r; } /** * Returns eOfn(x) * @param x - input * @returns - eOfn(x) */ export function e0fn(x) { return 1 - 0.25 * x * (1 + (x / 16) * (3 + 1.25 * x)); } /** * Returns e1fn(x) * @param x - input * @returns - e1fn(x) */ export function e1fn(x) { return 0.375 * x * (1 + 0.25 * x * (1 + 0.46875 * x)); } /** * Returns e2fn(x) * @param x - input * @returns - e2fn(x) */ export function e2fn(x) { return 0.05859375 * x * x * (1 + 0.75 * x); } /** * Returns e3fn(x) * @param x - input * @returns - e3fn(x) */ export function e3fn(x) { return x * x * x * (35 / 3072); } /** * Convenience function to compute fL(x, L) * @param x - input * @param L - exponent * @returns - fL(x, L) */ export function fL(x, L) { return 2 * Math.atan(x * Math.exp(L)) - HALF_PI; } /** * Convenience function to compute gatg(pp, B) * @param pp - array of coefficients * @param B - input * @returns - gatg(pp, B) */ export function gatg(pp, B) { const cos_2B = 2 * Math.cos(2 * B); let i = pp.length - 1; let h1 = pp[i]; let h2 = 0; let h = 0; while (--i >= 0) { h = -h2 + cos_2B * h1 + pp[i]; h2 = h1; h1 = h; } return B + h * Math.sin(2 * B); } /** * Returns gN(a, e, sinphi) * @param a - input * @param e - input * @param sinphi - sin of latitude * @returns - gN(a, e, sinphi) */ export function gN(a, e, sinphi) { const temp = e * sinphi; return a / Math.sqrt(1 - temp * temp); } /** * Returns the hypotenuse of x and y * @param x - input * @param y - input * @returns - hypot(x, y) */ export function hypot(x, y) { x = Math.abs(x); y = Math.abs(y); const a = Math.max(x, y); const b = Math.min(x, y) / (a !== 0 ? a : 1); return a * Math.sqrt(1 + Math.pow(b, 2)); } /** * Convenience function to compute iMLfn(ml, e0, e1, e2, e3) * @param ml - input * @param e0 - input * @param e1 - input * @param e2 - input * @param e3 - input * @returns - iMLfn(ml, e0, e1, e2, e3) */ export function imlfn(ml, e0, e1, e2, e3) { let phi; let dphi; phi = ml / e0; for (let i = 0; i < 15; i++) { dphi = (ml - (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi))) / (e0 - 2 * e1 * Math.cos(2 * phi) + 4 * e2 * Math.cos(4 * phi) - 6 * e3 * Math.cos(6 * phi)); phi += dphi; if (Math.abs(dphi) <= 0.0000000001) { return phi; } } throw new Error('IMLFN-CONV:Latitude failed to converge after 15 iterations'); } /** * Inverse of iLfn * @param eccent - eccentricity * @param ts - input * @returns - invlatiso(eccent, ts) */ export function invlatiso(eccent, ts) { let phi = fL(1, ts); let Iphi = 0; let con = 0; do { Iphi = phi; con = eccent * Math.sin(Iphi); phi = fL(Math.exp((eccent * Math.log((1 + con) / (1 - con))) / 2), ts); } while (Math.abs(phi - Iphi) > 1.0e-12); return phi; } /** * Convienience function to compute iqsfnz(eccent, q) * @param eccent - eccentricity * @param q - input * @returns - iqsfnz(eccent, q) */ export function iqsfnz(eccent, q) { const temp = 1 - ((1 - eccent * eccent) / (2 * eccent)) * Math.log((1 - eccent) / (1 + eccent)); if (Math.abs(Math.abs(q) - temp) < 1.0e-6) { if (q < 0) { return -1 * HALF_PI; } else { return HALF_PI; } } //var phi = 0.5* q/(1-eccent*eccent); let phi = Math.asin(0.5 * q); let dphi; let sin_phi; let cos_phi; let con; for (let i = 0; i < 30; i++) { sin_phi = Math.sin(phi); cos_phi = Math.cos(phi); con = eccent * sin_phi; dphi = (Math.pow(1 - con * con, 2) / (2 * cos_phi)) * (q / (1 - eccent * eccent) - sin_phi / (1 - con * con) + (0.5 / eccent) * Math.log((1 - con) / (1 + con))); phi += dphi; if (Math.abs(dphi) <= 0.0000000001) { return phi; } } throw new Error('IQSFN-CONV:Latitude failed to converge after 30 iterations'); } /** * Convenience function to compute latiso(eccent, phi, sinphi) * @param eccent - eccentricity * @param phi - latitude * @param sinphi - sin of latitude * @returns - latiso(eccent, phi, sinphi) */ export function latiso(eccent, phi, sinphi) { if (Math.abs(phi) > HALF_PI) return Number.NaN; if (phi === HALF_PI) return Number.POSITIVE_INFINITY; if (phi === -1 * HALF_PI) return Number.NEGATIVE_INFINITY; const con = eccent * sinphi; return Math.log(Math.tan((HALF_PI + phi) / 2)) + (eccent * Math.log((1 - con) / (1 + con))) / 2; } /** * Convenience function to compute log1py(x) * @param x - input * @returns - log1py(x) */ export function log1py(x) { const y = 1 + x; const z = y - 1; return z === 0 ? x : (x * Math.log(y)) / z; } /** * Convienience function to compute mlfn * @param e0 - input * @param e1 - input * @param e2 - input * @param e3 - input * @param phi - latitude * @returns - mlfn(e0, e1, e2, e3, phi) */ export function mlfn(e0, e1, e2, e3, phi) { return e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi); } /** * Convienience function to compute msfnz(eccent, sinphi, cosphi) * @param eccent - eccentricity * @param sinphi - sin of latitude * @param cosphi - cos of latitude * @returns - msfnz(eccent, sinphi, cosphi) */ export function msfnz(eccent, sinphi, cosphi) { const con = eccent * sinphi; return cosphi / Math.sqrt(1 - con * con); } /** * Convenience function to compute phi2z(eccent, ts) * @param eccent - eccentricity * @param ts - input * @returns - phi2z(eccent, ts) */ export function phi2z(eccent, ts) { const eccnth = 0.5 * eccent; let con, dphi; let phi = HALF_PI - 2 * Math.atan(ts); for (let i = 0; i <= 15; i++) { con = eccent * Math.sin(phi); dphi = HALF_PI - 2 * Math.atan(ts * Math.pow((1 - con) / (1 + con), eccnth)) - phi; phi += dphi; if (Math.abs(dphi) <= 0.0000000001) { return phi; } } throw new Error('phi2z has NoConvergence'); } /** * Convenience function to compute enfn(es) * @param es - eccentricity * @returns - enfn(es) */ export function pjEnfn(es) { const C00 = 1; const C02 = 0.25; const C04 = 0.046875; const C06 = 0.01953125; const C08 = 0.01068115234375; const C22 = 0.75; const C44 = 0.46875; const C46 = 0.01302083333333333333; const C48 = 0.00712076822916666666; const C66 = 0.36458333333333333333; const C68 = 0.00569661458333333333; const C88 = 0.3076171875; const en = [0, 0, 0, 0, 0]; en[0] = C00 - es * (C02 + es * (C04 + es * (C06 + es * C08))); en[1] = es * (C22 - es * (C04 + es * (C06 + es * C08))); let t = es * es; en[2] = t * (C44 - es * (C46 + es * C48)); t *= es; en[3] = t * (C66 - es * C68); en[4] = t * es * C88; return en; } /** * Convenience function for pjInvMlfn(arg, es, en) * @param arg - latitude * @param es - eccentricity * @param en - input * @returns - pjInvMlfn(arg, es, en) */ export function pjInvMlfn(arg, es, en) { const MAX_ITER = 20; const k = 1 / (1 - es); let phi = arg; for (let i = MAX_ITER; i !== 0; --i) { /* rarely goes over 2 iterations */ const s = Math.sin(phi); let t = 1 - es * s * s; //t = this.pj_mlfn(phi, s, Math.cos(phi), en) - arg; //phi -= t * (t * Math.sqrt(t)) * k; t = (pjMlfn(phi, s, Math.cos(phi), en) - arg) * (t * Math.sqrt(t)) * k; phi -= t; if (Math.abs(t) < EPSLN) { return phi; } } throw new Error('cass:pj_inv_mlfn: Convergence error'); } /** * Convenience function for pjMlfn(phi, sphi, cphi, en) * @param phi - latitude * @param sphi - sin of latitude * @param cphi - cos of latitude * @param en - input * @returns - pjMlfn(phi, sphi, cphi, en) */ export function pjMlfn(phi, sphi, cphi, en) { cphi *= sphi; sphi *= sphi; return en[0] * phi - cphi * (en[1] + sphi * (en[2] + sphi * (en[3] + sphi * en[4]))); } /** * Convenience function for qsfnz(eccent, sinphi) * @param eccent - eccentricity * @param sinphi - sin of latitude * @returns - qsfnz(eccent, sinphi) */ export function qsfnz(eccent, sinphi) { let con; if (eccent > 1.0e-7) { con = eccent * sinphi; return ((1 - eccent * eccent) * (sinphi / (1 - con * con) - (0.5 / eccent) * Math.log((1 - con) / (1 + con)))); } else { return 2 * sinphi; } } /** * Returns the sign of x * @param x - The value to get the sign of * @returns - 1 if x is positive, -1 if x is negative */ export function sign(x) { return x < 0 ? -1 : 1; } /** * Returns the hyperbolic sine of x. * @param x - The value to calculate the hyperbolic sine of * @returns - sinh(x) */ export function sinh(x) { let r = Math.exp(x); r = (r - 1 / r) / 2; return r; } /** * Returns the ratio of the exponential of two numbers * @param esinp - The ratio * @param exp - The exponent * @returns - srat(esinp, exp) */ export function srat(esinp, exp) { return Math.pow((1 - esinp) / (1 + esinp), exp); } /** * Returns the hyperbolic tangent of x. * @param x - The value to calculate the hyperbolic tangent of * @returns - tanh(x) */ export function tanh(x) { let r = Math.exp(x); r = (r - 1 / r) / (r + 1 / r); return r; } /** * Apply the spherical formulae to obtain the conformal latitude * @param eccent - eccentricity * @param phi - latitude * @param sinphi - Math.sin(latitude) * @returns - conformal latitude */ export function tsfnz(eccent, phi, sinphi) { let con = eccent * sinphi; const com = 0.5 * eccent; con = Math.pow((1 - con) / (1 + con), com); return Math.tan(0.5 * (HALF_PI - phi)) / con; } //# sourceMappingURL=common.js.map