170 lines
4.1 KiB
JavaScript
170 lines
4.1 KiB
JavaScript
|
|
||
|
var mode = {
|
||
|
N_POLE: 0,
|
||
|
S_POLE: 1,
|
||
|
EQUIT: 2,
|
||
|
OBLIQ: 3
|
||
|
};
|
||
|
|
||
|
import { D2R, HALF_PI, EPSLN } from "../constants/values";
|
||
|
import hypot from "../common/hypot";
|
||
|
|
||
|
var params = {
|
||
|
h: { def: 100000, num: true }, // default is Karman line, no default in PROJ.7
|
||
|
azi: { def: 0, num: true, degrees: true }, // default is North
|
||
|
tilt: { def: 0, num: true, degrees: true }, // default is Nadir
|
||
|
long0: { def: 0, num: true }, // default is Greenwich, conversion to rad is automatic
|
||
|
lat0: { def: 0, num: true } // default is Equator, conversion to rad is automatic
|
||
|
};
|
||
|
|
||
|
export function init() {
|
||
|
Object.keys(params).forEach(function (p) {
|
||
|
if (typeof this[p] === "undefined") {
|
||
|
this[p] = params[p].def;
|
||
|
} else if (params[p].num && isNaN(this[p])) {
|
||
|
throw new Error("Invalid parameter value, must be numeric " + p + " = " + this[p]);
|
||
|
} else if (params[p].num) {
|
||
|
this[p] = parseFloat(this[p]);
|
||
|
}
|
||
|
if (params[p].degrees) {
|
||
|
this[p] = this[p] * D2R;
|
||
|
}
|
||
|
}.bind(this));
|
||
|
|
||
|
if (Math.abs((Math.abs(this.lat0) - HALF_PI)) < EPSLN) {
|
||
|
this.mode = this.lat0 < 0 ? mode.S_POLE : mode.N_POLE;
|
||
|
} else if (Math.abs(this.lat0) < EPSLN) {
|
||
|
this.mode = mode.EQUIT;
|
||
|
} else {
|
||
|
this.mode = mode.OBLIQ;
|
||
|
this.sinph0 = Math.sin(this.lat0);
|
||
|
this.cosph0 = Math.cos(this.lat0);
|
||
|
}
|
||
|
|
||
|
this.pn1 = this.h / this.a; // Normalize relative to the Earth's radius
|
||
|
|
||
|
if (this.pn1 <= 0 || this.pn1 > 1e10) {
|
||
|
throw new Error("Invalid height");
|
||
|
}
|
||
|
|
||
|
this.p = 1 + this.pn1;
|
||
|
this.rp = 1 / this.p;
|
||
|
this.h1 = 1 / this.pn1;
|
||
|
this.pfact = (this.p + 1) * this.h1;
|
||
|
this.es = 0;
|
||
|
|
||
|
var omega = this.tilt;
|
||
|
var gamma = this.azi;
|
||
|
this.cg = Math.cos(gamma);
|
||
|
this.sg = Math.sin(gamma);
|
||
|
this.cw = Math.cos(omega);
|
||
|
this.sw = Math.sin(omega);
|
||
|
}
|
||
|
|
||
|
export function forward(p) {
|
||
|
p.x -= this.long0;
|
||
|
var sinphi = Math.sin(p.y);
|
||
|
var cosphi = Math.cos(p.y);
|
||
|
var coslam = Math.cos(p.x);
|
||
|
var x, y;
|
||
|
switch (this.mode) {
|
||
|
case mode.OBLIQ:
|
||
|
y = this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
|
||
|
break;
|
||
|
case mode.EQUIT:
|
||
|
y = cosphi * coslam;
|
||
|
break;
|
||
|
case mode.S_POLE:
|
||
|
y = -sinphi;
|
||
|
break;
|
||
|
case mode.N_POLE:
|
||
|
y = sinphi;
|
||
|
break;
|
||
|
}
|
||
|
y = this.pn1 / (this.p - y);
|
||
|
x = y * cosphi * Math.sin(p.x);
|
||
|
|
||
|
switch (this.mode) {
|
||
|
case mode.OBLIQ:
|
||
|
y *= this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;
|
||
|
break;
|
||
|
case mode.EQUIT:
|
||
|
y *= sinphi;
|
||
|
break;
|
||
|
case mode.N_POLE:
|
||
|
y *= -(cosphi * coslam);
|
||
|
break;
|
||
|
case mode.S_POLE:
|
||
|
y *= cosphi * coslam;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Tilt
|
||
|
var yt, ba;
|
||
|
yt = y * this.cg + x * this.sg;
|
||
|
ba = 1 / (yt * this.sw * this.h1 + this.cw);
|
||
|
x = (x * this.cg - y * this.sg) * this.cw * ba;
|
||
|
y = yt * ba;
|
||
|
|
||
|
p.x = x * this.a;
|
||
|
p.y = y * this.a;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
export function inverse(p) {
|
||
|
p.x /= this.a;
|
||
|
p.y /= this.a;
|
||
|
var r = { x: p.x, y: p.y };
|
||
|
|
||
|
// Un-Tilt
|
||
|
var bm, bq, yt;
|
||
|
yt = 1 / (this.pn1 - p.y * this.sw);
|
||
|
bm = this.pn1 * p.x * yt;
|
||
|
bq = this.pn1 * p.y * this.cw * yt;
|
||
|
p.x = bm * this.cg + bq * this.sg;
|
||
|
p.y = bq * this.cg - bm * this.sg;
|
||
|
|
||
|
var rh = hypot(p.x, p.y);
|
||
|
if (Math.abs(rh) < EPSLN) {
|
||
|
r.x = 0;
|
||
|
r.y = p.y;
|
||
|
} else {
|
||
|
var cosz, sinz;
|
||
|
sinz = 1 - rh * rh * this.pfact;
|
||
|
sinz = (this.p - Math.sqrt(sinz)) / (this.pn1 / rh + rh / this.pn1);
|
||
|
cosz = Math.sqrt(1 - sinz * sinz);
|
||
|
switch (this.mode) {
|
||
|
case mode.OBLIQ:
|
||
|
r.y = Math.asin(cosz * this.sinph0 + p.y * sinz * this.cosph0 / rh);
|
||
|
p.y = (cosz - this.sinph0 * Math.sin(r.y)) * rh;
|
||
|
p.x *= sinz * this.cosph0;
|
||
|
break;
|
||
|
case mode.EQUIT:
|
||
|
r.y = Math.asin(p.y * sinz / rh);
|
||
|
p.y = cosz * rh;
|
||
|
p.x *= sinz;
|
||
|
break;
|
||
|
case mode.N_POLE:
|
||
|
r.y = Math.asin(cosz);
|
||
|
p.y = -p.y;
|
||
|
break;
|
||
|
case mode.S_POLE:
|
||
|
r.y = -Math.asin(cosz);
|
||
|
break;
|
||
|
}
|
||
|
r.x = Math.atan2(p.x, p.y);
|
||
|
}
|
||
|
|
||
|
p.x = r.x + this.long0;
|
||
|
p.y = r.y;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
export var names = ["Tilted_Perspective", "tpers"];
|
||
|
export default {
|
||
|
init: init,
|
||
|
forward: forward,
|
||
|
inverse: inverse,
|
||
|
names: names
|
||
|
};
|