Blame | Last modification | View Log | RSS feed
<?php/*** Author : Julien Moquet** Simple conversion from javascript to PHP of Proj4php by Mike Adair madairATdmsolutions.ca and Richard Greenwood rich@greenwoodmap.com** License: LGPL as per: http://www.gnu.org/copyleft/lesser.html*/$dir = dirname( __FILE__ );require_once($dir . "/proj4phpProj.php");require_once($dir . "/proj4phpCommon.php");require_once($dir . "/proj4phpDatum.php");require_once($dir . "/proj4phpLongLat.php");require_once($dir . "/proj4phpPoint.php");class Proj4php {protected $defaultDatum = 'WGS84';public static $ellipsoid = array( );public static $common = null;public static $datum = array( );public static $defs = array( );public static $wktProjections = array( );public static $WGS84 = null;public static $primeMeridian = array( );public static $proj = array( );/*** Property: defsLookupService* service to retreive projection definition parameters from*/public static $defsLookupService = 'http://spatialreference.org/ref';/**Proj4php.defs is a collection of coordinate system definition objects in thePROJ.4 command line format.Generally a def is added by means of a separate .js file for example:<SCRIPT type="text/javascript" src="defs/EPSG26912.js"></SCRIPT>def is a CS definition in PROJ.4 WKT format, for example:+proj="tmerc" //longlat, etc.+a=majorRadius+b=minorRadius+lat0=somenumber+long=somenumber*/protected function initDefs() {// These are so widely used, we'll go ahead and throw them in// without requiring a separate .js fileself::$defs['WGS84'] = "+title=long/lat:WGS84 +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees";self::$defs['EPSG:4326'] = "+title=long/lat:WGS84 +proj=longlat +a=6378137.0 +b=6356752.31424518 +ellps=WGS84 +datum=WGS84 +units=degrees";self::$defs['EPSG:4269'] = "+title=long/lat:NAD83 +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees";self::$defs['EPSG:3875'] = "+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";self::$defs['EPSG:3785'] = self::$defs['EPSG:3875'];self::$defs['GOOGLE'] = self::$defs['EPSG:3875'];self::$defs['EPSG:900913'] = self::$defs['EPSG:3875'];self::$defs['EPSG:102113'] = self::$defs['EPSG:3875'];}//lookup table to go from the projection name in WKT to the Proj4php projection name//build this out as requiredprotected function initWKTProjections() {self::$wktProjections["Lambert Tangential Conformal Conic Projection"] = "lcc";self::$wktProjections["Mercator"] = "merc";self::$wktProjections["Mercator_1SP"] = "merc";self::$wktProjections["Transverse_Mercator"] = "tmerc";self::$wktProjections["Transverse Mercator"] = "tmerc";self::$wktProjections["Lambert Azimuthal Equal Area"] = "laea";self::$wktProjections["Universal Transverse Mercator System"] = "utm";}protected function initDatum() {self::$datum["WGS84"] = array( 'towgs84' => "0,0,0", 'ellipse' => "WGS84", 'datumName' => "WGS84" );self::$datum["GGRS87"] = array( 'towgs84' => "-199.87,74.79,246.62", 'ellipse' => "GRS80", 'datumName' => "Greek_Geodetic_Reference_System_1987" );self::$datum["NAD83"] = array( 'towgs84' => "0,0,0", 'ellipse' => "GRS80", 'datumName' => "North_American_Datum_1983" );self::$datum["NAD27"] = array( 'nadgrids' => "@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", 'ellipse' => "clrk66", 'datumName' => "North_American_Datum_1927" );self::$datum["potsdam"] = array( 'towgs84' => "606.0,23.0,413.0", 'ellipse' => "bessel", 'datumName' => "Potsdam Rauenberg 1950 DHDN" );self::$datum["carthage"] = array( 'towgs84' => "-263.0,6.0,431.0", 'ellipse' => "clark80", 'datumName' => "Carthage 1934 Tunisia" );self::$datum["hermannskogel"] = array( 'towgs84' => "653.0,-212.0,449.0", 'ellipse' => "bessel", 'datumName' => "Hermannskogel" );self::$datum["ire65"] = array( 'towgs84' => "482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", 'ellipse' => "mod_airy", 'datumName' => "Ireland 1965" );self::$datum["nzgd49"] = array( 'towgs84' => "59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", 'ellipse' => "intl", 'datumName' => "New Zealand Geodetic Datum 1949" );self::$datum["OSGB36"] = array( 'towgs84' => "446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", 'ellipse' => "airy", 'datumName' => "Airy 1830" );}protected function initEllipsoid() {self::$ellipsoid["MERIT"] = array( 'a' => 6378137.0, 'rf' => 298.257, 'ellipseName' => "MERIT 1983" );self::$ellipsoid["SGS85"] = array( 'a' => 6378136.0, 'rf' => 298.257, 'ellipseName' => "Soviet Geodetic System 85" );self::$ellipsoid["GRS80"] = array( 'a' => 6378137.0, 'rf' => 298.257222101, 'ellipseName' => "GRS 1980(IUGG, 1980)" );self::$ellipsoid["IAU76"] = array( 'a' => 6378140.0, 'rf' => 298.257, 'ellipseName' => "IAU 1976" );self::$ellipsoid["airy"] = array( 'a' => 6377563.396, 'b' => 6356256.910, 'ellipseName' => "Airy 1830" );self::$ellipsoid["APL4."] = array( 'a' => 6378137, 'rf' => 298.25, 'ellipseName' => "Appl. Physics. 1965" );self::$ellipsoid["NWL9D"] = array( 'a' => 6378145.0, 'rf' => 298.25, 'ellipseName' => "Naval Weapons Lab., 1965" );self::$ellipsoid["mod_airy"] = array( 'a' => 6377340.189, 'b' => 6356034.446, 'ellipseName' => "Modified Airy" );self::$ellipsoid["andrae"] = array( 'a' => 6377104.43, 'rf' => 300.0, 'ellipseName' => "Andrae 1876 (Den., Iclnd.)" );self::$ellipsoid["aust_SA"] = array( 'a' => 6378160.0, 'rf' => 298.25, 'ellipseName' => "Australian Natl & S. Amer. 1969" );self::$ellipsoid["GRS67"] = array( 'a' => 6378160.0, 'rf' => 298.2471674270, 'ellipseName' => "GRS 67(IUGG 1967)" );self::$ellipsoid["bessel"] = array( 'a' => 6377397.155, 'rf' => 299.1528128, 'ellipseName' => "Bessel 1841" );self::$ellipsoid["bess_nam"] = array( 'a' => 6377483.865, 'rf' => 299.1528128, 'ellipseName' => "Bessel 1841 (Namibia)" );self::$ellipsoid["clrk66"] = array( 'a' => 6378206.4, 'b' => 6356583.8, 'ellipseName' => "Clarke 1866" );self::$ellipsoid["clrk80"] = array( 'a' => 6378249.145, 'rf' => 293.4663, 'ellipseName' => "Clarke 1880 mod." );self::$ellipsoid["CPM"] = array( 'a' => 6375738.7, 'rf' => 334.29, 'ellipseName' => "Comm. des Poids et Mesures 1799" );self::$ellipsoid["delmbr"] = array( 'a' => 6376428.0, 'rf' => 311.5, 'ellipseName' => "Delambre 1810 (Belgium)" );self::$ellipsoid["engelis"] = array( 'a' => 6378136.05, 'rf' => 298.2566, 'ellipseName' => "Engelis 1985" );self::$ellipsoid["evrst30"] = array( 'a' => 6377276.345, 'rf' => 300.8017, 'ellipseName' => "Everest 1830" );self::$ellipsoid["evrst48"] = array( 'a' => 6377304.063, 'rf' => 300.8017, 'ellipseName' => "Everest 1948" );self::$ellipsoid["evrst56"] = array( 'a' => 6377301.243, 'rf' => 300.8017, 'ellipseName' => "Everest 1956" );self::$ellipsoid["evrst69"] = array( 'a' => 6377295.664, 'rf' => 300.8017, 'ellipseName' => "Everest 1969" );self::$ellipsoid["evrstSS"] = array( 'a' => 6377298.556, 'rf' => 300.8017, 'ellipseName' => "Everest (Sabah & Sarawak)" );self::$ellipsoid["fschr60"] = array( 'a' => 6378166.0, 'rf' => 298.3, 'ellipseName' => "Fischer (Mercury Datum) 1960" );self::$ellipsoid["fschr60m"] = array( 'a' => 6378155.0, 'rf' => 298.3, 'ellipseName' => "Fischer 1960" );self::$ellipsoid["fschr68"] = array( 'a' => 6378150.0, 'rf' => 298.3, 'ellipseName' => "Fischer 1968" );self::$ellipsoid["helmert"] = array( 'a' => 6378200.0, 'rf' => 298.3, 'ellipseName' => "Helmert 1906" );self::$ellipsoid["hough"] = array( 'a' => 6378270.0, 'rf' => 297.0, 'ellipseName' => "Hough" );self::$ellipsoid["intl"] = array( 'a' => 6378388.0, 'rf' => 297.0, 'ellipseName' => "International 1909 (Hayford)" );self::$ellipsoid["kaula"] = array( 'a' => 6378163.0, 'rf' => 298.24, 'ellipseName' => "Kaula 1961" );self::$ellipsoid["lerch"] = array( 'a' => 6378139.0, 'rf' => 298.257, 'ellipseName' => "Lerch 1979" );self::$ellipsoid["mprts"] = array( 'a' => 6397300.0, 'rf' => 191.0, 'ellipseName' => "Maupertius 1738" );self::$ellipsoid["new_intl"] = array( 'a' => 6378157.5, 'b' => 6356772.2, 'ellipseName' => "New International 1967" );self::$ellipsoid["plessis"] = array( 'a' => 6376523.0, 'rf' => 6355863.0, 'ellipseName' => "Plessis 1817 (France)" );self::$ellipsoid["krass"] = array( 'a' => 6378245.0, 'rf' => 298.3, 'ellipseName' => "Krassovsky, 1942" );self::$ellipsoid["SEasia"] = array( 'a' => 6378155.0, 'b' => 6356773.3205, 'ellipseName' => "Southeast Asia" );self::$ellipsoid["walbeck"] = array( 'a' => 6376896.0, 'b' => 6355834.8467, 'ellipseName' => "Walbeck" );self::$ellipsoid["WGS60"] = array( 'a' => 6378165.0, 'rf' => 298.3, 'ellipseName' => "WGS 60" );self::$ellipsoid["WGS66"] = array( 'a' => 6378145.0, 'rf' => 298.25, 'ellipseName' => "WGS 66" );self::$ellipsoid["WGS72"] = array( 'a' => 6378135.0, 'rf' => 298.26, 'ellipseName' => "WGS 72" );self::$ellipsoid["WGS84"] = array( 'a' => 6378137.0, 'rf' => 298.257223563, 'ellipseName' => "WGS 84" );self::$ellipsoid["sphere"] = array( 'a' => 6370997.0, 'b' => 6370997.0, 'ellipseName' => "Normal Sphere (r=6370997)" );}protected function initPrimeMeridian() {self::$primeMeridian["greenwich"] = '0.0'; //"0dE",self::$primeMeridian["lisbon"] = -9.131906111111; //"9d07'54.862\"W",self::$primeMeridian["paris"] = 2.337229166667; //"2d20'14.025\"E",self::$primeMeridian["bogota"] = -74.080916666667; //"74d04'51.3\"W",self::$primeMeridian["madrid"] = -3.687938888889; //"3d41'16.58\"W",self::$primeMeridian["rome"] = 12.452333333333; //"12d27'8.4\"E",self::$primeMeridian["bern"] = 7.439583333333; //"7d26'22.5\"E",self::$primeMeridian["jakarta"] = 106.807719444444; //"106d48'27.79\"E",self::$primeMeridian["ferro"] = -17.666666666667; //"17d40'W",self::$primeMeridian["brussels"] = 4.367975; //"4d22'4.71\"E",self::$primeMeridian["stockholm"] = 18.058277777778; //"18d3'29.8\"E",self::$primeMeridian["athens"] = 23.7163375; //"23d42'58.815\"E",self::$primeMeridian["oslo"] = 10.722916666667; //"10d43'22.5\"E"}/****/public function __construct() {$this->initWKTProjections();$this->initDefs();$this->initDatum();$this->initEllipsoid();$this->initPrimeMeridian();self::$proj['longlat'] = new proj4phpLongLat();self::$proj['identity'] = new proj4phpLongLat();self::$common = new proj4phpCommon();self::$WGS84 = new Proj4phpProj( 'WGS84' );}/*** Method: transform(source, dest, point)* Transform a point coordinate from one map projection to another. This is* really the only public method you should need to use.** Parameters:* source - {Proj4phpProj} source map projection for the transformation* dest - {Proj4phpProj} destination map projection for the transformation* point - {Object} point to transform, may be geodetic (long, lat) or* projected Cartesian (x,y), but should always have x,y properties.*/public function transform( $source, $dest, $point ) {if( !$source->readyToUse ) {self::reportError( "Proj4php initialization for:" . $source->srsCode . " not yet complete" );return $point;}if( !$dest->readyToUse ) {self::reportError( "Proj4php initialization for:" . $dest->srsCode . " not yet complete" );return $point;}// Workaround for datum shifts towgs84, if either source or destination projection is not wgs84if ( isset($source->datum) && isset($dest->datum) && ((($source->datum->datum_type == Proj4php::$common->PJD_3PARAM || $source->datum->datum_type == Proj4php::$common->PJD_7PARAM) && (isset($dest->datumCode) && $dest->datumCode != "WGS84")) ||(($dest->datum->datum_type == Proj4php::$common->PJD_3PARAM || $dest->datum->datum_type == Proj4php::$common->PJD_7PARAM) && (isset($source->datumCode) && $source->datumCode != "WGS84")))) {$wgs84 = Proj4php::$WGS84;$this->transform($source, $wgs84, $point);$source = $wgs84;}// Workaround for Spherical Mercator => skipped in proj4js 1.1.0/*if( ($source->srsProjNumber == "900913" && $dest->datumCode != "WGS84") ||($dest->srsProjNumber == "900913" && $source->datumCode != "WGS84") ) {$wgs84 = Proj4php::$WGS84; // DONT KNOW WHAT YET$this->transform( $source, $wgs84, $point );$source = $wgs84;}*/// DGR, 2010/11/12if( $source->axis != "enu" ) {$this->adjust_axis( $source, false, $point );}// Transform source points to long/lat, if they aren't already.if( $source->projName == "longlat" ) {$point->x *= Proj4php::$common->D2R; // convert degrees to radians$point->y *= Proj4php::$common->D2R;} else {if( isset($source->to_meter) ) {$point->x *= $source->to_meter;$point->y *= $source->to_meter;}$source->inverse( $point ); // Convert Cartesian to longlat}// Adjust for the prime meridian if necessaryif( isset( $source->from_greenwich ) ) {$point->x += $source->from_greenwich;}// Convert datums if needed, and if possible.$point = $this->datum_transform( $source->datum, $dest->datum, $point );// Adjust for the prime meridian if necessaryif( isset( $dest->from_greenwich ) ) {$point->x -= $dest->from_greenwich;}if( $dest->projName == "longlat" ) {// convert radians to decimal degrees$point->x *= Proj4php::$common->R2D;$point->y *= Proj4php::$common->R2D;} else { // else project$dest->forward( $point );if( isset($dest->to_meter) ) {$point->x /= $dest->to_meter;$point->y /= $dest->to_meter;}}// DGR, 2010/11/12if( $dest->axis != "enu" ) {$this->adjust_axis( $dest, true, $point );}return $point;}/** datum_transform()source coordinate system definition,destination coordinate system definition,point to transform in geodetic coordinates (long, lat, height)*/public function datum_transform( $source, $dest, $point ) {// Short cut if the datums are identical.if( $source->compare_datums( $dest ) ) {return $point; // in this case, zero is sucess,// whereas cs_compare_datums returns 1 to indicate TRUE// confusing, should fix this}// Explicitly skip datum transform by setting 'datum=none' as parameter for either source or destif( $source->datum_type == Proj4php::$common->PJD_NODATUM|| $dest->datum_type == Proj4php::$common->PJD_NODATUM ) {return $point;}/*// If this datum requires grid shifts, then apply it to geodetic coordinates.if( $source->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));}if( $dest->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));}*/// Do we need to go through geocentric coordinates?if( $source->es != $dest->es || $source->a != $dest->a|| $source->datum_type == Proj4php::$common->PJD_3PARAM|| $source->datum_type == Proj4php::$common->PJD_7PARAM|| $dest->datum_type == Proj4php::$common->PJD_3PARAM|| $dest->datum_type == Proj4php::$common->PJD_7PARAM ) {// Convert to geocentric coordinates.$source->geodetic_to_geocentric( $point );// CHECK_RETURN;// Convert between datumsif( $source->datum_type == Proj4php::$common->PJD_3PARAM || $source->datum_type == Proj4php::$common->PJD_7PARAM ) {$source->geocentric_to_wgs84( $point );// CHECK_RETURN;}if( $dest->datum_type == Proj4php::$common->PJD_3PARAM || $dest->datum_type == Proj4php::$common->PJD_7PARAM ) {$dest->geocentric_from_wgs84( $point );// CHECK_RETURN;}// Convert back to geodetic coordinates$dest->geocentric_to_geodetic( $point );// CHECK_RETURN;}// Apply grid shift to destination if required/*if( $dest->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));// pj_apply_gridshift( pj_param(dest.params,"snadgrids").s, 1, point);// CHECK_RETURN;}*/return $point;}/*** Function: adjust_axis* Normalize or de-normalized the x/y/z axes. The normal form is "enu"* (easting, northing, up).* Parameters:* crs {Proj4php.Proj} the coordinate reference system* denorm {Boolean} when false, normalize* point {Object} the coordinates to adjust*/public function adjust_axis( $crs, $denorm, $point ) {$xin = $point->x;$yin = $point->y;$zin = isset( $point->z ) ? $point->z : 0.0;#$v;#$t;for( $i = 0; $i < 3; $i++ ) {if( $denorm && $i == 2 && !isset( $point->z ) ) {continue;}if( $i == 0 ) {$v = $xin;$t = 'x';} else if( $i == 1 ) {$v = $yin;$t = 'y';} else {$v = $zin;$t = 'z';}switch( $crs->axis[$i] ) {case 'e':$point[$t] = $v;break;case 'w':$point[$t] = -$v;break;case 'n':$point[$t] = $v;break;case 's':$point[$t] = -$v;break;case 'u':if( isset( $point[$t] ) ) {$point->z = $v;}break;case 'd':if( isset( $point[$t] ) ) {$point->z = -$v;}break;default :throw(new Exception( "ERROR: unknow axis (" . $crs->axis[$i] . ") - check definition of " . $crs->projName ));return null;}}return $point;}/*** Function: reportError* An internal method to report errors back to user.* Override this in applications to report error messages or throw exceptions.*/public static function reportError( $msg ) {//console.log(msg);echo $msg . "<br />\n";}/*** Function : loadScript* adapted from original. PHP is simplier.*/public static function loadScript( $filename, $onload = null, $onfail = null, $loadCheck = null ) {if( stripos($filename, 'http://') !== false ) {return @file_get_contents($filename);}elseif( file_exists( $filename ) ) {require_once($filename);return true;}else {throw(new Exception( "File $filename could not be found or was not able to be loaded." ));return false;}}/*** Function: extend* Copy all properties of a source object to a destination object. Modifies* the passed in destination object. Any properties on the source object* that are set to undefined will not be (re)set on the destination object.** Parameters:* destination - {Object} The object that will be modified* source - {Object} The object with properties to be set on the destination** Returns:* {Object} The destination object.*/public static function extend( $destination, $source ) {if( $source != null )foreach( $source as $key => $value ) {$destination->$key = $value;}return $destination;}}