sg-heatmap
Version:
Open-source all-in-one Swiss Army knife tool for creating Choropleth maps
166 lines (142 loc) • 6.17 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = SVY21;
function SVY21(config) {
/**
* Ref: http://www.linz.govt.nz/geodetic/conversion-coordinates/projection-conversions/transverse-mercator-preliminary-computations/index.aspx
*
* SVY21 Projection
* Fundamental point: Base 7 at Pierce Resevoir.
* Latitude: 1 22 02.9154 N, longitude: 103 49 31.9752 E (of Greenwich).
*
* Known Issue: Setting (oLat, oLon) to the exact coordinates specified above
* results in computation being slightly off. The values below give the most
* accurate represenation of test data.
*/
var defaultConfig = {
// WGS84 Datum
a: 6378137,
f: 1 / 298.257223563,
oLat: 1.366666, // origin's lat in degrees
oLon: 103.833333, // origin's lon in degrees
oN: 38744.572, // false Northing
oE: 28001.642, // false Easting
k: 1 // scale factor
};
Object.assign(this, defaultConfig, config);
this.b = this.a * (1 - this.f);
this.e2 = 2 * this.f - this.f * this.f;
this.e4 = this.e2 * this.e2;
this.e6 = this.e4 * this.e2;
this.A0 = 1 - this.e2 / 4 - 3 * this.e4 / 64 - 5 * this.e6 / 256;
this.A2 = 3.0 / 8.0 * (this.e2 + this.e4 / 4 + 15 * this.e6 / 128);
this.A4 = 15.0 / 256.0 * (this.e4 + 3 * this.e6 / 4);
this.A6 = 35 * this.e6 / 3072;
}
// Returns a pair (N, E) representing Northings and Eastings in SVY21.
SVY21.prototype.computeSVY21 = function (lat, lon) {
var latR = lat * Math.PI / 180;
var sinLat = Math.sin(latR);
var sin2Lat = sinLat * sinLat;
var cosLat = Math.cos(latR);
var cos2Lat = cosLat * cosLat;
var cos3Lat = cos2Lat * cosLat;
var cos4Lat = cos3Lat * cosLat;
var cos5Lat = cos4Lat * cosLat;
var cos6Lat = cos5Lat * cosLat;
var cos7Lat = cos6Lat * cosLat;
var rho = this.calcRho(sin2Lat);
var v = this.calcV(sin2Lat);
var psi = v / rho;
var t = Math.tan(latR);
var w = (lon - this.oLon) * Math.PI / 180;
var M = this.calcM(lat);
var Mo = this.calcM(this.oLat);
var w2 = w * w;
var w4 = w2 * w2;
var w6 = w4 * w2;
var w8 = w6 * w2;
var psi2 = psi * psi;
var psi3 = psi2 * psi;
var psi4 = psi3 * psi;
var t2 = t * t;
var t4 = t2 * t2;
var t6 = t4 * t2;
// Compute Northing
var nTerm1 = w2 / 2 * v * sinLat * cosLat;
var nTerm2 = w4 / 24 * v * sinLat * cos3Lat * (4 * psi2 + psi - t2);
var nTerm3 = w6 / 720 * v * sinLat * cos5Lat * (8 * psi4 * (11 - 24 * t2) - 28 * psi3 * (1 - 6 * t2) + psi2 * (1 - 32 * t2) - psi * 2 * t2 + t4);
var nTerm4 = w8 / 40320 * v * sinLat * cos7Lat * (1385 - 3111 * t2 + 543 * t4 - t6);
var N = this.oN + this.k * (M - Mo + nTerm1 + nTerm2 + nTerm3 + nTerm4);
// Compute Easting
var eTerm1 = w2 / 6 * cos2Lat * (psi - t2);
var eTerm2 = w4 / 120 * cos4Lat * (4 * psi3 * (1 - 6 * t2) + psi2 * (1 + 8 * t2) - psi * 2 * t2 + t4);
var eTerm3 = w6 / 5040 * cos6Lat * (61 - 479 * t2 + 179 * t4 - t6);
var E = this.oE + this.k * v * w * cosLat * (1 + eTerm1 + eTerm2 + eTerm3);
return { N: N, E: E };
};
// Returns a pair (lat, lon) representing Latitude and Longitude.
SVY21.prototype.computeLatLon = function (N, E) {
var Nprime = N - this.oN;
var Mo = this.calcM(this.oLat);
var Mprime = Mo + Nprime / this.k;
var n = (this.a - this.b) / (this.a + this.b);
var n2 = n * n;
var n3 = n2 * n;
var n4 = n2 * n2;
var G = this.a * (1 - n) * (1 - n2) * (1 + 9 * n2 / 4 + 225 * n4 / 64) * (Math.PI / 180);
var sigma = Mprime * Math.PI / (180.0 * G);
var latPrimeT1 = (3 * n / 2 - 27 * n3 / 32) * Math.sin(2 * sigma);
var latPrimeT2 = (21 * n2 / 16 - 55 * n4 / 32) * Math.sin(4 * sigma);
var latPrimeT3 = 151 * n3 / 96 * Math.sin(6 * sigma);
var latPrimeT4 = 1097 * n4 / 512 * Math.sin(8 * sigma);
var latPrime = sigma + latPrimeT1 + latPrimeT2 + latPrimeT3 + latPrimeT4;
var sinLatPrime = Math.sin(latPrime);
var sin2LatPrime = sinLatPrime * sinLatPrime;
var rhoPrime = this.calcRho(sin2LatPrime);
var vPrime = this.calcV(sin2LatPrime);
var psiPrime = vPrime / rhoPrime;
var psiPrime2 = psiPrime * psiPrime;
var psiPrime3 = psiPrime2 * psiPrime;
var psiPrime4 = psiPrime3 * psiPrime;
var tPrime = Math.tan(latPrime);
var tPrime2 = tPrime * tPrime;
var tPrime4 = tPrime2 * tPrime2;
var tPrime6 = tPrime4 * tPrime2;
var Eprime = E - this.oE;
var x = Eprime / (this.k * vPrime);
var x2 = x * x;
var x3 = x2 * x;
var x5 = x3 * x2;
var x7 = x5 * x2;
// Compute Latitude
var latFactor = tPrime / (this.k * rhoPrime);
var latTerm1 = latFactor * (Eprime * x / 2);
var latTerm2 = latFactor * (Eprime * x3 / 24) * (-4 * psiPrime2 + 9 * psiPrime * (1 - tPrime2) + 12 * tPrime2);
var latTerm3 = latFactor * (Eprime * x5 / 720) * (8 * psiPrime4 * (11 - 24 * tPrime2) - 12 * psiPrime3 * (21 - 71 * tPrime2) + 15 * psiPrime2 * (15 - 98 * tPrime2 + 15 * tPrime4) + 180 * psiPrime * (5 * tPrime2 - 3 * tPrime4) + 360 * tPrime4);
var latTerm4 = latFactor * (Eprime * x7 / 40320) * (1385 - 3633 * tPrime2 + 4095 * tPrime4 + 1575 * tPrime6);
var lat = latPrime - latTerm1 + latTerm2 - latTerm3 + latTerm4;
// Compute Longitude
var secLatPrime = 1.0 / Math.cos(lat);
var lonTerm1 = x * secLatPrime;
var lonTerm2 = x3 * secLatPrime / 6 * (psiPrime + 2 * tPrime2);
var lonTerm3 = x5 * secLatPrime / 120 * (-4 * psiPrime3 * (1 - 6 * tPrime2) + psiPrime2 * (9 - 68 * tPrime2) + 72 * psiPrime * tPrime2 + 24 * tPrime4);
var lonTerm4 = x7 * secLatPrime / 5040 * (61 + 662 * tPrime2 + 1320 * tPrime4 + 720 * tPrime6);
var lon = this.oLon * Math.PI / 180 + lonTerm1 - lonTerm2 + lonTerm3 - lonTerm4;
return { lat: lat / (Math.PI / 180), lon: lon / (Math.PI / 180) };
};
SVY21.prototype.calcM = function (lat, lon) {
var latR = lat * Math.PI / 180;
return this.a * (this.A0 * latR - this.A2 * Math.sin(2 * latR) + this.A4 * Math.sin(4 * latR) - this.A6 * Math.sin(6 * latR));
};
SVY21.prototype.calcRho = function (sin2Lat) {
var num = this.a * (1 - this.e2);
var denom = Math.pow(1 - this.e2 * sin2Lat, 3.0 / 2.0);
return num / denom;
};
SVY21.prototype.calcV = function (sin2Lat) {
var poly = 1 - this.e2 * sin2Lat;
return this.a / Math.sqrt(poly);
};