299 lines
7.4 KiB
JavaScript
299 lines
7.4 KiB
JavaScript
|
|
import {HALF_PI, EPSLN, FORTPI} from '../constants/values';
|
|
|
|
import qsfnz from '../common/qsfnz';
|
|
import adjust_lon from '../common/adjust_lon';
|
|
|
|
/*
|
|
reference
|
|
"New Equal-Area Map Projections for Noncircular Regions", John P. Snyder,
|
|
The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.
|
|
*/
|
|
|
|
export var S_POLE = 1;
|
|
|
|
export var N_POLE = 2;
|
|
export var EQUIT = 3;
|
|
export var OBLIQ = 4;
|
|
|
|
/* Initialize the Lambert Azimuthal Equal Area projection
|
|
------------------------------------------------------*/
|
|
export function init() {
|
|
var t = Math.abs(this.lat0);
|
|
if (Math.abs(t - HALF_PI) < EPSLN) {
|
|
this.mode = this.lat0 < 0 ? this.S_POLE : this.N_POLE;
|
|
}
|
|
else if (Math.abs(t) < EPSLN) {
|
|
this.mode = this.EQUIT;
|
|
}
|
|
else {
|
|
this.mode = this.OBLIQ;
|
|
}
|
|
if (this.es > 0) {
|
|
var sinphi;
|
|
|
|
this.qp = qsfnz(this.e, 1);
|
|
this.mmf = 0.5 / (1 - this.es);
|
|
this.apa = authset(this.es);
|
|
switch (this.mode) {
|
|
case this.N_POLE:
|
|
this.dd = 1;
|
|
break;
|
|
case this.S_POLE:
|
|
this.dd = 1;
|
|
break;
|
|
case this.EQUIT:
|
|
this.rq = Math.sqrt(0.5 * this.qp);
|
|
this.dd = 1 / this.rq;
|
|
this.xmf = 1;
|
|
this.ymf = 0.5 * this.qp;
|
|
break;
|
|
case this.OBLIQ:
|
|
this.rq = Math.sqrt(0.5 * this.qp);
|
|
sinphi = Math.sin(this.lat0);
|
|
this.sinb1 = qsfnz(this.e, sinphi) / this.qp;
|
|
this.cosb1 = Math.sqrt(1 - this.sinb1 * this.sinb1);
|
|
this.dd = Math.cos(this.lat0) / (Math.sqrt(1 - this.es * sinphi * sinphi) * this.rq * this.cosb1);
|
|
this.ymf = (this.xmf = this.rq) / this.dd;
|
|
this.xmf *= this.dd;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (this.mode === this.OBLIQ) {
|
|
this.sinph0 = Math.sin(this.lat0);
|
|
this.cosph0 = Math.cos(this.lat0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Lambert Azimuthal Equal Area forward equations--mapping lat,long to x,y
|
|
-----------------------------------------------------------------------*/
|
|
export function forward(p) {
|
|
|
|
/* Forward equations
|
|
-----------------*/
|
|
var x, y, coslam, sinlam, sinphi, q, sinb, cosb, b, cosphi;
|
|
var lam = p.x;
|
|
var phi = p.y;
|
|
|
|
lam = adjust_lon(lam - this.long0);
|
|
if (this.sphere) {
|
|
sinphi = Math.sin(phi);
|
|
cosphi = Math.cos(phi);
|
|
coslam = Math.cos(lam);
|
|
if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
|
|
y = (this.mode === this.EQUIT) ? 1 + cosphi * coslam : 1 + this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
|
|
if (y <= EPSLN) {
|
|
return null;
|
|
}
|
|
y = Math.sqrt(2 / y);
|
|
x = y * cosphi * Math.sin(lam);
|
|
y *= (this.mode === this.EQUIT) ? sinphi : this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;
|
|
}
|
|
else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
|
|
if (this.mode === this.N_POLE) {
|
|
coslam = -coslam;
|
|
}
|
|
if (Math.abs(phi + this.lat0) < EPSLN) {
|
|
return null;
|
|
}
|
|
y = FORTPI - phi * 0.5;
|
|
y = 2 * ((this.mode === this.S_POLE) ? Math.cos(y) : Math.sin(y));
|
|
x = y * Math.sin(lam);
|
|
y *= coslam;
|
|
}
|
|
}
|
|
else {
|
|
sinb = 0;
|
|
cosb = 0;
|
|
b = 0;
|
|
coslam = Math.cos(lam);
|
|
sinlam = Math.sin(lam);
|
|
sinphi = Math.sin(phi);
|
|
q = qsfnz(this.e, sinphi);
|
|
if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
|
|
sinb = q / this.qp;
|
|
cosb = Math.sqrt(1 - sinb * sinb);
|
|
}
|
|
switch (this.mode) {
|
|
case this.OBLIQ:
|
|
b = 1 + this.sinb1 * sinb + this.cosb1 * cosb * coslam;
|
|
break;
|
|
case this.EQUIT:
|
|
b = 1 + cosb * coslam;
|
|
break;
|
|
case this.N_POLE:
|
|
b = HALF_PI + phi;
|
|
q = this.qp - q;
|
|
break;
|
|
case this.S_POLE:
|
|
b = phi - HALF_PI;
|
|
q = this.qp + q;
|
|
break;
|
|
}
|
|
if (Math.abs(b) < EPSLN) {
|
|
return null;
|
|
}
|
|
switch (this.mode) {
|
|
case this.OBLIQ:
|
|
case this.EQUIT:
|
|
b = Math.sqrt(2 / b);
|
|
if (this.mode === this.OBLIQ) {
|
|
y = this.ymf * b * (this.cosb1 * sinb - this.sinb1 * cosb * coslam);
|
|
}
|
|
else {
|
|
y = (b = Math.sqrt(2 / (1 + cosb * coslam))) * sinb * this.ymf;
|
|
}
|
|
x = this.xmf * b * cosb * sinlam;
|
|
break;
|
|
case this.N_POLE:
|
|
case this.S_POLE:
|
|
if (q >= 0) {
|
|
x = (b = Math.sqrt(q)) * sinlam;
|
|
y = coslam * ((this.mode === this.S_POLE) ? b : -b);
|
|
}
|
|
else {
|
|
x = y = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
p.x = this.a * x + this.x0;
|
|
p.y = this.a * y + this.y0;
|
|
return p;
|
|
}
|
|
|
|
/* Inverse equations
|
|
-----------------*/
|
|
export function inverse(p) {
|
|
p.x -= this.x0;
|
|
p.y -= this.y0;
|
|
var x = p.x / this.a;
|
|
var y = p.y / this.a;
|
|
var lam, phi, cCe, sCe, q, rho, ab;
|
|
if (this.sphere) {
|
|
var cosz = 0,
|
|
rh, sinz = 0;
|
|
|
|
rh = Math.sqrt(x * x + y * y);
|
|
phi = rh * 0.5;
|
|
if (phi > 1) {
|
|
return null;
|
|
}
|
|
phi = 2 * Math.asin(phi);
|
|
if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
|
|
sinz = Math.sin(phi);
|
|
cosz = Math.cos(phi);
|
|
}
|
|
switch (this.mode) {
|
|
case this.EQUIT:
|
|
phi = (Math.abs(rh) <= EPSLN) ? 0 : Math.asin(y * sinz / rh);
|
|
x *= sinz;
|
|
y = cosz * rh;
|
|
break;
|
|
case this.OBLIQ:
|
|
phi = (Math.abs(rh) <= EPSLN) ? this.lat0 : Math.asin(cosz * this.sinph0 + y * sinz * this.cosph0 / rh);
|
|
x *= sinz * this.cosph0;
|
|
y = (cosz - Math.sin(phi) * this.sinph0) * rh;
|
|
break;
|
|
case this.N_POLE:
|
|
y = -y;
|
|
phi = HALF_PI - phi;
|
|
break;
|
|
case this.S_POLE:
|
|
phi -= HALF_PI;
|
|
break;
|
|
}
|
|
lam = (y === 0 && (this.mode === this.EQUIT || this.mode === this.OBLIQ)) ? 0 : Math.atan2(x, y);
|
|
}
|
|
else {
|
|
ab = 0;
|
|
if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
|
|
x /= this.dd;
|
|
y *= this.dd;
|
|
rho = Math.sqrt(x * x + y * y);
|
|
if (rho < EPSLN) {
|
|
p.x = this.long0;
|
|
p.y = this.lat0;
|
|
return p;
|
|
}
|
|
sCe = 2 * Math.asin(0.5 * rho / this.rq);
|
|
cCe = Math.cos(sCe);
|
|
x *= (sCe = Math.sin(sCe));
|
|
if (this.mode === this.OBLIQ) {
|
|
ab = cCe * this.sinb1 + y * sCe * this.cosb1 / rho;
|
|
q = this.qp * ab;
|
|
y = rho * this.cosb1 * cCe - y * this.sinb1 * sCe;
|
|
}
|
|
else {
|
|
ab = y * sCe / rho;
|
|
q = this.qp * ab;
|
|
y = rho * cCe;
|
|
}
|
|
}
|
|
else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
|
|
if (this.mode === this.N_POLE) {
|
|
y = -y;
|
|
}
|
|
q = (x * x + y * y);
|
|
if (!q) {
|
|
p.x = this.long0;
|
|
p.y = this.lat0;
|
|
return p;
|
|
}
|
|
ab = 1 - q / this.qp;
|
|
if (this.mode === this.S_POLE) {
|
|
ab = -ab;
|
|
}
|
|
}
|
|
lam = Math.atan2(x, y);
|
|
phi = authlat(Math.asin(ab), this.apa);
|
|
}
|
|
|
|
p.x = adjust_lon(this.long0 + lam);
|
|
p.y = phi;
|
|
return p;
|
|
}
|
|
|
|
/* determine latitude from authalic latitude */
|
|
var P00 = 0.33333333333333333333;
|
|
|
|
var P01 = 0.17222222222222222222;
|
|
var P02 = 0.10257936507936507936;
|
|
var P10 = 0.06388888888888888888;
|
|
var P11 = 0.06640211640211640211;
|
|
var P20 = 0.01641501294219154443;
|
|
|
|
function authset(es) {
|
|
var t;
|
|
var APA = [];
|
|
APA[0] = es * P00;
|
|
t = es * es;
|
|
APA[0] += t * P01;
|
|
APA[1] = t * P10;
|
|
t *= es;
|
|
APA[0] += t * P02;
|
|
APA[1] += t * P11;
|
|
APA[2] = t * P20;
|
|
return APA;
|
|
}
|
|
|
|
function authlat(beta, APA) {
|
|
var t = beta + beta;
|
|
return (beta + APA[0] * Math.sin(t) + APA[1] * Math.sin(t + t) + APA[2] * Math.sin(t + t + t));
|
|
}
|
|
|
|
export var names = ["Lambert Azimuthal Equal Area", "Lambert_Azimuthal_Equal_Area", "laea"];
|
|
export default {
|
|
init: init,
|
|
forward: forward,
|
|
inverse: inverse,
|
|
names: names,
|
|
S_POLE: S_POLE,
|
|
N_POLE: N_POLE,
|
|
EQUIT: EQUIT,
|
|
OBLIQ: OBLIQ
|
|
};
|