1 | <?php |
||
33 | final class Nominatim extends AbstractHttpProvider implements Provider |
||
34 | { |
||
35 | /** |
||
36 | * @var string |
||
37 | */ |
||
38 | private $rootUrl; |
||
39 | |||
40 | /** |
||
41 | * @var string |
||
42 | */ |
||
43 | private $userAgent; |
||
44 | |||
45 | /** |
||
46 | * @var string |
||
47 | */ |
||
48 | private $referer; |
||
49 | |||
50 | /** |
||
51 | * @param HttpClient $client an HTTP client |
||
52 | * @param string $userAgent Value of the User-Agent header |
||
53 | * @param string $referer Value of the Referer header |
||
54 | * |
||
55 | * @return Nominatim |
||
56 | */ |
||
57 | 22 | public static function withOpenStreetMapServer(HttpClient $client, string $userAgent, string $referer = '') |
|
58 | { |
||
59 | 22 | return new self($client, 'https://nominatim.openstreetmap.org', $userAgent, $referer); |
|
60 | } |
||
61 | |||
62 | /** |
||
63 | * @param HttpClient $client an HTTP client |
||
64 | * @param string $rootUrl Root URL of the nominatim server |
||
65 | * @param string $userAgent Value of the User-Agent header |
||
66 | * @param string $referer Value of the Referer header |
||
67 | */ |
||
68 | 22 | public function __construct(HttpClient $client, $rootUrl, string $userAgent, string $referer = '') |
|
69 | { |
||
70 | 22 | parent::__construct($client); |
|
71 | |||
72 | 22 | $this->rootUrl = rtrim($rootUrl, '/'); |
|
73 | 22 | $this->userAgent = $userAgent; |
|
74 | 22 | $this->referer = $referer; |
|
75 | |||
76 | 22 | if (empty($this->userAgent)) { |
|
77 | throw new InvalidArgument('The User-Agent must be set to use the Nominatim provider.'); |
||
78 | } |
||
79 | 22 | } |
|
80 | |||
81 | /** |
||
82 | * {@inheritdoc} |
||
83 | */ |
||
84 | 13 | public function geocodeQuery(GeocodeQuery $query): Collection |
|
85 | { |
||
86 | 13 | $address = $query->getText(); |
|
87 | |||
88 | // This API doesn't handle IPs |
||
89 | 13 | if (filter_var($address, FILTER_VALIDATE_IP)) { |
|
90 | 3 | throw new UnsupportedOperation('The Nominatim provider does not support IP addresses.'); |
|
91 | } |
||
92 | |||
93 | 10 | $url = sprintf($this->getGeocodeEndpointUrl(), urlencode($address), $query->getLimit()); |
|
94 | 10 | $content = $this->executeQuery($url, $query->getLocale()); |
|
95 | |||
96 | 5 | $doc = new \DOMDocument(); |
|
97 | 5 | if (!@$doc->loadXML($content) || null === $doc->getElementsByTagName('searchresults')->item(0)) { |
|
98 | 2 | throw InvalidServerResponse::create($url); |
|
99 | } |
||
100 | |||
101 | 3 | $searchResult = $doc->getElementsByTagName('searchresults')->item(0); |
|
102 | 3 | $attribution = $searchResult->getAttribute('attribution'); |
|
103 | 3 | $places = $searchResult->getElementsByTagName('place'); |
|
104 | |||
105 | 3 | if (null === $places || 0 === $places->length) { |
|
106 | 1 | return new AddressCollection([]); |
|
107 | } |
||
108 | |||
109 | 2 | $results = []; |
|
110 | 2 | foreach ($places as $place) { |
|
111 | 2 | $results[] = $this->xmlResultToArray($place, $place, $attribution, false); |
|
112 | } |
||
113 | |||
114 | 2 | return new AddressCollection($results); |
|
115 | } |
||
116 | |||
117 | /** |
||
118 | * {@inheritdoc} |
||
119 | */ |
||
120 | 9 | public function reverseQuery(ReverseQuery $query): Collection |
|
140 | |||
141 | /** |
||
142 | * @param \DOMElement $resultNode |
||
143 | * @param \DOMElement $addressNode |
||
144 | * |
||
145 | * @return Location |
||
146 | */ |
||
147 | 4 | private function xmlResultToArray(\DOMElement $resultNode, \DOMElement $addressNode, string $attribution, bool $reverse): Location |
|
209 | |||
210 | /** |
||
211 | * {@inheritdoc} |
||
212 | */ |
||
213 | 4 | public function getName(): string |
|
217 | |||
218 | /** |
||
219 | * @param string $url |
||
220 | * @param string|null $locale |
||
221 | * |
||
222 | * @return string |
||
223 | */ |
||
224 | 19 | private function executeQuery(string $url, string $locale = null): string |
|
239 | |||
240 | 10 | private function getGeocodeEndpointUrl(): string |
|
244 | |||
245 | 9 | private function getReverseEndpointUrl(): string |
|
249 | |||
250 | 4 | private function getNodeValue(\DOMNodeList $element) |
|
254 | } |
||
255 |