<?php /** * PHP class to convert Latitude & Longitude coordinates into UTM & Lambert Conic Conformal Northing/Easting coordinates. * * This class encapsulates the methods for representing a geographic point on the earth in three different coordinate systema. Lat/Long, UTM and Lambert Conic Conformal. * * Code for datum and UTM conversion was converted from C++ code written by Chuck Gantz (chuck.gantz@globalstar.com) from http://www.gpsy.com/gpsinfo/geotoutm/ * This code was converted into PHP by Brenor Brophy (brenor@sbcglobal.net) and later refactored for PHP 5.3 by Hans Duedal (hd@onlinecity.dk). * * @author chuck.gantz@globalstar.com, brenor@sbcglobal.net, hd@onlinecity.dk * @version 1.0 * * COPYRIGHT (c) 2005, 2006, 2007, 2008 BRENOR BROPHY * The source code included in this package is free software; you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation. This license can be read at: * * http://www.opensource.org/licenses/gpl-license.php * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * @link http://www.gpsy.com/gpsinfo/geotoutm/ * @link http://www.phpclasses.org/browse/file/10671.html * @link https://gist.github.com/840476 */ class gPoint { /** * Reference ellipsoids derived from Peter H. Dana's website: * http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html * Department of Geography, University of Texas at Austin * Internet: pdana@mail.utexas.edu 3/22/95 * Source: * Defense Mapping Agency. 1987b. DMA Technical Report: Supplement to Department of Defense World Geodetic System 1984 Technical Report. Part I and II. * Washington, DC: Defense Mapping Agency * Alternative names added in for easy compatibility by hd@onlinecity.dk * * @var array - format ("Ellipsoid name" => array(Equatorial Radius, square of eccentricity)) */ public static $ellipsoid = array( "Airy" =>array (6377563, 0.00667054), "Australian National" =>array (6378160, 0.006694542), "Bessel 1841" =>array (6377397, 0.006674372), "Bessel 1841 Nambia" =>array (6377484, 0.006674372), "Clarke 1866" =>array (6378206, 0.006768658), "Clarke 1880" =>array (6378249, 0.006803511), "Everest" =>array (6377276, 0.006637847), "Fischer 1960 Mercury" =>array (6378166, 0.006693422), "Fischer 1968" =>array (6378150, 0.006693422), "GRS 1967" =>array (6378160, 0.006694605), "GRS 1980" =>array (6378137, 0.00669438), "Helmert 1906" =>array (6378200, 0.006693422), "Hough" =>array (6378270, 0.00672267), "International" =>array (6378388, 0.00672267), "Krassovsky" =>array (6378245, 0.006693422), "Modified Airy" =>array (6377340, 0.00667054), "Modified Everest" =>array (6377304, 0.006637847), "Modified Fischer 1960" =>array (6378155, 0.006693422), "South American 1969" =>array (6378160, 0.006694542), "WGS 60" =>array (6378165, 0.006693422), "WGS 66" =>array (6378145, 0.006694542), "WGS 72" =>array (6378135, 0.006694318), "WGS 84" =>array (6378137, 0.00669438), // Alternative names, added in for easy compatibility by hd@onlinecity.dk "ED50" =>array (6378388, 0.00672267), // International Ellipsoid "EUREF89" =>array (6378137, 0.00669438), // Max deviation from WGS 84 is 40 cm/km see (in danish) http://www2.kms.dk/C1256AED004E87BA/(AllDocsByDocId)/3382517647F695C9C1256BC700265CE7 "ETRS89" =>array (6378137, 0.00669438) // Same as EUREF89 ); // Properties protected $a; // Equatorial Radius protected $e2; // Square of eccentricity protected $datum; // Selected datum protected $Xp, $Yp; // X,Y pixel location protected $lat, $long; // Latitude & Longitude of the point protected $utmNorthing, $utmEasting, $utmZone; // UTM Coordinates of the point protected $lccNorthing, $lccEasting; // Lambert coordinates of the point protected $falseNorthing, $falseEasting; // Origin coordinates for Lambert Projection protected $latOfOrigin; // For Lambert Projection protected $longOfOrigin; // For Lambert Projection protected $firstStdParallel; // For lambert Projection protected $secondStdParallel; // For lambert Projection /** * Constructs the object and sets the datum * * @param string $datum */ public function __construct($datum='WGS 84') // Default datum is WGS 84 { $this->a = self::$ellipsoid[$datum][0]; // Set datum Equatorial Radius $this->e2 = self::$ellipsoid[$datum][1]; // Set datum Square of eccentricity $this->datum = $datum; // Save the datum } /** * Set the datum * * @param string $datum */ public function setDatum($datum='WGS 84') { $this->a = self::$ellipsoid[$datum][0]; // Set datum Equatorial Radius $this->e2 = self::$ellipsoid[$datum][1]; // Set datum Square of eccentricity $this->datum = $datum; // Save the datum } /** * Set X & Y pixel of the point (used if it is being drawn on an image) * * @param integer $x * @param integer $y */ public function setXY($x, $y) { $this->Xp = $x; $this->Yp = $y; } /** * Get X pixel location */ public function Xp() { return $this->Xp; } /** * Get Y pixel location */ public function Yp() { return $this->Yp; } /** * Set/Get/Output Longitude & Latitude of the point * @param float $long * @param float $lat */ public function setLongLat($long, $lat) { $this->long = $long; $this->lat = $lat; } /** * Get latitude * */ public function Lat() { return $this->lat; } /** * Get longitude * */ public function Long() { return $this->long; } /** * Print latitude/longitude * */ public function printLatLong() { printf("Latitude: %1.5f Longitude: %1.5f",$this->lat, $this->long); } /** * Set Universal Transverse Mercator Coordinates * * @param integer $easting * @param integer $northing * @param string $zone */ public function setUTM($easting, $northing, $zone='') // Zone is optional { $this->utmNorthing = $northing; $this->utmEasting = $easting; $this->utmZone = $zone; } /** * Get utm northing * */ public function N() { return $this->utmNorthing; } /** * Get utm easting * */ public function E() { return $this->utmEasting; } /** * Get utm zone */ public function Z() { return $this->utmZone; } /** * Print UTM coordinates * */ public function printUTM() { print( "Northing: ".(int)$this->utmNorthing.", Easting: ".(int)$this->utmEasting.", Zone: ".$this->utmZone); } /** * Set the lambert coordinates * * @param integer $northing * @param integer $easting */ public function setLambert($northing, $easting) { $this->lccNorthing = $northing; $this->lccEasting = $easting; } /** * Get lccNorthing */ public function lccN() { return $this->lccNorthing; } /** * Get lccEasting */ public function lccE() { return $this->lccEasting; } /** * Print lambert coordinates * */ public function printLambert() { print( "Northing: ".(int)$this->lccNorthing.", Easting: ".(int)$this->lccEasting); } /** * Convert Longitude/Latitude to UTM * * Equations from USGS Bulletin 1532 * East Longitudes are positive, West longitudes are negative. * North latitudes are positive, South latitudes are negative * Lat and Long are in decimal degrees * Written by Chuck Gantz- chuck.gantz@globalstar.com, converted to PHP by * Brenor Brophy, brenor@sbcglobal.net * * UTM coordinates are useful when dealing with paper maps. Basically the * map will can cover a single UTM zone which is 6 degrees on longitude. * So you really don't care about an object crossing two zones. You just get a * second map of the other zone. However, if you happen to live in a place that * straddles two zones (For example the Santa Babara area in CA straddles zone 10 * and zone 11) Then it can become a real pain having to have two maps all the time. * So relatively small parts of the world (like say California) creat their own * version of UTM coordinates that are adjusted to conver the whole area of interest * on a single map. These are called state grids. The projection system is the * usually same as UTM (i.e. Transverse Mercator), but the central meridian * aka Longitude of Origin is selected to suit the logitude of the area being * mapped (like being moved to the central meridian of the area) and the grid * may cover more than the 6 degrees of longitude found on a UTM map. Areas * that are wide rather than long - think Montana as an example. May still * have to have a couple of maps to cover the whole state because TM projection * looses accuracy as you move further away from the Longitude of Origin, 15 degrees * is usually the limit. * * Now, in the case where we want to generate electronic maps that may be * placed pretty much anywhere on the globe we really don't to deal with the * issue of UTM zones in our coordinate system. We would really just like a * grid that is fully contigious over the area of the map we are drawing. Similiar * to the state grid, but local to the area we are interested in. I call this * Local Transverse Mercator and I have modified the function below to also * make this conversion. If you pass a Longitude value to the function as $LongOrigin * then that is the Longitude of Origin that will be used for the projection. * Easting coordinates will be returned (in meters) relative to that line of * longitude - So an Easting coordinate for a point located East of the longitude * of origin will be a positive value in meters, an Easting coordinate for a point * West of the longitude of Origin will have a negative value in meters. Northings * will always be returned in meters from the equator same as the UTM system. The * UTMZone value will be valid for Long/Lat given - thought it is not meaningful * in the context of Local TM. If a NULL value is passed for $LongOrigin * then the standard UTM coordinates are calculated. * * @param float $LongOrigin */ public function convertLLtoTM($LongOrigin) { $k0 = 0.9996; $falseEasting = 0.0; //Make sure the longitude is between -180.00 .. 179.9 $LongTemp = ($this->long+180)-(integer)(($this->long+180)/360)*360-180; // -180.00 .. 179.9; $LatRad = deg2rad($this->lat); $LongRad = deg2rad($LongTemp); if (!$LongOrigin) { // Do a standard UTM conversion - so findout what zone the point is in $ZoneNumber = (integer)(($LongTemp + 180)/6) + 1; if( $this->lat >= 56.0 && $this->lat < 64.0 && $LongTemp >= 3.0 && $LongTemp < 12.0 ) $ZoneNumber = 32; // Special zones for Svalbard if( $this->lat >= 72.0 && $this->lat < 84.0 ) { if($LongTemp >= 0.0 && $LongTemp < 9.0) { $ZoneNumber = 31; } else if($LongTemp >= 9.0 && $LongTemp < 21.0) { $ZoneNumber = 33; } else if($LongTemp >= 21.0 && $LongTemp < 33.0) { $ZoneNumber = 35; } else if($LongTemp >= 33.0 && $LongTemp < 42.0) { $ZoneNumber = 37; } } $LongOrigin = ($ZoneNumber - 1)*6 - 180 + 3; //+3 puts origin in middle of zone //compute the UTM Zone from the latitude and longitude $this->utmZone = sprintf("%d%s", $ZoneNumber, $this->UTMLetterDesignator()); // We also need to set the false Easting value adjust the UTM easting coordinate $falseEasting = 500000.0; } $LongOriginRad = deg2rad($LongOrigin); $eccPrimeSquared = ($this->e2)/(1-$this->e2); $N = $this->a/sqrt(1-$this->e2*sin($LatRad)*sin($LatRad)); $T = tan($LatRad)*tan($LatRad); $C = $eccPrimeSquared*cos($LatRad)*cos($LatRad); $A = cos($LatRad)*($LongRad-$LongOriginRad); $M = $this->a*((1 - $this->e2/4 - 3*$this->e2*$this->e2/64 - 5*$this->e2*$this->e2*$this->e2/256)*$LatRad - (3*$this->e2/8 + 3*$this->e2*$this->e2/32 + 45*$this->e2*$this->e2*$this->e2/1024)*sin(2*$LatRad) + (15*$this->e2*$this->e2/256 + 45*$this->e2*$this->e2*$this->e2/1024)*sin(4*$LatRad) - (35*$this->e2*$this->e2*$this->e2/3072)*sin(6*$LatRad)); $this->utmEasting = ($k0*$N*($A+(1-$T+$C)*$A*$A*$A/6 + (5-18*$T+$T*$T+72*$C-58*$eccPrimeSquared)*$A*$A*$A*$A*$A/120) + $falseEasting); $this->utmNorthing = ($k0*($M+$N*tan($LatRad)*($A*$A/2+(5-$T+9*$C+4*$C*$C)*$A*$A*$A*$A/24 + (61-58*$T+$T*$T+600*$C-330*$eccPrimeSquared)*$A*$A*$A*$A*$A*$A/720))); if($this->lat < 0) $this->utmNorthing += 10000000.0; //10000000 meter offset for southern hemisphere } /** * This routine determines the correct UTM letter designator for the given latitude * returns 'Z' if latitude is outside the UTM limits of 84N to 80S * Written by Chuck Gantz- chuck.gantz@globalstar.com, converted to PHP by Brenor Brophy, brenor@sbcglobal.net */ public function UTMLetterDesignator() { if((84 >= $this->lat) && ($this->lat >= 72)) $LetterDesignator = 'X'; else if((72 > $this->lat) && ($this->lat >= 64)) $LetterDesignator = 'W'; else if((64 > $this->lat) && ($this->lat >= 56)) $LetterDesignator = 'V'; else if((56 > $this->lat) && ($this->lat >= 48)) $LetterDesignator = 'U'; else if((48 > $this->lat) && ($this->lat >= 40)) $LetterDesignator = 'T'; else if((40 > $this->lat) && ($this->lat >= 32)) $LetterDesignator = 'S'; else if((32 > $this->lat) && ($this->lat >= 24)) $LetterDesignator = 'R'; else if((24 > $this->lat) && ($this->lat >= 16)) $LetterDesignator = 'Q'; else if((16 > $this->lat) && ($this->lat >= 8)) $LetterDesignator = 'P'; else if(( 8 > $this->lat) && ($this->lat >= 0)) $LetterDesignator = 'N'; else if(( 0 > $this->lat) && ($this->lat >= -8)) $LetterDesignator = 'M'; else if((-8 > $this->lat) && ($this->lat >= -16)) $LetterDesignator = 'L'; else if((-16 > $this->lat) && ($this->lat >= -24)) $LetterDesignator = 'K'; else if((-24 > $this->lat) && ($this->lat >= -32)) $LetterDesignator = 'J'; else if((-32 > $this->lat) && ($this->lat >= -40)) $LetterDesignator = 'H'; else if((-40 > $this->lat) && ($this->lat >= -48)) $LetterDesignator = 'G'; else if((-48 > $this->lat) && ($this->lat >= -56)) $LetterDesignator = 'F'; else if((-56 > $this->lat) && ($this->lat >= -64)) $LetterDesignator = 'E'; else if((-64 > $this->lat) && ($this->lat >= -72)) $LetterDesignator = 'D'; else if((-72 > $this->lat) && ($this->lat >= -80)) $LetterDesignator = 'C'; else $LetterDesignator = 'Z'; //This is here as an error flag to show that the Latitude is outside the UTM limits return($LetterDesignator); } /** * Convert UTM to Longitude/Latitude * * Equations from USGS Bulletin 1532 * East Longitudes are positive, West longitudes are negative. * North latitudes are positive, South latitudes are negative * Lat and Long are in decimal degrees. * Written by Chuck Gantz- chuck.gantz@globalstar.com, converted to PHP by * Brenor Brophy, brenor@sbcglobal.net * * If a value is passed for $LongOrigin the the function assumes that * a Local (to the Longitude of Origin passed in) Transverse Mercator * coordinates is to be converted - not a UTM coordinate. This is the * complementary function to the previous one. The function cannot * tell if a set of LOCALNorthing/Easting coordinates are in the North * or South hemesphere - they just give distance from the equator not * direction - so only northern hemesphere lat/long coordinates are returned. * If you live south of the equator there is a note later in the code * explaining how to have it just return southern hemesphere lat/longs. * * @param float $LongOrigin */ public function convertTMtoLL($LongOrigin=null) { $k0 = 0.9996; $e1 = (1-sqrt(1-$this->e2))/(1+sqrt(1-$this->e2)); $falseEasting = 0.0; $y = $this->utmNorthing; if (!$LongOrigin) { // It is a UTM coordinate we want to convert sscanf($this->utmZone,"%d%s",$ZoneNumber,$ZoneLetter); if($ZoneLetter >= 'N') { $NorthernHemisphere = 1;//point is in northern hemisphere } else { $NorthernHemisphere = 0;//point is in southern hemisphere $y -= 10000000.0;//remove 10,000,000 meter offset used for southern hemisphere } $LongOrigin = ($ZoneNumber - 1)*6 - 180 + 3; //+3 puts origin in middle of zone $falseEasting = 500000.0; } // $y -= 10000000.0; // Uncomment line to make LOCAL coordinates return southern hemesphere Lat/Long $x = $this->utmEasting - $falseEasting; //remove 500,000 meter offset for longitude $eccPrimeSquared = ($this->e2)/(1-$this->e2); $M = $y / $k0; $mu = $M/($this->a*(1-$this->e2/4-3*$this->e2*$this->e2/64-5*$this->e2*$this->e2*$this->e2/256)); $phi1Rad = $mu + (3*$e1/2-27*$e1*$e1*$e1/32)*sin(2*$mu) + (21*$e1*$e1/16-55*$e1*$e1*$e1*$e1/32)*sin(4*$mu) +(151*$e1*$e1*$e1/96)*sin(6*$mu); $phi1 = rad2deg($phi1Rad); $N1 = $this->a/sqrt(1-$this->e2*sin($phi1Rad)*sin($phi1Rad)); $T1 = tan($phi1Rad)*tan($phi1Rad); $C1 = $eccPrimeSquared*cos($phi1Rad)*cos($phi1Rad); $R1 = $this->a*(1-$this->e2)/pow(1-$this->e2*sin($phi1Rad)*sin($phi1Rad), 1.5); $D = $x/($N1*$k0); $tlat = $phi1Rad - ($N1*tan($phi1Rad)/$R1)*($D*$D/2-(5+3*$T1+10*$C1-4*$C1*$C1-9*$eccPrimeSquared)*$D*$D*$D*$D/24 +(61+90*$T1+298*$C1+45*$T1*$T1-252*$eccPrimeSquared-3*$C1*$C1)*$D*$D*$D*$D*$D*$D/720); $this->lat = rad2deg($tlat); $tlong = ($D-(1+2*$T1+$C1)*$D*$D*$D/6+(5-2*$C1+28*$T1-3*$C1*$C1+8*$eccPrimeSquared+24*$T1*$T1) *$D*$D*$D*$D*$D/120)/cos($phi1Rad); $this->long = $LongOrigin + rad2deg($tlong); } /** * Configure a Lambert Conic Conformal Projection * * falseEasting & falseNorthing are just an offset in meters added to the final * coordinate calculated. * * longOfOrigin & LatOfOrigin are the "center" latitiude and longitude of the * area being projected. All coordinates will be calculated in meters relative * to this point on the earth. * * firstStdParallel & secondStdParallel are the two lines of longitude (that * is they run east-west) that define where the "cone" intersects the earth. * Simply put they should bracket the area being projected. * * google is your friend to find out more * * @param integer $falseEasting * @param integer $falseNorthing * @param float $longOfOrigin * @param float $latOfOrigin * @param float $firstStdParallel * @param float $secondStdParallel */ public function configLambertProjection ($falseEasting, $falseNorthing, $longOfOrigin, $latOfOrigin, $firstStdParallel, $secondStdParallel) { $this->falseEasting = $falseEasting; $this->falseNorthing = $falseNorthing; $this->longOfOrigin = $longOfOrigin; $this->latOfOrigin = $latOfOrigin; $this->firstStdParallel = $firstStdParallel; $this->secondStdParallel = $secondStdParallel; } /** * Convert Longitude/Latitude to Lambert Conic Easting/Northing * * This routine will convert a Latitude/Longitude coordinate to an Northing/ * Easting coordinate on a Lambert Conic Projection. The configLambertProjection() * function should have been called prior to this one to setup the specific * parameters for the projection. The Northing/Easting parameters calculated are * in meters (because the datum used is in meters) and are relative to the * falseNorthing/falseEasting coordinate. Which in turn is relative to the * Lat/Long of origin The formula were obtained from URL: * http://www.ihsenergy.com/epsg/guid7_2.html. * Code was written by Brenor Brophy, brenor@sbcglobal.net * */ public function convertLLtoLCC() { $e = sqrt($this->e2); $phi = deg2rad($this->lat); // Latitude to convert $phi1 = deg2rad($this->firstStdParallel); // Latitude of 1st std parallel $phi2 = deg2rad($this->secondStdParallel); // Latitude of 2nd std parallel $lamda = deg2rad($this->long); // Lonitude to convert $phio = deg2rad($this->latOfOrigin); // Latitude of Origin $lamdao = deg2rad($this->longOfOrigin); // Longitude of Origin $m1 = cos($phi1) / sqrt(( 1 - $this->e2*sin($phi1)*sin($phi1))); $m2 = cos($phi2) / sqrt(( 1 - $this->e2*sin($phi2)*sin($phi2))); $t1 = tan((pi()/4)-($phi1/2)) / pow(( ( 1 - $e*sin($phi1) ) / ( 1 + $e*sin($phi1) )),$e/2); $t2 = tan((pi()/4)-($phi2/2)) / pow(( ( 1 - $e*sin($phi2) ) / ( 1 + $e*sin($phi2) )),$e/2); $to = tan((pi()/4)-($phio/2)) / pow(( ( 1 - $e*sin($phio) ) / ( 1 + $e*sin($phio) )),$e/2); $t = tan((pi()/4)-($phi /2)) / pow(( ( 1 - $e*sin($phi ) ) / ( 1 + $e*sin($phi ) )),$e/2); $n = (log($m1)-log($m2)) / (log($t1)-log($t2)); $F = $m1/($n*pow($t1,$n)); $rf = $this->a*$F*pow($to,$n); $r = $this->a*$F*pow($t,$n); $theta = $n*($lamda - $lamdao); $this->lccEasting = $this->falseEasting + $r*sin($theta); $this->lccNorthing = $this->falseNorthing + $rf - $r*cos($theta); } /** * Convert Easting/Northing on a Lambert Conic projection to Longitude/Latitude * * This routine will convert a Lambert Northing/Easting coordinate to an * Latitude/Longitude coordinate. The configLambertProjection() function should * have been called prior to this one to setup the specific parameters for the * projection. The Northing/Easting parameters are in meters (because the datum * used is in meters) and are relative to the falseNorthing/falseEasting * coordinate. Which in turn is relative to the Lat/Long of origin The formula * were obtained from URL http://www.ihsenergy.com/epsg/guid7_2.html. Code * was written by Brenor Brophy, brenor@sbcglobal.net */ public function convertLCCtoLL() { $e = sqrt($e2); $phi1 = deg2rad($this->firstStdParallel); // Latitude of 1st std parallel $phi2 = deg2rad($this->secondStdParallel); // Latitude of 2nd std parallel $phio = deg2rad($this->latOfOrigin); // Latitude of Origin $lamdao = deg2rad($this->longOfOrigin); // Longitude of Origin $E = $this->lccEasting; $N = $this->lccNorthing; $Ef = $this->falseEasting; $Nf = $this->falseNorthing; $m1 = cos($phi1) / sqrt(( 1 - $this->e2*sin($phi1)*sin($phi1))); $m2 = cos($phi2) / sqrt(( 1 - $this->e2*sin($phi2)*sin($phi2))); $t1 = tan((pi()/4)-($phi1/2)) / pow(( ( 1 - $e*sin($phi1) ) / ( 1 + $e*sin($phi1) )),$e/2); $t2 = tan((pi()/4)-($phi2/2)) / pow(( ( 1 - $e*sin($phi2) ) / ( 1 + $e*sin($phi2) )),$e/2); $to = tan((pi()/4)-($phio/2)) / pow(( ( 1 - $e*sin($phio) ) / ( 1 + $e*sin($phio) )),$e/2); $n = (log($m1)-log($m2)) / (log($t1)-log($t2)); $F = $m1/($n*pow($t1,$n)); $rf = $this->a*$F*pow($to,$n); $r_ = sqrt( pow(($E-$Ef),2) + pow(($rf-($N-$Nf)),2) ); $t_ = pow($r_/($this->a*$F),(1/$n)); $theta_ = atan(($E-$Ef)/($rf-($N-$Nf))); $lamda = $theta_/$n + $lamdao; $phi0 = (pi()/2) - 2*atan($t_); $phi1 = (pi()/2) - 2*atan($t_*pow(((1-$e*sin($phi0))/(1+$e*sin(phi0))),$e/2)); $phi2 = (pi()/2) - 2*atan($t_*pow(((1-$e*sin($phi1))/(1+$e*sin(phi1))),$e/2)); $phi = (pi()/2) - 2*atan($t_*pow(((1-$e*sin($phi2))/(1+$e*sin(phi2))),$e/2)); $this->lat = rad2deg($phi); $this->long = rad2deg($lamda); } /** * This is a useful function that returns the Great Circle distance from the gPoint to another Long/Lat coordinate * * Result is returned as meters * @param float $lon1 * @param float $lat1 */ public function distanceFrom($lon1, $lat1) { $lon2 = deg2rad($this->Long()); $lat2 = deg2rad($this->Lat()); $theta = $lon2 - $lon1; $dist = acos(sin($lat1) * sin($lat2) + cos($lat1) * cos($lat2) * cos($theta)); // Alternative formula supposed to be more accurate for short distances // $dist = 2*asin(sqrt( pow(sin(($lat1-$lat2)/2),2) + cos($lat1)*cos($lat2)*pow(sin(($lon1-$lon2)/2),2))); return ( $dist * 6366710 ); // from http://williams.best.vwh.net/avform.htm#GCF } /** * This function also calculates the distance between two points. In this case it just uses Pythagoras's theorm using TM coordinates. * @param gPoint $pt */ public function distanceFromTM(&$pt) { $E1 = $pt->E(); $N1 = $pt->N(); $E2 = $this->E(); $N2 = $this->N(); $dist = sqrt(pow(($E1-$E2),2)+pow(($N1-$N2),2)); return $dist; } /** * This function geo-references a gePoint to a given map. This means that it * calculates the x,y pixel coordinate that coresponds to the Lat/Long value of * the geoPoint. The calculation is done using the Transverse Mercator(TM) * coordinates of the gPoint with respect to the TM coordinates of the center * point of the map. So this only makes sense if you are using Local TM * projection. * * $rX & $rY are the pixel coordinates that correspond to the Northing/Easting * ($rE/$rN) coordinate it is to this coordinate that he point will be * geo-referenced. The $LongOrigin is needed to make sure the Easting/Northing * coordinates of the point are correctly converted. * * @param integer $rX * @param integer $rY * @param integer $rE * @param integer $rN * @param integer $Scale * @param float $LongOrigin */ public function gRef($rX, $rY, $rE, $rN, $Scale, $LongOrigin) { $this->convertLLtoTM($LongOrigin); $x = (($this->E() - $rE) / $Scale) // The easting in meters times the scale to get pixels // is relative to the center of the image so adjust to + ($rX); // the left coordinate. $y = $rY - // Adjust to bottom coordinate. (($rN - $this->N()) / $Scale); // The northing in meters // relative to the equator. Subtract center point northing // to get relative to image center and convert meters to pixels $this->setXY((int)$x,(int)$y); // Save the geo-referenced result. } }