s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
214 lines • 6.68 kB
JavaScript
import { ProjectionBase } from '.';
import { adjustLon } from '../common';
import { D2R, EPSLN, HALF_PI, R2D } from '../constants';
const COEFS_X = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
];
const COEFS_Y = [
[-5.20417e-18, 0.0124, 1.21431e-18, -8.45284e-11],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
];
const FXC = 0.8487;
const FYC = 1.3523;
const NODES = 18;
/**
* # Robinson
*
* **Classification**: Pseudocylindrical
*
* **Available forms**: Forward and inverse, spherical projection
*
* **Defined area**: Global
*
* **Alias**: robin
*
* **Domain**: 2D
*
* **Input type**: Geodetic coordinates
*
* **Output type**: Projected coordinates
*
* ## Projection String
* ```
* +proj=robin
* ```
*
* ## Required Parameters
* - None
*
* ## Optional Parameters
* - `+lon_0=<value>`: Central meridian.
* - `+R=<value>`: Radius of the projection sphere.
* - `+x_0=<value>`: False easting.
* - `+y_0=<value>`: False northing.
*
* 
*/
export class Robinson extends ProjectionBase {
name = 'Robinson';
static names = ['Robinson', 'robin'];
/**
* Preps an Robinson projection
* Based on https://github.com/OSGeo/proj.4/blob/master/src/PJ_robin.c
* Polynomial coeficients from http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039
* @param params - projection specific parameters
*/
constructor(params) {
super(params);
this.x0 = this.x0 ?? 0;
this.y0 = this.y0 ?? 0;
this.long0 = this.long0 ?? 0;
}
/**
* Robinson forward equations--mapping lon-lat to x-y
* @param p - lon-lat WGS84 point
*/
forward(p) {
const { abs, floor } = Math;
const C1 = R2D / 5; // rad to 5-degree interval
const RC1 = 1 / C1;
const lon = adjustLon(p.x - this.long0);
let dphi = abs(p.y);
let i = floor(dphi * C1);
if (i < 0) {
i = 0;
}
else if (i >= NODES) {
i = NODES - 1;
}
dphi = R2D * (dphi - RC1 * i);
const xy = {
x: poly3Val(COEFS_X[i], dphi) * lon,
y: poly3Val(COEFS_Y[i], dphi),
z: p.z,
m: p.m,
};
if (p.y < 0) {
xy.y = -xy.y;
}
p.x = xy.x * this.a * FXC + this.x0;
p.y = xy.y * this.a * FYC + this.y0;
}
/**
* Robinson inverse equations--mapping x-y to lon-lat
* @param p - Robinson point
*/
inverse(p) {
const { abs, floor } = Math;
const ll = {
x: (p.x - this.x0) / (this.a * FXC),
y: abs(p.y - this.y0) / (this.a * FYC),
};
if (ll.y >= 1) {
// pathologic case
ll.x /= COEFS_X[NODES][0];
ll.y = p.y < 0 ? -HALF_PI : HALF_PI;
}
else {
// find table interval
let i = floor(ll.y * NODES);
if (i < 0) {
i = 0;
}
else if (i >= NODES) {
i = NODES - 1;
}
while (true) {
if (COEFS_Y[i][0] > ll.y) {
--i;
}
else if (COEFS_Y[i + 1][0] <= ll.y) {
++i;
}
else {
break;
}
}
// linear interpolation in 5 degree interval
const coefs = COEFS_Y[i];
let t = (5 * (ll.y - coefs[0])) / (COEFS_Y[i + 1][0] - coefs[0]);
// find t so that poly3Val(coefs, t) = ll.y
t = newtonRapshon((x) => {
return (poly3Val(coefs, x) - ll.y) / poly3Der(coefs, x);
}, t, EPSLN, 100);
ll.x /= poly3Val(COEFS_X[i], t);
ll.y = (5 * i + t) * D2R;
if (p.y < 0) {
ll.y = -ll.y;
}
}
ll.x = adjustLon(ll.x + this.long0);
p.x = ll.x;
p.y = ll.y;
}
}
/**
* @param coefs - coefficient array
* @param x - argument
* @returns - value
*/
function poly3Val(coefs, x) {
return coefs[0] + x * (coefs[1] + x * (coefs[2] + x * coefs[3]));
}
/**
* @param coefs - coefficient array
* @param x - argument
* @returns - derivative
*/
function poly3Der(coefs, x) {
return coefs[1] + x * (2 * coefs[2] + x * 3 * coefs[3]);
}
/**
* @param fDf - derivative function of f
* @param start - starting guess
* @param max_err - maximum error
* @param iters - maximum number of iterations
* @returns - new guess
*/
function newtonRapshon(fDf, start, max_err, iters) {
let x = start;
for (; iters !== 0; --iters) {
const upd = fDf(x);
x -= upd;
if (Math.abs(upd) < max_err) {
break;
}
}
return x;
}
//# sourceMappingURL=robin.js.map