Skip to content

Commit b20a63b

Browse files
committed
Merge pull request #215 from baikunz/arcgis-online-provider
Add ArcGIS Online provider
2 parents ae08bec + 1178055 commit b20a63b

File tree

3 files changed

+466
-2
lines changed

3 files changed

+466
-2
lines changed

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ Currently, there are many providers for the following APIs:
5151
* [Geonames](http://www.geonames.org/) as Place-Based geocoding and reverse geocoding provider;
5252
* [IpGeoBase](http://ipgeobase.ru/) as IP-Based geocoding provider (very accurate in Russia);
5353
* [Baidu](http://developer.baidu.com/map/geocoding-api.htm) as Address-Based geocoding and reverse geocoding provider (exclusively in China);
54-
* [TomTom](http://developer.tomtom.com/docs/read/Geocoding) as Address-Based geocoding and reverse geocoding provider.
54+
* [TomTom](http://developer.tomtom.com/docs/read/Geocoding) as Address-Based geocoding and reverse geocoding provider;
55+
* [ArcGIS Online](http://resources.arcgis.com/en/help/arcgis-online-geocoding-rest-api/) as Address-Based geocoding and reverse geocoding provider.
5556

5657
Installation
5758
------------
@@ -260,6 +261,12 @@ The `TomTomProvider` named `tomtom` is able to geocode and reverse geocode **str
260261
The default langage-locale is `en`, you can choose between `de`, `es`, `fr`, `it`, `nl`, `pl`, `pt` and `sv`.
261262
A valid api key is required.
262263

264+
### ArcGISOnlineProvider ###
265+
266+
The `ArcGISOnlineProvider` named `arcgis_online` is able to geocode and reverse geocode **street addresses**.
267+
It's possible to specify a sourceCountry to restrict result to this specific country thus reducing
268+
request time (note that this doesn't work on reverse geocoding). This provider also supports SSL.
269+
263270

264271
### Using The Providers ###
265272

@@ -286,6 +293,9 @@ $geocoder->registerProviders(array(
286293
new \Geocoder\Provider\MaxMindProvider(
287294
$adapter, '<MAXMIND_API_KEY>', $service, $useSsl
288295
),
296+
new \Geocoder\Provider\ArcGISOnlineProvider(
297+
$adapter, $sourceCountry, $useSsl
298+
),
289299
));
290300
```
291301

@@ -295,7 +305,8 @@ Parameters:
295305
* `$region` is available for `GoogleMapsProvider` and `GoogleMapsBusinessProvider`.
296306
* `$toponym` is available for `YandexProvider`.
297307
* `$service` is available for `MaxMindProvider`.
298-
* `$useSsl` is available for `GoogleMapsProvider`, `GoogleMapsBusinessProvider` and `MaxMindProvider`.
308+
* `$useSsl` is available for `GoogleMapsProvider`, `GoogleMapsBusinessProvider`, `MaxMindProvider` and `ArcGISOnlineProvider`.
309+
* `$sourceCountry` is available for `ArcGISOnlineProvider`.
299310

300311
Everything is ok, enjoy!
301312

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Geocoder package.
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*
8+
* @license MIT License
9+
*/
10+
11+
namespace Geocoder\Provider;
12+
13+
use Geocoder\Exception\NoResultException;
14+
use Geocoder\Exception\UnsupportedException;
15+
use Geocoder\HttpAdapter\HttpAdapterInterface;
16+
17+
/**
18+
* @author ALKOUM Dorian <baikunz@gmail.com>
19+
*/
20+
class ArcGISOnlineProvider extends AbstractProvider implements ProviderInterface
21+
{
22+
/**
23+
* @var string
24+
*/
25+
const ENDPOINT_URL = '%s://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find?text=%s';
26+
27+
/**
28+
* @var string
29+
*/
30+
const REVERSE_ENDPOINT_URL = '%s://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode?location=%F,%F';
31+
32+
/**
33+
* @var string
34+
*/
35+
protected $sourceCountry = null;
36+
37+
/**
38+
* @var string
39+
*/
40+
protected $protocol = 'http';
41+
42+
/**
43+
* @param HttpAdapterInterface $adapter An HTTP adapter.
44+
* @param string $sourceCountry Country biasing (optional).
45+
* @param bool $useSsl Whether to use an SSL connection (optional)
46+
*/
47+
public function __construct(HttpAdapterInterface $adapter, $sourceCountry = null, $useSsl = false)
48+
{
49+
parent::__construct($adapter);
50+
51+
$this->sourceCountry = $sourceCountry;
52+
$this->protocol = $useSsl ? 'https' : 'http';
53+
}
54+
55+
/**
56+
* {@inheritDoc}
57+
*/
58+
public function getGeocodedData($address)
59+
{
60+
if (filter_var($address, FILTER_VALIDATE_IP)) {
61+
throw new UnsupportedException('The ArcGISOnlineProvider does not support IP addresses.');
62+
}
63+
64+
// Save a request if no valid address entered
65+
if (empty($address)) {
66+
throw new NoResultException('Invalid address.');
67+
}
68+
69+
$query = sprintf(
70+
static::ENDPOINT_URL,
71+
$this->protocol,
72+
urlencode($address)
73+
);
74+
75+
$json = $this->executeQuery($query);
76+
77+
// no result
78+
if (empty($json->locations)) {
79+
throw new NoResultException(sprintf('No results found for query %s', $query));
80+
}
81+
82+
$location = reset($json->locations);
83+
$data = $location->feature->attributes;
84+
85+
$coordinates = (array) $location->feature->geometry;
86+
$streetName = !empty($data->Match_addr) ? $data->Match_addr : null;
87+
$streetNumber = !empty($data->AddNum) ? $data->AddNum : null;
88+
$city = !empty($data->City) ? $data->City : null;
89+
$zipcode = !empty($data->Postal) ? $data->Postal : null;
90+
$region = !empty($data->Region) ? $data->Region : null;
91+
$county = !empty($data->Subregion) ? $data->Subregion : null;
92+
$countryCode = !empty($data->Country) ? $data->Country : null;
93+
94+
return array_merge($this->getDefaults(), array(
95+
'latitude' => $coordinates['y'],
96+
'longitude' => $coordinates['x'],
97+
'streetNumber' => $streetNumber,
98+
'streetName' => $streetName,
99+
'city' => $city,
100+
'zipcode' => $zipcode,
101+
'region' => $region,
102+
'countryCode' => $countryCode,
103+
'county' => $county,
104+
));
105+
}
106+
107+
/**
108+
* {@inheritDoc}
109+
*/
110+
public function getReversedData(array $coordinates)
111+
{
112+
$query = sprintf(
113+
static::REVERSE_ENDPOINT_URL,
114+
$this->protocol,
115+
$coordinates[1],
116+
$coordinates[0]
117+
);
118+
119+
$json = $this->executeQuery($query);
120+
121+
if (property_exists($json, 'error')) {
122+
throw new NoResultException(sprintf('No results found for query %s', $query));
123+
}
124+
125+
$data = $json->address;
126+
127+
$streetName = !empty($data->Address) ? $data->Address : null;
128+
$city = !empty($data->City) ? $data->City : null;
129+
$zipcode = !empty($data->Postal) ? $data->Postal : null;
130+
$region = !empty($data->Region) ? $data->Region : null;
131+
$county = !empty($data->Subregion) ? $data->Subregion : null;
132+
$countryCode = !empty($data->CountryCode) ? $data->CountryCode : null;
133+
134+
return array_merge($this->getDefaults(), array(
135+
'latitude' => $coordinates[0],
136+
'longitude' => $coordinates[1],
137+
'streetName' => $streetName,
138+
'city' => $city,
139+
'zipcode' => $zipcode,
140+
'region' => $region,
141+
'countryCode' => $countryCode,
142+
'county' => $county,
143+
));
144+
}
145+
146+
/**
147+
* {@inheritDoc}
148+
*/
149+
public function getName()
150+
{
151+
return 'arcgis_online';
152+
}
153+
154+
/**
155+
* @param string $query
156+
*
157+
* @return string Query with extra params
158+
*/
159+
protected function buildQuery($query)
160+
{
161+
if (null !== $this->getSourceCountry()) {
162+
$query = sprintf('%s&sourceCountry=%s', $query, $this->getSourceCountry());
163+
}
164+
165+
$query = sprintf('%s&maxLocations=%d', $query, 1); // Limit results to 1
166+
$query = sprintf('%s&f=%s', $query, 'json'); // set format to json
167+
$query = sprintf('%s&outFields=*', $query); // Get all result fields
168+
169+
return $query;
170+
}
171+
172+
/**
173+
* Executes a query
174+
*
175+
* @param string $query
176+
*
177+
* @throws NoResultException
178+
*
179+
* @return stdClass json object representing the query result
180+
*/
181+
protected function executeQuery($query)
182+
{
183+
$query = $this->buildQuery($query);
184+
185+
$content = $this->getAdapter()->getContent($query);
186+
if (null === $content) {
187+
throw new NoResultException(sprintf('Could not execute query %s', $query));
188+
}
189+
190+
$json = json_decode($content);
191+
192+
// API error
193+
if (!isset($json)) {
194+
throw new NoResultException(sprintf('Could not execute query %s', $query));
195+
}
196+
197+
return $json;
198+
}
199+
200+
/**
201+
* Returns the configured source country or null.
202+
*
203+
* @return string|null
204+
*/
205+
protected function getSourceCountry()
206+
{
207+
return $this->sourceCountry;
208+
}
209+
}

0 commit comments

Comments
 (0)