Created
June 25, 2015 17:00
-
-
Save shoaibi/6bb2c50c1768e94e1a13 to your computer and use it in GitHub Desktop.
A quick and dirty GoogleGeocoder component for Yii v1.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class GoogleMapsGeocoder extends CApplicationComponent | |
{ | |
/** | |
* @var string | |
*/ | |
const ENDPOINT_URL = 'http://maps.googleapis.com/maps/api/geocode/json?address=%s'; | |
/** | |
* @var string | |
*/ | |
const ENDPOINT_URL_SSL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s'; | |
/** | |
* @var string | |
*/ | |
public $locale = 'en_US'; | |
/** | |
* @var string | |
*/ | |
public $region = 'US'; | |
/** | |
* @var bool | |
*/ | |
public $sensor = false; | |
/** | |
* @var bool | |
*/ | |
public $useSsl = true; | |
/** | |
* @var string | |
*/ | |
public $apiKey = null; | |
/** | |
* @param $address | |
* @return array | |
* @throws Exception | |
*/ | |
public function geocode($address) | |
{ | |
return $this->executeQuery($this->getQueryParameters($address)); | |
} | |
/** | |
* @return array | |
*/ | |
protected function getDefaults() | |
{ | |
return array( | |
'room' => null, | |
'floor' => null, | |
'street_number' => null, | |
'street_address' => null, | |
'route' => null, | |
'intersection' => null, | |
'neighborhood' => null, | |
'subpremise' => null, | |
'premise' => null, | |
'sublocality' => null, | |
'locality' => null, | |
'colloquial_area' => null, | |
'administrative_area_level_5' => null, | |
'administrative_area_level_4' => null, | |
'administrative_area_level_3' => null, | |
'administrative_area_level_2' => null, | |
'administrative_area_level_1' => null, | |
'country' => null, | |
'postal_code' => null, | |
); | |
} | |
/** | |
* @param $address | |
* @param bool $qualified | |
* @return array | |
* @throws Exception | |
*/ | |
protected function getQueryParameters($address, $qualified = true) | |
{ | |
$address = trim($address); | |
if (empty($address)) | |
{ | |
throw new Exception("Address can not be empty"); | |
} | |
$parameters = compact('address'); | |
if ($qualified) | |
{ | |
$this->qualifyQueryParametersForLocaleRegionAndKey($parameters); | |
} | |
return $parameters; | |
} | |
/** | |
* @param array $parameters | |
*/ | |
protected function qualifyQueryParametersForLocaleRegionAndKey(array & $parameters = array()) | |
{ | |
$additionalParameters = array( | |
'sensor' => (($this->sensor)? 'true' : 'false'), | |
'language' => $this->locale, | |
'region' => $this->region, | |
'key' => $this->apiKey, | |
); | |
$parameters = $additionalParameters + $parameters; | |
} | |
/** | |
* @param array $parameters | |
* @return array | |
* @throws Exception | |
*/ | |
protected function executeQuery(array $parameters) | |
{ | |
$mapJson = $this->getJsonByQuery($parameters); | |
$result = $this->parseResultByJson($mapJson); | |
return $result; | |
} | |
/** | |
* @param array $parameters | |
* @return mixed | |
* @throws Exception | |
*/ | |
protected function getJsonByQuery(array $parameters) | |
{ | |
$endPoint = ($this->useSsl)? static::ENDPOINT_URL_SSL : static::ENDPOINT_URL; | |
$mapsResponse = Yii::app()->curl->get($endPoint, $parameters); | |
$mapJson = json_decode($mapsResponse); | |
if (empty($mapsResponse)) | |
{ | |
throw new Exception("No response recieved : ${parameters['address']}"); | |
} | |
if (!isset($mapJson) || json_last_error() !== JSON_ERROR_NONE) | |
{ | |
throw new Exception("Unable to decode response: ${parameters['address']}"); | |
} | |
if ($mapJson->status == 'ZERO_RESULTS' || !isset($mapJson->results) || !count($mapJson->results)) | |
{ | |
throw new Exception("No results found: ${parameters['address']}"); | |
} | |
if ($mapJson->status !== 'OK') | |
{ | |
throw new Exception("Unable to execute query: ${parameters['address']}"); | |
} | |
if ($mapJson->status == 'REQUEST_DENIED' && $mapJson->error_message == 'The provided API key is invalid.') | |
{ | |
throw new Exception("API key is invalid: ${parameters['address']}"); | |
} | |
if ($mapJson->status == 'OVER_QUERY_LIMIT') | |
{ | |
throw new Exception("Daily quota exceeded: ${parameters['address']}"); | |
} | |
if (count($mapJson->results) > 1) | |
{ | |
//throw new Exception("Multiple results found: ${parameters['address']}"); | |
} | |
return $mapJson; | |
} | |
/** | |
* @param stdClass $json | |
* @return array | |
*/ | |
protected function parseResultByJson(stdClass $jsonObject) | |
{ | |
$mapResult = $jsonObject->results[0]; | |
$result = $this->getDefaults(); | |
$this->parseAddressComponents($result, $mapResult->address_components); | |
$this->parseGeometry($result, $mapResult->geometry); | |
return array_filter($result); | |
} | |
/** | |
* @param array $result | |
* @param array $components | |
*/ | |
protected function parseAddressComponents(array & $result, array $components) | |
{ | |
foreach ($components as $component) | |
{ | |
foreach ($component->types as $type) | |
{ | |
$this->setAddressType($result, $type, $component); | |
} | |
} | |
} | |
/** | |
* @param array $result | |
* @param string $type | |
* @param stdClass $values | |
*/ | |
protected function setAddressType(array & $result, $type, stdClass $values) | |
{ | |
if (array_key_exists($type, $result) && !isset($result[$type])) | |
{ | |
$result[$type] = $values->long_name; | |
} | |
} | |
/** | |
* @param array $results | |
* @param stdClass $mapGeometry | |
*/ | |
protected function parseGeometry(array & $results, stdClass $mapGeometry) | |
{ | |
$mapCoordinates = $mapGeometry->location; | |
$geometry['type'] = $mapGeometry->location_type; | |
$geometry['latitude'] = $mapCoordinates->lat; | |
$geometry['longitude'] = $mapCoordinates->lng; | |
$geometry['bounds'] = null; | |
if (isset($mapGeometry->bounds) && $mapBounds = $mapGeometry->bounds) | |
{ | |
$geometry['bounds'] = array( | |
'southwest_latitude' => $mapBounds->southwest->lat, | |
'southwest_longitude' => $mapBounds->southwest->lng, | |
'northeast_latitude' => $mapBounds->northeast->lat, | |
'northeast_longitude' => $mapBounds->northeast->lng | |
); | |
} | |
elseif ($geometry['type'] === 'ROOFTOP') | |
{ | |
// Fake bounds | |
$geometry['bounds'] = array( | |
'southwest_latitude' => $mapCoordinates->lat, | |
'southwest_longitude' => $mapCoordinates->lng, | |
'northeast_latitude' => $mapCoordinates->lat, | |
'northeast_longitude' => $mapCoordinates->lng | |
); | |
} | |
$results['geometry'] = $geometry; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment