Skip to content

Instantly share code, notes, and snippets.

@shoaibi
Created June 25, 2015 17:00
Show Gist options
  • Save shoaibi/6bb2c50c1768e94e1a13 to your computer and use it in GitHub Desktop.
Save shoaibi/6bb2c50c1768e94e1a13 to your computer and use it in GitHub Desktop.
A quick and dirty GoogleGeocoder component for Yii v1.0
<?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