Skip to content

Instantly share code, notes, and snippets.

@toin0u
Last active December 17, 2015 00:49
Show Gist options
  • Save toin0u/5524236 to your computer and use it in GitHub Desktop.
Save toin0u/5524236 to your computer and use it in GitHub Desktop.
<?php
/**
* This file is part of the Geocoder package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Geocoder\Provider;
use Geocoder\Exception\NoResultException;
use Geocoder\Exception\QuotaExceededException;
use Geocoder\Exception\UnsupportedException;
use Geocoder\HttpAdapter\HttpAdapterInterface;
/**
* @author William Durand <william.durand1@gmail.com>
*/
class GoogleMapsProvider extends AbstractProvider implements ProviderInterface
{
/**
* @var string
*/
const ENDPOINT_URL = 'http://maps.googleapis.com/maps/api/geocode/json?address=%s&sensor=false';
/**
* @var string
*/
const ENDPOINT_URL_SSL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s&sensor=false';
/**
* @var string
*/
private $region = null;
/**
* @var bool
*/
private $useSsl = false;
/**
* @param HttpAdapterInterface $adapter An HTTP adapter.
* @param string $locale A locale (optional).
* @param string $region Region biasing (optional).
* @param bool $useSsl Whether to use an SSL connection (optional)
*/
public function __construct(HttpAdapterInterface $adapter, $locale = null, $region = null, $useSsl = false)
{
parent::__construct($adapter, $locale);
$this->region = $region;
$this->useSsl = $useSsl;
}
/**
* {@inheritDoc}
*/
public function getGeocodedData($address)
{
// Google API returns invalid data if IP address given
// This API doesn't handle IPs
if (filter_var($address, FILTER_VALIDATE_IP)) {
throw new UnsupportedException('The GoogleMapsProvider does not support IP addresses.');
}
$query = sprintf(
$this->useSsl ? self::ENDPOINT_URL_SSL : self::ENDPOINT_URL,
rawurlencode($address)
);
return $this->executeQuery($query);
}
/**
* {@inheritDoc}
*/
public function getReversedData(array $coordinates)
{
return $this->getGeocodedData(sprintf('%F,%F', $coordinates[0], $coordinates[1]));
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'google_maps';
}
/**
* @param string $query
*
* @return string Query with extra params
*/
protected function buildQuery($query)
{
if (null !== $this->getLocale()) {
$query = sprintf('%s&language=%s', $query, $this->getLocale());
}
if (null !== $this->getRegion()) {
$query = sprintf('%s&region=%s', $query, $this->getRegion());
}
return $query;
}
/**
* @param string $query
*
* @return array
*/
protected function executeQuery($query)
{
$query = $this->buildQuery($query);
$content = $this->getAdapter()->getContent($query);
if (null === $content) {
throw new NoResultException(sprintf('Could not execute query %s', $query));
}
$json = json_decode($content);
// API error
if (!isset($json)) {
throw new NoResultException(sprintf('Could not execute query %s', $query));
}
// you are over your quota
if ('OVER_QUERY_LIMIT' === $json->status) {
throw new QuotaExceededException(sprintf('Daily quota exceeded %s', $query));
}
// no result
if (!isset($json->results) || !count($json->results) || 'OK' !== $json->status) {
throw new NoResultException(sprintf('Could not execute query %s', $query));
}
$results = array();
foreach ($json->results as $result) {
$resultset = $this->getDefaults();
// update address components
foreach ($result->address_components as $component) {
foreach ($component->types as $type) {
$this->updateAddressComponent($resultset, $type, $component);
}
}
// update coordinates
$coordinates = $result->geometry->location;
$resultset['latitude'] = $coordinates->lat;
$resultset['longitude'] = $coordinates->lng;
$resultset['bounds'] = null;
if (isset($result->geometry->bounds)) {
$resultset['bounds'] = array(
'south' => $result->geometry->bounds->southwest->lat,
'west' => $result->geometry->bounds->southwest->lng,
'north' => $result->geometry->bounds->northeast->lat,
'east' => $result->geometry->bounds->northeast->lng
);
} elseif ('ROOFTOP' === $result->geometry->location_type) {
// Fake bounds
$resultset['bounds'] = array(
'south' => $coordinates->lat,
'west' => $coordinates->lng,
'north' => $coordinates->lat,
'east' => $coordinates->lng
);
}
$results[] = array_merge($this->getDefaults(), $resultset);
}
return $results;
}
/**
* Update current resultset with given key/value.
*
* @param array $resultset Resultset to update.
* @param string $type Component type.
* @param object $values The component values;
*
* @return array
*/
protected function updateAddressComponent(&$resultset, $type, $values)
{
switch ($type) {
case 'postal_code':
$resultset['zipcode'] = $values->long_name;
break;
case 'locality':
$resultset['city'] = $values->long_name;
break;
case 'administrative_area_level_2':
$resultset['county'] = $values->long_name;
$resultset['countyCode'] = $values->short_name;
break;
case 'administrative_area_level_1':
$resultset['region'] = $values->long_name;
$resultset['regionCode'] = $values->short_name;
break;
case 'country':
$resultset['country'] = $values->long_name;
$resultset['countryCode'] = $values->short_name;
break;
case 'street_number':
$resultset['streetNumber'] = $values->long_name;
break;
case 'route':
$resultset['streetName'] = $values->long_name;
break;
case 'sublocality':
$resultset['cityDistrict'] = $values->long_name;
break;
default:
}
return $resultset;
}
/**
* Returns the configured region or null.
*
* @return string|null
*/
protected function getRegion()
{
return $this->region;
}
}
Array
(
[0] => Array
(
[latitude] => 48.856614
[longitude] => 2.3522219
[bounds] => Array
(
[south] => 48.815573
[west] => 2.224199
[north] => 48.9021449
[east] => 2.4699208
)
[streetNumber] =>
[streetName] =>
[city] => Paris
[zipcode] =>
[cityDistrict] =>
[county] => Paris
[countyCode] => 75
[region] => Île-de-France
[regionCode] => IdF
[country] => France
[countryCode] => FR
[timezone] =>
)
[1] => Array
(
[latitude] => 33.6609389
[longitude] => -95.555513
[bounds] => Array
(
[south] => 33.6118529
[west] => -95.6279279
[north] => 33.7383781
[east] => -95.435455
)
[streetNumber] =>
[streetName] =>
[city] => Paris
[zipcode] =>
[cityDistrict] =>
[county] => Lamar
[countyCode] => Lamar
[region] => Texas
[regionCode] => TX
[country] => United States
[countryCode] => US
[timezone] =>
)
[2] => Array
(
[latitude] => 36.3020023
[longitude] => -88.3267107
[bounds] => Array
(
[south] => 36.266
[west] => -88.3671149
[north] => 36.3291321
[east] => -88.2650759
)
[streetNumber] =>
[streetName] =>
[city] => Paris
[zipcode] => 38242
[cityDistrict] =>
[county] => Henry
[countyCode] => Henry
[region] => Tennessee
[regionCode] => TN
[country] => United States
[countryCode] => US
[timezone] =>
)
[3] => Array
(
[latitude] => 39.611146
[longitude] => -87.6961374
[bounds] => Array
(
[south] => 39.581415
[west] => -87.721046
[north] => 39.6485756
[east] => -87.6505408
)
[streetNumber] =>
[streetName] =>
[city] => Paris
[zipcode] => 61944
[cityDistrict] =>
[county] => Edgar
[countyCode] => Edgar
[region] => Illinois
[regionCode] => IL
[country] => United States
[countryCode] => US
[timezone] =>
)
[4] => Array
(
[latitude] => 38.2097987
[longitude] => -84.2529869
[bounds] => Array
(
[south] => 38.164922
[west] => -84.3073259
[north] => 38.238271
[east] => -84.232089
)
[streetNumber] =>
[streetName] =>
[city] => Paris
[zipcode] => 40361
[cityDistrict] =>
[county] => Bourbon
[countyCode] => Bourbon
[region] => Kentucky
[regionCode] => KY
[country] => United States
[countryCode] => US
[timezone] =>
)
)
Array
(
[latitude] => 48.8234055
[longitude] => 2.3072664
[bounds] => Array
(
[south] => 48.8234055
[west] => 2.3072664
[north] => 48.8234055
[east] => 2.3072664
)
[streetNumber] => 10
[streetName] => Rue Gambetta
[city] => Paris
[zipcode] => 92240
[cityDistrict] => 14th arrondissement of Paris
[county] => Hauts-de-Seine
[countyCode] => 92
[region] => Île-de-France
[regionCode] => IdF
[country] => France
[countryCode] => FR
[timezone] =>
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment