proj4
Version:
Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.
186 lines (175 loc) • 6.31 kB
JavaScript
import { EPSLN, HALF_PI } from '../constants/values';
import sign from '../common/sign';
import msfnz from '../common/msfnz';
import tsfnz from '../common/tsfnz';
import phi2z from '../common/phi2z';
import adjust_lon from '../common/adjust_lon';
/**
* @typedef {Object} LocalThis
* @property {number} coslat0
* @property {number} sinlat0
* @property {number} ms1
* @property {number} X0
* @property {number} cosX0
* @property {number} sinX0
* @property {number} con
* @property {number} cons
* @property {number} e
*/
export function ssfn_(phit, sinphi, eccen) {
sinphi *= eccen;
return (Math.tan(0.5 * (HALF_PI + phit)) * Math.pow((1 - sinphi) / (1 + sinphi), 0.5 * eccen));
}
/** @this {import('../defs.js').ProjectionDefinition & LocalThis} */
export function init() {
// setting default parameters
this.x0 = this.x0 || 0;
this.y0 = this.y0 || 0;
this.lat0 = this.lat0 || 0;
this.long0 = this.long0 || 0;
this.coslat0 = Math.cos(this.lat0);
this.sinlat0 = Math.sin(this.lat0);
if (this.sphere) {
if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= EPSLN) {
this.k0 = 0.5 * (1 + sign(this.lat0) * Math.sin(this.lat_ts));
}
} else {
if (Math.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 = Math.sqrt(Math.pow(1 + this.e, 1 + this.e) * Math.pow(1 - this.e, 1 - this.e));
if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= EPSLN && Math.abs(Math.cos(this.lat_ts)) > EPSLN) {
// When k0 is 1 (default value) and lat_ts is a vaild number and lat0 is at a pole and lat_ts 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, Math.sin(this.lat_ts), Math.cos(this.lat_ts)) / tsfnz(this.e, this.con * this.lat_ts, this.con * Math.sin(this.lat_ts));
}
this.ms1 = msfnz(this.e, this.sinlat0, this.coslat0);
this.X0 = 2 * Math.atan(ssfn_(this.lat0, this.sinlat0, this.e)) - HALF_PI;
this.cosX0 = Math.cos(this.X0);
this.sinX0 = Math.sin(this.X0);
}
}
// Stereographic forward equations--mapping lat,long to x,y
export function forward(p) {
var lon = p.x;
var lat = p.y;
var sinlat = Math.sin(lat);
var coslat = Math.cos(lat);
var A, X, sinX, cosX, ts, rh;
var dlon = adjust_lon(lon - this.long0, this.over);
if (Math.abs(Math.abs(lon - this.long0) - Math.PI) <= EPSLN && Math.abs(lat + this.lat0) <= EPSLN) {
// case of the origine point
// trace('stere:this is the origin point');
p.x = NaN;
p.y = NaN;
return p;
}
if (this.sphere) {
// trace('stere:sphere case');
A = 2 * this.k0 / (1 + this.sinlat0 * sinlat + this.coslat0 * coslat * Math.cos(dlon));
p.x = this.a * A * coslat * Math.sin(dlon) + this.x0;
p.y = this.a * A * (this.coslat0 * sinlat - this.sinlat0 * coslat * Math.cos(dlon)) + this.y0;
return p;
} else {
X = 2 * Math.atan(ssfn_(lat, sinlat, this.e)) - HALF_PI;
cosX = Math.cos(X);
sinX = Math.sin(X);
if (Math.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 * Math.sin(lon - this.long0);
p.y = this.y0 - this.con * rh * Math.cos(lon - this.long0);
// trace(p.toString());
return p;
} else if (Math.abs(this.sinlat0) < EPSLN) {
// Eq
// trace('stere:equateur');
A = 2 * this.a * this.k0 / (1 + cosX * Math.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 * Math.cos(dlon)));
p.y = A * (this.cosX0 * sinX - this.sinX0 * cosX * Math.cos(dlon)) + this.y0;
}
p.x = A * cosX * Math.sin(dlon) + this.x0;
}
// trace(p.toString());
return p;
}
//* Stereographic inverse equations--mapping x,y to lat/long
export function inverse(p) {
p.x -= this.x0;
p.y -= this.y0;
var lon, lat, ts, ce, Chi;
var rh = Math.sqrt(p.x * p.x + p.y * p.y);
if (this.sphere) {
var c = 2 * Math.atan(rh / (2 * this.a * this.k0));
lon = this.long0;
lat = this.lat0;
if (rh <= EPSLN) {
p.x = lon;
p.y = lat;
return p;
}
lat = Math.asin(Math.cos(c) * this.sinlat0 + p.y * Math.sin(c) * this.coslat0 / rh);
if (Math.abs(this.coslat0) < EPSLN) {
if (this.lat0 > 0) {
lon = adjust_lon(this.long0 + Math.atan2(p.x, -1 * p.y), this.over);
} else {
lon = adjust_lon(this.long0 + Math.atan2(p.x, p.y), this.over);
}
} else {
lon = adjust_lon(this.long0 + Math.atan2(p.x * Math.sin(c), rh * this.coslat0 * Math.cos(c) - p.y * this.sinlat0 * Math.sin(c)), this.over);
}
p.x = lon;
p.y = lat;
return p;
} else {
if (Math.abs(this.coslat0) <= EPSLN) {
if (rh <= EPSLN) {
lat = this.lat0;
lon = this.long0;
p.x = lon;
p.y = lat;
// trace(p.toString());
return p;
}
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 * adjust_lon(this.con * this.long0 + Math.atan2(p.x, -1 * p.y), this.over);
} else {
ce = 2 * Math.atan(rh * this.cosX0 / (2 * this.a * this.k0 * this.ms1));
lon = this.long0;
if (rh <= EPSLN) {
Chi = this.X0;
} else {
Chi = Math.asin(Math.cos(ce) * this.sinX0 + p.y * Math.sin(ce) * this.cosX0 / rh);
lon = adjust_lon(this.long0 + Math.atan2(p.x * Math.sin(ce), rh * this.cosX0 * Math.cos(ce) - p.y * this.sinX0 * Math.sin(ce)), this.over);
}
lat = -1 * phi2z(this.e, Math.tan(0.5 * (HALF_PI + Chi)));
}
}
p.x = lon;
p.y = lat;
// trace(p.toString());
return p;
}
export var names = ['stere', 'Stereographic_South_Pole', 'Polar_Stereographic_variant_A', 'Polar_Stereographic_variant_B', 'Polar_Stereographic'];
export default {
init: init,
forward: forward,
inverse: inverse,
names: names,
ssfn_: ssfn_
};