s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
244 lines • 8.78 kB
JavaScript
import { ProjectionBase } from '.';
import { EPSLN, HALF_PI } from '../constants';
import { adjustLon, msfnz, phi2z, sign, tsfnz } from '../common';
/**
* # Stereographic
*
* **Classification**: Azimuthal
*
* **Available forms**: Forward and inverse, spherical and ellipsoidal
*
* **Defined area**: Global
*
* **Alias**: stere
*
* **Domain**: 2D
*
* **Input type**: Geodetic coordinates
*
* **Output type**: Projected coordinates
*
* ## Projection String
* ```
* +proj=stere +lat_0=90 +latTs=75
* ```
*
* Note:
* This projection method gives different results than the :ref:`sterea`
* method in the non-polar cases (i.e. the oblique and equatorial case). The later
* projection method is the one referenced by EPSG as "Oblique Stereographic".
*
* ## Required Parameters
* - None
*
* ## Optional Parameters
* - `+lat_0=<value>`: Latitude of origin.
* - `+latTs=<value>`: Latitude where scale is not distorted.
* - `+k_0=<value>`: Scale factor.
* - `+lon_0=<value>`: Central meridian.
* - `+ellps=<value>`: Ellipsoid used.
* - `+R=<value>`: Radius of the projection sphere.
* - `+x_0=<value>`: False easting.
* - `+y_0=<value>`: False northing.
*
* 
*/
export class StereographicSouthPole extends ProjectionBase {
name = 'StereographicSouthPole';
static names = [
'StereographicSouthPole',
'stere',
'Stereographic_South_Pole',
'Polar Stereographic (variant B)',
'Polar_Stereographic',
];
// StereographicSouthPole specific variables
coslat0;
sinlat0;
con = 0;
cons = 0;
ms1 = 0;
X0 = 0;
cosX0 = 0;
sinX0 = 0;
/**
* Preps an StereographicSouthPole projection
* @param params - projection specific parameters
*/
constructor(params) {
const { abs, pow, sin, cos, sqrt, atan } = Math;
super(params);
this.x0 = this.x0 ?? 0;
this.y0 = this.y0 ?? 0;
this.lat0 = this.lat0 ?? 0;
this.long0 = this.long0 ?? 0;
this.coslat0 = cos(this.lat0);
this.sinlat0 = sin(this.lat0);
if (this.sphere) {
if (this.k0 === 1 && this.latTs !== undefined && abs(this.coslat0) <= EPSLN) {
this.k0 = 0.5 * (1 + sign(this.lat0) * sin(this.latTs));
}
}
else {
if (abs(this.coslat0) <= EPSLN) {
if (this.lat0 > 0) {
//North pole
//trace('stere:north pole');
this.con = 1;
}
else {
//South pole
//trace('stere:south pole');
this.con = -1;
}
}
this.cons = sqrt(pow(1 + this.e, 1 + this.e) * pow(1 - this.e, 1 - this.e));
if (this.k0 === 1 &&
this.latTs !== undefined &&
abs(this.coslat0) <= EPSLN &&
abs(cos(this.latTs)) > EPSLN) {
// When k0 is 1 (default value) and latTs is a vaild number and lat0 is at a pole and latTs is not at a pole
// Recalculate k0 using formula 21-35 from p161 of Snyder, 1987
this.k0 =
(0.5 * this.cons * msfnz(this.e, sin(this.latTs), cos(this.latTs))) /
tsfnz(this.e, this.con * this.latTs, this.con * sin(this.latTs));
}
this.ms1 = msfnz(this.e, this.sinlat0, this.coslat0);
this.X0 = 2 * atan(this.#ssfn(this.lat0, this.sinlat0, this.e)) - HALF_PI;
this.cosX0 = cos(this.X0);
this.sinX0 = sin(this.X0);
}
}
/**
* StereographicSouthPole forward equations--mapping lon-lat to x-y
* @param p - lon-lat WGS84 point
*/
forward(p) {
const { abs, sin, cos, atan, PI } = Math;
const { x: lon, y: lat } = p;
const sinlat = sin(lat);
const coslat = cos(lat);
let A, X, sinX, cosX, ts, rh;
const dlon = adjustLon(lon - this.long0);
if (abs(abs(lon - this.long0) - PI) <= EPSLN && abs(lat + this.lat0) <= EPSLN) {
//case of the origine point
throw new Error('cass:pj_init_stere: lon == long0 == 180');
}
if (this.sphere) {
//trace('stere:sphere case');
A = (2 * this.k0) / (1 + this.sinlat0 * sinlat + this.coslat0 * coslat * cos(dlon));
p.x = this.a * A * coslat * sin(dlon) + this.x0;
p.y = this.a * A * (this.coslat0 * sinlat - this.sinlat0 * coslat * cos(dlon)) + this.y0;
return;
}
else {
X = 2 * atan(this.#ssfn(lat, sinlat, this.e)) - HALF_PI;
cosX = cos(X);
sinX = sin(X);
if (abs(this.coslat0) <= EPSLN) {
ts = tsfnz(this.e, lat * this.con, this.con * sinlat);
rh = (2 * this.a * this.k0 * ts) / this.cons;
p.x = this.x0 + rh * sin(lon - this.long0);
p.y = this.y0 - this.con * rh * cos(lon - this.long0);
//trace(p.toString());
return;
}
else if (abs(this.sinlat0) < EPSLN) {
//Eq
//trace('stere:equateur');
A = (2 * this.a * this.k0) / (1 + cosX * cos(dlon));
p.y = A * sinX;
}
else {
//other case
//trace('stere:normal case');
A =
(2 * this.a * this.k0 * this.ms1) /
(this.cosX0 * (1 + this.sinX0 * sinX + this.cosX0 * cosX * cos(dlon)));
p.y = A * (this.cosX0 * sinX - this.sinX0 * cosX * cos(dlon)) + this.y0;
}
p.x = A * cosX * sin(dlon) + this.x0;
}
}
/**
* StereographicSouthPole inverse equations--mapping x-y to lon-lat
* @param p - StereographicSouthPole point
*/
inverse(p) {
const { abs, sin, cos, sqrt, atan2, asin, tan, atan } = Math;
p.x -= this.x0;
p.y -= this.y0;
let lon, lat, ts, ce, Chi;
const rh = sqrt(p.x * p.x + p.y * p.y);
if (this.sphere) {
const c = 2 * atan(rh / (2 * this.a * this.k0));
lon = this.long0;
lat = this.lat0;
if (rh <= EPSLN) {
p.x = lon;
p.y = lat;
return;
}
lat = asin(cos(c) * this.sinlat0 + (p.y * sin(c) * this.coslat0) / rh);
if (abs(this.coslat0) < EPSLN) {
if (this.lat0 > 0) {
lon = adjustLon(this.long0 + atan2(p.x, -1 * p.y));
}
else {
lon = adjustLon(this.long0 + atan2(p.x, p.y));
}
}
else {
lon = adjustLon(this.long0 +
atan2(p.x * sin(c), rh * this.coslat0 * cos(c) - p.y * this.sinlat0 * sin(c)));
}
p.x = lon;
p.y = lat;
return;
}
else {
if (abs(this.coslat0) <= EPSLN) {
if (rh <= EPSLN) {
lat = this.lat0;
lon = this.long0;
p.x = lon;
p.y = lat;
//trace(p.toString());
return;
}
p.x *= this.con;
p.y *= this.con;
ts = (rh * this.cons) / (2 * this.a * this.k0);
lat = this.con * phi2z(this.e, ts);
lon = this.con * adjustLon(this.con * this.long0 + atan2(p.x, -1 * p.y));
}
else {
ce = 2 * atan((rh * this.cosX0) / (2 * this.a * this.k0 * this.ms1));
lon = this.long0;
if (rh <= EPSLN) {
Chi = this.X0;
}
else {
Chi = asin(cos(ce) * this.sinX0 + (p.y * sin(ce) * this.cosX0) / rh);
lon = adjustLon(this.long0 +
atan2(p.x * sin(ce), rh * this.cosX0 * cos(ce) - p.y * this.sinX0 * sin(ce)));
}
lat = -1 * phi2z(this.e, tan(0.5 * (HALF_PI + Chi)));
}
}
p.x = lon;
p.y = lat;
}
/**
* @param phit - phi
* @param sinphi - sin(phi)
* @param eccen - eccentricity
* @returns - tan(0.5*(HALF_PI+phit))
*/
#ssfn(phit, sinphi, eccen) {
const { pow, tan } = Math;
sinphi *= eccen;
return tan(0.5 * (HALF_PI + phit)) * pow((1 - sinphi) / (1 + sinphi), 0.5 * eccen);
}
}
//# sourceMappingURL=stere.js.map