| @ -1,227 +0,0 @@ | |||
| <?php | |||
| /* SVN FILE: $Id: BaseGeocode.php 2 2008-02-27 04:11:15Z coderjoe@coderjoe.net $ */ | |||
| /** | |||
| * GoGeocode base abstract class | |||
| * | |||
| * Copyright (c) 2008. | |||
| * Licensed under the MIT License. | |||
| * See LICENSE for detailed information. | |||
| * For credits and origins, see AUTHORS. | |||
| * | |||
| * PHP 5 | |||
| * | |||
| * @filesource | |||
| * @version $Revision: 2 $ | |||
| * @modifiedby $LastChangedBy: coderjoe@coderjoe.net $ | |||
| * @lastmodified $Date: 2008-02-26 23:11:15 -0500 (Tue, 26 Feb 2008) $ | |||
| * @license http://www.opensource.org/licenses/mit-license.php The MIT License | |||
| * | |||
| */ | |||
| /** | |||
| * Core base class for the various Geocoders | |||
| */ | |||
| abstract class BaseGeocode | |||
| { | |||
| /** | |||
| * The service API key as set by the user | |||
| * @var string | |||
| * @access protected | |||
| */ | |||
| protected $apiKey; | |||
| /** | |||
| * The earth's radius in a given unit system | |||
| * The default is 3963.1676 miles. The user can override this | |||
| * value with another value in annother system of measurement through | |||
| * the setEarthRadius() function. | |||
| * | |||
| * @var float | |||
| * @access protected | |||
| */ | |||
| protected $earthRadius; | |||
| /** | |||
| * Basic public constructor which accepts an API key. | |||
| * The public constructor also sets the earth's radius to its default value | |||
| * in miles. | |||
| * | |||
| * @param string $key The geocoding service's API key | |||
| */ | |||
| public function __construct( $key ) { | |||
| //Default to default unit of miles | |||
| //by providing the earth radius in miles | |||
| $this->setEarthRadius( 3963.1676 ); | |||
| $this->setKey( $key ); | |||
| } | |||
| /** | |||
| * Modifier for the earth mean radius | |||
| * | |||
| * @param float $rad The new radius of the earth to use. | |||
| * @access public | |||
| */ | |||
| public function setEarthRadius( $rad ) { | |||
| $this->earthRadius = $rad; | |||
| } | |||
| /** | |||
| * Modifier for the API key | |||
| * | |||
| * @param string $key The geocoding service API key to use. | |||
| * @access public | |||
| */ | |||
| public function setKey( $key ) { | |||
| $this->apiKey = $key; | |||
| } | |||
| /** | |||
| * Load XML from an address | |||
| * | |||
| * @param string $address The address representing the XML source | |||
| * @access protected | |||
| */ | |||
| protected function loadXML( $address ) { | |||
| $retVal = array(); | |||
| $contents = file_get_contents( $address ); | |||
| if( !empty( $http_response_header ) ) { | |||
| $code = $http_response_header[0]; | |||
| $matches = array(); | |||
| preg_match('/^HTTP\/\d+\.\d+\s+(\d+)\s+[\w\s]+$/',$code, $matches); | |||
| $retVal['response'] = $matches[1]; | |||
| $retVal['contents'] = $contents; | |||
| } | |||
| return $retVal; | |||
| } | |||
| /** | |||
| * Abstract function which will accept a string address | |||
| * and return an array of geocoded information for the given address. | |||
| * | |||
| * Return types for this function are mixed based on HTTP Response Codes: | |||
| * Server not found: array() | |||
| * | |||
| * 404: array( 'Response' => array( | |||
| * 'Status' => 404, | |||
| * 'Request' => the subclass specific request | |||
| * ) | |||
| * ); | |||
| * | |||
| * 200: The returned geocode information will be presented in the following format | |||
| * While the example below only contains a single result, multiple results for a single | |||
| * geocode request are possible and should be supported by subclasses | |||
| * | |||
| * array( 'Response' => array( | |||
| * 'Status' => ... | |||
| * 'Request' => ... | |||
| * ), | |||
| * 'Placemarks' => array( | |||
| * array( | |||
| * 'Accuracy' => ..., | |||
| * 'Country' => ..., | |||
| * 'AdministrativeArea' => ..., | |||
| * 'SubAdministrativeArea => ..., | |||
| * 'Locality' => ..., | |||
| * 'Thoroughfare' => ..., | |||
| * 'PostalCode' => ..., | |||
| * 'Latitude' => ..., | |||
| * 'Longitude' => ... | |||
| * ), | |||
| * array( | |||
| * 'Accuracy' => ..., | |||
| * 'Country' => ..., | |||
| * . | |||
| * . | |||
| * . | |||
| * ) | |||
| * ) | |||
| * ) | |||
| * | |||
| * @param string $address A string representing the address the user wants decoded. | |||
| * @return array This function returns an array of geocoded location information for the given address. | |||
| * @access public | |||
| */ | |||
| abstract public function geocode( $address ); | |||
| /** | |||
| * Find the distance between the two latitude and longitude coordinates | |||
| * Where the latitude and longitude coordinates are in decimal degrees format. | |||
| * | |||
| * This function uses the haversine formula as published in the article | |||
| * "Virtues of the Haversine", Sky and Telescope, vol. 68 no. 2, 1984, p. 159 | |||
| * | |||
| * References: | |||
| * http://en.wikipedia.org/w/index.php?title=Haversine_formula&oldid=176737064 | |||
| * http://www.movable-type.co.uk/scripts/gis-faq-5.1.html | |||
| * | |||
| * @param float $lat1 The first coordinate's latitude | |||
| * @param float $ong1 The first coordinate's longitude | |||
| * @param float $lat2 The second coordinate's latitude | |||
| * @param float $long2 The second coordinate's longitude | |||
| * @return float The distance between the two points in the same unit as the earth radius as set by setEarthRadius() (default miles). | |||
| * @access public | |||
| */ | |||
| public function haversinDistance( $lat1, $long1, $lat2, $long2 ) | |||
| { | |||
| $lat1 = deg2rad( $lat1 ); | |||
| $lat2 = deg2rad( $lat2 ); | |||
| $long1 = deg2rad( $long1); | |||
| $long2 = deg2rad( $long2); | |||
| $dlong = $long2 - $long1; | |||
| $dlat = $lat2 - $lat1; | |||
| $sinlat = sin( $dlat/2 ); | |||
| $sinlong = sin( $dlong/2 ); | |||
| $a = ($sinlat * $sinlat) + cos( $lat1 ) * cos( $lat2 ) * ($sinlong * $sinlong); | |||
| $c = 2 * asin( min( 1, sqrt( $a ) )); | |||
| return $this->earthRadius * $c; | |||
| } | |||
| /** | |||
| * Find the distance between two latitude and longitude points using the | |||
| * spherical law of cosines. | |||
| * | |||
| * @param float $lat1 The first coordinate's latitude | |||
| * @param float $ong1 The first coordinate's longitude | |||
| * @param float $lat2 The second coordinate's latitude | |||
| * @param float $long2 The second coordinate's longitude | |||
| * @return float The distance between the two points in the same unit as the earth radius as set by setEarthRadius() (default miles). | |||
| * @access public | |||
| */ | |||
| public function sphericalLawOfCosinesDistance( $lat1, $long1, $lat2, $long2 ) | |||
| { | |||
| $lat1 = deg2rad( $lat1 ); | |||
| $lat2 = deg2rad( $lat2 ); | |||
| $long1 = deg2rad( $long1); | |||
| $long2 = deg2rad( $long2); | |||
| return $this->earthRadius * acos( | |||
| sin( $lat1 ) * sin( $lat2 ) + | |||
| cos( $lat1 ) * cos( $lat2 ) * cos( $long2 - $long1 ) | |||
| ); | |||
| } | |||
| /** | |||
| * Find the distance between two latitude and longitude coordinates | |||
| * Where the latitude and the longitude coordinates are in decimal degrees format. | |||
| * | |||
| * @param float $lat1 The first coordinate's latitude | |||
| * @param float $ong1 The first coordinate's longitude | |||
| * @param float $lat2 The second coordinate's latitude | |||
| * @param float $long2 The second coordinate's longitude | |||
| * @return float The distance between the two points in the same unit as the earth radius as set by setEarthRadius() (default miles). | |||
| * @access public | |||
| */ | |||
| public function distanceBetween( $lat1, $long1, $lat2, $long2 ) | |||
| { | |||
| return $this->haversinDistance( $lat1, $long1, $lat2, $long2 ); | |||
| } | |||
| } | |||
| ?> | |||
| @ -1,216 +0,0 @@ | |||
| <?php | |||
| /* SVN FILE: $Id: GoogleGeocode.php 4 2009-08-31 13:58:02Z coderjoe@coderjoe.net $ */ | |||
| /** | |||
| * GoGeocode object for use with Google's Geocoding API | |||
| * | |||
| * Copyright (c) 2008. | |||
| * Licensed under the MIT License. | |||
| * See LICENSE for detailed information. | |||
| * For credits and origins, see AUTHORS. | |||
| * | |||
| * PHP 5 | |||
| * | |||
| * @filesource | |||
| * @version $Revision: 4 $ | |||
| * @modifiedby $LastChangedBy: coderjoe@coderjoe.net $ | |||
| * @lastmodified $Date: 2009-08-31 09:58:02 -0400 (Mon, 31 Aug 2009) $ | |||
| * @license http://www.opensource.org/licenses/mit-license.php The MIT License | |||
| * | |||
| */ | |||
| require_once('BaseGeocode.php'); | |||
| /** | |||
| * Geocoder object for use with Google's geocoding API | |||
| */ | |||
| class GoogleGeocode extends BaseGeocode | |||
| { | |||
| /* | |||
| * Status code information grokked from: | |||
| * http://code.google.com/apis/maps/documentation/reference.html#GGeoStatusCode | |||
| */ | |||
| /** | |||
| * Status Code: | |||
| * No errors occurred; the address was successfully parsed and its geocode has been returned. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const SUCCESS = 200; | |||
| /** | |||
| * Status Code: | |||
| * HTTP Status Code 404 Not Found | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const NOT_FOUND = 404; | |||
| /** | |||
| * Status Code: | |||
| * A directions request could not be successfully parsed. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const BAD_REQUEST = 400; | |||
| /** | |||
| * Status Code: | |||
| * A geocoding or directions request could not be successfully processed, | |||
| * yet the exact reason for the failure is not known. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const SERVER_ERROR = 500; | |||
| /** | |||
| * Status Code: | |||
| * The HTTP q parameter was either missing or had no value. | |||
| * For geocoding requests, this means that an empty address was specified as input. | |||
| * For directions requests, this means that no query was specified in the input. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const MISSING_QUERY = 601; | |||
| /** | |||
| * Status Code: | |||
| * Synonym for MISSING_QUERY. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const MISSING_ADDRESS = 601; | |||
| /** | |||
| * Status Code: | |||
| * No corresponding geographic location could be found for the specified address. | |||
| * This may be due to the fact that the address is relatively new, or it may be incorrect. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const UNKNOWN_ADDRESS = 602; | |||
| /** | |||
| * Status Code: | |||
| * The geocode for the given address or the route for the given directions query | |||
| * cannot be returned due to legal or contractual reasons. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const UNAVAILABLE_ADDRESS = 603; | |||
| /** | |||
| * Status Code: | |||
| * The GDirections object could not compute directions between the points mentioned | |||
| * in the query. This is usually because there is no route available between the two | |||
| * points, or because we do not have data for routing in that region. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const UNKNOWN_DIRECTIONS = 604; | |||
| /** | |||
| * Status Code: | |||
| * The given key is either invalid or does not match the domain for which it was given. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const BAD_KEY = 610; | |||
| /** | |||
| * Status Code: | |||
| * The given key has gone over the requests limit in the 24 hour period. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const TOO_MANY_QUERIES = 620; | |||
| /** | |||
| * Geocode the provided API. See BaseGeocode::geocode for detailed information | |||
| * about this function's return type. | |||
| * | |||
| * @param string $address The string address to retrieve geocode information about | |||
| * @return array An empty array on server not found. Otherwise an array of geocoded location information. | |||
| */ | |||
| public function geocode( $address ) | |||
| { | |||
| $retVal = array(); | |||
| $url = "http://maps.google.com/maps/geo?q="; | |||
| $url .= urlencode( $address ) . "&output=xml&oe=UTF-8&key=" . $this->apiKey; | |||
| $nsKml = 'http://earth.google.com/kml/2.0'; | |||
| $nsUrn = 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'; | |||
| $file = $this->loadXML( $url ); | |||
| if( empty( $file ) ) { | |||
| return $retVal; | |||
| } | |||
| $retVal['Response'] = array( | |||
| 'Status' => (int)$file['response'], | |||
| 'Request' => 'geo' | |||
| ); | |||
| if( $file['response'] == 200 ) { | |||
| $xml = new SimpleXMLElement( $file['contents'] ); | |||
| $xml->registerXPathNamespace( 'kml', $nsKml ); | |||
| $xml->registerXPathNamespace( 'urn', $nsUrn ); | |||
| //Now that we have the google request, and we succeeded in getting a response | |||
| //from the server, lets replace oure response portion with the google response | |||
| $retVal['Response']['Status'] = (int)$xml->Response->Status->code; | |||
| $retVal['Response']['Request'] = (string)$xml->Response->Status->request; | |||
| $retVal['Placemarks'] = array(); | |||
| if( $xml && $retVal['Response']['Status'] == GoogleGeocode::SUCCESS ) | |||
| { | |||
| $placemarks = $xml->xpath('//kml:Placemark'); | |||
| $countries = $xml->xpath('//urn:CountryNameCode'); | |||
| $adminAreas = $xml->xpath('//urn:AdministrativeAreaName'); | |||
| $subAdminAreas = $xml->xpath('//urn:SubAdministrativeAreaName'); | |||
| $localities = $xml->xpath('//urn:LocalityName'); | |||
| $thoroughfares = $xml->xpath('//urn:ThoroughfareName'); | |||
| $postalCodes = $xml->xpath('//urn:PostalCodeNumber'); | |||
| for( $i = 0; $i < count( $placemarks ); $i++ ) | |||
| { | |||
| list($longitude, $latitude) = explode( ',' , $placemarks[$i]->Point->coordinates ); | |||
| $attributes = $placemarks[$i]->AddressDetails->attributes(); | |||
| $retVal['Placemarks'][$i] = array(); | |||
| $retVal['Placemarks'][$i]['Accuracy'] = (int)$attributes['Accuracy']; | |||
| $retVal['Placemarks'][$i]['Country'] = (string)$countries[$i]; | |||
| if( count( $adminAreas ) > $i ) { | |||
| $retVal['Placemarks'][$i]['AdministrativeArea'] = (string)$adminAreas[$i]; | |||
| } | |||
| if( count( $subAdminAreas ) > $i ) { | |||
| $retVal['Placemarks'][$i]['SubAdministrativeArea'] = (string)$subAdminAreas[$i]; | |||
| } | |||
| if( count( $localities ) > $i ) { | |||
| $retVal['Placemarks'][$i]['Locality'] = (string)$localities[$i]; | |||
| } | |||
| if( count( $thoroughfares ) > $i ) { | |||
| $retVal['Placemarks'][$i]['Thoroughfare'] = (string)$thoroughfares[$i]; | |||
| } | |||
| if( count( $postalCodes ) > $i ) { | |||
| $retVal['Placemarks'][$i]['PostalCode'] = (int)$postalCodes[$i]; | |||
| } | |||
| $retVal['Placemarks'][$i]['Latitude']= (double)$latitude; | |||
| $retVal['Placemarks'][$i]['Longitude'] = (double)$longitude; | |||
| } | |||
| } | |||
| } | |||
| return $retVal; | |||
| } | |||
| } | |||
| ?> | |||
| @ -1,168 +0,0 @@ | |||
| <?php | |||
| /* SVN FILE: $Id: YahooGeocode.php 2 2008-02-27 04:11:15Z coderjoe@coderjoe.net $ */ | |||
| /** | |||
| * GoGeocode object for use with the Yahoo Geocoding API | |||
| * | |||
| * Copyright (c) 2008. | |||
| * Licensed under the MIT License. | |||
| * See LICENSE for detailed information. | |||
| * For credits and origins, see AUTHORS. | |||
| * | |||
| * PHP 5 | |||
| * | |||
| * @filesource | |||
| * @version $Revision: 2 $ | |||
| * @modifiedby $LastChangedBy: coderjoe@coderjoe.net $ | |||
| * @lastmodified $Date: 2008-02-26 23:11:15 -0500 (Tue, 26 Feb 2008) $ | |||
| * @license http://www.opensource.org/licenses/mit-license.php The MIT License | |||
| * | |||
| */ | |||
| require_once('BaseGeocode.php'); | |||
| /** | |||
| * Geocoder object for use with the Yahoo Geocoding API | |||
| */ | |||
| class YahooGeocode extends BaseGeocode | |||
| { | |||
| /* | |||
| * Yahoo status codes grokked from: | |||
| * http://developer.yahoo.com/search/errors.html | |||
| */ | |||
| /** | |||
| * Status Code: | |||
| * HTTP Status 200 Success! | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const SUCCESS = 200; | |||
| /** | |||
| * Status Code: | |||
| * HTTP Status 404 Not Found | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const NOT_FOUND = 404; | |||
| /** | |||
| * Status Code: | |||
| * Bad request. The parameters passed to the service did not match as expected. | |||
| * The Message should tell you what was missing or incorrect. | |||
| * (Note: BaseGeocode does not return the error message) | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const BAD_REQUEST = 400; | |||
| /** | |||
| * Status Code: | |||
| * Forbidden. You do not have permission to access this resource, or are over your rate limit. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const BAD_KEY = 403; | |||
| /** | |||
| * Status Code: | |||
| * Forbidden. You do not have permission to access this resource, or are over your rate limit. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const TOO_MANY_QUERIES = 403; | |||
| /** | |||
| * Status Code: | |||
| * Service unavailable. An internal problem prevented us from returning data to you. | |||
| * @var int | |||
| * @access public | |||
| */ | |||
| const SERVER_ERROR = 503; | |||
| /** | |||
| * Geocode the given address. See BaseGeocode::geocode for detailed information | |||
| * about this function's return type. | |||
| * | |||
| * @param string $address The string address to retrieve geocode information about | |||
| * @return array An empty array on server not found. Otherwise an array of request and geocoded location information. | |||
| */ | |||
| public function geocode( $address ) | |||
| { | |||
| $retVal = array(); | |||
| $urlBase = 'http://api.local.yahoo.com'; | |||
| $serviceName = '/MapsService'; | |||
| $version = '/V1'; | |||
| $method = '/geocode'; | |||
| $request = $urlBase; | |||
| $request .= $serviceName; | |||
| $request .= $version; | |||
| $request .= $method . '?location=' . urlencode( $address ) . '&appid=' . $this->apiKey; | |||
| $file = $this->loadXML( $request ); | |||
| if( empty( $file ) ) { | |||
| return $retVal; | |||
| } | |||
| $retVal['Response'] = array( | |||
| 'Status' => $file['response'], | |||
| 'Request' => 'geocode' | |||
| ); | |||
| if( $retVal['Response']['Status'] == YahooGeocode::SUCCESS ) { | |||
| $xml = new SimpleXMLElement( $file['contents'] ); | |||
| $xml->registerXPathNamespace('urn','urn:yahoo:maps'); | |||
| $retVal['Placemarks'] = array(); | |||
| if( $xml ) { | |||
| $results = $xml->xpath('//urn:Result'); | |||
| $countries = $xml->xpath('//urn:Country'); | |||
| $adminAreas = $xml->xpath('//urn:State'); | |||
| //Yahoo Geocoding has no Sub-Administrative Area (County) support. | |||
| $localities = $xml->xpath('//urn:City'); | |||
| $thoroughfares = $xml->xpath('//urn:Address'); | |||
| $postalCodes = $xml->xpath('//urn:Zip'); | |||
| $latitudes = $xml->xpath('//urn:Latitude'); | |||
| $longitudes = $xml->xpath('//urn:Longitude'); | |||
| if( $results ) { | |||
| for( $i = 0; $i < count( $results ); $i++ ) { | |||
| $attributes = $results[$i]->attributes(); | |||
| $retVal['Placemarks'][$i]['Accuracy'] = (string)$attributes['precision']; | |||
| $retVal['Placemarks'][$i]['Country'] = (string)$countries[$i]; | |||
| if( count($adminAreas) > $i && !empty($adminAreas[$i]) ) { | |||
| $retVal['Placemarks'][$i]['AdministrativeArea'] = (string) $adminAreas[$i]; | |||
| } | |||
| if( count($localities) > $i && !empty($localities[$i]) ) { | |||
| $retVal['Placemarks'][$i]['Locality'] = (string) $localities[$i]; | |||
| } | |||
| if( count($thoroughfares) > $i && !empty($thoroughfares[$i]) ) { | |||
| $retVal['Placemarks'][$i]['Thoroughfare'] = (string) $thoroughfares[$i]; | |||
| } | |||
| if( count($postalCodes) > $i && !empty($postalCodes[$i]) ) { | |||
| $postalCode = explode( '-', $postalCodes[$i] ); | |||
| $retVal['Placemarks'][$i]['PostalCode'] = (int) $postalCode[0]; | |||
| } | |||
| $retVal['Placemarks'][$i]['Latitude'] = (double)$latitudes[$i]; | |||
| $retVal['Placemarks'][$i]['Longitude'] = (double)$longitudes[$i]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return $retVal; | |||
| } | |||
| } | |||
| ?> | |||