Completed
Push — master ( c6923c...0c872f )
by Jasper
02:59
created

NationaalGeoregister   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 211
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.67%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 8
dl 0
loc 211
ccs 58
cts 60
cp 0.9667
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getOptions() 0 4 1
A setOptions() 0 4 1
A geocodeQuery() 0 9 2
A getGeocodeOptions() 0 12 1
A reverseQuery() 0 4 1
A getReverseOptions() 0 13 1
A getName() 0 4 1
A executeQuery() 0 30 4
A getResultsForQuery() 0 16 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Swis\Geocoder\NationaalGeoregister;
6
7
use Geocoder\Collection;
8
use Geocoder\Exception\InvalidServerResponse;
9
use Geocoder\Exception\UnsupportedOperation;
10
use Geocoder\Http\Provider\AbstractHttpProvider;
11
use Geocoder\Model\AddressBuilder;
12
use Geocoder\Model\AddressCollection;
13
use Geocoder\Provider\Provider;
14
use Geocoder\Query\GeocodeQuery;
15
use Geocoder\Query\ReverseQuery;
16
use Http\Client\HttpClient;
17
18
class NationaalGeoregister extends AbstractHttpProvider implements Provider
19
{
20
    /**
21
     * @var string
22
     */
23
    const ENDPOINT_URL_FREE = 'https://geodata.nationaalgeoregister.nl/locatieserver/v3/free?%s';
24
25
    /**
26
     * @var string
27
     */
28
    const ENDPOINT_URL_REVERSE = 'https://geodata.nationaalgeoregister.nl/locatieserver/revgeo?%s';
29
30
    /**
31
     * @var string[]
32
     */
33
    const BLACKLISTED_OPTIONS = [
34
        'fl',
35
        'rows',
36
        'type',
37
        'wt',
38
    ];
39
40
    /**
41
     * @var array
42
     */
43
    const DEFAULT_OPTIONS = [
44
        'bq' => 'type:gemeente^0.5 type:woonplaats^0.5 type:weg^1.0 type:postcode^1.5 type:adres^1.5',
45
        'fl' => 'centroide_ll,huis_nlt,huisnummer,straatnaam,postcode,woonplaatsnaam,gemeentenaam,gemeentecode,provincienaam,provinciecode',
46
    ];
47
48
    /**
49
     * @var array
50
     */
51
    const REQUIRED_OPTIONS_GEOCODE = [];
52
53
    /**
54
     * @var array
55
     */
56
    const REQUIRED_OPTIONS_REVERSE = [
57
        'type' => 'adres',
58
    ];
59
60
    /**
61
     * @var array
62
     */
63
    protected $options = [];
64
65
    /**
66
     * @param \Http\Client\HttpClient $client  An HTTP adapter
67
     * @param array                   $options Extra query parameters (optional)
68
     */
69 51
    public function __construct(HttpClient $client, array $options = [])
70
    {
71 51
        parent::__construct($client);
72
73 51
        $this->setOptions($options);
74 51
    }
75
76
    /**
77
     * @return array
78
     */
79 6
    public function getOptions(): array
80
    {
81 6
        return $this->options;
82
    }
83
84
    /**
85
     * @param array $options
86
     */
87 51
    public function setOptions(array $options)
88
    {
89 51
        $this->options = array_diff_key($options, array_fill_keys(self::BLACKLISTED_OPTIONS, true));
90 51
    }
91
92
    /**
93
     * @param \Geocoder\Query\GeocodeQuery $query
94
     *
95
     * @throws \Geocoder\Exception\InvalidServerResponse
96
     * @throws \Geocoder\Exception\UnsupportedOperation
97
     *
98
     * @return \Geocoder\Collection
99
     */
100 39
    public function geocodeQuery(GeocodeQuery $query): Collection
101
    {
102
        // This API doesn't handle IPs.
103 39
        if (filter_var($query->getText(), FILTER_VALIDATE_IP)) {
104 3
            throw new UnsupportedOperation('The NationaalGeoregister provider does not support IP addresses.');
105
        }
106
107 36
        return $this->executeQuery(sprintf(self::ENDPOINT_URL_FREE, http_build_query($this->getGeocodeOptions($query))));
108
    }
109
110
    /**
111
     * @param \Geocoder\Query\GeocodeQuery $query
112
     *
113
     * @return array
114
     */
115 36
    protected function getGeocodeOptions(GeocodeQuery $query): array
116
    {
117 36
        return array_merge(
118 36
            static::DEFAULT_OPTIONS,
119 36
            $this->options,
120 36
            static::REQUIRED_OPTIONS_GEOCODE,
121
            [
122 36
                'rows' => $query->getLimit(),
123 36
                'q'    => $query->getText(),
124
            ]
125
        );
126
    }
127
128
    /**
129
     * @param \Geocoder\Query\ReverseQuery $query
130
     *
131
     * @throws \Geocoder\Exception\InvalidServerResponse
132
     *
133
     * @return \Geocoder\Collection
134
     */
135 3
    public function reverseQuery(ReverseQuery $query): Collection
136
    {
137 3
        return $this->executeQuery(sprintf(self::ENDPOINT_URL_REVERSE, http_build_query($this->getReverseOptions($query))));
138
    }
139
140
    /**
141
     * @param \Geocoder\Query\ReverseQuery $query
142
     *
143
     * @return array
144
     */
145 3
    protected function getReverseOptions(ReverseQuery $query): array
146
    {
147 3
        return array_merge(
148 3
            static::DEFAULT_OPTIONS,
149 3
            $this->options,
150 3
            static::REQUIRED_OPTIONS_REVERSE,
151
            [
152 3
                'rows' => $query->getLimit(),
153 3
                'lat'  => $query->getCoordinates()->getLatitude(),
154 3
                'lon'  => $query->getCoordinates()->getLongitude(),
155
            ]
156
        );
157
    }
158
159
    /**
160
     * @return string
161
     */
162 21
    public function getName(): string
163
    {
164 21
        return 'nationaal_georegister';
165
    }
166
167
    /**
168
     * @param string $query
169
     *
170
     * @throws \Geocoder\Exception\InvalidServerResponse
171
     *
172
     * @return \Geocoder\Model\AddressCollection
173
     */
174 39
    protected function executeQuery(string $query): AddressCollection
175
    {
176 39
        $results = $this->getResultsForQuery($query);
177
178 21
        $addresses = [];
179 21
        foreach ($results->response->docs as $doc) {
180 18
            $position = explode(' ', trim(str_replace(['POINT(', ')'], '', $doc->centroide_ll)));
181
182 18
            $builder = new AddressBuilder($this->getName());
183
184 18
            $builder->setCoordinates((float)$position[1], (float)$position[0]);
185 18
            $builder->setStreetNumber($doc->huis_nlt ?? $doc->huisnummer ?? null);
186 18
            $builder->setStreetName($doc->straatnaam ?? null);
187 18
            $builder->setPostalCode($doc->postcode ?? null);
188 18
            $builder->setLocality($doc->woonplaatsnaam ?? null);
189 18
            if (isset($doc->gemeentenaam)) {
190 15
                $builder->addAdminLevel(2, $doc->gemeentenaam, $doc->gemeentecode);
191
            }
192 18
            if (isset($doc->provincienaam)) {
193 15
                $builder->addAdminLevel(1, $doc->provincienaam, $doc->provinciecode);
194
            }
195 18
            $builder->setCountry('Netherlands');
196 18
            $builder->setCountryCode('NL');
197 18
            $builder->setTimezone('Europe/Amsterdam');
198
199 18
            $addresses[] = $builder->build();
200
        }
201
202 21
        return new AddressCollection($addresses);
203
    }
204
205
    /**
206
     * @param string $query
207
     *
208
     * @throws \Geocoder\Exception\InvalidServerResponse
209
     *
210
     * @return \stdClass
211
     */
212 39
    protected function getResultsForQuery(string $query): \stdClass
213
    {
214 39
        $content = $this->getUrlContents($query);
215
216 21
        $result = json_decode($content);
217
218 21
        if (json_last_error() === JSON_ERROR_UTF8) {
219
            $result = json_decode(utf8_encode($content));
220
        }
221
222 21
        if (json_last_error() !== JSON_ERROR_NONE) {
223
            throw new InvalidServerResponse(sprintf('Could not execute query "%s": %s', $query, json_last_error_msg()));
224
        }
225
226 21
        return $result;
227
    }
228
}
229