This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository proj4js.
commit 7f09e8d348cb92bcc854e777e460f68b70fd1e7c Author: Bas Couwenberg <[email protected]> Date: Mon Jan 23 22:33:13 2017 +0100 Imported Upstream version 2.3.16+ds --- .gitignore | 3 +- Gruntfile.js | 3 +- README.md | 9 +- bower.json | 2 +- component.json | 2 +- dist/proj4-src.js | 1336 +++++++++++++++++++++------------------------ dist/test.html | 11 - lib/Proj.js | 54 +- lib/adjust_axis.js | 15 +- lib/common/adjust_zone.js | 19 + lib/datum.js | 413 +------------- lib/datumUtils.js | 247 +++++++++ lib/datum_transform.js | 96 +--- lib/deriveConstants.js | 89 ++- lib/index.js | 6 +- lib/parseCode.js | 15 +- lib/projString.js | 12 +- lib/projections/tmerc.js | 142 +++-- lib/projections/utm.js | 11 +- lib/transform.js | 60 +- lib/version-browser.js | 1 + lib/version.js | 1 + package.json | 16 +- publish.sh | 3 +- test/test.js | 9 +- test/testData.js | 84 ++- 26 files changed, 1269 insertions(+), 1390 deletions(-) diff --git a/.gitignore b/.gitignore index 312583b..0f3e170 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules coverage projs.js .DS_STORE -dist \ No newline at end of file +dist +lib/version-browser.js diff --git a/Gruntfile.js b/Gruntfile.js index ebd05c0..c35d3c4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -61,7 +61,8 @@ module.exports = function(grunt) { standalone: 'proj4' }, alias: [ - './projs:./includedProjections' + './projs:./includedProjections', + './lib/version-browser:./lib/version' ] } } diff --git a/README.md b/README.md index 3bb37fe..f4aeae1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Depending on your preferences ```bash npm install proj4 bower install proj4 -jam install proj4 component install proj4js/proj4js ``` @@ -114,6 +113,14 @@ proj4.defs can also be used to define a named alias: proj4.defs('urn:x-ogc:def:crs:EPSG:4326', proj4.defs('EPSG:4326')); ``` +## TypeScript + +TypeScript implementation was added to the [DefinitelyTyped repository](https://github.com/DefinitelyTyped/DefinitelyTyped). + +```bash +$ npm install --save @types/proj4 +``` + ## Developing to set up build tools make sure you have node and grunt-cli installed and then run `npm install` diff --git a/bower.json b/bower.json index 814d89c..2781321 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "proj4", - "version": "2.3.15-alpha", + "version": "2.3.16-alpha", "description": "Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.", "homepage": "https://github.com/proj4js/proj4js", "main": "dist/proj4.js", diff --git a/component.json b/component.json index e600be3..d5a3a3b 100644 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name": "proj4", - "version": "2.3.15-alpha", + "version": "2.3.16-alpha", "description": "Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.", "repo": "proj4js/proj4js", "keywords": [ diff --git a/dist/proj4-src.js b/dist/proj4-src.js index 24da1e7..865a89e 100644 --- a/dist/proj4-src.js +++ b/dist/proj4-src.js @@ -29,7 +29,10 @@ module.exports = function(proj4){ proj4.Proj.projections.add(proj); }); } -},{"./lib/projections/aea":40,"./lib/projections/aeqd":41,"./lib/projections/cass":42,"./lib/projections/cea":43,"./lib/projections/eqc":44,"./lib/projections/eqdc":45,"./lib/projections/gnom":47,"./lib/projections/krovak":48,"./lib/projections/laea":49,"./lib/projections/lcc":50,"./lib/projections/mill":53,"./lib/projections/moll":54,"./lib/projections/nzmg":55,"./lib/projections/omerc":56,"./lib/projections/ortho":57,"./lib/projections/poly":58,"./lib/projections/sinu":59,"./lib/projec [...] +},{"./lib/projections/aea":42,"./lib/projections/aeqd":43,"./lib/projections/cass":44,"./lib/projections/cea":45,"./lib/projections/eqc":46,"./lib/projections/eqdc":47,"./lib/projections/gnom":49,"./lib/projections/krovak":50,"./lib/projections/laea":51,"./lib/projections/lcc":52,"./lib/projections/mill":55,"./lib/projections/moll":56,"./lib/projections/nzmg":57,"./lib/projections/omerc":58,"./lib/projections/ortho":59,"./lib/projections/poly":60,"./lib/projections/sinu":61,"./lib/projec [...] +module.exports = '2.3.16'; + +},{}],1:[function(require,module,exports){ var mgrs = require('mgrs'); function Point(x, y, z) { @@ -65,11 +68,14 @@ Point.prototype.toMGRS = function(accuracy) { }; module.exports = Point; -},{"mgrs":68}],2:[function(require,module,exports){ -var parseCode = require("./parseCode"); +},{"mgrs":70}],2:[function(require,module,exports){ +var parseCode = require('./parseCode'); var extend = require('./extend'); var projections = require('./projections'); var deriveConstants = require('./deriveConstants'); +var Datum = require('./constants/Datum'); +var datum = require('./datum'); + function Projection(srsCode,callback) { if (!(this instanceof Projection)) { @@ -85,27 +91,61 @@ function Projection(srsCode,callback) { callback(srsCode); return; } - var modifiedJSON = deriveConstants(json); - var ourProj = Projection.projections.get(modifiedJSON.projName); - if(ourProj){ - extend(this, modifiedJSON); - extend(this, ourProj); - this.init(); - callback(null, this); - }else{ + var ourProj = Projection.projections.get(json.projName); + if(!ourProj){ callback(srsCode); + return; } + if (json.datumCode && json.datumCode !== 'none') { + var datumDef = Datum[json.datumCode]; + if (datumDef) { + json.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null; + json.ellps = datumDef.ellipse; + json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode; + } + } + json.k0 = json.k0 || 1.0; + json.axis = json.axis || 'enu'; + + var sphere = deriveConstants.sphere(json.a, json.b, json.rf, json.ellps, json.sphere); + var ecc = deriveConstants.eccentricity(sphere.a, sphere.b, sphere.rf, json.R_A); + var datumObj = json.datum || datum(json.datumCode, json.datum_params, sphere.a, sphere.b, ecc.es, ecc.ep2); + + extend(this, json); // transfer everything over from the projection because we don't know what we'll need + extend(this, ourProj); // transfer all the methods from the projection + + // copy the 4 things over we calulated in deriveConstants.sphere + this.a = sphere.a; + this.b = sphere.b; + this.rf = sphere.rf; + this.sphere = sphere.sphere; + + // copy the 3 things we calculated in deriveConstants.eccentricity + this.es = ecc.es; + this.e = ecc.e; + this.ep2 = ecc.ep2; + + // add in the datum object + this.datum = datumObj; + + // init the projection + this.init(); + + // legecy callback from back in the day when it went to spatialreference.org + callback(null, this); + } Projection.projections = projections; Projection.projections.start(); module.exports = Projection; -},{"./deriveConstants":33,"./extend":34,"./parseCode":37,"./projections":39}],3:[function(require,module,exports){ +},{"./constants/Datum":26,"./datum":31,"./deriveConstants":35,"./extend":36,"./parseCode":39,"./projections":41}],3:[function(require,module,exports){ module.exports = function(crs, denorm, point) { var xin = point.x, yin = point.y, zin = point.z || 0.0; var v, t, i; + var out = {}; for (i = 0; i < 3; i++) { if (denorm && i === 2 && point.z === undefined) { continue; @@ -124,25 +164,25 @@ module.exports = function(crs, denorm, point) { } switch (crs.axis[i]) { case 'e': - point[t] = v; + out[t] = v; break; case 'w': - point[t] = -v; + out[t] = -v; break; case 'n': - point[t] = v; + out[t] = v; break; case 's': - point[t] = -v; + out[t] = -v; break; case 'u': if (point[t] !== undefined) { - point.z = v; + out.z = v; } break; case 'd': if (point[t] !== undefined) { - point.z = -v; + out.z = -v; } break; default: @@ -150,7 +190,7 @@ module.exports = function(crs, denorm, point) { return null; } } - return point; + return out; }; },{}],4:[function(require,module,exports){ @@ -160,7 +200,7 @@ var sign = require('./sign'); module.exports = function(x) { return (Math.abs(x) < HALF_PI) ? x : (x - (sign(x) * Math.PI)); }; -},{"./sign":21}],5:[function(require,module,exports){ +},{"./sign":22}],5:[function(require,module,exports){ var TWO_PI = Math.PI * 2; // SPI is slightly greater than Math.PI, so values that exceed the -180..180 // degree range by a tiny amount don't get wrapped. This prevents points that @@ -172,35 +212,56 @@ var sign = require('./sign'); module.exports = function(x) { return (Math.abs(x) <= SPI) ? x : (x - (sign(x) * TWO_PI)); }; -},{"./sign":21}],6:[function(require,module,exports){ +},{"./sign":22}],6:[function(require,module,exports){ +var adjust_lon = require('./adjust_lon'); + +module.exports = function(zone, lon) { + if (!zone) { + zone = Math.floor((adjust_lon(lon) + Math.PI) * 30 / Math.PI); + + if (zone < 0) { + return 0; + } + else if (zone >= 60) { + return 59; + } + } + else { + if (zone > 0 && zone <= 60) { + return zone - 1; + } + } +}; + +},{"./adjust_lon":5}],7:[function(require,module,exports){ module.exports = function(x) { if (Math.abs(x) > 1) { x = (x > 1) ? 1 : -1; } return Math.asin(x); }; -},{}],7:[function(require,module,exports){ +},{}],8:[function(require,module,exports){ module.exports = function(x) { return (1 - 0.25 * x * (1 + x / 16 * (3 + 1.25 * x))); }; -},{}],8:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ module.exports = function(x) { return (0.375 * x * (1 + 0.25 * x * (1 + 0.46875 * x))); }; -},{}],9:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ module.exports = function(x) { return (0.05859375 * x * x * (1 + 0.75 * x)); }; -},{}],10:[function(require,module,exports){ +},{}],11:[function(require,module,exports){ module.exports = function(x) { return (x * x * x * (35 / 3072)); }; -},{}],11:[function(require,module,exports){ +},{}],12:[function(require,module,exports){ module.exports = function(a, e, sinphi) { var temp = e * sinphi; return a / Math.sqrt(1 - temp * temp); }; -},{}],12:[function(require,module,exports){ +},{}],13:[function(require,module,exports){ module.exports = function(ml, e0, e1, e2, e3) { var phi; var dphi; @@ -217,7 +278,7 @@ module.exports = function(ml, e0, e1, e2, e3) { //..reportError("IMLFN-CONV:Latitude failed to converge after 15 iterations"); return NaN; }; -},{}],13:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ var HALF_PI = Math.PI/2; module.exports = function(eccent, q) { @@ -250,16 +311,16 @@ module.exports = function(eccent, q) { //console.log("IQSFN-CONV:Latitude failed to converge after 30 iterations"); return NaN; }; -},{}],14:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ module.exports = function(e0, e1, e2, e3, phi) { return (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi)); }; -},{}],15:[function(require,module,exports){ +},{}],16:[function(require,module,exports){ module.exports = function(eccent, sinphi, cosphi) { var con = eccent * sinphi; return cosphi / (Math.sqrt(1 - con * con)); }; -},{}],16:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ var HALF_PI = Math.PI/2; module.exports = function(eccent, ts) { var eccnth = 0.5 * eccent; @@ -276,7 +337,7 @@ module.exports = function(eccent, ts) { //console.log("phi2z has NoConvergence"); return -9999; }; -},{}],17:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ var C00 = 1; var C02 = 0.25; var C04 = 0.046875; @@ -301,7 +362,7 @@ module.exports = function(es) { en[4] = t * es * C88; return en; }; -},{}],18:[function(require,module,exports){ +},{}],19:[function(require,module,exports){ var pj_mlfn = require("./pj_mlfn"); var EPSLN = 1.0e-10; var MAX_ITER = 20; @@ -322,13 +383,13 @@ module.exports = function(arg, es, en) { //..reportError("cass:pj_inv_mlfn: Convergence error"); return phi; }; -},{"./pj_mlfn":19}],19:[function(require,module,exports){ +},{"./pj_mlfn":20}],20:[function(require,module,exports){ module.exports = function(phi, sphi, cphi, en) { cphi *= sphi; sphi *= sphi; return (en[0] * phi - cphi * (en[1] + sphi * (en[2] + sphi * (en[3] + sphi * en[4])))); }; -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ module.exports = function(eccent, sinphi) { var con; if (eccent > 1.0e-7) { @@ -339,15 +400,15 @@ module.exports = function(eccent, sinphi) { return (2 * sinphi); } }; -},{}],21:[function(require,module,exports){ +},{}],22:[function(require,module,exports){ module.exports = function(x) { return x<0 ? -1 : 1; }; -},{}],22:[function(require,module,exports){ +},{}],23:[function(require,module,exports){ module.exports = function(esinp, exp) { return (Math.pow((1 - esinp) / (1 + esinp), exp)); }; -},{}],23:[function(require,module,exports){ +},{}],24:[function(require,module,exports){ module.exports = function (array){ var out = { x: array[0], @@ -361,7 +422,7 @@ module.exports = function (array){ } return out; }; -},{}],24:[function(require,module,exports){ +},{}],25:[function(require,module,exports){ var HALF_PI = Math.PI/2; module.exports = function(eccent, phi, sinphi) { @@ -370,7 +431,7 @@ module.exports = function(eccent, phi, sinphi) { con = Math.pow(((1 - con) / (1 + con)), com); return (Math.tan(0.5 * (HALF_PI - phi)) / con); }; -},{}],25:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ exports.wgs84 = { towgs84: "0,0,0", ellipse: "WGS84", @@ -451,7 +512,7 @@ exports.rnb72 = { ellipse: "intl", datumName: "Reseau National Belge 1972" }; -},{}],26:[function(require,module,exports){ +},{}],27:[function(require,module,exports){ exports.MERIT = { a: 6378137.0, rf: 298.257, @@ -667,7 +728,7 @@ exports.sphere = { b: 6370997.0, ellipseName: "Normal Sphere (r=6370997)" }; -},{}],27:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ exports.greenwich = 0.0; //"0dE", exports.lisbon = -9.131906111111; //"9d07'54.862\"W", exports.paris = 2.337229166667; //"2d20'14.025\"E", @@ -681,11 +742,11 @@ exports.brussels = 4.367975; //"4d22'4.71\"E", exports.stockholm = 18.058277777778; //"18d3'29.8\"E", exports.athens = 23.7163375; //"23d42'58.815\"E", exports.oslo = 10.722916666667; //"10d43'22.5\"E" -},{}],28:[function(require,module,exports){ +},{}],29:[function(require,module,exports){ exports.ft = {to_meter: 0.3048}; exports['us-ft'] = {to_meter: 1200 / 3937}; -},{}],29:[function(require,module,exports){ +},{}],30:[function(require,module,exports){ var proj = require('./Proj'); var transform = require('./transform'); var wgs84 = proj('WGS84'); @@ -750,426 +811,306 @@ function proj4(fromProj, toProj, coord) { } } module.exports = proj4; -},{"./Proj":2,"./transform":66}],30:[function(require,module,exports){ -var HALF_PI = Math.PI/2; +},{"./Proj":2,"./transform":68}],31:[function(require,module,exports){ 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) { - this.datum_params = proj.datum_params.map(parseFloat); - if (this.datum_params[0] !== 0 || this.datum_params[1] !== 0 || this.datum_params[2] !== 0) { - this.datum_type = PJD_3PARAM; - } - if (this.datum_params.length > 3) { - if (this.datum_params[3] !== 0 || this.datum_params[4] !== 0 || this.datum_params[5] !== 0 || this.datum_params[6] !== 0) { - this.datum_type = PJD_7PARAM; - this.datum_params[3] *= SEC_TO_RAD; - this.datum_params[4] *= SEC_TO_RAD; - this.datum_params[5] *= SEC_TO_RAD; - this.datum_params[6] = (this.datum_params[6] / 1000000.0) + 1.0; +function datum(datumCode, datum_params, a, b, es, ep2) { + var out = {}; + out.datum_type = PJD_WGS84; //default setting + if (datumCode && datumCode === 'none') { + out.datum_type = PJD_NODATUM; + } + + if (datum_params) { + out.datum_params = datum_params.map(parseFloat); + if (out.datum_params[0] !== 0 || out.datum_params[1] !== 0 || out.datum_params[2] !== 0) { + out.datum_type = PJD_3PARAM; + } + if (out.datum_params.length > 3) { + if (out.datum_params[3] !== 0 || out.datum_params[4] !== 0 || out.datum_params[5] !== 0 || out.datum_params[6] !== 0) { + out.datum_type = PJD_7PARAM; + out.datum_params[3] *= SEC_TO_RAD; + out.datum_params[4] *= SEC_TO_RAD; + out.datum_params[5] *= SEC_TO_RAD; + out.datum_params[6] = (out.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; - if (this.datum_type === PJD_GRIDSHIFT) { - this.grids = proj.grids; + out.a = a; //datum object also uses these values + out.b = b; + out.es = es; + out.ep2 = ep2; + return out; +} + +module.exports = datum; + +},{}],32:[function(require,module,exports){ +'use strict'; +var PJD_3PARAM = 1; +var PJD_7PARAM = 2; +var HALF_PI = Math.PI/2; + +exports.compareDatums = function(source, dest) { + if (source.datum_type !== dest.datum_type) { + return false; // false, datums are not equal + } else if (source.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 (source.datum_type === PJD_3PARAM) { + return (this.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2]); + } else if (source.datum_type === PJD_7PARAM) { + return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2] && source.datum_params[3] === dest.datum_params[3] && source.datum_params[4] === dest.datum_params[4] && source.datum_params[5] === dest.datum_params[5] && source.datum_params[6] === dest.datum_params[6]); + } else { + return true; // datums are equal } -}; -datum.prototype = { +}; // 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) + * + */ +exports.geodeticToGeocentric = function(p, es, a) { + var Longitude = p.x; + var Latitude = p.y; + var Height = p.z ? p.z : 0; //Z value not always supplied - /****************************************************************/ - // 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() + 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) */ /* - * 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) - * + ** 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. */ - 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 (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; - } + 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 = a / (Math.sqrt(1.0e0 - es * Sin2_Lat)); + return { + x: (Rn + Height) * Cos_Lat * Math.cos(Longitude), + y: (Rn + Height) * Cos_Lat * Math.sin(Longitude), + z: ((Rn * (1 - es)) + Height) * Sin_Lat + }; +}; // cs_geodetic_to_geocentric() + + +exports.geocentricToGeodetic = function(p, es, a, b) { + /* 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 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; + + P = Math.sqrt(X * X + Y * Y); + RR = Math.sqrt(X * X + Y * Y + Z * Z); + + /* special cases for latitude and longitude */ + if (P / a < genau) { + + /* special case, if P=0. (X=0., Y=0.) */ + 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 / a < genau) { + Latitude = HALF_PI; + Height = -b; + return { + x: p.x, + y: p.y, + z: p.z + }; } - 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 + } 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. + * -------------------------------------------------------------- */ - 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); - } + CT = Z / RR; + ST = P / RR; + RX = 1.0 / Math.sqrt(1.0 - es * (2.0 - es) * ST * ST); + CPHI0 = ST * (1.0 - 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 = a / Math.sqrt(1.0 - es * SPHI0 * SPHI0); - 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() + /* ellipsoidal (geodetic) height */ + Height = P * CPHI0 + Z * SPHI0 - RN * (1.0 - es * SPHI0 * SPHI0); + + RK = 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)); + return { + x: Longitude, + y: Latitude, + z: Height + }; +}; // cs_geocentric_to_geodetic() + + +/****************************************************************/ +// pj_geocentic_to_wgs84( p ) +// p = point to transform in geocentric coordinates (x,y,z) + /** 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; +exports.geocentricToWgs84 = function(p, datum_type, datum_params) { + + if (datum_type === PJD_3PARAM) { + // if( x[io] === HUGE_VAL ) + // continue; + return { + x: p.x + datum_params[0], + y: p.y + datum_params[1], + z: p.z + datum_params[2], + }; + } else if (datum_type === PJD_7PARAM) { + var Dx_BF = datum_params[0]; + var Dy_BF = datum_params[1]; + var Dz_BF = datum_params[2]; + var Rx_BF = datum_params[3]; + var Ry_BF = datum_params[4]; + var Rz_BF = datum_params[5]; + var M_BF = datum_params[6]; + // if( x[io] === HUGE_VAL ) + // continue; + return { + x: M_BF * (p.x - Rz_BF * p.y + Ry_BF * p.z) + Dx_BF, + y: M_BF * (Rz_BF * p.x + p.y - Rx_BF * p.z) + Dy_BF, + z: M_BF * (-Ry_BF * p.x + Rx_BF * p.y + p.z) + Dz_BF + }; + } +}; // cs_geocentric_to_wgs84 + +/****************************************************************/ +// pj_geocentic_from_wgs84() +// coordinate system definition, +// point to transform in geocentric coordinates (x,y,z) +exports.geocentricFromWgs84 = function(p, datum_type, datum_params) { + + if (datum_type === PJD_3PARAM) { + //if( x[io] === HUGE_VAL ) + // continue; + return { + x: p.x - datum_params[0], + y: p.y - datum_params[1], + z: p.z - datum_params[2], + }; + + } else if (datum_type === PJD_7PARAM) { + var Dx_BF = datum_params[0]; + var Dy_BF = datum_params[1]; + var Dz_BF = datum_params[2]; + var Rx_BF = datum_params[3]; + var Ry_BF = datum_params[4]; + var Rz_BF = datum_params[5]; + var M_BF = 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; + + return { + x: x_tmp + Rz_BF * y_tmp - Ry_BF * z_tmp, + y: -Rz_BF * x_tmp + y_tmp + Rx_BF * z_tmp, + z: Ry_BF * x_tmp - Rx_BF * y_tmp + z_tmp + }; + } //cs_geocentric_from_wgs84() +}; -},{}],31:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ var PJD_3PARAM = 1; var PJD_7PARAM = 2; -var PJD_GRIDSHIFT = 3; var PJD_NODATUM = 5; // WGS84 or equivalent -var SRS_WGS84_SEMIMAJOR = 6378137; // only used in grid shift transforms -var SRS_WGS84_ESQUARED = 0.006694379990141316; //DGR: 2012-07-29 +var datum = require('./datumUtils'); +function checkParams(type) { + return (type === PJD_3PARAM || type === PJD_7PARAM); +} module.exports = function(source, dest, point) { - var wp, i, l; - - function checkParams(fallback) { - return (fallback === PJD_3PARAM || fallback === PJD_7PARAM); - } // Short cut if the datums are identical. - if (source.compare_datums(dest)) { + if (datum.compareDatums(source, dest)) { return point; // in this case, zero is sucess, // whereas cs_compare_datums returns 1 to indicate TRUE // confusing, should fix this @@ -1180,83 +1121,27 @@ module.exports = function(source, dest, point) { return point; } - //DGR: 2012-07-29 : add nadgrids support (begin) - var src_a = source.a; - var src_es = source.es; + // If this datum requires grid shifts, then apply it to geodetic coordinates. - var dst_a = dest.a; - var dst_es = dest.es; + // Do we need to go through geocentric coordinates? + if (source.es === dest.es && source.a === dest.a && !checkParams(source.datum_type) && !checkParams(dest.datum_type)) { + return point; + } - var fallback = source.datum_type; - // If this datum requires grid shifts, then apply it to geodetic coordinates. - if (fallback === PJD_GRIDSHIFT) { - if (this.apply_gridshift(source, 0, point) === 0) { - source.a = SRS_WGS84_SEMIMAJOR; - source.es = SRS_WGS84_ESQUARED; - } - else { - // try 3 or 7 params transformation or nothing ? - if (!source.datum_params) { - source.a = src_a; - source.es = source.es; - return point; - } - wp = 1; - for (i = 0, l = source.datum_params.length; i < l; i++) { - wp *= source.datum_params[i]; - } - if (wp === 0) { - source.a = src_a; - source.es = source.es; - return point; - } - if (source.datum_params.length > 3) { - fallback = PJD_7PARAM; - } - else { - fallback = PJD_3PARAM; - } - } + // Convert to geocentric coordinates. + point = datum.geodeticToGeocentric(point, source.es, source.a); + // Convert between datums + if (checkParams(source.datum_type)) { + point = datum.geocentricToWgs84(point, source.datum_type, source.datum_params); } - if (dest.datum_type === PJD_GRIDSHIFT) { - dest.a = SRS_WGS84_SEMIMAJOR; - dest.es = SRS_WGS84_ESQUARED; + if (checkParams(dest.datum_type)) { + point = datum.geocentricFromWgs84(point, dest.datum_type, dest.datum_params); } - // Do we need to go through geocentric coordinates? - if (source.es !== dest.es || source.a !== dest.a || checkParams(fallback) || checkParams(dest.datum_type)) { - //DGR: 2012-07-29 : add nadgrids support (end) - // Convert to geocentric coordinates. - source.geodetic_to_geocentric(point); - // CHECK_RETURN; - // Convert between datums - if (checkParams(source.datum_type)) { - source.geocentric_to_wgs84(point); - // CHECK_RETURN; - } - if (checkParams(dest.datum_type)) { - dest.geocentric_from_wgs84(point); - // CHECK_RETURN; - } - // Convert back to geodetic coordinates - dest.geocentric_to_geodetic(point); - // CHECK_RETURN; - } - // Apply grid shift to destination if required - if (dest.datum_type === PJD_GRIDSHIFT) { - this.apply_gridshift(dest, 1, point); - // CHECK_RETURN; - } - - source.a = src_a; - source.es = src_es; - dest.a = dst_a; - dest.es = dst_es; + return datum.geocentricToGeodetic(point, dest.es, dest.a, dest.b); - return point; }; - -},{}],32:[function(require,module,exports){ +},{"./datumUtils":32}],34:[function(require,module,exports){ var globals = require('./global'); var parseProj = require('./projString'); var wkt = require('./wkt'); @@ -1313,65 +1198,62 @@ function defs(name) { globals(defs); module.exports = defs; -},{"./global":35,"./projString":38,"./wkt":67}],33:[function(require,module,exports){ -var Datum = require('./constants/Datum'); -var Ellipsoid = require('./constants/Ellipsoid'); -var extend = require('./extend'); -var datum = require('./datum'); -var EPSLN = 1.0e-10; +},{"./global":37,"./projString":40,"./wkt":69}],35:[function(require,module,exports){ // ellipoid pj_set_ell.c var SIXTH = 0.1666666666666666667; /* 1/6 */ var RA4 = 0.04722222222222222222; /* 17/360 */ var RA6 = 0.02215608465608465608; -module.exports = function(json) { - // DGR 2011-03-20 : nagrids -> nadgrids - if (json.datumCode && json.datumCode !== 'none') { - var datumDef = Datum[json.datumCode]; - if (datumDef) { - json.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null; - json.ellps = datumDef.ellipse; - json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode; - } - } - if (!json.a) { // do we have an ellipsoid? - var ellipse = Ellipsoid[json.ellps] ? Ellipsoid[json.ellps] : Ellipsoid.WGS84; - extend(json, ellipse); - } - if (json.rf && !json.b) { - json.b = (1.0 - 1.0 / json.rf) * json.a; - } - if (json.rf === 0 || Math.abs(json.a - json.b) < EPSLN) { - json.sphere = true; - json.b = json.a; - } - json.a2 = json.a * json.a; // used in geocentric - json.b2 = json.b * json.b; // used in geocentric - json.es = (json.a2 - json.b2) / json.a2; // e ^ 2 - json.e = Math.sqrt(json.es); // eccentricity - if (json.R_A) { - json.a *= 1 - json.es * (SIXTH + json.es * (RA4 + json.es * RA6)); - json.a2 = json.a * json.a; - json.b2 = json.b * json.b; - json.es = 0; - } - json.ep2 = (json.a2 - json.b2) / json.b2; // used in geocentric - if (!json.k0) { - json.k0 = 1.0; //default value +var EPSLN = 1.0e-10; +var Ellipsoid = require('./constants/Ellipsoid'); + +exports.eccentricity = function(a, b, rf, R_A) { + var a2 = a * a; // used in geocentric + var b2 = b * b; // used in geocentric + var es = (a2 - b2) / a2; // e ^ 2 + var e = 0; + if (R_A) { + a *= 1 - es * (SIXTH + es * (RA4 + es * RA6)); + a2 = a * a; + es = 0; + } else { + e = Math.sqrt(es); // eccentricity } - //DGR 2010-11-12: axis - if (!json.axis) { - json.axis = "enu"; + var ep2 = (a2 - b2) / b2; // used in geocentric + return { + es: es, + e: e, + ep2: ep2 + }; +}; +exports.sphere = function (a, b, rf, ellps, sphere) { + if (!a) { // do we have an ellipsoid? + var ellipse = Ellipsoid[ellps]; + if (!ellipse) { + ellipse = Ellipsoid.WGS84; + } + a = ellipse.a; + b = ellipse.b; + rf = ellipse.rf; } - if (!json.datum) { - json.datum = datum(json); + if (rf && !b) { + b = (1.0 - 1.0 / rf) * a; + } + if (rf === 0 || Math.abs(a - b) < EPSLN) { + sphere = true; + b = a; } - return json; + return { + a: a, + b: b, + rf: rf, + sphere: sphere + }; }; -},{"./constants/Datum":25,"./constants/Ellipsoid":26,"./datum":30,"./extend":34}],34:[function(require,module,exports){ +},{"./constants/Ellipsoid":27}],36:[function(require,module,exports){ module.exports = function(destination, source) { destination = destination || {}; var value, property; @@ -1387,7 +1269,7 @@ module.exports = function(destination, source) { return destination; }; -},{}],35:[function(require,module,exports){ +},{}],37:[function(require,module,exports){ module.exports = function(defs) { defs('EPSG:4326', "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees"); defs('EPSG:4269', "+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees"); @@ -1400,20 +1282,21 @@ module.exports = function(defs) { defs['EPSG:102113'] = defs['EPSG:3857']; }; -},{}],36:[function(require,module,exports){ +},{}],38:[function(require,module,exports){ var proj4 = require('./core'); proj4.defaultDatum = 'WGS84'; //default datum proj4.Proj = require('./Proj'); proj4.WGS84 = new proj4.Proj('WGS84'); proj4.Point = require('./Point'); -proj4.toPoint = require("./common/toPoint"); +proj4.toPoint = require('./common/toPoint'); proj4.defs = require('./defs'); proj4.transform = require('./transform'); proj4.mgrs = require('mgrs'); -proj4.version = require('../package.json').version; +proj4.version = require('./version'); require('./includedProjections')(proj4); module.exports = proj4; -},{"../package.json":69,"./Point":1,"./Proj":2,"./common/toPoint":23,"./core":29,"./defs":32,"./includedProjections":"./includedProjections","./transform":66,"mgrs":68}],37:[function(require,module,exports){ + +},{"./Point":1,"./Proj":2,"./common/toPoint":24,"./core":30,"./defs":34,"./includedProjections":"./includedProjections","./transform":68,"./version":"./lib/version","mgrs":70}],39:[function(require,module,exports){ var defs = require('./defs'); var wkt = require('./wkt'); var projStr = require('./projString'); @@ -1423,11 +1306,12 @@ function testObj(code){ function testDef(code){ return code in defs; } +var codeWords = ['GEOGCS','GEOCCS','PROJCS','LOCAL_CS']; + function testWKT(code){ - var codeWords = ['GEOGCS','GEOCCS','PROJCS','LOCAL_CS']; - return codeWords.reduce(function(a,b){ - return a+1+code.indexOf(b); - },0); + return codeWords.some(function (word) { + return code.indexOf(word) > -1; + }); } function testProj(code){ return code[0] === '+'; @@ -1438,10 +1322,10 @@ function parse(code){ if (testDef(code)) { return defs[code]; } - else if (testWKT(code)) { + if (testWKT(code)) { return wkt(code); } - else if (testProj(code)) { + if (testProj(code)) { return projStr(code); } }else{ @@ -1450,23 +1334,24 @@ function parse(code){ } module.exports = parse; -},{"./defs":32,"./projString":38,"./wkt":67}],38:[function(require,module,exports){ + +},{"./defs":34,"./projString":40,"./wkt":69}],40:[function(require,module,exports){ var D2R = 0.01745329251994329577; var PrimeMeridian = require('./constants/PrimeMeridian'); var units = require('./constants/units'); module.exports = function(defData) { var self = {}; - var paramObj = {}; - defData.split("+").map(function(v) { + var paramObj = defData.split('+').map(function(v) { return v.trim(); }).filter(function(a) { return a; - }).forEach(function(a) { - var split = a.split("="); + }).reduce(function(p, a) { + var split = a.split('='); split.push(true); - paramObj[split[0].toLowerCase()] = split[1]; - }); + p[split[0].toLowerCase()] = split[1]; + return p; + }, {}); var paramName, paramVal, paramOutname; var params = { proj: 'projName', @@ -1584,7 +1469,7 @@ module.exports = function(defData) { return self; }; -},{"./constants/PrimeMeridian":27,"./constants/units":28}],39:[function(require,module,exports){ +},{"./constants/PrimeMeridian":28,"./constants/units":29}],41:[function(require,module,exports){ var projs = [ require('./projections/merc'), require('./projections/longlat') @@ -1620,7 +1505,7 @@ exports.start = function() { projs.forEach(add); }; -},{"./projections/longlat":51,"./projections/merc":52}],40:[function(require,module,exports){ +},{"./projections/longlat":53,"./projections/merc":54}],42:[function(require,module,exports){ var EPSLN = 1.0e-10; var msfnz = require('../common/msfnz'); var qsfnz = require('../common/qsfnz'); @@ -1743,7 +1628,7 @@ exports.phi1z = function(eccent, qs) { }; exports.names = ["Albers_Conic_Equal_Area", "Albers", "aea"]; -},{"../common/adjust_lon":5,"../common/asinz":6,"../common/msfnz":15,"../common/qsfnz":20}],41:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/asinz":7,"../common/msfnz":16,"../common/qsfnz":21}],43:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var HALF_PI = Math.PI/2; var EPSLN = 1.0e-10; @@ -1942,7 +1827,7 @@ exports.inverse = function(p) { }; exports.names = ["Azimuthal_Equidistant", "aeqd"]; -},{"../common/adjust_lon":5,"../common/asinz":6,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/gN":11,"../common/imlfn":12,"../common/mlfn":14}],42:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/asinz":7,"../common/e0fn":8,"../common/e1fn":9,"../common/e2fn":10,"../common/e3fn":11,"../common/gN":12,"../common/imlfn":13,"../common/mlfn":15}],44:[function(require,module,exports){ var mlfn = require('../common/mlfn'); var e0fn = require('../common/e0fn'); var e1fn = require('../common/e1fn'); @@ -2046,7 +1931,7 @@ exports.inverse = function(p) { }; exports.names = ["Cassini", "Cassini_Soldner", "cass"]; -},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/gN":11,"../common/imlfn":12,"../common/mlfn":14}],43:[function(require,module,exports){ +},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":8,"../common/e1fn":9,"../common/e2fn":10,"../common/e3fn":11,"../common/gN":12,"../common/imlfn":13,"../common/mlfn":15}],45:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var qsfnz = require('../common/qsfnz'); var msfnz = require('../common/msfnz'); @@ -2111,7 +1996,7 @@ exports.inverse = function(p) { }; exports.names = ["cea"]; -},{"../common/adjust_lon":5,"../common/iqsfnz":13,"../common/msfnz":15,"../common/qsfnz":20}],44:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/iqsfnz":14,"../common/msfnz":16,"../common/qsfnz":21}],46:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var adjust_lat = require('../common/adjust_lat'); exports.init = function() { @@ -2154,7 +2039,7 @@ exports.inverse = function(p) { }; exports.names = ["Equirectangular", "Equidistant_Cylindrical", "eqc"]; -},{"../common/adjust_lat":4,"../common/adjust_lon":5}],45:[function(require,module,exports){ +},{"../common/adjust_lat":4,"../common/adjust_lon":5}],47:[function(require,module,exports){ var e0fn = require('../common/e0fn'); var e1fn = require('../common/e1fn'); var e2fn = require('../common/e2fn'); @@ -2266,7 +2151,7 @@ exports.inverse = function(p) { }; exports.names = ["Equidistant_Conic", "eqdc"]; -},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/imlfn":12,"../common/mlfn":14,"../common/msfnz":15}],46:[function(require,module,exports){ +},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":8,"../common/e1fn":9,"../common/e2fn":10,"../common/e3fn":11,"../common/imlfn":13,"../common/mlfn":15,"../common/msfnz":16}],48:[function(require,module,exports){ var FORTPI = Math.PI/4; var srat = require('../common/srat'); var HALF_PI = Math.PI/2; @@ -2313,7 +2198,7 @@ exports.inverse = function(p) { }; exports.names = ["gauss"]; -},{"../common/srat":22}],47:[function(require,module,exports){ +},{"../common/srat":23}],49:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var EPSLN = 1.0e-10; var asinz = require('../common/asinz'); @@ -2414,7 +2299,7 @@ exports.inverse = function(p) { }; exports.names = ["gnom"]; -},{"../common/adjust_lon":5,"../common/asinz":6}],48:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/asinz":7}],50:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); exports.init = function() { this.a = 6377397.155; @@ -2514,7 +2399,7 @@ exports.inverse = function(p) { }; exports.names = ["Krovak", "krovak"]; -},{"../common/adjust_lon":5}],49:[function(require,module,exports){ +},{"../common/adjust_lon":5}],51:[function(require,module,exports){ var HALF_PI = Math.PI/2; var FORTPI = Math.PI/4; var EPSLN = 1.0e-10; @@ -2804,7 +2689,7 @@ exports.authlat = function(beta, APA) { }; exports.names = ["Lambert Azimuthal Equal Area", "Lambert_Azimuthal_Equal_Area", "laea"]; -},{"../common/adjust_lon":5,"../common/qsfnz":20}],50:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/qsfnz":21}],52:[function(require,module,exports){ var EPSLN = 1.0e-10; var msfnz = require('../common/msfnz'); var tsfnz = require('../common/tsfnz'); @@ -2941,7 +2826,7 @@ exports.inverse = function(p) { exports.names = ["Lambert Tangential Conformal Conic Projection", "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_2SP", "lcc"]; -},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/sign":21,"../common/tsfnz":24}],51:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/msfnz":16,"../common/phi2z":17,"../common/sign":22,"../common/tsfnz":25}],53:[function(require,module,exports){ exports.init = function() { //no-op for longlat }; @@ -2953,7 +2838,7 @@ exports.forward = identity; exports.inverse = identity; exports.names = ["longlat", "identity"]; -},{}],52:[function(require,module,exports){ +},{}],54:[function(require,module,exports){ var msfnz = require('../common/msfnz'); var HALF_PI = Math.PI/2; var EPSLN = 1.0e-10; @@ -3052,7 +2937,7 @@ exports.inverse = function(p) { exports.names = ["Mercator", "Popular Visualisation Pseudo Mercator", "Mercator_1SP", "Mercator_Auxiliary_Sphere", "merc"]; -},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/tsfnz":24}],53:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/msfnz":16,"../common/phi2z":17,"../common/tsfnz":25}],55:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); /* reference @@ -3099,7 +2984,7 @@ exports.inverse = function(p) { }; exports.names = ["Miller_Cylindrical", "mill"]; -},{"../common/adjust_lon":5}],54:[function(require,module,exports){ +},{"../common/adjust_lon":5}],56:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var EPSLN = 1.0e-10; exports.init = function() {}; @@ -3178,7 +3063,7 @@ exports.inverse = function(p) { }; exports.names = ["Mollweide", "moll"]; -},{"../common/adjust_lon":5}],55:[function(require,module,exports){ +},{"../common/adjust_lon":5}],57:[function(require,module,exports){ var SEC_TO_RAD = 4.84813681109535993589914102357e-6; /* reference @@ -3398,7 +3283,7 @@ exports.inverse = function(p) { return p; }; exports.names = ["New_Zealand_Map_Grid", "nzmg"]; -},{}],56:[function(require,module,exports){ +},{}],58:[function(require,module,exports){ var tsfnz = require('../common/tsfnz'); var adjust_lon = require('../common/adjust_lon'); var phi2z = require('../common/phi2z'); @@ -3567,7 +3452,7 @@ exports.inverse = function(p) { }; exports.names = ["Hotine_Oblique_Mercator", "Hotine Oblique Mercator", "Hotine_Oblique_Mercator_Azimuth_Natural_Origin", "Hotine_Oblique_Mercator_Azimuth_Center", "omerc"]; -},{"../common/adjust_lon":5,"../common/phi2z":16,"../common/tsfnz":24}],57:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/phi2z":17,"../common/tsfnz":25}],59:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var EPSLN = 1.0e-10; var asinz = require('../common/asinz'); @@ -3655,7 +3540,7 @@ exports.inverse = function(p) { }; exports.names = ["ortho"]; -},{"../common/adjust_lon":5,"../common/asinz":6}],58:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/asinz":7}],60:[function(require,module,exports){ var e0fn = require('../common/e0fn'); var e1fn = require('../common/e1fn'); var e2fn = require('../common/e2fn'); @@ -3784,7 +3669,7 @@ exports.inverse = function(p) { return p; }; exports.names = ["Polyconic", "poly"]; -},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/gN":11,"../common/mlfn":14}],59:[function(require,module,exports){ +},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":8,"../common/e1fn":9,"../common/e2fn":10,"../common/e3fn":11,"../common/gN":12,"../common/mlfn":15}],61:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var adjust_lat = require('../common/adjust_lat'); var pj_enfn = require('../common/pj_enfn'); @@ -3891,7 +3776,7 @@ exports.inverse = function(p) { return p; }; exports.names = ["Sinusoidal", "sinu"]; -},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/asinz":6,"../common/pj_enfn":17,"../common/pj_inv_mlfn":18,"../common/pj_mlfn":19}],60:[function(require,module,exports){ +},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/asinz":7,"../common/pj_enfn":18,"../common/pj_inv_mlfn":19,"../common/pj_mlfn":20}],62:[function(require,module,exports){ /* references: Formules et constantes pour le Calcul pour la @@ -3973,7 +3858,7 @@ exports.inverse = function(p) { exports.names = ["somerc"]; -},{}],61:[function(require,module,exports){ +},{}],63:[function(require,module,exports){ var HALF_PI = Math.PI/2; var EPSLN = 1.0e-10; var sign = require('../common/sign'); @@ -4141,7 +4026,7 @@ exports.inverse = function(p) { }; exports.names = ["stere", "Stereographic_South_Pole", "Polar Stereographic (variant B)"]; -},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/sign":21,"../common/tsfnz":24}],62:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/msfnz":16,"../common/phi2z":17,"../common/sign":22,"../common/tsfnz":25}],64:[function(require,module,exports){ var gauss = require('./gauss'); var adjust_lon = require('../common/adjust_lon'); exports.init = function() { @@ -4200,24 +4085,28 @@ exports.inverse = function(p) { exports.names = ["Stereographic_North_Pole", "Oblique_Stereographic", "Polar_Stereographic", "sterea","Oblique Stereographic Alternative"]; -},{"../common/adjust_lon":5,"./gauss":46}],63:[function(require,module,exports){ -var e0fn = require('../common/e0fn'); -var e1fn = require('../common/e1fn'); -var e2fn = require('../common/e2fn'); -var e3fn = require('../common/e3fn'); -var mlfn = require('../common/mlfn'); +},{"../common/adjust_lon":5,"./gauss":48}],65:[function(require,module,exports){ +// Heavily based on this tmerc projection implementation +// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js + +var pj_enfn = require('../common/pj_enfn'); +var pj_mlfn = require('../common/pj_mlfn'); +var pj_inv_mlfn = require('../common/pj_inv_mlfn'); var adjust_lon = require('../common/adjust_lon'); -var HALF_PI = Math.PI/2; +var HALF_PI = Math.PI / 2; var EPSLN = 1.0e-10; var sign = require('../common/sign'); -var asinz = require('../common/asinz'); exports.init = function() { - this.e0 = e0fn(this.es); - this.e1 = e1fn(this.es); - this.e2 = e2fn(this.es); - this.e3 = e3fn(this.es); - this.ml0 = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0); + this.x0 = this.x0 !== undefined ? this.x0 : 0; + this.y0 = this.y0 !== undefined ? this.y0 : 0; + this.long0 = this.long0 !== undefined ? this.long0 : 0; + this.lat0 = this.lat0 !== undefined ? this.lat0 : 0; + + if (this.es) { + this.en = pj_enfn(this.es); + this.ml0 = pj_mlfn(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en); + } }; /** @@ -4234,36 +4123,65 @@ exports.forward = function(p) { var sin_phi = Math.sin(lat); var cos_phi = Math.cos(lat); - if (this.sphere) { + if (!this.es) { var b = cos_phi * Math.sin(delta_lon); - if ((Math.abs(Math.abs(b) - 1)) < 0.0000000001) { + + if ((Math.abs(Math.abs(b) - 1)) < EPSLN) { return (93); } else { - x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)); - con = Math.acos(cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - b * b)); + x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)) + this.x0; + y = cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - Math.pow(b, 2)); + b = Math.abs(y); + + if (b >= 1) { + if ((b - 1) > EPSLN) { + return (93); + } + else { + y = 0; + } + } + else { + y = Math.acos(y); + } + if (lat < 0) { - con = -con; + y = -y; } - y = this.a * this.k0 * (con - this.lat0); + + y = this.a * this.k0 * (y - this.lat0) + this.y0; } } else { var al = cos_phi * delta_lon; var als = Math.pow(al, 2); var c = this.ep2 * Math.pow(cos_phi, 2); - var tq = Math.tan(lat); + var cs = Math.pow(c, 2); + var tq = Math.abs(cos_phi) > EPSLN ? Math.tan(lat) : 0; var t = Math.pow(tq, 2); + var ts = Math.pow(t, 2); con = 1 - this.es * Math.pow(sin_phi, 2); - var n = this.a / Math.sqrt(con); - var ml = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, lat); + al = al / Math.sqrt(con); + var ml = pj_mlfn(lat, sin_phi, cos_phi, this.en); - x = this.k0 * n * al * (1 + als / 6 * (1 - t + c + als / 20 * (5 - 18 * t + Math.pow(t, 2) + 72 * c - 58 * this.ep2))) + this.x0; - y = this.k0 * (ml - this.ml0 + n * tq * (als * (0.5 + als / 24 * (5 - t + 9 * c + 4 * Math.pow(c, 2) + als / 30 * (61 - 58 * t + Math.pow(t, 2) + 600 * c - 330 * this.ep2))))) + this.y0; + x = this.a * (this.k0 * al * (1 + + als / 6 * (1 - t + c + + als / 20 * (5 - 18 * t + ts + 14 * c - 58 * t * c + + als / 42 * (61 + 179 * ts - ts * t - 479 * t))))) + + this.x0; + y = this.a * (this.k0 * (ml - this.ml0 + + sin_phi * delta_lon * al / 2 * (1 + + als / 12 * (5 - t + 9 * c + 4 * cs + + als / 30 * (61 + ts - 58 * t + 270 * c - 330 * t * c + + als / 56 * (1385 + 543 * ts - ts * t - 3111 * t)))))) + + this.y0; } + p.x = x; p.y = y; + return p; }; @@ -4272,81 +4190,84 @@ exports.forward = function(p) { */ exports.inverse = function(p) { var con, phi; - var delta_phi; - var i; - var max_iter = 6; var lat, lon; + var x = (p.x - this.x0) * (1 / this.a); + var y = (p.y - this.y0) * (1 / this.a); - if (this.sphere) { - var f = Math.exp(p.x / (this.a * this.k0)); + if (!this.es) { + var f = Math.exp(x / this.k0); var g = 0.5 * (f - 1 / f); - var temp = this.lat0 + p.y / (this.a * this.k0); + var temp = this.lat0 + y / this.k0; var h = Math.cos(temp); - con = Math.sqrt((1 - h * h) / (1 + g * g)); - lat = asinz(con); - if (temp < 0) { + con = Math.sqrt((1 - Math.pow(h, 2)) / (1 + Math.pow(g, 2))); + lat = Math.asin(con); + + if (y < 0) { lat = -lat; } + if ((g === 0) && (h === 0)) { - lon = this.long0; + lon = 0; } else { lon = adjust_lon(Math.atan2(g, h) + this.long0); } } else { // ellipsoidal form - var x = p.x - this.x0; - var y = p.y - this.y0; - - con = (this.ml0 + y / this.k0) / this.a; - phi = con; - for (i = 0; true; i++) { - delta_phi = ((con + this.e1 * Math.sin(2 * phi) - this.e2 * Math.sin(4 * phi) + this.e3 * Math.sin(6 * phi)) / this.e0) - phi; - phi += delta_phi; - if (Math.abs(delta_phi) <= EPSLN) { - break; - } - if (i >= max_iter) { - return (95); - } - } // for() + con = this.ml0 + y / this.k0; + phi = pj_inv_mlfn(con, this.es, this.en); + if (Math.abs(phi) < HALF_PI) { var sin_phi = Math.sin(phi); var cos_phi = Math.cos(phi); - var tan_phi = Math.tan(phi); + var tan_phi = Math.abs(cos_phi) > EPSLN ? Math.tan(phi) : 0; var c = this.ep2 * Math.pow(cos_phi, 2); var cs = Math.pow(c, 2); var t = Math.pow(tan_phi, 2); var ts = Math.pow(t, 2); con = 1 - this.es * Math.pow(sin_phi, 2); - var n = this.a / Math.sqrt(con); - var r = n * (1 - this.es) / con; - var d = x / (n * this.k0); + var d = x * Math.sqrt(con) / this.k0; var ds = Math.pow(d, 2); - lat = phi - (n * tan_phi * ds / r) * (0.5 - ds / 24 * (5 + 3 * t + 10 * c - 4 * cs - 9 * this.ep2 - ds / 30 * (61 + 90 * t + 298 * c + 45 * ts - 252 * this.ep2 - 3 * cs))); - lon = adjust_lon(this.long0 + (d * (1 - ds / 6 * (1 + 2 * t + c - ds / 20 * (5 - 2 * c + 28 * t - 3 * cs + 8 * this.ep2 + 24 * ts))) / cos_phi)); + con = con * tan_phi; + + lat = phi - (con * ds / (1 - this.es)) * 0.5 * (1 - + ds / 12 * (5 + 3 * t - 9 * c * t + c - 4 * cs - + ds / 30 * (61 + 90 * t - 252 * c * t + 45 * ts + 46 * c - + ds / 56 * (1385 + 3633 * t + 4095 * ts + 1574 * ts * t)))); + + lon = adjust_lon(this.long0 + (d * (1 - + ds / 6 * (1 + 2 * t + c - + ds / 20 * (5 + 28 * t + 24 * ts + 8 * c * t + 6 * c - + ds / 42 * (61 + 662 * t + 1320 * ts + 720 * ts * t)))) / cos_phi)); } else { lat = HALF_PI * sign(y); - lon = this.long0; + lon = 0; } } + p.x = lon; p.y = lat; + return p; }; + exports.names = ["Transverse_Mercator", "Transverse Mercator", "tmerc"]; -},{"../common/adjust_lon":5,"../common/asinz":6,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/mlfn":14,"../common/sign":21}],64:[function(require,module,exports){ -var D2R = 0.01745329251994329577; +},{"../common/adjust_lon":5,"../common/pj_enfn":18,"../common/pj_inv_mlfn":19,"../common/pj_mlfn":20,"../common/sign":22}],66:[function(require,module,exports){ +var adjust_zone = require('../common/adjust_zone'); var tmerc = require('./tmerc'); + exports.dependsOn = 'tmerc'; + exports.init = function() { - if (!this.zone) { + var zone = adjust_zone(this.zone, this.long0); + if (!zone) { return; } + this.lat0 = 0; - this.long0 = ((6 * Math.abs(this.zone)) - 183) * D2R; + this.long0 = (zone + 0.5) * Math.PI / 30 - Math.PI; this.x0 = 500000; this.y0 = this.utmSouth ? 10000000 : 0; this.k0 = 0.9996; @@ -4355,9 +4276,10 @@ exports.init = function() { this.forward = tmerc.forward; this.inverse = tmerc.inverse; }; + exports.names = ["Universal Transverse Mercator System", "utm"]; -},{"./tmerc":63}],65:[function(require,module,exports){ +},{"../common/adjust_zone":6,"./tmerc":65}],67:[function(require,module,exports){ var adjust_lon = require('../common/adjust_lon'); var HALF_PI = Math.PI/2; var EPSLN = 1.0e-10; @@ -4478,7 +4400,7 @@ exports.inverse = function(p) { return p; }; exports.names = ["Van_der_Grinten_I", "VanDerGrinten", "vandg"]; -},{"../common/adjust_lon":5,"../common/asinz":6}],66:[function(require,module,exports){ +},{"../common/adjust_lon":5,"../common/asinz":7}],68:[function(require,module,exports){ var D2R = 0.01745329251994329577; var R2D = 57.29577951308232088; var PJD_3PARAM = 1; @@ -4487,36 +4409,40 @@ var datum_transform = require('./datum_transform'); var adjust_axis = require('./adjust_axis'); var proj = require('./Proj'); var toPoint = require('./common/toPoint'); +function checkNotWGS(source, dest) { + return ((source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM) && dest.datumCode !== 'WGS84') || ((dest.datum.datum_type === PJD_3PARAM || dest.datum.datum_type === PJD_7PARAM) && source.datumCode !== 'WGS84'); +} module.exports = function transform(source, dest, point) { var wgs84; if (Array.isArray(point)) { point = toPoint(point); } - function checkNotWGS(source, dest) { - return ((source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM) && dest.datumCode !== "WGS84"); - } // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84 - if (source.datum && dest.datum && (checkNotWGS(source, dest) || checkNotWGS(dest, source))) { + if (source.datum && dest.datum && checkNotWGS(source, dest)) { wgs84 = new proj('WGS84'); - transform(source, wgs84, point); + point = transform(source, wgs84, point); source = wgs84; } // DGR, 2010/11/12 - if (source.axis !== "enu") { - adjust_axis(source, false, point); + if (source.axis !== 'enu') { + point = adjust_axis(source, false, point); } // Transform source points to long/lat, if they aren't already. - if (source.projName === "longlat") { - point.x *= D2R; // convert degrees to radians - point.y *= D2R; + if (source.projName === 'longlat') { + point = { + x: point.x * D2R, + y: point.y * D2R + }; } else { if (source.to_meter) { - point.x *= source.to_meter; - point.y *= source.to_meter; + point = { + x: point.x * source.to_meter, + y: point.y * source.to_meter + }; } - source.inverse(point); // Convert Cartesian to longlat + point = source.inverse(point); // Convert Cartesian to longlat } // Adjust for the prime meridian if necessary if (source.from_greenwich) { @@ -4528,30 +4454,37 @@ module.exports = function transform(source, dest, point) { // Adjust for the prime meridian if necessary if (dest.from_greenwich) { - point.x -= dest.from_greenwich; + point = { + x: point.x - dest.grom_greenwich, + y: point.y + }; } - if (dest.projName === "longlat") { + if (dest.projName === 'longlat') { // convert radians to decimal degrees - point.x *= R2D; - point.y *= R2D; - } - else { // else project - dest.forward(point); + point = { + x: point.x * R2D, + y: point.y * R2D + }; + } else { // else project + point = dest.forward(point); if (dest.to_meter) { - point.x /= dest.to_meter; - point.y /= dest.to_meter; + point = { + x: point.x / dest.to_meter, + y: point.y / dest.to_meter + }; } } // DGR, 2010/11/12 - if (dest.axis !== "enu") { - adjust_axis(dest, true, point); + if (dest.axis !== 'enu') { + return adjust_axis(dest, true, point); } return point; }; -},{"./Proj":2,"./adjust_axis":3,"./common/toPoint":23,"./datum_transform":31}],67:[function(require,module,exports){ + +},{"./Proj":2,"./adjust_axis":3,"./common/toPoint":24,"./datum_transform":33}],69:[function(require,module,exports){ var D2R = 0.01745329251994329577; var extend = require('./extend'); @@ -4776,7 +4709,7 @@ module.exports = function(wkt, self) { return extend(self, obj.output); }; -},{"./extend":34}],68:[function(require,module,exports){ +},{"./extend":36}],70:[function(require,module,exports){ @@ -5520,52 +5453,5 @@ function getMinNorthing(zoneLetter) { } -},{}],69:[function(require,module,exports){ -module.exports={ - "name": "proj4", - "version": "2.3.15", - "description": "Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.", - "main": "lib/index.js", - "directories": { - "test": "test", - "doc": "docs" - }, - "scripts": { - "test": "./node_modules/istanbul/lib/cli.js test ./node_modules/mocha/bin/_mocha test/test.js" - }, - "repository": { - "type": "git", - "url": "git://github.com/proj4js/proj4js.git" - }, - "author": "", - "license": "MIT", - "jam": { - "main": "dist/proj4.js", - "include": [ - "dist/proj4.js", - "README.md", - "AUTHORS", - "LICENSE.md" - ] - }, - "devDependencies": { - "grunt-cli": "~0.1.13", - "grunt": "~0.4.2", - "grunt-contrib-connect": "~0.6.0", - "grunt-contrib-jshint": "~0.8.0", - "chai": "~1.8.1", - "mocha": "~1.17.1", - "grunt-mocha-phantomjs": "~0.4.0", - "browserify": "~12.0.1", - "grunt-browserify": "~4.0.1", - "grunt-contrib-uglify": "~0.11.1", - "curl": "git://github.com/cujojs/curl.git", - "istanbul": "~0.2.4", - "tin": "~0.4.0" - }, - "dependencies": { - "mgrs": "~0.0.2" - } -} -},{}]},{},[36])(36) +},{}]},{},[38])(38) }); \ No newline at end of file diff --git a/dist/test.html b/dist/test.html deleted file mode 100644 index 89f04d8..0000000 --- a/dist/test.html +++ /dev/null @@ -1,11 +0,0 @@ -<html> -<body> - <div id="result"></div> - <script src="proj4-src.js"></script> - <script> - document.getElementById('result').innerHTML = - proj4('EPSG:4326', 'EPSG:3857', [16, 48]); - </script> -</body> -</html> - diff --git a/lib/Proj.js b/lib/Proj.js index f4a55bd..7bc65db 100644 --- a/lib/Proj.js +++ b/lib/Proj.js @@ -1,7 +1,10 @@ -var parseCode = require("./parseCode"); +var parseCode = require('./parseCode'); var extend = require('./extend'); var projections = require('./projections'); var deriveConstants = require('./deriveConstants'); +var Datum = require('./constants/Datum'); +var datum = require('./datum'); + function Projection(srsCode,callback) { if (!(this instanceof Projection)) { @@ -17,16 +20,49 @@ function Projection(srsCode,callback) { callback(srsCode); return; } - var modifiedJSON = deriveConstants(json); - var ourProj = Projection.projections.get(modifiedJSON.projName); - if(ourProj){ - extend(this, modifiedJSON); - extend(this, ourProj); - this.init(); - callback(null, this); - }else{ + var ourProj = Projection.projections.get(json.projName); + if(!ourProj){ callback(srsCode); + return; + } + if (json.datumCode && json.datumCode !== 'none') { + var datumDef = Datum[json.datumCode]; + if (datumDef) { + json.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null; + json.ellps = datumDef.ellipse; + json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode; + } } + json.k0 = json.k0 || 1.0; + json.axis = json.axis || 'enu'; + + var sphere = deriveConstants.sphere(json.a, json.b, json.rf, json.ellps, json.sphere); + var ecc = deriveConstants.eccentricity(sphere.a, sphere.b, sphere.rf, json.R_A); + var datumObj = json.datum || datum(json.datumCode, json.datum_params, sphere.a, sphere.b, ecc.es, ecc.ep2); + + extend(this, json); // transfer everything over from the projection because we don't know what we'll need + extend(this, ourProj); // transfer all the methods from the projection + + // copy the 4 things over we calulated in deriveConstants.sphere + this.a = sphere.a; + this.b = sphere.b; + this.rf = sphere.rf; + this.sphere = sphere.sphere; + + // copy the 3 things we calculated in deriveConstants.eccentricity + this.es = ecc.es; + this.e = ecc.e; + this.ep2 = ecc.ep2; + + // add in the datum object + this.datum = datumObj; + + // init the projection + this.init(); + + // legecy callback from back in the day when it went to spatialreference.org + callback(null, this); + } Projection.projections = projections; Projection.projections.start(); diff --git a/lib/adjust_axis.js b/lib/adjust_axis.js index 1690be1..8a84296 100644 --- a/lib/adjust_axis.js +++ b/lib/adjust_axis.js @@ -3,6 +3,7 @@ module.exports = function(crs, denorm, point) { yin = point.y, zin = point.z || 0.0; var v, t, i; + var out = {}; for (i = 0; i < 3; i++) { if (denorm && i === 2 && point.z === undefined) { continue; @@ -21,25 +22,25 @@ module.exports = function(crs, denorm, point) { } switch (crs.axis[i]) { case 'e': - point[t] = v; + out[t] = v; break; case 'w': - point[t] = -v; + out[t] = -v; break; case 'n': - point[t] = v; + out[t] = v; break; case 's': - point[t] = -v; + out[t] = -v; break; case 'u': if (point[t] !== undefined) { - point.z = v; + out.z = v; } break; case 'd': if (point[t] !== undefined) { - point.z = -v; + out.z = -v; } break; default: @@ -47,5 +48,5 @@ module.exports = function(crs, denorm, point) { return null; } } - return point; + return out; }; diff --git a/lib/common/adjust_zone.js b/lib/common/adjust_zone.js new file mode 100644 index 0000000..861773b --- /dev/null +++ b/lib/common/adjust_zone.js @@ -0,0 +1,19 @@ +var adjust_lon = require('./adjust_lon'); + +module.exports = function(zone, lon) { + if (!zone) { + zone = Math.floor((adjust_lon(lon) + Math.PI) * 30 / Math.PI); + + if (zone < 0) { + return 0; + } + else if (zone >= 60) { + return 59; + } + } + else { + if (zone > 0 && zone <= 60) { + return zone - 1; + } + } +}; diff --git a/lib/datum.js b/lib/datum.js index 40d3328..a7391c5 100644 --- a/lib/datum.js +++ b/lib/datum.js @@ -1,403 +1,38 @@ -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) { - this.datum_params = proj.datum_params.map(parseFloat); - if (this.datum_params[0] !== 0 || this.datum_params[1] !== 0 || this.datum_params[2] !== 0) { - this.datum_type = PJD_3PARAM; - } - if (this.datum_params.length > 3) { - if (this.datum_params[3] !== 0 || this.datum_params[4] !== 0 || this.datum_params[5] !== 0 || this.datum_params[6] !== 0) { - this.datum_type = PJD_7PARAM; - this.datum_params[3] *= SEC_TO_RAD; - this.datum_params[4] *= SEC_TO_RAD; - this.datum_params[5] *= SEC_TO_RAD; - this.datum_params[6] = (this.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; - if (this.datum_type === PJD_GRIDSHIFT) { - this.grids = proj.grids; +function datum(datumCode, datum_params, a, b, es, ep2) { + var out = {}; + out.datum_type = PJD_WGS84; //default setting + if (datumCode && datumCode === 'none') { + out.datum_type = PJD_NODATUM; } -}; -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; + if (datum_params) { + out.datum_params = datum_params.map(parseFloat); + if (out.datum_params[0] !== 0 || out.datum_params[1] !== 0 || out.datum_params[2] !== 0) { + out.datum_type = PJD_3PARAM; + } + if (out.datum_params.length > 3) { + if (out.datum_params[3] !== 0 || out.datum_params[4] !== 0 || out.datum_params[5] !== 0 || out.datum_params[6] !== 0) { + out.datum_type = PJD_7PARAM; + out.datum_params[3] *= SEC_TO_RAD; + out.datum_params[4] *= SEC_TO_RAD; + out.datum_params[5] *= SEC_TO_RAD; + out.datum_params[6] = (out.datum_params[6] / 1000000.0) + 1.0; } } - 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() - } -}; + out.a = a; //datum object also uses these values + out.b = b; + out.es = es; + out.ep2 = ep2; + return out; +} -/** 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; diff --git a/lib/datumUtils.js b/lib/datumUtils.js new file mode 100644 index 0000000..d5a257e --- /dev/null +++ b/lib/datumUtils.js @@ -0,0 +1,247 @@ +'use strict'; +var PJD_3PARAM = 1; +var PJD_7PARAM = 2; +var HALF_PI = Math.PI/2; + +exports.compareDatums = function(source, dest) { + if (source.datum_type !== dest.datum_type) { + return false; // false, datums are not equal + } else if (source.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 (source.datum_type === PJD_3PARAM) { + return (this.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2]); + } else if (source.datum_type === PJD_7PARAM) { + return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2] && source.datum_params[3] === dest.datum_params[3] && source.datum_params[4] === dest.datum_params[4] && source.datum_params[5] === dest.datum_params[5] && source.datum_params[6] === dest.datum_params[6]); + } 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) + * + */ +exports.geodeticToGeocentric = function(p, es, a) { + var Longitude = p.x; + var Latitude = p.y; + var Height = p.z ? p.z : 0; //Z value not always supplied + + 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 = a / (Math.sqrt(1.0e0 - es * Sin2_Lat)); + return { + x: (Rn + Height) * Cos_Lat * Math.cos(Longitude), + y: (Rn + Height) * Cos_Lat * Math.sin(Longitude), + z: ((Rn * (1 - es)) + Height) * Sin_Lat + }; +}; // cs_geodetic_to_geocentric() + + +exports.geocentricToGeodetic = function(p, es, a, b) { + /* 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 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; + + P = Math.sqrt(X * X + Y * Y); + RR = Math.sqrt(X * X + Y * Y + Z * Z); + + /* special cases for latitude and longitude */ + if (P / a < genau) { + + /* special case, if P=0. (X=0., Y=0.) */ + 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 / a < genau) { + Latitude = HALF_PI; + Height = -b; + return { + x: p.x, + y: p.y, + z: p.z + }; + } + } 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 - es * (2.0 - es) * ST * ST); + CPHI0 = ST * (1.0 - 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 = a / Math.sqrt(1.0 - es * SPHI0 * SPHI0); + + /* ellipsoidal (geodetic) height */ + Height = P * CPHI0 + Z * SPHI0 - RN * (1.0 - es * SPHI0 * SPHI0); + + RK = 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)); + return { + x: Longitude, + y: Latitude, + z: Height + }; +}; // cs_geocentric_to_geodetic() + + +/****************************************************************/ +// pj_geocentic_to_wgs84( p ) +// p = point to transform in geocentric coordinates (x,y,z) + + +/** 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. +*/ +exports.geocentricToWgs84 = function(p, datum_type, datum_params) { + + if (datum_type === PJD_3PARAM) { + // if( x[io] === HUGE_VAL ) + // continue; + return { + x: p.x + datum_params[0], + y: p.y + datum_params[1], + z: p.z + datum_params[2], + }; + } else if (datum_type === PJD_7PARAM) { + var Dx_BF = datum_params[0]; + var Dy_BF = datum_params[1]; + var Dz_BF = datum_params[2]; + var Rx_BF = datum_params[3]; + var Ry_BF = datum_params[4]; + var Rz_BF = datum_params[5]; + var M_BF = datum_params[6]; + // if( x[io] === HUGE_VAL ) + // continue; + return { + x: M_BF * (p.x - Rz_BF * p.y + Ry_BF * p.z) + Dx_BF, + y: M_BF * (Rz_BF * p.x + p.y - Rx_BF * p.z) + Dy_BF, + z: M_BF * (-Ry_BF * p.x + Rx_BF * p.y + p.z) + Dz_BF + }; + } +}; // cs_geocentric_to_wgs84 + +/****************************************************************/ +// pj_geocentic_from_wgs84() +// coordinate system definition, +// point to transform in geocentric coordinates (x,y,z) +exports.geocentricFromWgs84 = function(p, datum_type, datum_params) { + + if (datum_type === PJD_3PARAM) { + //if( x[io] === HUGE_VAL ) + // continue; + return { + x: p.x - datum_params[0], + y: p.y - datum_params[1], + z: p.z - datum_params[2], + }; + + } else if (datum_type === PJD_7PARAM) { + var Dx_BF = datum_params[0]; + var Dy_BF = datum_params[1]; + var Dz_BF = datum_params[2]; + var Rx_BF = datum_params[3]; + var Ry_BF = datum_params[4]; + var Rz_BF = datum_params[5]; + var M_BF = 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; + + return { + x: x_tmp + Rz_BF * y_tmp - Ry_BF * z_tmp, + y: -Rz_BF * x_tmp + y_tmp + Rx_BF * z_tmp, + z: Ry_BF * x_tmp - Rx_BF * y_tmp + z_tmp + }; + } //cs_geocentric_from_wgs84() +}; diff --git a/lib/datum_transform.js b/lib/datum_transform.js index 65173a8..b5a8b3f 100644 --- a/lib/datum_transform.js +++ b/lib/datum_transform.js @@ -1,17 +1,13 @@ var PJD_3PARAM = 1; var PJD_7PARAM = 2; -var PJD_GRIDSHIFT = 3; var PJD_NODATUM = 5; // WGS84 or equivalent -var SRS_WGS84_SEMIMAJOR = 6378137; // only used in grid shift transforms -var SRS_WGS84_ESQUARED = 0.006694379990141316; //DGR: 2012-07-29 +var datum = require('./datumUtils'); +function checkParams(type) { + return (type === PJD_3PARAM || type === PJD_7PARAM); +} module.exports = function(source, dest, point) { - var wp, i, l; - - function checkParams(fallback) { - return (fallback === PJD_3PARAM || fallback === PJD_7PARAM); - } // Short cut if the datums are identical. - if (source.compare_datums(dest)) { + if (datum.compareDatums(source, dest)) { return point; // in this case, zero is sucess, // whereas cs_compare_datums returns 1 to indicate TRUE // confusing, should fix this @@ -22,78 +18,22 @@ module.exports = function(source, dest, point) { return point; } - //DGR: 2012-07-29 : add nadgrids support (begin) - var src_a = source.a; - var src_es = source.es; - - var dst_a = dest.a; - var dst_es = dest.es; - - var fallback = source.datum_type; // If this datum requires grid shifts, then apply it to geodetic coordinates. - if (fallback === PJD_GRIDSHIFT) { - if (this.apply_gridshift(source, 0, point) === 0) { - source.a = SRS_WGS84_SEMIMAJOR; - source.es = SRS_WGS84_ESQUARED; - } - else { - // try 3 or 7 params transformation or nothing ? - if (!source.datum_params) { - source.a = src_a; - source.es = source.es; - return point; - } - wp = 1; - for (i = 0, l = source.datum_params.length; i < l; i++) { - wp *= source.datum_params[i]; - } - if (wp === 0) { - source.a = src_a; - source.es = source.es; - return point; - } - if (source.datum_params.length > 3) { - fallback = PJD_7PARAM; - } - else { - fallback = PJD_3PARAM; - } - } - } - if (dest.datum_type === PJD_GRIDSHIFT) { - dest.a = SRS_WGS84_SEMIMAJOR; - dest.es = SRS_WGS84_ESQUARED; - } + // Do we need to go through geocentric coordinates? - if (source.es !== dest.es || source.a !== dest.a || checkParams(fallback) || checkParams(dest.datum_type)) { - //DGR: 2012-07-29 : add nadgrids support (end) - // Convert to geocentric coordinates. - source.geodetic_to_geocentric(point); - // CHECK_RETURN; - // Convert between datums - if (checkParams(source.datum_type)) { - source.geocentric_to_wgs84(point); - // CHECK_RETURN; - } - if (checkParams(dest.datum_type)) { - dest.geocentric_from_wgs84(point); - // CHECK_RETURN; - } - // Convert back to geodetic coordinates - dest.geocentric_to_geodetic(point); - // CHECK_RETURN; - } - // Apply grid shift to destination if required - if (dest.datum_type === PJD_GRIDSHIFT) { - this.apply_gridshift(dest, 1, point); - // CHECK_RETURN; + if (source.es === dest.es && source.a === dest.a && !checkParams(source.datum_type) && !checkParams(dest.datum_type)) { + return point; } - source.a = src_a; - source.es = src_es; - dest.a = dst_a; - dest.es = dst_es; + // Convert to geocentric coordinates. + point = datum.geodeticToGeocentric(point, source.es, source.a); + // Convert between datums + if (checkParams(source.datum_type)) { + point = datum.geocentricToWgs84(point, source.datum_type, source.datum_params); + } + if (checkParams(dest.datum_type)) { + point = datum.geocentricFromWgs84(point, dest.datum_type, dest.datum_params); + } + return datum.geocentricToGeodetic(point, dest.es, dest.a, dest.b); - return point; }; - diff --git a/lib/deriveConstants.js b/lib/deriveConstants.js index b8a80a2..38faac0 100644 --- a/lib/deriveConstants.js +++ b/lib/deriveConstants.js @@ -1,56 +1,53 @@ -var Datum = require('./constants/Datum'); -var Ellipsoid = require('./constants/Ellipsoid'); -var extend = require('./extend'); -var datum = require('./datum'); -var EPSLN = 1.0e-10; // ellipoid pj_set_ell.c var SIXTH = 0.1666666666666666667; /* 1/6 */ var RA4 = 0.04722222222222222222; /* 17/360 */ var RA6 = 0.02215608465608465608; -module.exports = function(json) { - // DGR 2011-03-20 : nagrids -> nadgrids - if (json.datumCode && json.datumCode !== 'none') { - var datumDef = Datum[json.datumCode]; - if (datumDef) { - json.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null; - json.ellps = datumDef.ellipse; - json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode; +var EPSLN = 1.0e-10; +var Ellipsoid = require('./constants/Ellipsoid'); + +exports.eccentricity = function(a, b, rf, R_A) { + var a2 = a * a; // used in geocentric + var b2 = b * b; // used in geocentric + var es = (a2 - b2) / a2; // e ^ 2 + var e = 0; + if (R_A) { + a *= 1 - es * (SIXTH + es * (RA4 + es * RA6)); + a2 = a * a; + es = 0; + } else { + e = Math.sqrt(es); // eccentricity + } + var ep2 = (a2 - b2) / b2; // used in geocentric + return { + es: es, + e: e, + ep2: ep2 + }; +}; +exports.sphere = function (a, b, rf, ellps, sphere) { + if (!a) { // do we have an ellipsoid? + var ellipse = Ellipsoid[ellps]; + if (!ellipse) { + ellipse = Ellipsoid.WGS84; } - } - if (!json.a) { // do we have an ellipsoid? - var ellipse = Ellipsoid[json.ellps] ? Ellipsoid[json.ellps] : Ellipsoid.WGS84; - extend(json, ellipse); - } - if (json.rf && !json.b) { - json.b = (1.0 - 1.0 / json.rf) * json.a; - } - if (json.rf === 0 || Math.abs(json.a - json.b) < EPSLN) { - json.sphere = true; - json.b = json.a; - } - json.a2 = json.a * json.a; // used in geocentric - json.b2 = json.b * json.b; // used in geocentric - json.es = (json.a2 - json.b2) / json.a2; // e ^ 2 - json.e = Math.sqrt(json.es); // eccentricity - if (json.R_A) { - json.a *= 1 - json.es * (SIXTH + json.es * (RA4 + json.es * RA6)); - json.a2 = json.a * json.a; - json.b2 = json.b * json.b; - json.es = 0; - } - json.ep2 = (json.a2 - json.b2) / json.b2; // used in geocentric - if (!json.k0) { - json.k0 = 1.0; //default value - } - //DGR 2010-11-12: axis - if (!json.axis) { - json.axis = "enu"; + a = ellipse.a; + b = ellipse.b; + rf = ellipse.rf; } - if (!json.datum) { - json.datum = datum(json); - } - return json; + if (rf && !b) { + b = (1.0 - 1.0 / rf) * a; + } + if (rf === 0 || Math.abs(a - b) < EPSLN) { + sphere = true; + b = a; + } + return { + a: a, + b: b, + rf: rf, + sphere: sphere + }; }; diff --git a/lib/index.js b/lib/index.js index df6c187..a912bc0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,10 +3,10 @@ proj4.defaultDatum = 'WGS84'; //default datum proj4.Proj = require('./Proj'); proj4.WGS84 = new proj4.Proj('WGS84'); proj4.Point = require('./Point'); -proj4.toPoint = require("./common/toPoint"); +proj4.toPoint = require('./common/toPoint'); proj4.defs = require('./defs'); proj4.transform = require('./transform'); proj4.mgrs = require('mgrs'); -proj4.version = require('../package.json').version; +proj4.version = require('./version'); require('./includedProjections')(proj4); -module.exports = proj4; \ No newline at end of file +module.exports = proj4; diff --git a/lib/parseCode.js b/lib/parseCode.js index 88480a3..87386d9 100644 --- a/lib/parseCode.js +++ b/lib/parseCode.js @@ -7,11 +7,12 @@ function testObj(code){ function testDef(code){ return code in defs; } +var codeWords = ['GEOGCS','GEOCCS','PROJCS','LOCAL_CS']; + function testWKT(code){ - var codeWords = ['GEOGCS','GEOCCS','PROJCS','LOCAL_CS']; - return codeWords.reduce(function(a,b){ - return a+1+code.indexOf(b); - },0); + return codeWords.some(function (word) { + return code.indexOf(word) > -1; + }); } function testProj(code){ return code[0] === '+'; @@ -22,10 +23,10 @@ function parse(code){ if (testDef(code)) { return defs[code]; } - else if (testWKT(code)) { + if (testWKT(code)) { return wkt(code); } - else if (testProj(code)) { + if (testProj(code)) { return projStr(code); } }else{ @@ -33,4 +34,4 @@ function parse(code){ } } -module.exports = parse; \ No newline at end of file +module.exports = parse; diff --git a/lib/projString.js b/lib/projString.js index 53326f2..7affc89 100644 --- a/lib/projString.js +++ b/lib/projString.js @@ -4,16 +4,16 @@ var units = require('./constants/units'); module.exports = function(defData) { var self = {}; - var paramObj = {}; - defData.split("+").map(function(v) { + var paramObj = defData.split('+').map(function(v) { return v.trim(); }).filter(function(a) { return a; - }).forEach(function(a) { - var split = a.split("="); + }).reduce(function(p, a) { + var split = a.split('='); split.push(true); - paramObj[split[0].toLowerCase()] = split[1]; - }); + p[split[0].toLowerCase()] = split[1]; + return p; + }, {}); var paramName, paramVal, paramOutname; var params = { proj: 'projName', diff --git a/lib/projections/tmerc.js b/lib/projections/tmerc.js index 10fd51e..2eb3617 100644 --- a/lib/projections/tmerc.js +++ b/lib/projections/tmerc.js @@ -1,20 +1,24 @@ -var e0fn = require('../common/e0fn'); -var e1fn = require('../common/e1fn'); -var e2fn = require('../common/e2fn'); -var e3fn = require('../common/e3fn'); -var mlfn = require('../common/mlfn'); +// Heavily based on this tmerc projection implementation +// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js + +var pj_enfn = require('../common/pj_enfn'); +var pj_mlfn = require('../common/pj_mlfn'); +var pj_inv_mlfn = require('../common/pj_inv_mlfn'); var adjust_lon = require('../common/adjust_lon'); -var HALF_PI = Math.PI/2; +var HALF_PI = Math.PI / 2; var EPSLN = 1.0e-10; var sign = require('../common/sign'); -var asinz = require('../common/asinz'); exports.init = function() { - this.e0 = e0fn(this.es); - this.e1 = e1fn(this.es); - this.e2 = e2fn(this.es); - this.e3 = e3fn(this.es); - this.ml0 = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0); + this.x0 = this.x0 !== undefined ? this.x0 : 0; + this.y0 = this.y0 !== undefined ? this.y0 : 0; + this.long0 = this.long0 !== undefined ? this.long0 : 0; + this.lat0 = this.lat0 !== undefined ? this.lat0 : 0; + + if (this.es) { + this.en = pj_enfn(this.es); + this.ml0 = pj_mlfn(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en); + } }; /** @@ -31,36 +35,65 @@ exports.forward = function(p) { var sin_phi = Math.sin(lat); var cos_phi = Math.cos(lat); - if (this.sphere) { + if (!this.es) { var b = cos_phi * Math.sin(delta_lon); - if ((Math.abs(Math.abs(b) - 1)) < 0.0000000001) { + + if ((Math.abs(Math.abs(b) - 1)) < EPSLN) { return (93); } else { - x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)); - con = Math.acos(cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - b * b)); + x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)) + this.x0; + y = cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - Math.pow(b, 2)); + b = Math.abs(y); + + if (b >= 1) { + if ((b - 1) > EPSLN) { + return (93); + } + else { + y = 0; + } + } + else { + y = Math.acos(y); + } + if (lat < 0) { - con = -con; + y = -y; } - y = this.a * this.k0 * (con - this.lat0); + + y = this.a * this.k0 * (y - this.lat0) + this.y0; } } else { var al = cos_phi * delta_lon; var als = Math.pow(al, 2); var c = this.ep2 * Math.pow(cos_phi, 2); - var tq = Math.tan(lat); + var cs = Math.pow(c, 2); + var tq = Math.abs(cos_phi) > EPSLN ? Math.tan(lat) : 0; var t = Math.pow(tq, 2); + var ts = Math.pow(t, 2); con = 1 - this.es * Math.pow(sin_phi, 2); - var n = this.a / Math.sqrt(con); - var ml = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, lat); + al = al / Math.sqrt(con); + var ml = pj_mlfn(lat, sin_phi, cos_phi, this.en); - x = this.k0 * n * al * (1 + als / 6 * (1 - t + c + als / 20 * (5 - 18 * t + Math.pow(t, 2) + 72 * c - 58 * this.ep2))) + this.x0; - y = this.k0 * (ml - this.ml0 + n * tq * (als * (0.5 + als / 24 * (5 - t + 9 * c + 4 * Math.pow(c, 2) + als / 30 * (61 - 58 * t + Math.pow(t, 2) + 600 * c - 330 * this.ep2))))) + this.y0; + x = this.a * (this.k0 * al * (1 + + als / 6 * (1 - t + c + + als / 20 * (5 - 18 * t + ts + 14 * c - 58 * t * c + + als / 42 * (61 + 179 * ts - ts * t - 479 * t))))) + + this.x0; + y = this.a * (this.k0 * (ml - this.ml0 + + sin_phi * delta_lon * al / 2 * (1 + + als / 12 * (5 - t + 9 * c + 4 * cs + + als / 30 * (61 + ts - 58 * t + 270 * c - 330 * t * c + + als / 56 * (1385 + 543 * ts - ts * t - 3111 * t)))))) + + this.y0; } + p.x = x; p.y = y; + return p; }; @@ -69,67 +102,66 @@ exports.forward = function(p) { */ exports.inverse = function(p) { var con, phi; - var delta_phi; - var i; - var max_iter = 6; var lat, lon; + var x = (p.x - this.x0) * (1 / this.a); + var y = (p.y - this.y0) * (1 / this.a); - if (this.sphere) { - var f = Math.exp(p.x / (this.a * this.k0)); + if (!this.es) { + var f = Math.exp(x / this.k0); var g = 0.5 * (f - 1 / f); - var temp = this.lat0 + p.y / (this.a * this.k0); + var temp = this.lat0 + y / this.k0; var h = Math.cos(temp); - con = Math.sqrt((1 - h * h) / (1 + g * g)); - lat = asinz(con); - if (temp < 0) { + con = Math.sqrt((1 - Math.pow(h, 2)) / (1 + Math.pow(g, 2))); + lat = Math.asin(con); + + if (y < 0) { lat = -lat; } + if ((g === 0) && (h === 0)) { - lon = this.long0; + lon = 0; } else { lon = adjust_lon(Math.atan2(g, h) + this.long0); } } else { // ellipsoidal form - var x = p.x - this.x0; - var y = p.y - this.y0; - - con = (this.ml0 + y / this.k0) / this.a; - phi = con; - for (i = 0; true; i++) { - delta_phi = ((con + this.e1 * Math.sin(2 * phi) - this.e2 * Math.sin(4 * phi) + this.e3 * Math.sin(6 * phi)) / this.e0) - phi; - phi += delta_phi; - if (Math.abs(delta_phi) <= EPSLN) { - break; - } - if (i >= max_iter) { - return (95); - } - } // for() + con = this.ml0 + y / this.k0; + phi = pj_inv_mlfn(con, this.es, this.en); + if (Math.abs(phi) < HALF_PI) { var sin_phi = Math.sin(phi); var cos_phi = Math.cos(phi); - var tan_phi = Math.tan(phi); + var tan_phi = Math.abs(cos_phi) > EPSLN ? Math.tan(phi) : 0; var c = this.ep2 * Math.pow(cos_phi, 2); var cs = Math.pow(c, 2); var t = Math.pow(tan_phi, 2); var ts = Math.pow(t, 2); con = 1 - this.es * Math.pow(sin_phi, 2); - var n = this.a / Math.sqrt(con); - var r = n * (1 - this.es) / con; - var d = x / (n * this.k0); + var d = x * Math.sqrt(con) / this.k0; var ds = Math.pow(d, 2); - lat = phi - (n * tan_phi * ds / r) * (0.5 - ds / 24 * (5 + 3 * t + 10 * c - 4 * cs - 9 * this.ep2 - ds / 30 * (61 + 90 * t + 298 * c + 45 * ts - 252 * this.ep2 - 3 * cs))); - lon = adjust_lon(this.long0 + (d * (1 - ds / 6 * (1 + 2 * t + c - ds / 20 * (5 - 2 * c + 28 * t - 3 * cs + 8 * this.ep2 + 24 * ts))) / cos_phi)); + con = con * tan_phi; + + lat = phi - (con * ds / (1 - this.es)) * 0.5 * (1 - + ds / 12 * (5 + 3 * t - 9 * c * t + c - 4 * cs - + ds / 30 * (61 + 90 * t - 252 * c * t + 45 * ts + 46 * c - + ds / 56 * (1385 + 3633 * t + 4095 * ts + 1574 * ts * t)))); + + lon = adjust_lon(this.long0 + (d * (1 - + ds / 6 * (1 + 2 * t + c - + ds / 20 * (5 + 28 * t + 24 * ts + 8 * c * t + 6 * c - + ds / 42 * (61 + 662 * t + 1320 * ts + 720 * ts * t)))) / cos_phi)); } else { lat = HALF_PI * sign(y); - lon = this.long0; + lon = 0; } } + p.x = lon; p.y = lat; + return p; }; + exports.names = ["Transverse_Mercator", "Transverse Mercator", "tmerc"]; diff --git a/lib/projections/utm.js b/lib/projections/utm.js index 18245cc..5f37300 100644 --- a/lib/projections/utm.js +++ b/lib/projections/utm.js @@ -1,12 +1,16 @@ -var D2R = 0.01745329251994329577; +var adjust_zone = require('../common/adjust_zone'); var tmerc = require('./tmerc'); + exports.dependsOn = 'tmerc'; + exports.init = function() { - if (!this.zone) { + var zone = adjust_zone(this.zone, this.long0); + if (!zone) { return; } + this.lat0 = 0; - this.long0 = ((6 * Math.abs(this.zone)) - 183) * D2R; + this.long0 = (zone + 0.5) * Math.PI / 30 - Math.PI; this.x0 = 500000; this.y0 = this.utmSouth ? 10000000 : 0; this.k0 = 0.9996; @@ -15,4 +19,5 @@ exports.init = function() { this.forward = tmerc.forward; this.inverse = tmerc.inverse; }; + exports.names = ["Universal Transverse Mercator System", "utm"]; diff --git a/lib/transform.js b/lib/transform.js index 2ed0fd6..aed86b9 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -6,36 +6,40 @@ var datum_transform = require('./datum_transform'); var adjust_axis = require('./adjust_axis'); var proj = require('./Proj'); var toPoint = require('./common/toPoint'); +function checkNotWGS(source, dest) { + return ((source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM) && dest.datumCode !== 'WGS84') || ((dest.datum.datum_type === PJD_3PARAM || dest.datum.datum_type === PJD_7PARAM) && source.datumCode !== 'WGS84'); +} module.exports = function transform(source, dest, point) { var wgs84; if (Array.isArray(point)) { point = toPoint(point); } - function checkNotWGS(source, dest) { - return ((source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM) && dest.datumCode !== "WGS84"); - } // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84 - if (source.datum && dest.datum && (checkNotWGS(source, dest) || checkNotWGS(dest, source))) { + if (source.datum && dest.datum && checkNotWGS(source, dest)) { wgs84 = new proj('WGS84'); - transform(source, wgs84, point); + point = transform(source, wgs84, point); source = wgs84; } // DGR, 2010/11/12 - if (source.axis !== "enu") { - adjust_axis(source, false, point); + if (source.axis !== 'enu') { + point = adjust_axis(source, false, point); } // Transform source points to long/lat, if they aren't already. - if (source.projName === "longlat") { - point.x *= D2R; // convert degrees to radians - point.y *= D2R; + if (source.projName === 'longlat') { + point = { + x: point.x * D2R, + y: point.y * D2R + }; } else { if (source.to_meter) { - point.x *= source.to_meter; - point.y *= source.to_meter; + point = { + x: point.x * source.to_meter, + y: point.y * source.to_meter + }; } - source.inverse(point); // Convert Cartesian to longlat + point = source.inverse(point); // Convert Cartesian to longlat } // Adjust for the prime meridian if necessary if (source.from_greenwich) { @@ -47,26 +51,32 @@ module.exports = function transform(source, dest, point) { // Adjust for the prime meridian if necessary if (dest.from_greenwich) { - point.x -= dest.from_greenwich; + point = { + x: point.x - dest.grom_greenwich, + y: point.y + }; } - if (dest.projName === "longlat") { + if (dest.projName === 'longlat') { // convert radians to decimal degrees - point.x *= R2D; - point.y *= R2D; - } - else { // else project - dest.forward(point); + point = { + x: point.x * R2D, + y: point.y * R2D + }; + } else { // else project + point = dest.forward(point); if (dest.to_meter) { - point.x /= dest.to_meter; - point.y /= dest.to_meter; + point = { + x: point.x / dest.to_meter, + y: point.y / dest.to_meter + }; } } // DGR, 2010/11/12 - if (dest.axis !== "enu") { - adjust_axis(dest, true, point); + if (dest.axis !== 'enu') { + return adjust_axis(dest, true, point); } return point; -}; \ No newline at end of file +}; diff --git a/lib/version-browser.js b/lib/version-browser.js new file mode 100644 index 0000000..8e946b2 --- /dev/null +++ b/lib/version-browser.js @@ -0,0 +1 @@ +module.exports = '2.3.16'; diff --git a/lib/version.js b/lib/version.js new file mode 100644 index 0000000..81f6e78 --- /dev/null +++ b/lib/version.js @@ -0,0 +1 @@ +module.exports = require('../package.json').version; diff --git a/package.json b/package.json index b0605f5..ea276ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proj4", - "version": "2.3.15-alpha", + "version": "2.3.16-alpha", "description": "Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.", "main": "lib/index.js", "directories": { @@ -16,15 +16,6 @@ }, "author": "", "license": "MIT", - "jam": { - "main": "dist/proj4.js", - "include": [ - "dist/proj4.js", - "README.md", - "AUTHORS", - "LICENSE.md" - ] - }, "devDependencies": { "grunt-cli": "~0.1.13", "grunt": "~0.4.2", @@ -40,7 +31,10 @@ "istanbul": "~0.2.4", "tin": "~0.4.0" }, + "browser": { + "./lib/version.js": "./lib/version-browser.js" + }, "dependencies": { "mgrs": "~0.0.2" } -} \ No newline at end of file +} diff --git a/publish.sh b/publish.sh index 8d9ef84..93d042f 100755 --- a/publish.sh +++ b/publish.sh @@ -5,6 +5,8 @@ VERSION=$(npm ls --json=true proj4js | grep version | awk '{ print $2}'| sed -e # Build git checkout -b build +echo "module.exports = '$VERSION';" > ./lib/version-browser.js +git add ./lib/version-browser.js -f node_modules/.bin/grunt git add dist -f git commit -m "build $VERSION" @@ -15,7 +17,6 @@ git push --tags [email protected]:proj4js/proj4js.git $VERSION # Publish npm publish -jam publish # Cleanup git checkout master diff --git a/test/test.js b/test/test.js index 45bfc32..0d8fa5b 100644 --- a/test/test.js +++ b/test/test.js @@ -25,17 +25,18 @@ function startTests(chai, proj4, testPoints) { var sweref99tm = '+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'; var rt90 = '+lon_0=15.808277777799999 +lat_0=0.0 +k=1.0 +x_0=1500000.0 +y_0=0.0 +proj=tmerc +ellps=bessel +units=m +towgs84=414.1,41.3,603.1,-0.855,2.141,-7.023,0 +no_defs'; var rslt = proj4(sweref99tm, rt90).forward([319180, 6399862]); - assert.closeTo(rslt[0], 1271137.927154, 0.000001); - assert.closeTo(rslt[1], 6404230.291456, 0.000001); + assert.closeTo(rslt[0], 1271137.9275601401, 0.000001); + assert.closeTo(rslt[1], 6404230.291448903, 0.000001); }); it('should work with a proj object', function() { var sweref99tm = proj4('+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'); var rt90 = proj4('+lon_0=15.808277777799999 +lat_0=0.0 +k=1.0 +x_0=1500000.0 +y_0=0.0 +proj=tmerc +ellps=bessel +units=m +towgs84=414.1,41.3,603.1,-0.855,2.141,-7.023,0 +no_defs'); var rslt = proj4(sweref99tm, rt90).forward([319180, 6399862]); - assert.closeTo(rslt[0], 1271137.927154, 0.000001); - assert.closeTo(rslt[1], 6404230.291456, 0.000001); + assert.closeTo(rslt[0], 1271137.9275601401, 0.000001); + assert.closeTo(rslt[1], 6404230.291448903, 0.000001); }); }); + describe('proj4', function() { describe('core', function() { testPoints.forEach(function(testPoint) { diff --git a/test/testData.js b/test/testData.js index cb9bd80..b3cd064 100644 --- a/test/testData.js +++ b/test/testData.js @@ -147,20 +147,20 @@ var testPoints = [ { code:'PROJCS["Beduaram / TM 13 NE",GEOGCS["Beduaram",DATUM["Beduaram",SPHEROID["Clarke 1880 (IGN)",6378249.2,293.4660212936269,AUTHORITY["EPSG","7011"]],TOWGS84[-106,-87,188,0,0,0,0],AUTHORITY["EPSG","6213"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4213"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",13],PARA [...] ll:[5, 25], - xy:[-308919.1462828873, 2788738.252386554], + xy:[-308919.1234711099, 2788738.255936392], acc:{ ll:5 } }, - { + { code:'PROJCS["Beduaram / TM 13 NE",GEOGCS["Beduaram",DATUM["D_Beduaram",SPHEROID["Clarke_1880_IGN",6378249.2,293.4660212936269]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",13],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]]', ll:[5, 25], - xy:[-308919.1462828873, 2788738.252386554], + xy:[-308919.1234711099, 2788738.255936392], acc:{ ll:5 } - } - ,{ + }, + { code:'PROJCS["S-JTSK (Ferro) / Krovak",GEOGCS["S-JTSK (Ferro)",DATUM["S_JTSK_Ferro",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],AUTHORITY["EPSG","6818"]],PRIMEM["Ferro",-17.66666666666667,AUTHORITY["EPSG","8909"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4818"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Krovak"],PARAMETER["latitude_of_center",49.5],PARAMETER["longitude_of_center",42.5],PARAMETER["azimuth",30 [...] ll:[17.323583231075897, 49.39440725405376], xy:[-544115.474379, -1144058.330762] @@ -308,6 +308,80 @@ var testPoints = [ code: 'EPSG:3857', ll: [180, 0], xy: [20037508.342789, 0] + }, + // these test cases are taken from mapshaper-proj and the test results match + { + code: '+proj=tmerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5', + ll: [2, 1], + xy: [222650.79679577847, 110642.2294119271] + }, + { + code: '+proj=tmerc +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5 +datum=none', + ll: [2, 1], + xy: [223413.46640632232, 111769.14504059685] + }, + { + code: '+proj=utm +zone=30 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5', + ll: [2, 1], + xy: [1057002.4052152266, 110955.14117382761] + }, + // these test cases are related to the original issue on GitHub + { + code: '+proj=utm +zone=33 +datum=WGS84 +units=m +no_defs', + ll: [2, 1], + xy: [-959006.3439168662, 113457.31706492987], + acc: { + ll: 5 + } + }, + { + code: '+proj=utm +zone=33 +datum=WGS84 +units=m +no_defs', + ll: [31, 70], + xy: [1104629.4280255223, 7845845.076400871], + acc: { + ll: 4 + } + }, + // these test cases are for Norway snow flake zones + { + code: '+proj=utm +zone=31 +datum=WGS84 +units=m +no_defs', + ll: [59.121778, 1.508527], + xy: [8055639.601582392, 297536.7150416747], + acc: { + ll: 0 + } + }, + { + code: '+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs', + ll: [59.121778, 1.508527], + xy: [6958363.797581035, 260155.3254079497], + acc: { + ll: 0 + } + }, + { + code: '+proj=utm +zone=33 +datum=WGS84 +units=m +no_defs', + ll: [59.121778, 1.508527], + xy: [5980907.454031456, 232674.60895515585], + acc: { + ll: 1 + } + }, + { + code: '+proj=utm +zone=34 +datum=WGS84 +units=m +no_defs', + ll: [79.070672, 20.520579], + xy: [7442887.111291251, 3910285.3071145327], + acc: { + ll: -1.5 + } + }, + { + code: '+proj=utm +zone=35 +datum=WGS84 +units=m +no_defs', + ll: [79.070672, 20.520579], + xy: [6555309.538050345, 3474309.0216152733], + acc: { + ll: -0.5 + } } ]; if(typeof module !== 'undefined'){ -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/proj4js.git _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

