| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- var HALF_PI = Math.PI/2;
- var PJD_3PARAM = 1;
- var PJD_7PARAM = 2;
- var PJD_GRIDSHIFT = 3;
- var PJD_WGS84 = 4; // WGS84 or equivalent
- var PJD_NODATUM = 5; // WGS84 or equivalent
- var SEC_TO_RAD = 4.84813681109535993589914102357e-6;
- var AD_C = 1.0026000;
- var COS_67P5 = 0.38268343236508977;
- var datum = function(proj) {
- if (!(this instanceof datum)) {
- return new datum(proj);
- }
- this.datum_type = PJD_WGS84; //default setting
- if (!proj) {
- return;
- }
- if (proj.datumCode && proj.datumCode === 'none') {
- this.datum_type = PJD_NODATUM;
- }
- if (proj.datum_params) {
- for (var i = 0; i < proj.datum_params.length; i++) {
- proj.datum_params[i] = parseFloat(proj.datum_params[i]);
- }
- if (proj.datum_params[0] !== 0 || proj.datum_params[1] !== 0 || proj.datum_params[2] !== 0) {
- this.datum_type = PJD_3PARAM;
- }
- if (proj.datum_params.length > 3) {
- if (proj.datum_params[3] !== 0 || proj.datum_params[4] !== 0 || proj.datum_params[5] !== 0 || proj.datum_params[6] !== 0) {
- this.datum_type = PJD_7PARAM;
- proj.datum_params[3] *= SEC_TO_RAD;
- proj.datum_params[4] *= SEC_TO_RAD;
- proj.datum_params[5] *= SEC_TO_RAD;
- proj.datum_params[6] = (proj.datum_params[6] / 1000000.0) + 1.0;
- }
- }
- }
- // DGR 2011-03-21 : nadgrids support
- this.datum_type = proj.grids ? PJD_GRIDSHIFT : this.datum_type;
- this.a = proj.a; //datum object also uses these values
- this.b = proj.b;
- this.es = proj.es;
- this.ep2 = proj.ep2;
- this.datum_params = proj.datum_params;
- if (this.datum_type === PJD_GRIDSHIFT) {
- this.grids = proj.grids;
- }
- };
- datum.prototype = {
- /****************************************************************/
- // cs_compare_datums()
- // Returns TRUE if the two datums match, otherwise FALSE.
- compare_datums: function(dest) {
- if (this.datum_type !== dest.datum_type) {
- return false; // false, datums are not equal
- }
- else if (this.a !== dest.a || Math.abs(this.es - dest.es) > 0.000000000050) {
- // the tolerence for es is to ensure that GRS80 and WGS84
- // are considered identical
- return false;
- }
- else if (this.datum_type === PJD_3PARAM) {
- return (this.datum_params[0] === dest.datum_params[0] && this.datum_params[1] === dest.datum_params[1] && this.datum_params[2] === dest.datum_params[2]);
- }
- else if (this.datum_type === PJD_7PARAM) {
- return (this.datum_params[0] === dest.datum_params[0] && this.datum_params[1] === dest.datum_params[1] && this.datum_params[2] === dest.datum_params[2] && this.datum_params[3] === dest.datum_params[3] && this.datum_params[4] === dest.datum_params[4] && this.datum_params[5] === dest.datum_params[5] && this.datum_params[6] === dest.datum_params[6]);
- }
- else if (this.datum_type === PJD_GRIDSHIFT || dest.datum_type === PJD_GRIDSHIFT) {
- //alert("ERROR: Grid shift transformations are not implemented.");
- //return false
- //DGR 2012-07-29 lazy ...
- return this.nadgrids === dest.nadgrids;
- }
- else {
- return true; // datums are equal
- }
- }, // cs_compare_datums()
- /*
- * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
- * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
- * according to the current ellipsoid parameters.
- *
- * Latitude : Geodetic latitude in radians (input)
- * Longitude : Geodetic longitude in radians (input)
- * Height : Geodetic height, in meters (input)
- * X : Calculated Geocentric X coordinate, in meters (output)
- * Y : Calculated Geocentric Y coordinate, in meters (output)
- * Z : Calculated Geocentric Z coordinate, in meters (output)
- *
- */
- geodetic_to_geocentric: function(p) {
- var Longitude = p.x;
- var Latitude = p.y;
- var Height = p.z ? p.z : 0; //Z value not always supplied
- var X; // output
- var Y;
- var Z;
- var Error_Code = 0; // GEOCENT_NO_ERROR;
- var Rn; /* Earth radius at location */
- var Sin_Lat; /* Math.sin(Latitude) */
- var Sin2_Lat; /* Square of Math.sin(Latitude) */
- var Cos_Lat; /* Math.cos(Latitude) */
- /*
- ** Don't blow up if Latitude is just a little out of the value
- ** range as it may just be a rounding issue. Also removed longitude
- ** test, it should be wrapped by Math.cos() and Math.sin(). NFW for PROJ.4, Sep/2001.
- */
- if (Latitude < -HALF_PI && Latitude > -1.001 * HALF_PI) {
- Latitude = -HALF_PI;
- }
- else if (Latitude > HALF_PI && Latitude < 1.001 * HALF_PI) {
- Latitude = HALF_PI;
- }
- else if ((Latitude < -HALF_PI) || (Latitude > HALF_PI)) {
- /* Latitude out of range */
- //..reportError('geocent:lat out of range:' + Latitude);
- return null;
- }
- if (Longitude > Math.PI) {
- Longitude -= (2 * Math.PI);
- }
- Sin_Lat = Math.sin(Latitude);
- Cos_Lat = Math.cos(Latitude);
- Sin2_Lat = Sin_Lat * Sin_Lat;
- Rn = this.a / (Math.sqrt(1.0e0 - this.es * Sin2_Lat));
- X = (Rn + Height) * Cos_Lat * Math.cos(Longitude);
- Y = (Rn + Height) * Cos_Lat * Math.sin(Longitude);
- Z = ((Rn * (1 - this.es)) + Height) * Sin_Lat;
- p.x = X;
- p.y = Y;
- p.z = Z;
- return Error_Code;
- }, // cs_geodetic_to_geocentric()
- geocentric_to_geodetic: function(p) {
- /* local defintions and variables */
- /* end-criterium of loop, accuracy of sin(Latitude) */
- var genau = 1e-12;
- var genau2 = (genau * genau);
- var maxiter = 30;
- var P; /* distance between semi-minor axis and location */
- var RR; /* distance between center and location */
- var CT; /* sin of geocentric latitude */
- var ST; /* cos of geocentric latitude */
- var RX;
- var RK;
- var RN; /* Earth radius at location */
- var CPHI0; /* cos of start or old geodetic latitude in iterations */
- var SPHI0; /* sin of start or old geodetic latitude in iterations */
- var CPHI; /* cos of searched geodetic latitude */
- var SPHI; /* sin of searched geodetic latitude */
- var SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */
- var At_Pole; /* indicates location is in polar region */
- var iter; /* # of continous iteration, max. 30 is always enough (s.a.) */
- var X = p.x;
- var Y = p.y;
- var Z = p.z ? p.z : 0.0; //Z value not always supplied
- var Longitude;
- var Latitude;
- var Height;
- At_Pole = false;
- P = Math.sqrt(X * X + Y * Y);
- RR = Math.sqrt(X * X + Y * Y + Z * Z);
- /* special cases for latitude and longitude */
- if (P / this.a < genau) {
- /* special case, if P=0. (X=0., Y=0.) */
- At_Pole = true;
- Longitude = 0.0;
- /* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis
- * of ellipsoid (=center of mass), Latitude becomes PI/2 */
- if (RR / this.a < genau) {
- Latitude = HALF_PI;
- Height = -this.b;
- return;
- }
- }
- else {
- /* ellipsoidal (geodetic) longitude
- * interval: -PI < Longitude <= +PI */
- Longitude = Math.atan2(Y, X);
- }
- /* --------------------------------------------------------------
- * Following iterative algorithm was developped by
- * "Institut for Erdmessung", University of Hannover, July 1988.
- * Internet: www.ife.uni-hannover.de
- * Iterative computation of CPHI,SPHI and Height.
- * Iteration of CPHI and SPHI to 10**-12 radian resp.
- * 2*10**-7 arcsec.
- * --------------------------------------------------------------
- */
- CT = Z / RR;
- ST = P / RR;
- RX = 1.0 / Math.sqrt(1.0 - this.es * (2.0 - this.es) * ST * ST);
- CPHI0 = ST * (1.0 - this.es) * RX;
- SPHI0 = CT * RX;
- iter = 0;
- /* loop to find sin(Latitude) resp. Latitude
- * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */
- do {
- iter++;
- RN = this.a / Math.sqrt(1.0 - this.es * SPHI0 * SPHI0);
- /* ellipsoidal (geodetic) height */
- Height = P * CPHI0 + Z * SPHI0 - RN * (1.0 - this.es * SPHI0 * SPHI0);
- RK = this.es * RN / (RN + Height);
- RX = 1.0 / Math.sqrt(1.0 - RK * (2.0 - RK) * ST * ST);
- CPHI = ST * (1.0 - RK) * RX;
- SPHI = CT * RX;
- SDPHI = SPHI * CPHI0 - CPHI * SPHI0;
- CPHI0 = CPHI;
- SPHI0 = SPHI;
- }
- while (SDPHI * SDPHI > genau2 && iter < maxiter);
- /* ellipsoidal (geodetic) latitude */
- Latitude = Math.atan(SPHI / Math.abs(CPHI));
- p.x = Longitude;
- p.y = Latitude;
- p.z = Height;
- return p;
- }, // cs_geocentric_to_geodetic()
- /** Convert_Geocentric_To_Geodetic
- * The method used here is derived from 'An Improved Algorithm for
- * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
- */
- geocentric_to_geodetic_noniter: function(p) {
- var X = p.x;
- var Y = p.y;
- var Z = p.z ? p.z : 0; //Z value not always supplied
- var Longitude;
- var Latitude;
- var Height;
- var W; /* distance from Z axis */
- var W2; /* square of distance from Z axis */
- var T0; /* initial estimate of vertical component */
- var T1; /* corrected estimate of vertical component */
- var S0; /* initial estimate of horizontal component */
- var S1; /* corrected estimate of horizontal component */
- var Sin_B0; /* Math.sin(B0), B0 is estimate of Bowring aux variable */
- var Sin3_B0; /* cube of Math.sin(B0) */
- var Cos_B0; /* Math.cos(B0) */
- var Sin_p1; /* Math.sin(phi1), phi1 is estimated latitude */
- var Cos_p1; /* Math.cos(phi1) */
- var Rn; /* Earth radius at location */
- var Sum; /* numerator of Math.cos(phi1) */
- var At_Pole; /* indicates location is in polar region */
- X = parseFloat(X); // cast from string to float
- Y = parseFloat(Y);
- Z = parseFloat(Z);
- At_Pole = false;
- if (X !== 0.0) {
- Longitude = Math.atan2(Y, X);
- }
- else {
- if (Y > 0) {
- Longitude = HALF_PI;
- }
- else if (Y < 0) {
- Longitude = -HALF_PI;
- }
- else {
- At_Pole = true;
- Longitude = 0.0;
- if (Z > 0.0) { /* north pole */
- Latitude = HALF_PI;
- }
- else if (Z < 0.0) { /* south pole */
- Latitude = -HALF_PI;
- }
- else { /* center of earth */
- Latitude = HALF_PI;
- Height = -this.b;
- return;
- }
- }
- }
- W2 = X * X + Y * Y;
- W = Math.sqrt(W2);
- T0 = Z * AD_C;
- S0 = Math.sqrt(T0 * T0 + W2);
- Sin_B0 = T0 / S0;
- Cos_B0 = W / S0;
- Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
- T1 = Z + this.b * this.ep2 * Sin3_B0;
- Sum = W - this.a * this.es * Cos_B0 * Cos_B0 * Cos_B0;
- S1 = Math.sqrt(T1 * T1 + Sum * Sum);
- Sin_p1 = T1 / S1;
- Cos_p1 = Sum / S1;
- Rn = this.a / Math.sqrt(1.0 - this.es * Sin_p1 * Sin_p1);
- if (Cos_p1 >= COS_67P5) {
- Height = W / Cos_p1 - Rn;
- }
- else if (Cos_p1 <= -COS_67P5) {
- Height = W / -Cos_p1 - Rn;
- }
- else {
- Height = Z / Sin_p1 + Rn * (this.es - 1.0);
- }
- if (At_Pole === false) {
- Latitude = Math.atan(Sin_p1 / Cos_p1);
- }
- p.x = Longitude;
- p.y = Latitude;
- p.z = Height;
- return p;
- }, // geocentric_to_geodetic_noniter()
- /****************************************************************/
- // pj_geocentic_to_wgs84( p )
- // p = point to transform in geocentric coordinates (x,y,z)
- geocentric_to_wgs84: function(p) {
- if (this.datum_type === PJD_3PARAM) {
- // if( x[io] === HUGE_VAL )
- // continue;
- p.x += this.datum_params[0];
- p.y += this.datum_params[1];
- p.z += this.datum_params[2];
- }
- else if (this.datum_type === PJD_7PARAM) {
- var Dx_BF = this.datum_params[0];
- var Dy_BF = this.datum_params[1];
- var Dz_BF = this.datum_params[2];
- var Rx_BF = this.datum_params[3];
- var Ry_BF = this.datum_params[4];
- var Rz_BF = this.datum_params[5];
- var M_BF = this.datum_params[6];
- // if( x[io] === HUGE_VAL )
- // continue;
- var x_out = M_BF * (p.x - Rz_BF * p.y + Ry_BF * p.z) + Dx_BF;
- var y_out = M_BF * (Rz_BF * p.x + p.y - Rx_BF * p.z) + Dy_BF;
- var z_out = M_BF * (-Ry_BF * p.x + Rx_BF * p.y + p.z) + Dz_BF;
- p.x = x_out;
- p.y = y_out;
- p.z = z_out;
- }
- }, // cs_geocentric_to_wgs84
- /****************************************************************/
- // pj_geocentic_from_wgs84()
- // coordinate system definition,
- // point to transform in geocentric coordinates (x,y,z)
- geocentric_from_wgs84: function(p) {
- if (this.datum_type === PJD_3PARAM) {
- //if( x[io] === HUGE_VAL )
- // continue;
- p.x -= this.datum_params[0];
- p.y -= this.datum_params[1];
- p.z -= this.datum_params[2];
- }
- else if (this.datum_type === PJD_7PARAM) {
- var Dx_BF = this.datum_params[0];
- var Dy_BF = this.datum_params[1];
- var Dz_BF = this.datum_params[2];
- var Rx_BF = this.datum_params[3];
- var Ry_BF = this.datum_params[4];
- var Rz_BF = this.datum_params[5];
- var M_BF = this.datum_params[6];
- var x_tmp = (p.x - Dx_BF) / M_BF;
- var y_tmp = (p.y - Dy_BF) / M_BF;
- var z_tmp = (p.z - Dz_BF) / M_BF;
- //if( x[io] === HUGE_VAL )
- // continue;
- p.x = x_tmp + Rz_BF * y_tmp - Ry_BF * z_tmp;
- p.y = -Rz_BF * x_tmp + y_tmp + Rx_BF * z_tmp;
- p.z = Ry_BF * x_tmp - Rx_BF * y_tmp + z_tmp;
- } //cs_geocentric_from_wgs84()
- }
- };
- /** point object, nothing fancy, just allows values to be
- passed back and forth by reference rather than by value.
- Other point classes may be used as long as they have
- x and y properties, which will get modified in the transform method.
- */
- module.exports = datum;
|