Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/web/proj4js/lib/proj4js.js @ 76

Revision 76, 62.8 KB checked in by djay, 13 years ago (diff)

Ajout du répertoire web

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