com * License: LGPL as per: http://www.gnu.org/copyleft/lesser.html */ /** datum object */ class proj4phpDatum { public $datum_type; public $datum_params; /** * * @param type $proj */ public function __construct( $proj ) { $this->datum_type = Proj4php::$common->PJD_WGS84; //default setting if( isset($proj->datumCode) && $proj->datumCode == 'none' ) { $this->datum_type = Proj4php::$common->PJD_NODATUM; } if( isset( $proj->datum_params ) ) { for( $i = 0; $i < sizeof( $proj->datum_params ); $i++ ) { $proj->datum_params[$i] = floatval( $proj->datum_params[$i] ); } if( $proj->datum_params[0] != 0 || $proj->datum_params[1] != 0 || $proj->datum_params[2] != 0 ) { $this->datum_type = Proj4php::$common->PJD_3PARAM; } if( sizeof( $proj->datum_params ) > 3 ) { if( $proj->datum_params[3] != 0 || $proj->datum_params[4] != 0 || $proj->datum_params[5] != 0 || $proj->datum_params[6] != 0 ) { $this->datum_type = Proj4php::$common->PJD_7PARAM; $proj->datum_params[3] *= Proj4php::$common->SEC_TO_RAD; $proj->datum_params[4] *= Proj4php::$common->SEC_TO_RAD; $proj->datum_params[5] *= Proj4php::$common->SEC_TO_RAD; $proj->datum_params[6] = ($proj->datum_params[6] / 1000000.0) + 1.0; } } $this->datum_params = $proj->datum_params; } if( isset( $proj ) ) { $this->a = $proj->a; //datum object also uses these values $this->b = $proj->b; $this->es = $proj->es; $this->ep2 = $proj->ep2; #$this->datum_params = $proj->datum_params; } } /** * * @param type $dest * @return boolean Returns TRUE if the two datums match, otherwise FALSE. * @throws type */ public function compare_datums( $dest ) { if( $this->datum_type != $dest->datum_type ) { return false; // false, datums are not equal } else if( $this->a != $dest->a || abs( $this->es - $dest->es ) > 0.000000000050 ) { // the tolerence for es is to ensure that GRS80 and WGS84 // are considered identical return false; } else if( $this->datum_type == Proj4php::$common->PJD_3PARAM ) { return ($this->datum_params[0] == $dest->datum_params[0] && $this->datum_params[1] == $dest->datum_params[1] && $this->datum_params[2] == $dest->datum_params[2]); } else if( $this->datum_type == Proj4php::$common->PJD_7PARAM ) { return ($this->datum_params[0] == $dest->datum_params[0] && $this->datum_params[1] == $dest->datum_params[1] && $this->datum_params[2] == $dest->datum_params[2] && $this->datum_params[3] == $dest->datum_params[3] && $this->datum_params[4] == $dest->datum_params[4] && $this->datum_params[5] == $dest->datum_params[5] && $this->datum_params[6] == $dest->datum_params[6]); } else if( $this->datum_type == Proj4php::$common->PJD_GRIDSHIFT || $dest->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) { throw(new Exception( "ERROR: Grid shift transformations are not implemented." )); return false; } return true; // datums are equal } /* * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z), * according to the current ellipsoid parameters. * * Latitude : Geodetic latitude in radians (input) * Longitude : Geodetic longitude in radians (input) * Height : Geodetic height, in meters (input) * X : Calculated Geocentric X coordinate, in meters (output) * Y : Calculated Geocentric Y coordinate, in meters (output) * Z : Calculated Geocentric Z coordinate, in meters (output) * */ public function geodetic_to_geocentric( $p ) { $Longitude = $p->x; $Latitude = $p->y; $Height = isset( $p->z ) ? $p->z : 0; //Z value not always supplied $Error_Code = 0; // GEOCENT_NO_ERROR; /* * * Don't blow up if Latitude is just a little out of the value * * range as it may just be a rounding issue. Also removed longitude * * test, it should be wrapped by cos() and sin(). NFW for PROJ.4, Sep/2001. */ if( $Latitude < -Proj4php::$common->HALF_PI && $Latitude > -1.001 * Proj4php::$common->HALF_PI ) { $Latitude = -Proj4php::$common->HALF_PI; } else if( $Latitude > Proj4php::$common->HALF_PI && $Latitude < 1.001 * Proj4php::$common->HALF_PI ) { $Latitude = Proj4php::$common->HALF_PI; } else if( ($Latitude < -Proj4php::$common->HALF_PI) || ($Latitude > Proj4php::$common->HALF_PI) ) { /* Latitude out of range */ Proj4php::reportError( 'geocent:lat out of range:' . $Latitude ); return null; } if( $Longitude > Proj4php::$common->PI ) $Longitude -= (2 * Proj4php::$common->PI); $Sin_Lat = sin( $Latitude ); /* sin(Latitude) */ $Cos_Lat = cos( $Latitude ); /* cos(Latitude) */ $Sin2_Lat = $Sin_Lat * $Sin_Lat; /* Square of sin(Latitude) */ $Rn = $this->a / (sqrt( 1.0e0 - $this->es * $Sin2_Lat )); /* Earth radius at location */ $p->x = ($Rn + $Height) * $Cos_Lat * cos( $Longitude ); $p->y = ($Rn + $Height) * $Cos_Lat * sin( $Longitude ); $p->z = (($Rn * (1 - $this->es)) + $Height) * $Sin_Lat; return $Error_Code; } /** * * @param object $p * @return type */ public function geocentric_to_geodetic( $p ) { /* local defintions and variables */ /* end-criterium of loop, accuracy of sin(Latitude) */ $genau = 1.E-12; $genau2 = ($genau * $genau); $maxiter = 30; $X = $p->x; $Y = $p->y; $Z = $p->z ? $p->z : 0.0; //Z value not always supplied /* $P; // distance between semi-minor axis and location $RR; // distance between center and location $CT; // sin of geocentric latitude $ST; // cos of geocentric latitude $RX; $RK; $RN; // Earth radius at location $CPHI0; // cos of start or old geodetic latitude in iterations $SPHI0; // sin of start or old geodetic latitude in iterations $CPHI; // cos of searched geodetic latitude $SPHI; // sin of searched geodetic latitude $SDPHI; // end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) $At_Pole; // indicates location is in polar region $iter; // of continous iteration, max. 30 is always enough (s.a.) $Longitude; $Latitude; $Height; */ $At_Pole = false; $P = sqrt( $X * $X + $Y * $Y ); $RR = sqrt( $X * $X + $Y * $Y + $Z * $Z ); /* special cases for latitude and longitude */ if( $P / $this->a < $genau ) { /* special case, if P=0. (X=0., Y=0.) */ $At_Pole = true; $Longitude = 0.0; /* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis * of ellipsoid (=center of mass), Latitude becomes PI/2 */ if( $RR / $this->a < $genau ) { $Latitude = Proj4php::$common->HALF_PI; $Height = -$this->b; return; } } else { /* ellipsoidal (geodetic) longitude * interval: -PI < Longitude <= +PI */ $Longitude = atan2( $Y, $X ); } /* -------------------------------------------------------------- * Following iterative algorithm was developped by * "Institut für Erdmessung", University of Hannover, July 1988. * Internet: www.ife.uni-hannover.de * Iterative computation of CPHI,SPHI and Height. * Iteration of CPHI and SPHI to 10**-12 radian res$p-> * 2*10**-7 arcsec. * -------------------------------------------------------------- */ $CT = $Z / $RR; $ST = $P / $RR; $RX = 1.0 / sqrt( 1.0 - $this->es * (2.0 - $this->es) * $ST * $ST ); $CPHI0 = $ST * (1.0 - $this->es) * $RX; $SPHI0 = $CT * $RX; $iter = 0; /* loop to find sin(Latitude) res$p-> Latitude * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */ do { ++$iter; $RN = $this->a / sqrt( 1.0 - $this->es * $SPHI0 * $SPHI0 ); /* ellipsoidal (geodetic) height */ $Height = $P * $CPHI0 + $Z * $SPHI0 - $RN * (1.0 - $this->es * $SPHI0 * $SPHI0); $RK = $this->es * $RN / ($RN + $Height); $RX = 1.0 / sqrt( 1.0 - $RK * (2.0 - $RK) * $ST * $ST ); $CPHI = $ST * (1.0 - $RK) * $RX; $SPHI = $CT * $RX; $SDPHI = $SPHI * $CPHI0 - $CPHI * $SPHI0; $CPHI0 = $CPHI; $SPHI0 = $SPHI; } while( $SDPHI * $SDPHI > $genau2 && $iter < $maxiter ); /* ellipsoidal (geodetic) latitude */ $Latitude = atan( $SPHI / abs( $CPHI ) ); $p->x = $Longitude; $p->y = $Latitude; $p->z = $Height; return $p; } /** * Convert_Geocentric_To_Geodetic * The method used here is derived from 'An Improved Algorithm for * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996 * * @param object Point $p * @return object Point $p */ public function geocentric_to_geodetic_noniter( $p ) { /* $Longitude; $Latitude; $Height; $W; // distance from Z axis $W2; // square of distance from Z axis $T0; // initial estimate of vertical component $T1; // corrected estimate of vertical component $S0; // initial estimate of horizontal component $S1; // corrected estimate of horizontal component $Sin_B0; // sin(B0), B0 is estimate of Bowring aux variable $Sin3_B0; // cube of sin(B0) $Cos_B0; // cos(B0) $Sin_p1; // sin(phi1), phi1 is estimated latitude $Cos_p1; // cos(phi1) $Rn; // Earth radius at location $Sum; // numerator of cos(phi1) $At_Pole; // indicates location is in polar region */ $X = floatval( $p->x ); // cast from string to float $Y = floatval( $p->y ); $Z = floatval( $p->z ? $p->z : 0 ); $At_Pole = false; if( $X <> 0.0 ) { $Longitude = atan2( $Y, $X ); } else { if( $Y > 0 ) { $Longitude = Proj4php::$common->HALF_PI; } else if( Y < 0 ) { $Longitude = -Proj4php::$common->HALF_PI; } else { $At_Pole = true; $Longitude = 0.0; if( $Z > 0.0 ) { /* north pole */ $Latitude = Proj4php::$common->HALF_PI; } else if( Z < 0.0 ) { /* south pole */ $Latitude = -Proj4php::$common->HALF_PI; } else { /* center of earth */ $Latitude = Proj4php::$common->HALF_PI; $Height = -$this->b; return; } } } $W2 = $X * $X + $Y * $Y; $W = sqrt( $W2 ); $T0 = $Z * Proj4php::$common->AD_C; $S0 = sqrt( $T0 * $T0 + $W2 ); $Sin_B0 = $T0 / $S0; $Cos_B0 = $W / $S0; $Sin3_B0 = $Sin_B0 * $Sin_B0 * $Sin_B0; $T1 = $Z + $this->b * $this->ep2 * $Sin3_B0; $Sum = $W - $this->a * $this->es * $Cos_B0 * $Cos_B0 * $Cos_B0; $S1 = sqrt( $T1 * $T1 + $Sum * $Sum ); $Sin_p1 = $T1 / $S1; $Cos_p1 = $Sum / $S1; $Rn = $this->a / sqrt( 1.0 - $this->es * $Sin_p1 * $Sin_p1 ); if( $Cos_p1 >= Proj4php::$common->COS_67P5 ) { $Height = $W / $Cos_p1 - $Rn; } else if( $Cos_p1 <= -Proj4php::$common->COS_67P5 ) { $Height = $W / -$Cos_p1 - $Rn; } else { $Height = $Z / $Sin_p1 + $Rn * ($this->es - 1.0); } if( $At_Pole == false ) { $Latitude = atan( $Sin_p1 / $Cos_p1 ); } $p->x = $Longitude; $p->y = $Latitude; $p->z = $Height; return $p; } /************************************************************** */ // pj_geocentic_to_wgs84( p ) // p = point to transform in geocentric coordinates (x,y,z) public function geocentric_to_wgs84( $p ) { if( $this->datum_type == Proj4php::$common->PJD_3PARAM ) { // if( x[io] == HUGE_VAL ) // continue; $p->x += $this->datum_params[0]; $p->y += $this->datum_params[1]; $p->z += $this->datum_params[2]; } else if( $this->datum_type == Proj4php::$common->PJD_7PARAM ) { $Dx_BF = $this->datum_params[0]; $Dy_BF = $this->datum_params[1]; $Dz_BF = $this->datum_params[2]; $Rx_BF = $this->datum_params[3]; $Ry_BF = $this->datum_params[4]; $Rz_BF = $this->datum_params[5]; $M_BF = $this->datum_params[6]; // if( x[io] == HUGE_VAL ) // continue; $p->x = $M_BF * ( $p->x - $Rz_BF * $p->y + $Ry_BF * $p->z) + $Dx_BF; $p->y = $M_BF * ( $Rz_BF * $p->x + $p->y - $Rx_BF * $p->z) + $Dy_BF; $p->z = $M_BF * (-$Ry_BF * $p->x + $Rx_BF * $p->y + $p->z) + $Dz_BF; } } /*************************************************************** */ // pj_geocentic_from_wgs84() // coordinate system definition, // point to transform in geocentric coordinates (x,y,z) public function geocentric_from_wgs84( $p ) { if( $this->datum_type == Proj4php::$common->PJD_3PARAM ) { //if( x[io] == HUGE_VAL ) // continue; $p->x -= $this->datum_params[0]; $p->y -= $this->datum_params[1]; $p->z -= $this->datum_params[2]; } else if( $this->datum_type == Proj4php::$common->PJD_7PARAM ) { $Dx_BF = $this->datum_params[0]; $Dy_BF = $this->datum_params[1]; $Dz_BF = $this->datum_params[2]; $Rx_BF = $this->datum_params[3]; $Ry_BF = $this->datum_params[4]; $Rz_BF = $this->datum_params[5]; $M_BF = $this->datum_params[6]; $x_tmp = ($p->x - $Dx_BF) / $M_BF; $y_tmp = ($p->y - $Dy_BF) / $M_BF; $z_tmp = ($p->z - $Dz_BF) / $M_BF; //if( x[io] == HUGE_VAL ) // continue; $p->x = $x_tmp + $Rz_BF * $y_tmp - $Ry_BF * $z_tmp; $p->y = -$Rz_BF * $x_tmp + $y_tmp + $Rx_BF * $z_tmp; $p->z = $Ry_BF * $x_tmp - $Rx_BF * $y_tmp + $z_tmp; } //cs_geocentric_from_wgs84() } }