source: trunk/lib/proj4js.js

Last change on this file was 2323, checked in by dgrichard, 11 years ago

Skip null grid to avoid rounding errors : we know consider the null grid as identity grid !

File size: 76.6 KB
Line 
1/*
2Author: Mike Adair madairATdmsolutions.ca
3 Richard Greenwood rich@greenwoodmap.com
4License: MIT as per: ../LICENSE
5
6$Id: Proj.js 2956 2007-07-09 12:17:52Z steven $
7*/
8
9/**
10 * Namespace: Proj4js
11 *
12 * Proj4js is a JavaScript library to transform point coordinates from one
13 * coordinate system to another, including datum transformations.
14 *
15 * This library is a port of both the Proj.4 and GCTCP C libraries to JavaScript.
16 * Enabling these transformations in the browser allows geographic data stored
17 * in different projections to be combined in browser-based web mapping
18 * applications.
19 *
20 * Proj4js must have access to coordinate system initialization strings (which
21 * are the same as for PROJ.4 command line). Thes can be included in your
22 * application using a <script> tag or Proj4js can load CS initialization
23 * strings from a local directory or a web service such as spatialreference.org.
24 *
25 * Similarly, Proj4js must have access to projection transform code. These can
26 * be included individually using a <script> tag in your page, built into a
27 * custom build of Proj4js or loaded dynamically at run-time. Using the
28 * -combined and -compressed versions of Proj4js includes all projection class
29 * code by default.
30 *
31 * Note that dynamic loading of defs and code happens ascynchrously, check the
32 * Proj.readyToUse flag before using the Proj object. If the defs and code
33 * required by your application are loaded through script tags, dynamic loading
34 * is not required and the Proj object will be readyToUse on return from the
35 * constructor.
36 *
37 * All coordinates are handled as points which have a .x and a .y property
38 * which will be modified in place.
39 *
40 * Override Proj4js.reportError for output of alerts and warnings.
41 *
42 * See http://trac.osgeo.org/proj4js/wiki/UserGuide for full details.
43*/
44
45/**
46 * Global namespace object for Proj4js library
47 */
48var Proj4js = {
49
50 /**
51 * Property: defaultDatum
52 * The datum to use when no others a specified
53 */
54 defaultDatum: 'WGS84', //default datum
55
56 /**
57 * Method: transform(source, dest, point)
58 * Transform a point coordinate from one map projection to another. This is
59 * really the only public method you should need to use.
60 *
61 * Parameters:
62 * source - {Proj4js.Proj} source map projection for the transformation
63 * dest - {Proj4js.Proj} destination map projection for the transformation
64 * point - {Object} point to transform, may be geodetic (long, lat) or
65 * projected Cartesian (x,y), but should always have x,y properties.
66 */
67 transform: function(source, dest, point) {
68 if (!source.readyToUse) {
69 this.reportError("Proj4js initialization for:"+source.srsCode+" not yet complete");
70 return point;
71 }
72 if (!dest.readyToUse) {
73 this.reportError("Proj4js initialization for:"+dest.srsCode+" not yet complete");
74 return point;
75 }
76
77 // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84
78 if (source.datum && dest.datum && (
79 ((source.datum.datum_type == Proj4js.common.PJD_3PARAM || source.datum.datum_type == Proj4js.common.PJD_7PARAM) && dest.datumCode != "WGS84") ||
80 ((dest.datum.datum_type == Proj4js.common.PJD_3PARAM || dest.datum.datum_type == Proj4js.common.PJD_7PARAM) && source.datumCode != "WGS84"))) {
81 var wgs84 = Proj4js.WGS84;
82 this.transform(source, wgs84, point);
83 source = wgs84;
84 }
85
86 // DGR, 2010/11/12
87 if (source.axis!="enu") {
88 this.adjust_axis(source,false,point);
89 }
90
91 // Transform source points to long/lat, if they aren't already.
92 if (source.projName=="longlat") {
93 point.x *= Proj4js.common.D2R; // convert degrees to radians
94 point.y *= Proj4js.common.D2R;
95 } else {
96 if (source.to_meter) {
97 point.x *= source.to_meter;
98 point.y *= source.to_meter;
99 }
100 source.inverse(point); // Convert Cartesian to longlat
101 }
102
103 // Adjust for the prime meridian if necessary
104 if (source.from_greenwich) {
105 point.x += source.from_greenwich;
106 }
107
108 // Convert datums if needed, and if possible.
109 point = this.datum_transform( source.datum, dest.datum, point );
110
111 // Adjust for the prime meridian if necessary
112 if (dest.from_greenwich) {
113 point.x -= dest.from_greenwich;
114 }
115
116 if (dest.projName=="longlat") {
117 // convert radians to decimal degrees
118 point.x *= Proj4js.common.R2D;
119 point.y *= Proj4js.common.R2D;
120 } else { // else project
121 dest.forward(point);
122 if (dest.to_meter) {
123 point.x /= dest.to_meter;
124 point.y /= dest.to_meter;
125 }
126 }
127
128 // DGR, 2010/11/12
129 if (dest.axis!="enu") {
130 this.adjust_axis(dest,true,point);
131 }
132
133 return point;
134 }, // transform()
135
136 /** datum_transform()
137 source coordinate system definition,
138 destination coordinate system definition,
139 point to transform in geodetic coordinates (long, lat, height)
140 */
141 datum_transform : function( source, dest, point ) {
142
143 // Short cut if the datums are identical.
144 if( source.compare_datums( dest ) ) {
145 return point; // in this case, zero is sucess,
146 // whereas cs_compare_datums returns 1 to indicate TRUE
147 // confusing, should fix this
148 }
149
150 // Explicitly skip datum transform by setting 'datum=none' as parameter for either source or dest
151 if( source.datum_type == Proj4js.common.PJD_NODATUM
152 || dest.datum_type == Proj4js.common.PJD_NODATUM) {
153 return point;
154 }
155
156 //DGR: 2012-07-29 : add nadgrids support (begin)
157 var src_a = source.a;
158 var src_es = source.es;
159
160 var dst_a = dest.a;
161 var dst_es = dest.es;
162
163 var fallback= source.datum_type;
164 // If this datum requires grid shifts, then apply it to geodetic coordinates.
165 if( fallback == Proj4js.common.PJD_GRIDSHIFT )
166 {
167 if (this.apply_gridshift( source, 0, point )==0) {
168 source.a = Proj4js.common.SRS_WGS84_SEMIMAJOR;
169 source.es = Proj4js.common.SRS_WGS84_ESQUARED;
170 } else {
171
172 // try 3 or 7 params transformation or nothing ?
173 if (!source.datum_params) {
174 source.a = src_a;
175 source.es = source.es;
176 return point;
177 }
178 var wp= 1.0;
179 for (var i= 0, l= source.datum_params.length; i<l; i++) {
180 wp*= source.datum_params[i];
181 }
182 if (wp==0.0) {
183 source.a = src_a;
184 source.es = source.es;
185 return point;
186 }
187 fallback= source.datum_params.length>3?
188 Proj4js.common.PJD_7PARAM
189 : Proj4js.common.PJD_3PARAM;
190 // CHECK_RETURN;
191 }
192 }
193
194 if( dest.datum_type == Proj4js.common.PJD_GRIDSHIFT )
195 {
196 dest.a = Proj4js.common.SRS_WGS84_SEMIMAJOR;
197 dest.es = Proj4js.common.SRS_WGS84_ESQUARED;
198 }
199 // Do we need to go through geocentric coordinates?
200 if (source.es != dest.es || source.a != dest.a
201 || fallback == Proj4js.common.PJD_3PARAM
202 || fallback == Proj4js.common.PJD_7PARAM
203 || dest.datum_type == Proj4js.common.PJD_3PARAM
204 || dest.datum_type == Proj4js.common.PJD_7PARAM)
205 {
206 //DGR: 2012-07-29 : add nadgrids support (end)
207
208 // Convert to geocentric coordinates.
209 source.geodetic_to_geocentric( point );
210 // CHECK_RETURN;
211
212 // Convert between datums
213 if( source.datum_type == Proj4js.common.PJD_3PARAM || source.datum_type == Proj4js.common.PJD_7PARAM ) {
214 source.geocentric_to_wgs84(point);
215 // CHECK_RETURN;
216 }
217
218 if( dest.datum_type == Proj4js.common.PJD_3PARAM || dest.datum_type == Proj4js.common.PJD_7PARAM ) {
219 dest.geocentric_from_wgs84(point);
220 // CHECK_RETURN;
221 }
222
223 // Convert back to geodetic coordinates
224 dest.geocentric_to_geodetic( point );
225 // CHECK_RETURN;
226 }
227
228 // Apply grid shift to destination if required
229 if( dest.datum_type == Proj4js.common.PJD_GRIDSHIFT )
230 {
231 this.apply_gridshift( dest, 1, point);
232 // CHECK_RETURN;
233 }
234
235 source.a = src_a;
236 source.es = src_es;
237 dest.a = dst_a;
238 dest.es = dst_es;
239
240 return point;
241 }, // cs_datum_transform
242
243 /**
244 * This is the real workhorse, given a gridlist
245 * DGR: 2012-07-29 addition based on proj4 trunk
246 */
247 apply_gridshift : function(srs,inverse,point) {
248 if (srs.grids==null || srs.grids.length==0) {
249 return -38;
250 }
251 var input= {"x":point.x, "y":point.y};
252 var output= {"x":Number.NaN, "y":Number.NaN};
253 /* keep trying till we find a table that works */
254 var onlyMandatoryGrids= false;
255 for (var i= 0, l= srs.grids.length; i<l; i++) {
256 var gi= srs.grids[i];
257 onlyMandatoryGrids= gi.mandatory;
258 var ct= gi.grid;
259 if (ct==null) {
260 if (gi.mandatory) {
261 this.reportError("unable to find '"+gi.name+"' grid.");
262 return -48;
263 }
264 continue;//optional grid
265 }
266 /* skip tables that don't match our point at all. */
267 var epsilon= (Math.abs(ct.del[1])+Math.abs(ct.del[0]))/10000.0;
268 if( ct.ll[1]-epsilon>input.y || ct.ll[0]-epsilon>input.x ||
269 ct.ll[1]+(ct.lim[1]-1)*ct.del[1]+epsilon<input.y ||
270 ct.ll[0]+(ct.lim[0]-1)*ct.del[0]+epsilon<input.x ) {
271 continue;
272 }
273 /* If we have child nodes, check to see if any of them apply. */
274 /* TODO : only plain grid has been implemented ... */
275 /* we found a more refined child node to use */
276 /* load the grid shift info if we don't have it. */
277 /* TODO : Proj4js.grids pre-loaded (as they can be huge ...) */
278 /* skip numerical computing error when "null" grid (identity grid): */
279 if (gi.name=="null") {
280 output.x= input.x;
281 output.y= input.y;
282 } else {
283 output= Proj4js.common.nad_cvt(input, inverse, ct);
284 }
285 if (!isNaN(output.x)) {
286 break;
287 }
288 }
289 if (isNaN(output.x)) {
290 if (!onlyMandatoryGrids) {
291 this.reportError("failed to find a grid shift table for location '"+
292 input.x*Proj4js.common.R2D+" "+input.y*Proj4js.common.R2D+
293 " tried: '"+srs.nadgrids+"'");
294 return -48;
295 }
296 return -1;//FIXME: no shift applied ...
297 }
298 point.x= output.x;
299 point.y= output.y;
300 return 0;
301 },
302
303 /**
304 * Function: adjust_axis
305 * Normalize or de-normalized the x/y/z axes. The normal form is "enu"
306 * (easting, northing, up).
307 * Parameters:
308 * crs {Proj4js.Proj} the coordinate reference system
309 * denorm {Boolean} when false, normalize
310 * point {Object} the coordinates to adjust
311 */
312 adjust_axis: function(crs, denorm, point) {
313 var xin= point.x, yin= point.y, zin= point.z || 0.0;
314 var v, t;
315 for (var i= 0; i<3; i++) {
316 if (denorm && i==2 && point.z===undefined) { continue; }
317 if (i==0) { v= xin; t= 'x'; }
318 else if (i==1) { v= yin; t= 'y'; }
319 else { v= zin; t= 'z'; }
320 switch(crs.axis[i]) {
321 case 'e':
322 point[t]= v;
323 break;
324 case 'w':
325 point[t]= -v;
326 break;
327 case 'n':
328 point[t]= v;
329 break;
330 case 's':
331 point[t]= -v;
332 break;
333 case 'u':
334 if (point[t]!==undefined) { point.z= v; }
335 break;
336 case 'd':
337 if (point[t]!==undefined) { point.z= -v; }
338 break;
339 default :
340 alert("ERROR: unknow axis ("+crs.axis[i]+") - check definition of "+crs.projName);
341 return null;
342 }
343 }
344 return point;
345 },
346
347 /**
348 * Function: reportError
349 * An internal method to report errors back to user.
350 * Override this in applications to report error messages or throw exceptions.
351 */
352 reportError: function(msg) {
353 //console.log(msg);
354 },
355
356/**
357 *
358 * Title: Private Methods
359 * The following properties and methods are intended for internal use only.
360 *
361 * This is a minimal implementation of JavaScript inheritance methods so that
362 * Proj4js can be used as a stand-alone library.
363 * These are copies of the equivalent OpenLayers methods at v2.7
364 */
365
366/**
367 * Function: extend
368 * Copy all properties of a source object to a destination object. Modifies
369 * the passed in destination object. Any properties on the source object
370 * that are set to undefined will not be (re)set on the destination object.
371 *
372 * Parameters:
373 * destination - {Object} The object that will be modified
374 * source - {Object} The object with properties to be set on the destination
375 *
376 * Returns:
377 * {Object} The destination object.
378 */
379 extend: function(destination, source) {
380 destination = destination || {};
381 if(source) {
382 for(var property in source) {
383 var value = source[property];
384 if(value !== undefined) {
385 destination[property] = value;
386 }
387 }
388 }
389 return destination;
390 },
391
392/**
393 * Constructor: Class
394 * Base class used to construct all other classes. Includes support for
395 * multiple inheritance.
396 *
397 */
398 Class: function() {
399 var Class = function() {
400 this.initialize.apply(this, arguments);
401 };
402
403 var extended = {};
404 var parent;
405 for(var i=0; i<arguments.length; ++i) {
406 if(typeof arguments[i] == "function") {
407 // get the prototype of the superclass
408 parent = arguments[i].prototype;
409 } else {
410 // in this case we're extending with the prototype
411 parent = arguments[i];
412 }
413 Proj4js.extend(extended, parent);
414 }
415 Class.prototype = extended;
416
417 return Class;
418 },
419
420 /**
421 * Function: bind
422 * Bind a function to an object. Method to easily create closures with
423 * 'this' altered.
424 *
425 * Parameters:
426 * func - {Function} Input function.
427 * object - {Object} The object to bind to the input function (as this).
428 *
429 * Returns:
430 * {Function} A closure with 'this' set to the passed in object.
431 */
432 bind: function(func, object) {
433 // create a reference to all arguments past the second one
434 var args = Array.prototype.slice.apply(arguments, [2]);
435 return function() {
436 // Push on any additional arguments from the actual function call.
437 // These will come after those sent to the bind call.
438 var newArgs = args.concat(
439 Array.prototype.slice.apply(arguments, [0])
440 );
441 return func.apply(object, newArgs);
442 };
443 },
444
445/**
446 * The following properties and methods handle dynamic loading of JSON objects.
447 */
448
449 /**
450 * Property: scriptName
451 * {String} The filename of this script without any path.
452 */
453 scriptName: "proj4js.js",
454
455 /**
456 * Property: defsLookupService
457 * AJAX service to retreive projection definition parameters from
458 */
459 defsLookupService: 'http://spatialreference.org/ref',
460
461 /**
462 * Property: libPath
463 * internal: http server path to library code.
464 */
465 libPath: null,
466
467 /**
468 * Function: getScriptLocation
469 * Return the path to this script.
470 *
471 * Returns:
472 * Path to this script
473 */
474 getScriptLocation: function () {
475 if (this.libPath) return this.libPath;
476 var scriptName = this.scriptName;
477 var scriptNameLen = scriptName.length;
478
479 var scripts = document.getElementsByTagName('script');
480 for (var i = 0; i < scripts.length; i++) {
481 var src = scripts[i].getAttribute('src');
482 if (src) {
483 var index = src.lastIndexOf(scriptName);
484 // is it found, at the end of the URL?
485 if ((index > -1) && (index + scriptNameLen == src.length)) {
486 this.libPath = src.slice(0, -scriptNameLen);
487 break;
488 }
489 }
490 }
491 return this.libPath||"";
492 },
493
494 /**
495 * Function: loadScript
496 * Load a JS file from a URL into a <script> tag in the page.
497 *
498 * Parameters:
499 * url - {String} The URL containing the script to load
500 * onload - {Function} A method to be executed when the script loads successfully
501 * onfail - {Function} A method to be executed when there is an error loading the script
502 * loadCheck - {Function} A boolean method that checks to see if the script
503 * has loaded. Typically this just checks for the existance of
504 * an object in the file just loaded.
505 */
506 loadScript: function(url, onload, onfail, loadCheck) {
507 var script = document.createElement('script');
508 script.defer = false;
509 script.type = "text/javascript";
510 script.id = url;
511 script.onload = onload;
512 script.onerror = onfail;
513 script.loadCheck = loadCheck;
514 if (/MSIE/.test(navigator.userAgent)) {
515 script.onreadystatechange = this.checkReadyState;
516 }
517 document.getElementsByTagName('head')[0].appendChild(script);
518 script.src = url;
519 },
520
521 /**
522 * Function: checkReadyState
523 * IE workaround since there is no onerror handler. Calls the user defined
524 * loadCheck method to determine if the script is loaded.
525 *
526 */
527 checkReadyState: function() {
528 if (this.readyState == 'loaded') {
529 if (!this.loadCheck()) {
530 this.onerror();
531 } else {
532 this.onload();
533 }
534 }
535 }
536};
537
538/**
539 * Class: Proj4js.Proj
540 *
541 * Proj objects provide transformation methods for point coordinates
542 * between geodetic latitude/longitude and a projected coordinate system.
543 * once they have been initialized with a projection code.
544 *
545 * Initialization of Proj objects is with a projection code, usually EPSG codes,
546 * which is the key that will be used with the Proj4js.defs array.
547 *
548 * The code passed in will be stripped of colons and converted to uppercase
549 * to locate projection definition files.
550 *
551 * A projection object has properties for units and title strings.
552 */
553Proj4js.Proj = Proj4js.Class({
554
555 /**
556 * Property: readyToUse
557 * Flag to indicate if initialization is complete for this Proj object
558 */
559 readyToUse: false,
560
561 /**
562 * Property: title
563 * The title to describe the projection
564 */
565 title: null,
566
567 /**
568 * Property: projName
569 * The projection class for this projection, e.g. lcc (lambert conformal conic,
570 * or merc for mercator). These are exactly equivalent to their Proj4
571 * counterparts.
572 */
573 projName: null,
574 /**
575 * Property: units
576 * The units of the projection. Values include 'm' and 'degrees'
577 */
578 units: null,
579 /**
580 * Property: datum
581 * The datum specified for the projection
582 */
583 datum: null,
584 /**
585 * Property: x0
586 * The x coordinate origin
587 */
588 x0: 0,
589 /**
590 * Property: y0
591 * The y coordinate origin
592 */
593 y0: 0,
594 /**
595 * Property: localCS
596 * Flag to indicate if the projection is a local one in which no transforms
597 * are required.
598 */
599 localCS: false,
600
601 /**
602 * Property: queue
603 * Buffer (FIFO) to hold callbacks waiting to be called when projection loaded.
604 */
605 queue: null,
606
607 /**
608 * Constructor: initialize
609 * Constructor for Proj4js.Proj objects
610 *
611 * Parameters:
612 * srsCode - a code for map projection definition parameters. These are usually
613 * (but not always) EPSG codes.
614 */
615 initialize: function(srsCode, callback) {
616 this.srsCodeInput = srsCode;
617
618 //Register callbacks prior to attempting to process definition
619 this.queue = [];
620 if( callback ){
621 this.queue.push( callback );
622 }
623
624 //check to see if this is a WKT string
625 if ((srsCode.indexOf('GEOGCS') >= 0) ||
626 (srsCode.indexOf('GEOCCS') >= 0) ||
627 (srsCode.indexOf('PROJCS') >= 0) ||
628 (srsCode.indexOf('LOCAL_CS') >= 0)) {
629 this.parseWKT(srsCode);
630 this.deriveConstants();
631 this.loadProjCode(this.projName);
632 return;
633 }
634
635 // DGR 2008-08-03 : support urn and url
636 if (srsCode.indexOf('urn:') == 0) {
637 //urn:ORIGINATOR:def:crs:CODESPACE:VERSION:ID
638 var urn = srsCode.split(':');
639 if ((urn[1] == 'ogc' || urn[1] =='x-ogc') &&
640 (urn[2] =='def') &&
641 (urn[3] =='crs')) {
642 srsCode = urn[4]+':'+urn[urn.length-1];
643 }
644 } else if (srsCode.indexOf('http://') == 0) {
645 //url#ID
646 var url = srsCode.split('#');
647 if (url[0].match(/epsg.org/)) {
648 // http://www.epsg.org/#
649 srsCode = 'EPSG:'+url[1];
650 } else if (url[0].match(/RIG.xml/)) {
651 //http://librairies.ign.fr/geoportail/resources/RIG.xml#
652 //http://interop.ign.fr/registers/ign/RIG.xml#
653 srsCode = 'IGNF:'+url[1];
654 } else if (url[0].indexOf('/def/crs/')!=-1) {
655 // http://www.opengis.net/def/crs/EPSG/0/code
656 url= srsCode.split('/');
657 srsCode = url.pop();//code
658 url.pop();//version FIXME
659 srsCode = url.pop()+':'+srsCode;//authority
660 }
661 }
662 this.srsCode = srsCode.toUpperCase();
663 if (this.srsCode.indexOf("EPSG") == 0) {
664 this.srsCode = this.srsCode;
665 this.srsAuth = 'epsg';
666 this.srsProjNumber = this.srsCode.substring(5);
667 // DGR 2007-11-20 : authority IGNF
668 } else if (this.srsCode.indexOf("IGNF") == 0) {
669 this.srsCode = this.srsCode;
670 this.srsAuth = 'IGNF';
671 this.srsProjNumber = this.srsCode.substring(5);
672 // DGR 2008-06-19 : pseudo-authority CRS for WMS
673 } else if (this.srsCode.indexOf("CRS") == 0) {
674 this.srsCode = this.srsCode;
675 this.srsAuth = 'CRS';
676 this.srsProjNumber = this.srsCode.substring(4);
677 } else {
678 this.srsAuth = '';
679 this.srsProjNumber = this.srsCode;
680 }
681
682 this.loadProjDefinition();
683 },
684
685/**
686 * Function: loadProjDefinition
687 * Loads the coordinate system initialization string if required.
688 * Note that dynamic loading happens asynchronously so an application must
689 * wait for the readyToUse property is set to true.
690 * To prevent dynamic loading, include the defs through a script tag in
691 * your application.
692 *
693 */
694 loadProjDefinition: function() {
695 //check in memory
696 if (Proj4js.defs[this.srsCode]) {
697 this.defsLoaded();
698 return;
699 }
700
701 //else check for def on the server
702 var url = Proj4js.getScriptLocation() + 'defs/' + this.srsAuth.toUpperCase() + this.srsProjNumber + '.js';
703 Proj4js.loadScript(url,
704 Proj4js.bind(this.defsLoaded, this),
705 Proj4js.bind(this.loadFromService, this),
706 Proj4js.bind(this.checkDefsLoaded, this) );
707 },
708
709/**
710 * Function: loadFromService
711 * Creates the REST URL for loading the definition from a web service and
712 * loads it.
713 *
714 */
715 loadFromService: function() {
716 //else load from web service
717 var url = Proj4js.defsLookupService +'/' + this.srsAuth +'/'+ this.srsProjNumber + '/proj4js/';
718 Proj4js.loadScript(url,
719 Proj4js.bind(this.defsLoaded, this),
720 Proj4js.bind(this.defsFailed, this),
721 Proj4js.bind(this.checkDefsLoaded, this) );
722 },
723
724/**
725 * Function: defsLoaded
726 * Continues the Proj object initilization once the def file is loaded
727 *
728 */
729 defsLoaded: function() {
730 this.parseDefs();
731 this.loadProjCode(this.projName);
732 },
733
734/**
735 * Function: checkDefsLoaded
736 * This is the loadCheck method to see if the def object exists
737 *
738 */
739 checkDefsLoaded: function() {
740 if (Proj4js.defs[this.srsCode]) {
741 return true;
742 } else {
743 return false;
744 }
745 },
746
747 /**
748 * Function: defsFailed
749 * Report an error in loading the defs file, but continue on using WGS84
750 *
751 */
752 defsFailed: function() {
753 Proj4js.reportError('failed to load projection definition for: '+this.srsCode);
754 Proj4js.defs[this.srsCode] = Proj4js.defs['WGS84']; //set it to something so it can at least continue
755 this.defsLoaded();
756 },
757
758/**
759 * Function: loadProjCode
760 * Loads projection class code dynamically if required.
761 * Projection code may be included either through a script tag or in
762 * a built version of proj4js
763 *
764 */
765 loadProjCode: function(projName) {
766 if (Proj4js.Proj[projName]) {
767 this.initTransforms();
768 return;
769 }
770
771 //the URL for the projection code
772 var url = Proj4js.getScriptLocation() + 'projCode/' + projName + '.js';
773 Proj4js.loadScript(url,
774 Proj4js.bind(this.loadProjCodeSuccess, this, projName),
775 Proj4js.bind(this.loadProjCodeFailure, this, projName),
776 Proj4js.bind(this.checkCodeLoaded, this, projName) );
777 },
778
779 /**
780 * Function: loadProjCodeSuccess
781 * Loads any proj dependencies or continue on to final initialization.
782 *
783 */
784 loadProjCodeSuccess: function(projName) {
785 if (Proj4js.Proj[projName].dependsOn){
786 this.loadProjCode(Proj4js.Proj[projName].dependsOn);
787 } else {
788 this.initTransforms();
789 }
790 },
791
792 /**
793 * Function: defsFailed
794 * Report an error in loading the proj file. Initialization of the Proj
795 * object has failed and the readyToUse flag will never be set.
796 *
797 */
798 loadProjCodeFailure: function(projName) {
799 Proj4js.reportError("failed to find projection file for: " + projName);
800 //TBD initialize with identity transforms so proj will still work?
801 },
802
803/**
804 * Function: checkCodeLoaded
805 * This is the loadCheck method to see if the projection code is loaded
806 *
807 */
808 checkCodeLoaded: function(projName) {
809 if (Proj4js.Proj[projName]) {
810 return true;
811 } else {
812 return false;
813 }
814 },
815
816/**
817 * Function: initTransforms
818 * Finalize the initialization of the Proj object
819 *
820 */
821 initTransforms: function() {
822 Proj4js.extend(this, Proj4js.Proj[this.projName]);
823 this.init();
824 this.readyToUse = true;
825 if( this.queue ) {
826 var item;
827 while( (item = this.queue.shift()) ) {
828 item.call( this, this );
829 }
830 }
831 },
832
833/**
834 * Function: parseWKT
835 * Parses a WKT string to get initialization parameters
836 *
837 */
838 wktRE: /^(\w+)\[(.*)\]$/,
839 parseWKT: function(wkt) {
840 var wktMatch = wkt.match(this.wktRE);
841 if (!wktMatch) return;
842 var wktObject = wktMatch[1];
843 var wktContent = wktMatch[2];
844 var wktTemp = wktContent.split(",");
845 var wktName;
846 if (wktObject.toUpperCase() == "TOWGS84") {
847 wktName = wktObject; //no name supplied for the TOWGS84 array
848 } else {
849 wktName = wktTemp.shift();
850 }
851 wktName = wktName.replace(/^\"/,"");
852 wktName = wktName.replace(/\"$/,"");
853
854 /*
855 wktContent = wktTemp.join(",");
856 var wktArray = wktContent.split("],");
857 for (var i=0; i<wktArray.length-1; ++i) {
858 wktArray[i] += "]";
859 }
860 */
861
862 var wktArray = new Array();
863 var bkCount = 0;
864 var obj = "";
865 for (var i=0; i<wktTemp.length; ++i) {
866 var token = wktTemp[i];
867 for (var j=0; j<token.length; ++j) {
868 if (token.charAt(j) == "[") ++bkCount;
869 if (token.charAt(j) == "]") --bkCount;
870 }
871 obj += token;
872 if (bkCount === 0) {
873 wktArray.push(obj);
874 obj = "";
875 } else {
876 obj += ",";
877 }
878 }
879
880 //do something based on the type of the wktObject being parsed
881 //add in variations in the spelling as required
882 switch (wktObject) {
883 case 'LOCAL_CS':
884 this.projName = 'identity';
885 this.localCS = true;
886 this.srsCode = wktName;
887 break;
888 case 'GEOGCS':
889 this.projName = 'longlat';
890 this.geocsCode = wktName;
891 if (!this.srsCode) this.srsCode = wktName;
892 break;
893 case 'PROJCS':
894 this.srsCode = wktName;
895 break;
896 case 'GEOCCS':
897 break;
898 case 'PROJECTION':
899 this.projName = Proj4js.wktProjections[wktName];
900 break;
901 case 'DATUM':
902 this.datumName = wktName;
903 break;
904 case 'LOCAL_DATUM':
905 this.datumCode = 'none';
906 break;
907 case 'SPHEROID':
908 this.ellps = wktName;
909 this.a = parseFloat(wktArray.shift());
910 this.rf = parseFloat(wktArray.shift());
911 break;
912 case 'PRIMEM':
913 this.from_greenwich = parseFloat(wktArray.shift()); //to radians?
914 break;
915 case 'UNIT':
916 this.units = wktName;
917 this.unitsPerMeter = parseFloat(wktArray.shift());
918 break;
919 case 'PARAMETER':
920 var name = wktName.toLowerCase();
921 var value = parseFloat(wktArray.shift());
922 //there may be many variations on the wktName values, add in case
923 //statements as required
924 switch (name) {
925 case 'false_easting':
926 this.x0 = value;
927 break;
928 case 'false_northing':
929 this.y0 = value;
930 break;
931 case 'scale_factor':
932 this.k0 = value;
933 break;
934 case 'central_meridian':
935 this.long0 = value*Proj4js.common.D2R;
936 break;
937 case 'latitude_of_origin':
938 this.lat0 = value*Proj4js.common.D2R;
939 break;
940 case 'more_here':
941 break;
942 default:
943 break;
944 }
945 break;
946 case 'TOWGS84':
947 this.datum_params = wktArray;
948 break;
949 //DGR 2010-11-12: AXIS
950 case 'AXIS':
951 var name= wktName.toLowerCase();
952 var value= wktArray.shift();
953 switch (value) {
954 case 'EAST' : value= 'e'; break;
955 case 'WEST' : value= 'w'; break;
956 case 'NORTH': value= 'n'; break;
957 case 'SOUTH': value= 's'; break;
958 case 'UP' : value= 'u'; break;
959 case 'DOWN' : value= 'd'; break;
960 case 'OTHER':
961 default : value= ' '; break;//FIXME
962 }
963 if (!this.axis) { this.axis= "enu"; }
964 switch(name) {
965 case 'x': this.axis= value + this.axis.substr(1,2); break;
966 case 'y': this.axis= this.axis.substr(0,1) + value + this.axis.substr(2,1); break;
967 case 'z': this.axis= this.axis.substr(0,2) + value ; break;
968 default : break;
969 }
970 case 'MORE_HERE':
971 break;
972 default:
973 break;
974 }
975 for (var i=0; i<wktArray.length; ++i) {
976 this.parseWKT(wktArray[i]);
977 }
978 },
979
980/**
981 * Function: parseDefs
982 * Parses the PROJ.4 initialization string and sets the associated properties.
983 *
984 */
985 parseDefs: function() {
986 var re= new RegExp('(title|proj|units|datum|nadgrids|'+
987 'ellps|a|b|rf|'+
988 'lat_0|lat_1|lat_2|lat_ts|lon_0|lon_1|lon_2|alpha|lonc|'+
989 'x_0|y_0|k_0|k|r_a|zone|south|'+
990 'towgs84|to_meter|from_greenwich|pm|axis|czech|'+
991 'wktext|no_rot|no_off|no_defs)');
992 this.defData = Proj4js.defs[this.srsCode];
993 var paramName, paramVal;
994 if (!this.defData) {
995 return;
996 }
997 var paramArray=this.defData.split("+");
998
999 for (var prop=0; prop<paramArray.length; prop++) {
1000 var property = paramArray[prop].split("=");
1001 paramName = property[0].toLowerCase();
1002 paramVal = property[1];
1003
1004 switch (paramName.replace(/\s/gi,"")) { // trim out spaces
1005 case "": break; // throw away nameless parameter
1006 // DGR 2012-10-13 : + in title (EPSG:2056: CH1903+ / LV95)
1007 case "title": this.title = paramVal;
1008 while (!paramArray[prop+1].match(re)) {
1009 this.title+= '+'+paramArray[++prop];
1010 }
1011 break;
1012 case "proj": this.projName = paramVal.replace(/\s/gi,""); break;
1013 case "units": this.units = paramVal.replace(/\s/gi,""); break;
1014 case "datum": this.datumCode = paramVal.replace(/\s/gi,""); break;
1015 // DGR 2011-03-20 : nagrids -> nadgrids
1016 case "nadgrids": this.nadgrids = paramVal.replace(/\s/gi,""); break;// DGR 2012-07-29
1017 case "ellps": this.ellps = paramVal.replace(/\s/gi,""); break;
1018 case "a": this.a = parseFloat(paramVal); break; // semi-major radius
1019 case "b": this.b = parseFloat(paramVal); break; // semi-minor radius
1020 // DGR 2007-11-20
1021 case "rf": this.rf = parseFloat(paramVal); break; // inverse flattening rf= a/(a-b)
1022 case "lat_0": this.lat0 = paramVal*Proj4js.common.D2R; break; // phi0, central latitude
1023 case "lat_1": this.lat1 = paramVal*Proj4js.common.D2R; break; //standard parallel 1
1024 case "lat_2": this.lat2 = paramVal*Proj4js.common.D2R; break; //standard parallel 2
1025 case "lat_ts": this.lat_ts = paramVal*Proj4js.common.D2R; break; // used in merc and eqc
1026 case "lon_0": this.long0 = paramVal*Proj4js.common.D2R; break; // lam0, central longitude
1027 case "lon_1": this.long1 = paramVal*Proj4js.common.D2R; break;
1028 case "lon_2": this.long2 = paramVal*Proj4js.common.D2R; break;
1029 case "no_rot": this.no_rot = true; break;
1030 case "no_off": this.no_off = true; break;
1031 case "alpha": this.alpha = parseFloat(paramVal)*Proj4js.common.D2R; break; //for somerc projection
1032 case "lonc": this.longc = paramVal*Proj4js.common.D2R; break; //for somerc projection
1033 case "x_0": this.x0 = parseFloat(paramVal); break; // false easting
1034 case "y_0": this.y0 = parseFloat(paramVal); break; // false northing
1035 case "k_0": this.k0 = parseFloat(paramVal); break; // projection scale factor
1036 case "k": this.k0 = parseFloat(paramVal); break; // both forms returned
1037 case "r_a": this.R_A = true; break; // sphere--area of ellipsoid
1038 case "zone": this.zone = parseInt(paramVal,10); break; // UTM Zone
1039 case "south": this.utmSouth = true; break; // UTM north/south
1040 case "towgs84":this.datum_params = paramVal.split(","); break;
1041 case "to_meter": this.to_meter = parseFloat(paramVal); break; // cartesian scaling
1042 case "from_greenwich": this.from_greenwich = paramVal*Proj4js.common.D2R; break;
1043 case "czech": this.czech = true; break;
1044 // DGR 2008-07-09 : if pm is not a well-known prime meridian take
1045 // the value instead of 0.0, then convert to radians
1046 case "pm": paramVal = paramVal.replace(/\s/gi,"");
1047 this.from_greenwich = Proj4js.PrimeMeridian[paramVal] ?
1048 Proj4js.PrimeMeridian[paramVal] : parseFloat(paramVal);
1049 this.from_greenwich *= Proj4js.common.D2R;
1050 break;
1051 // DGR 2010-11-12: axis
1052 case "axis": paramVal = paramVal.replace(/\s/gi,"");
1053 var legalAxis= "ewnsud";
1054 if (paramVal.length==3 &&
1055 legalAxis.indexOf(paramVal.substr(0,1))!=-1 &&
1056 legalAxis.indexOf(paramVal.substr(1,1))!=-1 &&
1057 legalAxis.indexOf(paramVal.substr(2,1))!=-1) {
1058 this.axis= paramVal;
1059 } //FIXME: be silent ?
1060 break;
1061 case "wktext": break;//DGR 2012-07-29
1062 case "no_defs": break;
1063 default: //alert("Unrecognized parameter: " + paramName);
1064 } // switch()
1065 } // for paramArray
1066 this.deriveConstants();
1067 },
1068
1069/**
1070 * Function: deriveConstants
1071 * Sets several derived constant values and initialization of datum and ellipse
1072 * parameters.
1073 *
1074 */
1075 deriveConstants: function() {
1076 // DGR 2011-03-20 : nagrids -> nadgrids
1077 if (this.nadgrids && this.nadgrids.length==0) {
1078 this.nadgrids= null;
1079 }
1080 if (this.nadgrids) {
1081 this.grids= this.nadgrids.split(",");
1082 var g= null, l= this.grids.length;
1083 if (l>0) {
1084 for (var i= 0; i<l; i++) {
1085 g= this.grids[i];
1086 var fg= g.split("@");
1087 if (fg[fg.length-1]=="") {
1088 Proj4js.reportError("nadgrids syntax error '"+this.nadgrids+"' : empty grid found");
1089 continue;
1090 }
1091 this.grids[i]= {
1092 mandatory: fg.length==1,//@=> optional grid (no error if not found)
1093 name:fg[fg.length-1],
1094 grid: Proj4js.grids[fg[fg.length-1]]//FIXME: grids loading ...
1095 };
1096 if (this.grids[i].mandatory && !this.grids[i].grid) {
1097 Proj4js.reportError("Missing '"+this.grids[i].name+"'");
1098 }
1099 }
1100 }
1101 // DGR, 2011-03-20: grids is an array of objects that hold
1102 // the loaded grids, its name and the mandatory informations of it.
1103 }
1104 if (this.datumCode && this.datumCode != 'none') {
1105 var datumDef = Proj4js.Datum[this.datumCode];
1106 if (datumDef) {
1107 this.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null;
1108 this.ellps = datumDef.ellipse;
1109 this.datumName = datumDef.datumName ? datumDef.datumName : this.datumCode;
1110 }
1111 }
1112 if (!this.a) { // do we have an ellipsoid?
1113 var ellipse = Proj4js.Ellipsoid[this.ellps] ? Proj4js.Ellipsoid[this.ellps] : Proj4js.Ellipsoid['WGS84'];
1114 Proj4js.extend(this, ellipse);
1115 }
1116 if (this.rf && !this.b) this.b = (1.0 - 1.0/this.rf) * this.a;
1117 if (this.rf === 0 || Math.abs(this.a - this.b)<Proj4js.common.EPSLN) {
1118 this.sphere = true;
1119 this.b= this.a;
1120 }
1121 this.a2 = this.a * this.a; // used in geocentric
1122 this.b2 = this.b * this.b; // used in geocentric
1123 this.es = (this.a2-this.b2)/this.a2; // e ^ 2
1124 this.e = Math.sqrt(this.es); // eccentricity
1125 if (this.R_A) {
1126 this.a *= 1. - this.es * (Proj4js.common.SIXTH + this.es * (Proj4js.common.RA4 + this.es * Proj4js.common.RA6));
1127 this.a2 = this.a * this.a;
1128 this.b2 = this.b * this.b;
1129 this.es = 0.;
1130 }
1131 this.ep2=(this.a2-this.b2)/this.b2; // used in geocentric
1132 if (!this.k0) this.k0 = 1.0; //default value
1133 //DGR 2010-11-12: axis
1134 if (!this.axis) { this.axis= "enu"; }
1135
1136 this.datum = new Proj4js.datum(this);
1137 }
1138});
1139
1140Proj4js.Proj.longlat = {
1141 init: function() {
1142 //no-op for longlat
1143 },
1144 forward: function(pt) {
1145 //identity transform
1146 return pt;
1147 },
1148 inverse: function(pt) {
1149 //identity transform
1150 return pt;
1151 }
1152};
1153Proj4js.Proj.identity = Proj4js.Proj.longlat;
1154
1155/**
1156 Proj4js.defs is a collection of coordinate system definition objects in the
1157 PROJ.4 command line format.
1158 Generally a def is added by means of a separate .js file for example:
1159
1160 <SCRIPT type="text/javascript" src="defs/EPSG26912.js"></SCRIPT>
1161
1162 def is a CS definition in PROJ.4 WKT format, for example:
1163 +proj="tmerc" //longlat, etc.
1164 +a=majorRadius
1165 +b=minorRadius
1166 +lat0=somenumber
1167 +long=somenumber
1168*/
1169Proj4js.defs = {
1170 // These are so widely used, we'll go ahead and throw them in
1171 // without requiring a separate .js file
1172 'WGS84': "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees",
1173 'EPSG:4326': "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees",
1174 'EPSG:4269': "+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees",
1175 'EPSG:3857': "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"
1176};
1177Proj4js.defs['EPSG:3785'] = Proj4js.defs['EPSG:3857']; //maintain backward compat, official code is 3857
1178Proj4js.defs['GOOGLE'] = Proj4js.defs['EPSG:3857'];
1179Proj4js.defs['EPSG:900913'] = Proj4js.defs['EPSG:3857'];
1180Proj4js.defs['EPSG:102113'] = Proj4js.defs['EPSG:3857'];
1181
1182Proj4js.common = {
1183 PI : 3.141592653589793238, //Math.PI,
1184 HALF_PI : 1.570796326794896619, //Math.PI*0.5,
1185 TWO_PI : 6.283185307179586477, //Math.PI*2,
1186 FORTPI : 0.78539816339744833,
1187 R2D : 57.29577951308232088,
1188 D2R : 0.01745329251994329577,
1189 SEC_TO_RAD : 4.84813681109535993589914102357e-6, /* SEC_TO_RAD = Pi/180/3600 */
1190 EPSLN : 1.0e-10,
1191 MAX_ITER : 20,
1192 // following constants from geocent.c
1193 COS_67P5 : 0.38268343236508977, /* cosine of 67.5 degrees */
1194 AD_C : 1.0026000, /* Toms region 1 constant */
1195
1196 /* datum_type values */
1197 PJD_UNKNOWN : 0,
1198 PJD_3PARAM : 1,
1199 PJD_7PARAM : 2,
1200 PJD_GRIDSHIFT: 3,
1201 PJD_WGS84 : 4, // WGS84 or equivalent
1202 PJD_NODATUM : 5, // WGS84 or equivalent
1203 SRS_WGS84_SEMIMAJOR : 6378137.0, // only used in grid shift transforms
1204 SRS_WGS84_ESQUARED : 0.006694379990141316, //DGR: 2012-07-29
1205
1206 // ellipoid pj_set_ell.c
1207 SIXTH : .1666666666666666667, /* 1/6 */
1208 RA4 : .04722222222222222222, /* 17/360 */
1209 RA6 : .02215608465608465608, /* 67/3024 */
1210 RV4 : .06944444444444444444, /* 5/72 */
1211 RV6 : .04243827160493827160, /* 55/1296 */
1212
1213// Function to compute the constant small m which is the radius of
1214// a parallel of latitude, phi, divided by the semimajor axis.
1215// -----------------------------------------------------------------
1216 msfnz : function(eccent, sinphi, cosphi) {
1217 var con = eccent * sinphi;
1218 return cosphi/(Math.sqrt(1.0 - con * con));
1219 },
1220
1221// Function to compute the constant small t for use in the forward
1222// computations in the Lambert Conformal Conic and the Polar
1223// Stereographic projections.
1224// -----------------------------------------------------------------
1225 tsfnz : function(eccent, phi, sinphi) {
1226 var con = eccent * sinphi;
1227 var com = .5 * eccent;
1228 con = Math.pow(((1.0 - con) / (1.0 + con)), com);
1229 return (Math.tan(.5 * (this.HALF_PI - phi))/con);
1230 },
1231
1232// Function to compute the latitude angle, phi2, for the inverse of the
1233// Lambert Conformal Conic and Polar Stereographic projections.
1234// ----------------------------------------------------------------
1235 phi2z : function(eccent, ts) {
1236 var eccnth = .5 * eccent;
1237 var con, dphi;
1238 var phi = this.HALF_PI - 2 * Math.atan(ts);
1239 for (var i = 0; i <= 15; i++) {
1240 con = eccent * Math.sin(phi);
1241 dphi = this.HALF_PI - 2 * Math.atan(ts *(Math.pow(((1.0 - con)/(1.0 + con)),eccnth))) - phi;
1242 phi += dphi;
1243 if (Math.abs(dphi) <= .0000000001) return phi;
1244 }
1245 alert("phi2z has NoConvergence");
1246 return (-9999);
1247 },
1248
1249/* Function to compute constant small q which is the radius of a
1250 parallel of latitude, phi, divided by the semimajor axis.
1251------------------------------------------------------------*/
1252 qsfnz : function(eccent,sinphi) {
1253 var con;
1254 if (eccent > 1.0e-7) {
1255 con = eccent * sinphi;
1256 return (( 1.0- eccent * eccent) * (sinphi /(1.0 - con * con) - (.5/eccent)*Math.log((1.0 - con)/(1.0 + con))));
1257 } else {
1258 return(2.0 * sinphi);
1259 }
1260 },
1261
1262/* Function to compute the inverse of qsfnz
1263------------------------------------------------------------*/
1264 iqsfnz : function (eccent, q) {
1265 var temp = 1.0-(1.0-eccent*eccent)/(2.0*eccent)*Math.log((1-eccent)/(1+eccent));
1266 if (Math.abs(Math.abs(q)-temp)<1.0E-6) {
1267 if (q<0.0) {
1268 return (-1.0*Proj4js.common.HALF_PI);
1269 } else {
1270 return Proj4js.common.HALF_PI;
1271 }
1272 }
1273 //var phi = 0.5* q/(1-eccent*eccent);
1274 var phi = Math.asin(0.5*q);
1275 var dphi;
1276 var sin_phi;
1277 var cos_phi;
1278 var con;
1279 for (var i=0;i<30;i++){
1280 sin_phi = Math.sin(phi);
1281 cos_phi = Math.cos(phi);
1282 con = eccent*sin_phi;
1283 dphi=Math.pow(1.0-con*con,2.0)/(2.0*cos_phi)*(q/(1-eccent*eccent)-sin_phi/(1.0-con*con)+0.5/eccent*Math.log((1.0-con)/(1.0+con)));
1284 phi+=dphi;
1285 if (Math.abs(dphi) <= .0000000001) {
1286 return phi;
1287 }
1288 }
1289
1290 alert("IQSFN-CONV:Latitude failed to converge after 30 iterations");
1291 return (NaN);
1292 },
1293
1294/* Function to eliminate roundoff errors in asin
1295----------------------------------------------*/
1296 asinz : function(x) {
1297 if (Math.abs(x)>1.0) {
1298 x=(x>1.0)?1.0:-1.0;
1299 }
1300 return Math.asin(x);
1301 },
1302
1303// following functions from gctpc cproj.c for transverse mercator projections
1304 e0fn : function(x) {return(1.0-0.25*x*(1.0+x/16.0*(3.0+1.25*x)));},
1305 e1fn : function(x) {return(0.375*x*(1.0+0.25*x*(1.0+0.46875*x)));},
1306 e2fn : function(x) {return(0.05859375*x*x*(1.0+0.75*x));},
1307 e3fn : function(x) {return(x*x*x*(35.0/3072.0));},
1308 mlfn : function(e0,e1,e2,e3,phi) {return(e0*phi-e1*Math.sin(2.0*phi)+e2*Math.sin(4.0*phi)-e3*Math.sin(6.0*phi));},
1309 imlfn : function(ml, e0, e1, e2, e3) {
1310 var phi;
1311 var dphi;
1312
1313 phi=ml/e0;
1314 for (var i=0;i<15;i++){
1315 dphi=(ml-(e0*phi-e1*Math.sin(2.0*phi)+e2*Math.sin(4.0*phi)-e3*Math.sin(6.0*phi)))/(e0-2.0*e1*Math.cos(2.0*phi)+4.0*e2*Math.cos(4.0*phi)-6.0*e3*Math.cos(6.0*phi));
1316 phi+=dphi;
1317 if (Math.abs(dphi) <= .0000000001) {
1318 return phi;
1319 }
1320 }
1321
1322 Proj4js.reportError("IMLFN-CONV:Latitude failed to converge after 15 iterations");
1323 return NaN;
1324 },
1325
1326 srat : function(esinp, exp) {
1327 return(Math.pow((1.0-esinp)/(1.0+esinp), exp));
1328 },
1329
1330// Function to return the sign of an argument
1331 sign : function(x) { if (x < 0.0) return(-1); else return(1);},
1332
1333// Function to adjust longitude to -180 to 180; input in radians
1334 adjust_lon : function(x) {
1335 x = (Math.abs(x) < this.PI) ? x: (x - (this.sign(x)*this.TWO_PI) );
1336 return x;
1337 },
1338
1339// IGNF - DGR : algorithms used by IGN France
1340
1341// Function to adjust latitude to -90 to 90; input in radians
1342 adjust_lat : function(x) {
1343 x= (Math.abs(x) < this.HALF_PI) ? x: (x - (this.sign(x)*this.PI) );
1344 return x;
1345 },
1346
1347// Latitude Isometrique - close to tsfnz ...
1348 latiso : function(eccent, phi, sinphi) {
1349 if (Math.abs(phi) > this.HALF_PI) return +Number.NaN;
1350 if (phi==this.HALF_PI) return Number.POSITIVE_INFINITY;
1351 if (phi==-1.0*this.HALF_PI) return -1.0*Number.POSITIVE_INFINITY;
1352
1353 var con= eccent*sinphi;
1354 return Math.log(Math.tan((this.HALF_PI+phi)/2.0))+eccent*Math.log((1.0-con)/(1.0+con))/2.0;
1355 },
1356
1357 fL : function(x,L) {
1358 return 2.0*Math.atan(x*Math.exp(L)) - this.HALF_PI;
1359 },
1360
1361// Inverse Latitude Isometrique - close to ph2z
1362 invlatiso : function(eccent, ts) {
1363 var phi= this.fL(1.0,ts);
1364 var Iphi= 0.0;
1365 var con= 0.0;
1366 do {
1367 Iphi= phi;
1368 con= eccent*Math.sin(Iphi);
1369 phi= this.fL(Math.exp(eccent*Math.log((1.0+con)/(1.0-con))/2.0),ts);
1370 } while (Math.abs(phi-Iphi)>1.0e-12);
1371 return phi;
1372 },
1373
1374// Needed for Gauss Schreiber
1375// Original: Denis Makarov (info@binarythings.com)
1376// Web Site: http://www.binarythings.com
1377 sinh : function(x)
1378 {
1379 var r= Math.exp(x);
1380 r= (r-1.0/r)/2.0;
1381 return r;
1382 },
1383
1384 cosh : function(x)
1385 {
1386 var r= Math.exp(x);
1387 r= (r+1.0/r)/2.0;
1388 return r;
1389 },
1390
1391 tanh : function(x)
1392 {
1393 var r= Math.exp(x);
1394 r= (r-1.0/r)/(r+1.0/r);
1395 return r;
1396 },
1397
1398 asinh : function(x)
1399 {
1400 var s= (x>= 0? 1.0:-1.0);
1401 return s*(Math.log( Math.abs(x) + Math.sqrt(x*x+1.0) ));
1402 },
1403
1404 acosh : function(x)
1405 {
1406 return 2.0*Math.log(Math.sqrt((x+1.0)/2.0) + Math.sqrt((x-1.0)/2.0));
1407 },
1408
1409 atanh : function(x)
1410 {
1411 return Math.log((x-1.0)/(x+1.0))/2.0;
1412 },
1413
1414// Grande Normale
1415 gN : function(a,e,sinphi)
1416 {
1417 var temp= e*sinphi;
1418 return a/Math.sqrt(1.0 - temp*temp);
1419 },
1420
1421 //code from the PROJ.4 pj_mlfn.c file; this may be useful for other projections
1422 pj_enfn: function(es) {
1423 var en = new Array();
1424 en[0] = this.C00 - es * (this.C02 + es * (this.C04 + es * (this.C06 + es * this.C08)));
1425 en[1] = es * (this.C22 - es * (this.C04 + es * (this.C06 + es * this.C08)));
1426 var t = es * es;
1427 en[2] = t * (this.C44 - es * (this.C46 + es * this.C48));
1428 t *= es;
1429 en[3] = t * (this.C66 - es * this.C68);
1430 en[4] = t * es * this.C88;
1431 return en;
1432 },
1433
1434 pj_mlfn: function(phi, sphi, cphi, en) {
1435 cphi *= sphi;
1436 sphi *= sphi;
1437 return(en[0] * phi - cphi * (en[1] + sphi*(en[2]+ sphi*(en[3] + sphi*en[4]))));
1438 },
1439
1440 pj_inv_mlfn: function(arg, es, en) {
1441 var k = 1./(1.-es);
1442 var phi = arg;
1443 for (var i = Proj4js.common.MAX_ITER; i ; --i) { /* rarely goes over 2 iterations */
1444 var s = Math.sin(phi);
1445 var t = 1. - es * s * s;
1446 //t = this.pj_mlfn(phi, s, Math.cos(phi), en) - arg;
1447 //phi -= t * (t * Math.sqrt(t)) * k;
1448 t = (this.pj_mlfn(phi, s, Math.cos(phi), en) - arg) * (t * Math.sqrt(t)) * k;
1449 phi -= t;
1450 if (Math.abs(t) < Proj4js.common.EPSLN)
1451 return phi;
1452 }
1453 Proj4js.reportError("cass:pj_inv_mlfn: Convergence error");
1454 return phi;
1455 },
1456
1457 /**
1458 * Determine correction values
1459 * source: nad_intr.c (DGR: 2012-07-29)
1460 */
1461 nad_intr: function(pin,ct) {
1462 // force computation by decreasing by 1e-7 to be as closed as possible
1463 // from computation under C:C++ by leveraging rounding problems ...
1464 var t= {"x":(pin.x-1.e-7)/ct.del[0],"y":(pin.y-1e-7)/ct.del[1]};
1465 var indx= {"x":Math.floor(t.x),"y":Math.floor(t.y)};
1466 var frct= {"x":t.x-1.0*indx.x,"y":t.y-1.0*indx.y};
1467 var val= {"x":Number.NaN,"y":Number.NaN};
1468 var inx;
1469 if (indx.x<0) {
1470 if (!(indx.x==-1 && frct.x>0.99999999999)) {
1471 return val;
1472 }
1473 ++indx.x;
1474 frct.x= 0.0;
1475 } else {
1476 inx= indx.x+1;
1477 if (inx>=ct.lim[0]) {
1478 if (!(inx==ct.lim[0] && frct.x<1e-11)) {
1479 return val;
1480 }
1481 --indx.x;
1482 frct.x= 1.0;
1483 }
1484 }
1485 if (indx.y<0) {
1486 if (!(indx.y==-1 && frct.y>0.99999999999)) {
1487 return val;
1488 }
1489 ++indx.y;
1490 frct.y= 0.0;
1491 } else {
1492 inx= indx.y+1;
1493 if (inx>=ct.lim[1]) {
1494 if (!(inx==ct.lim[1] && frct.y<1e-11)) {
1495 return val;
1496 }
1497 --indx.y;
1498 frct.y= 1.0;
1499 }
1500 }
1501 inx= (indx.y*ct.lim[0])+indx.x;
1502 var f00= {"x":ct.cvs[inx][0], "y":ct.cvs[inx][1]};
1503 inx++;
1504 var f10= {"x":ct.cvs[inx][0], "y":ct.cvs[inx][1]};
1505 inx+= ct.lim[0];
1506 var f11= {"x":ct.cvs[inx][0], "y":ct.cvs[inx][1]};
1507 inx--;
1508 var f01= {"x":ct.cvs[inx][0], "y":ct.cvs[inx][1]};
1509 var m11= frct.x*frct.y, m10= frct.x*(1.0-frct.y),
1510 m00= (1.0-frct.x)*(1.0-frct.y), m01= (1.0-frct.x)*frct.y;
1511 val.x= (m00*f00.x + m10*f10.x + m01*f01.x + m11*f11.x);
1512 val.y= (m00*f00.y + m10*f10.y + m01*f01.y + m11*f11.y);
1513 return val;
1514 },
1515
1516 /**
1517 * Correct value
1518 * source: nad_cvt.c (DGR: 2012-07-29)
1519 */
1520 nad_cvt: function(pin,inverse,ct) {
1521 var val= {"x":Number.NaN, "y":Number.NaN};
1522 if (isNaN(pin.x)) { return val; }
1523 var tb= {"x":pin.x, "y":pin.y};
1524 tb.x-= ct.ll[0];
1525 tb.y-= ct.ll[1];
1526 tb.x= Proj4js.common.adjust_lon(tb.x - Proj4js.common.PI) + Proj4js.common.PI;
1527 var t= Proj4js.common.nad_intr(tb,ct);
1528 if (inverse) {
1529 if (isNaN(t.x)) {
1530 return val;
1531 }
1532 t.x= tb.x + t.x;
1533 t.y= tb.y - t.y;
1534 var i= 9, tol= 1e-12;
1535 var dif, del;
1536 do {
1537 del= Proj4js.common.nad_intr(t,ct);
1538 if (isNaN(del.x)) {
1539 this.reportError("Inverse grid shift iteration failed, presumably at grid edge. Using first approximation.");
1540 break;
1541 }
1542 dif= {"x":t.x-del.x-tb.x, "y":t.y+del.y-tb.y};
1543 t.x-= dif.x;
1544 t.y-= dif.y;
1545 } while (i-- && Math.abs(dif.x)>tol && Math.abs(dif.y)>tol);
1546 if (i<0) {
1547 this.reportError("Inverse grid shift iterator failed to converge.");
1548 return val;
1549 }
1550 val.x= Proj4js.common.adjust_lon(t.x+ct.ll[0]);
1551 val.y= t.y+ct.ll[1];
1552 } else {
1553 if (!isNaN(t.x)) {
1554 val.x= pin.x - t.x;
1555 val.y= pin.y + t.y;
1556 }
1557 }
1558 return val;
1559 },
1560
1561/* meridinal distance for ellipsoid and inverse
1562** 8th degree - accurate to < 1e-5 meters when used in conjuction
1563** with typical major axis values.
1564** Inverse determines phi to EPS (1e-11) radians, about 1e-6 seconds.
1565*/
1566 C00: 1.0,
1567 C02: .25,
1568 C04: .046875,
1569 C06: .01953125,
1570 C08: .01068115234375,
1571 C22: .75,
1572 C44: .46875,
1573 C46: .01302083333333333333,
1574 C48: .00712076822916666666,
1575 C66: .36458333333333333333,
1576 C68: .00569661458333333333,
1577 C88: .3076171875
1578
1579};
1580
1581/** datum object
1582*/
1583Proj4js.datum = Proj4js.Class({
1584
1585 initialize : function(proj) {
1586 this.datum_type = Proj4js.common.PJD_WGS84; //default setting
1587 if (!proj) { return; }
1588 if (proj.datumCode && proj.datumCode == 'none') {
1589 this.datum_type = Proj4js.common.PJD_NODATUM;
1590 }
1591 if (proj.datum_params) {
1592 for (var i=0; i<proj.datum_params.length; i++) {
1593 proj.datum_params[i]=parseFloat(proj.datum_params[i]);
1594 }
1595 if (proj.datum_params[0] != 0 || proj.datum_params[1] != 0 || proj.datum_params[2] != 0 ) {
1596 this.datum_type = Proj4js.common.PJD_3PARAM;
1597 }
1598 if (proj.datum_params.length > 3) {
1599 if (proj.datum_params[3] != 0 || proj.datum_params[4] != 0 ||
1600 proj.datum_params[5] != 0 || proj.datum_params[6] != 0 ) {
1601 this.datum_type = Proj4js.common.PJD_7PARAM;
1602 proj.datum_params[3] *= Proj4js.common.SEC_TO_RAD;
1603 proj.datum_params[4] *= Proj4js.common.SEC_TO_RAD;
1604 proj.datum_params[5] *= Proj4js.common.SEC_TO_RAD;
1605 proj.datum_params[6] = (proj.datum_params[6]/1000000.0) + 1.0;
1606 }
1607 }
1608 }
1609 // DGR 2011-03-21 : nadgrids support
1610 this.datum_type = proj.grids?
1611 Proj4js.common.PJD_GRIDSHIFT
1612 : this.datum_type;
1613
1614 this.a = proj.a; //datum object also uses these values
1615 this.b = proj.b;
1616 this.es = proj.es;
1617 this.ep2 = proj.ep2;
1618 this.datum_params = proj.datum_params;
1619 if (this.datum_type==Proj4js.common.PJD_GRIDSHIFT) {
1620 this.grids= proj.grids;
1621 }
1622 },
1623
1624 /****************************************************************/
1625 // cs_compare_datums()
1626 // Returns TRUE if the two datums match, otherwise FALSE.
1627 compare_datums : function( dest ) {
1628 if( this.datum_type != dest.datum_type ) {
1629 return false; // false, datums are not equal
1630 } else if( this.a != dest.a || Math.abs(this.es-dest.es) > 0.000000000050 ) {
1631 // the tolerence for es is to ensure that GRS80 and WGS84
1632 // are considered identical
1633 return false;
1634 } else if( this.datum_type == Proj4js.common.PJD_3PARAM ) {
1635 return (this.datum_params[0] == dest.datum_params[0]
1636 && this.datum_params[1] == dest.datum_params[1]
1637 && this.datum_params[2] == dest.datum_params[2]);
1638 } else if( this.datum_type == Proj4js.common.PJD_7PARAM ) {
1639 return (this.datum_params[0] == dest.datum_params[0]
1640 && this.datum_params[1] == dest.datum_params[1]
1641 && this.datum_params[2] == dest.datum_params[2]
1642 && this.datum_params[3] == dest.datum_params[3]
1643 && this.datum_params[4] == dest.datum_params[4]
1644 && this.datum_params[5] == dest.datum_params[5]
1645 && this.datum_params[6] == dest.datum_params[6]);
1646 } else if ( this.datum_type == Proj4js.common.PJD_GRIDSHIFT ||
1647 dest.datum_type == Proj4js.common.PJD_GRIDSHIFT ) {
1648 //alert("ERROR: Grid shift transformations are not implemented.");
1649 //return false
1650 //DGR 2012-07-29 lazy ...
1651 return this.nadgrids == dest.nadgrids;
1652 } else {
1653 return true; // datums are equal
1654 }
1655 }, // cs_compare_datums()
1656
1657 /*
1658 * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
1659 * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
1660 * according to the current ellipsoid parameters.
1661 *
1662 * Latitude : Geodetic latitude in radians (input)
1663 * Longitude : Geodetic longitude in radians (input)
1664 * Height : Geodetic height, in meters (input)
1665 * X : Calculated Geocentric X coordinate, in meters (output)
1666 * Y : Calculated Geocentric Y coordinate, in meters (output)
1667 * Z : Calculated Geocentric Z coordinate, in meters (output)
1668 *
1669 */
1670 geodetic_to_geocentric : function(p) {
1671 var Longitude = p.x;
1672 var Latitude = p.y;
1673 var Height = p.z ? p.z : 0; //Z value not always supplied
1674 var X; // output
1675 var Y;
1676 var Z;
1677
1678 var Error_Code=0; // GEOCENT_NO_ERROR;
1679 var Rn; /* Earth radius at location */
1680 var Sin_Lat; /* Math.sin(Latitude) */
1681 var Sin2_Lat; /* Square of Math.sin(Latitude) */
1682 var Cos_Lat; /* Math.cos(Latitude) */
1683
1684 /*
1685 ** Don't blow up if Latitude is just a little out of the value
1686 ** range as it may just be a rounding issue. Also removed longitude
1687 ** test, it should be wrapped by Math.cos() and Math.sin(). NFW for PROJ.4, Sep/2001.
1688 */
1689 if( Latitude < -Proj4js.common.HALF_PI && Latitude > -1.001 * Proj4js.common.HALF_PI ) {
1690 Latitude = -Proj4js.common.HALF_PI;
1691 } else if( Latitude > Proj4js.common.HALF_PI && Latitude < 1.001 * Proj4js.common.HALF_PI ) {
1692 Latitude = Proj4js.common.HALF_PI;
1693 } else if ((Latitude < -Proj4js.common.HALF_PI) || (Latitude > Proj4js.common.HALF_PI)) {
1694 /* Latitude out of range */
1695 Proj4js.reportError('geocent:lat out of range:'+Latitude);
1696 return null;
1697 }
1698
1699 if (Longitude > Proj4js.common.PI) Longitude -= (2*Proj4js.common.PI);
1700 Sin_Lat = Math.sin(Latitude);
1701 Cos_Lat = Math.cos(Latitude);
1702 Sin2_Lat = Sin_Lat * Sin_Lat;
1703 Rn = this.a / (Math.sqrt(1.0e0 - this.es * Sin2_Lat));
1704 X = (Rn + Height) * Cos_Lat * Math.cos(Longitude);
1705 Y = (Rn + Height) * Cos_Lat * Math.sin(Longitude);
1706 Z = ((Rn * (1 - this.es)) + Height) * Sin_Lat;
1707
1708 p.x = X;
1709 p.y = Y;
1710 p.z = Z;
1711 return Error_Code;
1712 }, // cs_geodetic_to_geocentric()
1713
1714
1715 geocentric_to_geodetic : function (p) {
1716/* local defintions and variables */
1717/* end-criterium of loop, accuracy of sin(Latitude) */
1718var genau = 1.E-12;
1719var genau2 = (genau*genau);
1720var maxiter = 30;
1721
1722 var P; /* distance between semi-minor axis and location */
1723 var RR; /* distance between center and location */
1724 var CT; /* sin of geocentric latitude */
1725 var ST; /* cos of geocentric latitude */
1726 var RX;
1727 var RK;
1728 var RN; /* Earth radius at location */
1729 var CPHI0; /* cos of start or old geodetic latitude in iterations */
1730 var SPHI0; /* sin of start or old geodetic latitude in iterations */
1731 var CPHI; /* cos of searched geodetic latitude */
1732 var SPHI; /* sin of searched geodetic latitude */
1733 var SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */
1734 var At_Pole; /* indicates location is in polar region */
1735 var iter; /* # of continous iteration, max. 30 is always enough (s.a.) */
1736
1737 var X = p.x;
1738 var Y = p.y;
1739 var Z = p.z ? p.z : 0.0; //Z value not always supplied
1740 var Longitude;
1741 var Latitude;
1742 var Height;
1743
1744 At_Pole = false;
1745 P = Math.sqrt(X*X+Y*Y);
1746 RR = Math.sqrt(X*X+Y*Y+Z*Z);
1747
1748/* special cases for latitude and longitude */
1749 if (P/this.a < genau) {
1750
1751/* special case, if P=0. (X=0., Y=0.) */
1752 At_Pole = true;
1753 Longitude = 0.0;
1754
1755/* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis
1756 * of ellipsoid (=center of mass), Latitude becomes PI/2 */
1757 if (RR/this.a < genau) {
1758 Latitude = Proj4js.common.HALF_PI;
1759 Height = -this.b;
1760 return;
1761 }
1762 } else {
1763/* ellipsoidal (geodetic) longitude
1764 * interval: -PI < Longitude <= +PI */
1765 Longitude=Math.atan2(Y,X);
1766 }
1767
1768/* --------------------------------------------------------------
1769 * Following iterative algorithm was developped by
1770 * "Institut f�r Erdmessung", University of Hannover, July 1988.
1771 * Internet: www.ife.uni-hannover.de
1772 * Iterative computation of CPHI,SPHI and Height.
1773 * Iteration of CPHI and SPHI to 10**-12 radian resp.
1774 * 2*10**-7 arcsec.
1775 * --------------------------------------------------------------
1776 */
1777 CT = Z/RR;
1778 ST = P/RR;
1779 RX = 1.0/Math.sqrt(1.0-this.es*(2.0-this.es)*ST*ST);
1780 CPHI0 = ST*(1.0-this.es)*RX;
1781 SPHI0 = CT*RX;
1782 iter = 0;
1783
1784/* loop to find sin(Latitude) resp. Latitude
1785 * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */
1786 do
1787 {
1788 iter++;
1789 RN = this.a/Math.sqrt(1.0-this.es*SPHI0*SPHI0);
1790
1791/* ellipsoidal (geodetic) height */
1792 Height = P*CPHI0+Z*SPHI0-RN*(1.0-this.es*SPHI0*SPHI0);
1793
1794 RK = this.es*RN/(RN+Height);
1795 RX = 1.0/Math.sqrt(1.0-RK*(2.0-RK)*ST*ST);
1796 CPHI = ST*(1.0-RK)*RX;
1797 SPHI = CT*RX;
1798 SDPHI = SPHI*CPHI0-CPHI*SPHI0;
1799 CPHI0 = CPHI;
1800 SPHI0 = SPHI;
1801 }
1802 while (SDPHI*SDPHI > genau2 && iter < maxiter);
1803
1804/* ellipsoidal (geodetic) latitude */
1805 Latitude=Math.atan(SPHI/Math.abs(CPHI));
1806
1807 p.x = Longitude;
1808 p.y = Latitude;
1809 p.z = Height;
1810 return p;
1811 }, // cs_geocentric_to_geodetic()
1812
1813 /** Convert_Geocentric_To_Geodetic
1814 * The method used here is derived from 'An Improved Algorithm for
1815 * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
1816 */
1817 geocentric_to_geodetic_noniter : function (p) {
1818 var X = p.x;
1819 var Y = p.y;
1820 var Z = p.z ? p.z : 0; //Z value not always supplied
1821 var Longitude;
1822 var Latitude;
1823 var Height;
1824
1825 var W; /* distance from Z axis */
1826 var W2; /* square of distance from Z axis */
1827 var T0; /* initial estimate of vertical component */
1828 var T1; /* corrected estimate of vertical component */
1829 var S0; /* initial estimate of horizontal component */
1830 var S1; /* corrected estimate of horizontal component */
1831 var Sin_B0; /* Math.sin(B0), B0 is estimate of Bowring aux variable */
1832 var Sin3_B0; /* cube of Math.sin(B0) */
1833 var Cos_B0; /* Math.cos(B0) */
1834 var Sin_p1; /* Math.sin(phi1), phi1 is estimated latitude */
1835 var Cos_p1; /* Math.cos(phi1) */
1836 var Rn; /* Earth radius at location */
1837 var Sum; /* numerator of Math.cos(phi1) */
1838 var At_Pole; /* indicates location is in polar region */
1839
1840 X = parseFloat(X); // cast from string to float
1841 Y = parseFloat(Y);
1842 Z = parseFloat(Z);
1843
1844 At_Pole = false;
1845 if (X != 0.0)
1846 {
1847 Longitude = Math.atan2(Y,X);
1848 }
1849 else
1850 {
1851 if (Y > 0)
1852 {
1853 Longitude = Proj4js.common.HALF_PI;
1854 }
1855 else if (Y < 0)
1856 {
1857 Longitude = -Proj4js.common.HALF_PI;
1858 }
1859 else
1860 {
1861 At_Pole = true;
1862 Longitude = 0.0;
1863 if (Z > 0.0)
1864 { /* north pole */
1865 Latitude = Proj4js.common.HALF_PI;
1866 }
1867 else if (Z < 0.0)
1868 { /* south pole */
1869 Latitude = -Proj4js.common.HALF_PI;
1870 }
1871 else
1872 { /* center of earth */
1873 Latitude = Proj4js.common.HALF_PI;
1874 Height = -this.b;
1875 return;
1876 }
1877 }
1878 }
1879 W2 = X*X + Y*Y;
1880 W = Math.sqrt(W2);
1881 T0 = Z * Proj4js.common.AD_C;
1882 S0 = Math.sqrt(T0 * T0 + W2);
1883 Sin_B0 = T0 / S0;
1884 Cos_B0 = W / S0;
1885 Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
1886 T1 = Z + this.b * this.ep2 * Sin3_B0;
1887 Sum = W - this.a * this.es * Cos_B0 * Cos_B0 * Cos_B0;
1888 S1 = Math.sqrt(T1*T1 + Sum * Sum);
1889 Sin_p1 = T1 / S1;
1890 Cos_p1 = Sum / S1;
1891 Rn = this.a / Math.sqrt(1.0 - this.es * Sin_p1 * Sin_p1);
1892 if (Cos_p1 >= Proj4js.common.COS_67P5)
1893 {
1894 Height = W / Cos_p1 - Rn;
1895 }
1896 else if (Cos_p1 <= -Proj4js.common.COS_67P5)
1897 {
1898 Height = W / -Cos_p1 - Rn;
1899 }
1900 else
1901 {
1902 Height = Z / Sin_p1 + Rn * (this.es - 1.0);
1903 }
1904 if (At_Pole == false)
1905 {
1906 Latitude = Math.atan(Sin_p1 / Cos_p1);
1907 }
1908
1909 p.x = Longitude;
1910 p.y = Latitude;
1911 p.z = Height;
1912 return p;
1913 }, // geocentric_to_geodetic_noniter()
1914
1915 /****************************************************************/
1916 // pj_geocentic_to_wgs84( p )
1917 // p = point to transform in geocentric coordinates (x,y,z)
1918 geocentric_to_wgs84 : function ( p ) {
1919
1920 if( this.datum_type == Proj4js.common.PJD_3PARAM )
1921 {
1922 // if( x[io] == HUGE_VAL )
1923 // continue;
1924 p.x += this.datum_params[0];
1925 p.y += this.datum_params[1];
1926 p.z += this.datum_params[2];
1927
1928 }
1929 else if (this.datum_type == Proj4js.common.PJD_7PARAM)
1930 {
1931 var Dx_BF =this.datum_params[0];
1932 var Dy_BF =this.datum_params[1];
1933 var Dz_BF =this.datum_params[2];
1934 var Rx_BF =this.datum_params[3];
1935 var Ry_BF =this.datum_params[4];
1936 var Rz_BF =this.datum_params[5];
1937 var M_BF =this.datum_params[6];
1938 // if( x[io] == HUGE_VAL )
1939 // continue;
1940 var x_out = M_BF*( p.x - Rz_BF*p.y + Ry_BF*p.z) + Dx_BF;
1941 var y_out = M_BF*( Rz_BF*p.x + p.y - Rx_BF*p.z) + Dy_BF;
1942 var z_out = M_BF*(-Ry_BF*p.x + Rx_BF*p.y + p.z) + Dz_BF;
1943 p.x = x_out;
1944 p.y = y_out;
1945 p.z = z_out;
1946 }
1947 }, // cs_geocentric_to_wgs84
1948
1949 /****************************************************************/
1950 // pj_geocentic_from_wgs84()
1951 // coordinate system definition,
1952 // point to transform in geocentric coordinates (x,y,z)
1953 geocentric_from_wgs84 : function( p ) {
1954
1955 if( this.datum_type == Proj4js.common.PJD_3PARAM )
1956 {
1957 //if( x[io] == HUGE_VAL )
1958 // continue;
1959 p.x -= this.datum_params[0];
1960 p.y -= this.datum_params[1];
1961 p.z -= this.datum_params[2];
1962
1963 }
1964 else if (this.datum_type == Proj4js.common.PJD_7PARAM)
1965 {
1966 var Dx_BF =this.datum_params[0];
1967 var Dy_BF =this.datum_params[1];
1968 var Dz_BF =this.datum_params[2];
1969 var Rx_BF =this.datum_params[3];
1970 var Ry_BF =this.datum_params[4];
1971 var Rz_BF =this.datum_params[5];
1972 var M_BF =this.datum_params[6];
1973 var x_tmp = (p.x - Dx_BF) / M_BF;
1974 var y_tmp = (p.y - Dy_BF) / M_BF;
1975 var z_tmp = (p.z - Dz_BF) / M_BF;
1976 //if( x[io] == HUGE_VAL )
1977 // continue;
1978
1979 p.x = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp;
1980 p.y = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp;
1981 p.z = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp;
1982 } //cs_geocentric_from_wgs84()
1983 }
1984});
1985
1986/** point object, nothing fancy, just allows values to be
1987 passed back and forth by reference rather than by value.
1988 Other point classes may be used as long as they have
1989 x and y properties, which will get modified in the transform method.
1990*/
1991Proj4js.Point = Proj4js.Class({
1992
1993 /**
1994 * Constructor: Proj4js.Point
1995 *
1996 * Parameters:
1997 * - x {float} or {Array} either the first coordinates component or
1998 * the full coordinates
1999 * - y {float} the second component
2000 * - z {float} the third component, optional.
2001 */
2002 initialize : function(x,y,z) {
2003 if (typeof x == 'object') {
2004 this.x = x[0];
2005 this.y = x[1];
2006 this.z = x[2] || 0.0;
2007 } else if (typeof x == 'string' && typeof y == 'undefined') {
2008 var coords = x.split(',');
2009 this.x = parseFloat(coords[0]);
2010 this.y = parseFloat(coords[1]);
2011 this.z = parseFloat(coords[2]) || 0.0;
2012 } else {
2013 this.x = x;
2014 this.y = y;
2015 this.z = z || 0.0;
2016 }
2017 },
2018
2019 /**
2020 * APIMethod: clone
2021 * Build a copy of a Proj4js.Point object.
2022 *
2023 * Return:
2024 * {Proj4js}.Point the cloned point.
2025 */
2026 clone : function() {
2027 return new Proj4js.Point(this.x, this.y, this.z);
2028 },
2029
2030 /**
2031 * APIMethod: toString
2032 * Return a readable string version of the point
2033 *
2034 * Return:
2035 * {String} String representation of Proj4js.Point object.
2036 * (ex. <i>"x=5,y=42"</i>)
2037 */
2038 toString : function() {
2039 return ("x=" + this.x + ",y=" + this.y);
2040 },
2041
2042 /**
2043 * APIMethod: toShortString
2044 * Return a short string version of the point.
2045 *
2046 * Return:
2047 * {String} Shortened String representation of Proj4js.Point object.
2048 * (ex. <i>"5, 42"</i>)
2049 */
2050 toShortString : function() {
2051 return (this.x + ", " + this.y);
2052 }
2053});
2054
2055Proj4js.PrimeMeridian = {
2056 "greenwich": 0.0, //"0dE",
2057 "lisbon": -9.131906111111, //"9d07'54.862\"W",
2058 "paris": 2.337229166667, //"2d20'14.025\"E",
2059 "bogota": -74.080916666667, //"74d04'51.3\"W",
2060 "madrid": -3.687938888889, //"3d41'16.58\"W",
2061 "rome": 12.452333333333, //"12d27'8.4\"E",
2062 "bern": 7.439583333333, //"7d26'22.5\"E",
2063 "jakarta": 106.807719444444, //"106d48'27.79\"E",
2064 "ferro": -17.666666666667, //"17d40'W",
2065 "brussels": 4.367975, //"4d22'4.71\"E",
2066 "stockholm": 18.058277777778, //"18d3'29.8\"E",
2067 "athens": 23.7163375, //"23d42'58.815\"E",
2068 "oslo": 10.722916666667 //"10d43'22.5\"E"
2069};
2070
2071Proj4js.Ellipsoid = {
2072 "MERIT": {a:6378137.0, rf:298.257, ellipseName:"MERIT 1983"},
2073 "SGS85": {a:6378136.0, rf:298.257, ellipseName:"Soviet Geodetic System 85"},
2074 "GRS80": {a:6378137.0, rf:298.257222101, ellipseName:"GRS 1980(IUGG, 1980)"},
2075 "IAU76": {a:6378140.0, rf:298.257, ellipseName:"IAU 1976"},
2076 "airy": {a:6377563.396, b:6356256.910, ellipseName:"Airy 1830"},
2077 "APL4.": {a:6378137, rf:298.25, ellipseName:"Appl. Physics. 1965"},
2078 "NWL9D": {a:6378145.0, rf:298.25, ellipseName:"Naval Weapons Lab., 1965"},
2079 "mod_airy": {a:6377340.189, b:6356034.446, ellipseName:"Modified Airy"},
2080 "andrae": {a:6377104.43, rf:300.0, ellipseName:"Andrae 1876 (Den., Iclnd.)"},
2081 "aust_SA": {a:6378160.0, rf:298.25, ellipseName:"Australian Natl & S. Amer. 1969"},
2082 "GRS67": {a:6378160.0, rf:298.2471674270, ellipseName:"GRS 67(IUGG 1967)"},
2083 "bessel": {a:6377397.155, rf:299.1528128, ellipseName:"Bessel 1841"},
2084 "bess_nam": {a:6377483.865, rf:299.1528128, ellipseName:"Bessel 1841 (Namibia)"},
2085 "clrk66": {a:6378206.4, b:6356583.8, ellipseName:"Clarke 1866"},
2086 "clrk80": {a:6378249.145, rf:293.4663, ellipseName:"Clarke 1880 mod."},
2087 "CPM": {a:6375738.7, rf:334.29, ellipseName:"Comm. des Poids et Mesures 1799"},
2088 "delmbr": {a:6376428.0, rf:311.5, ellipseName:"Delambre 1810 (Belgium)"},
2089 "engelis": {a:6378136.05, rf:298.2566, ellipseName:"Engelis 1985"},
2090 "evrst30": {a:6377276.345, rf:300.8017, ellipseName:"Everest 1830"},
2091 "evrst48": {a:6377304.063, rf:300.8017, ellipseName:"Everest 1948"},
2092 "evrst56": {a:6377301.243, rf:300.8017, ellipseName:"Everest 1956"},
2093 "evrst69": {a:6377295.664, rf:300.8017, ellipseName:"Everest 1969"},
2094 "evrstSS": {a:6377298.556, rf:300.8017, ellipseName:"Everest (Sabah & Sarawak)"},
2095 "fschr60": {a:6378166.0, rf:298.3, ellipseName:"Fischer (Mercury Datum) 1960"},
2096 "fschr60m": {a:6378155.0, rf:298.3, ellipseName:"Fischer 1960"},
2097 "fschr68": {a:6378150.0, rf:298.3, ellipseName:"Fischer 1968"},
2098 "helmert": {a:6378200.0, rf:298.3, ellipseName:"Helmert 1906"},
2099 "hough": {a:6378270.0, rf:297.0, ellipseName:"Hough"},
2100 "intl": {a:6378388.0, rf:297.0, ellipseName:"International 1909 (Hayford)"},
2101 "kaula": {a:6378163.0, rf:298.24, ellipseName:"Kaula 1961"},
2102 "lerch": {a:6378139.0, rf:298.257, ellipseName:"Lerch 1979"},
2103 "mprts": {a:6397300.0, rf:191.0, ellipseName:"Maupertius 1738"},
2104 "new_intl": {a:6378157.5, b:6356772.2, ellipseName:"New International 1967"},
2105 "plessis": {a:6376523.0, rf:6355863.0, ellipseName:"Plessis 1817 (France)"},
2106 "krass": {a:6378245.0, rf:298.3, ellipseName:"Krassovsky, 1942"},
2107 "SEasia": {a:6378155.0, b:6356773.3205, ellipseName:"Southeast Asia"},
2108 "walbeck": {a:6376896.0, b:6355834.8467, ellipseName:"Walbeck"},
2109 "WGS60": {a:6378165.0, rf:298.3, ellipseName:"WGS 60"},
2110 "WGS66": {a:6378145.0, rf:298.25, ellipseName:"WGS 66"},
2111 "WGS72": {a:6378135.0, rf:298.26, ellipseName:"WGS 72"},
2112 "WGS84": {a:6378137.0, rf:298.257223563, ellipseName:"WGS 84"},
2113 "sphere": {a:6370997.0, b:6370997.0, ellipseName:"Normal Sphere (r=6370997)"}
2114};
2115
2116Proj4js.Datum = {
2117 "WGS84": {towgs84: "0,0,0", ellipse: "WGS84", datumName: "WGS84"},
2118 "GGRS87": {towgs84: "-199.87,74.79,246.62", ellipse: "GRS80", datumName: "Greek_Geodetic_Reference_System_1987"},
2119 "NAD83": {towgs84: "0,0,0", ellipse: "GRS80", datumName: "North_American_Datum_1983"},
2120 "NAD27": {nadgrids: "@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", ellipse: "clrk66", datumName: "North_American_Datum_1927"},
2121 "potsdam": {towgs84: "606.0,23.0,413.0", ellipse: "bessel", datumName: "Potsdam Rauenberg 1950 DHDN"},
2122 "carthage": {towgs84: "-263.0,6.0,431.0", ellipse: "clark80", datumName: "Carthage 1934 Tunisia"},
2123 "hermannskogel": {towgs84: "653.0,-212.0,449.0", ellipse: "bessel", datumName: "Hermannskogel"},
2124 "ire65": {towgs84: "482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", ellipse: "mod_airy", datumName: "Ireland 1965"},
2125 "nzgd49": {towgs84: "59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", ellipse: "intl", datumName: "New Zealand Geodetic Datum 1949"},
2126 "OSGB36": {towgs84: "446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", ellipse: "airy", datumName: "Airy 1830"}
2127};
2128
2129Proj4js.WGS84 = new Proj4js.Proj('WGS84');
2130Proj4js.Datum['OSB36'] = Proj4js.Datum['OSGB36']; //as returned from spatialreference.org
2131
2132//lookup table to go from the projection name in WKT to the Proj4js projection name
2133//build this out as required
2134Proj4js.wktProjections = {
2135 "Lambert Tangential Conformal Conic Projection": "lcc",
2136 "Mercator": "merc",
2137 "Popular Visualisation Pseudo Mercator": "merc",
2138 "Mercator_1SP": "merc",
2139 "Transverse_Mercator": "tmerc",
2140 "Transverse Mercator": "tmerc",
2141 "Lambert Azimuthal Equal Area": "laea",
2142 "Universal Transverse Mercator System": "utm"
2143};
2144
2145// Based on proj4 CTABLE structure :
2146// FIXME: better to have array instead of object holding longitudes, latitudes members
2147// In the former case, one has to document index 0 is longitude and
2148// 1 is latitude ...
2149// In the later case, grid object gets bigger !!!!
2150// Solution 1 is chosen based on pj_gridinfo.c
2151Proj4js.grids= {
2152 "null":{ // name of grid's file
2153 "ll": [-3.14159265, -1.57079633], // lower-left coordinates in radians (longitude, latitude):
2154 "del":[ 3.14159265, 1.57079633], // cell's size in radians (longitude, latitude):
2155 "lim":[ 3, 3], // number of nodes in longitude, latitude (including edges):
2156 "count":9, // total number of nodes
2157 "cvs":[ // shifts : in ntv2 reverse order : lon, lat in radians ...
2158 [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], // for (lon= 0; lon<lim[0]; lon++) {
2159 [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], // for (lat= 0; lat<lim[1]; lat++) { p= cvs[lat*lim[0]+lon]; }
2160 [0.0, 0.0], [0.0, 0.0], [0.0, 0.0] // }
2161 ]
2162 }
2163};
2164
Note: See TracBrowser for help on using the repository browser.