NationaalGeoregister::executeQuery()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 19
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 29
ccs 20
cts 20
cp 1
crap 4
rs 9.6333
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 Psr\Http\Client\ClientInterface;
17
18
class NationaalGeoregister extends AbstractHttpProvider implements Provider
19
{
20
    /**
21
     * @var string
22
     */
23
    protected const ENDPOINT_URL_FREE = 'https://api.pdok.nl/bzk/locatieserver/search/v3_1/free?%s';
24
25
    /**
26
     * @var string
27
     */
28
    protected const ENDPOINT_URL_REVERSE = 'https://api.pdok.nl/bzk/locatieserver/search/v3_1/reverse?%s';
29
30
    /**
31
     * @var string[]
32
     */
33
    protected const BLACKLISTED_OPTIONS = [
34
        'fl',
35
        'rows',
36
        'type',
37
        'wt',
38
    ];
39
40
    /**
41
     * @var array
42
     */
43
    protected const DEFAULT_OPTIONS = [
44
        'fl' => 'centroide_ll,huis_nlt,huisnummer,straatnaam,postcode,woonplaatsnaam,gemeentenaam,gemeentecode,provincienaam,provinciecode',
45
    ];
46
47
    /**
48
     * @var array
49
     */
50
    protected const DEFAULT_OPTIONS_GEOCODE = [
51
        'bq' => 'type:gemeente^0.5 type:woonplaats^0.5 type:weg^1.0 type:postcode^1.5 type:adres^1.5',
52
    ];
53
54
    /**
55
     * @var array
56
     */
57
    protected const REQUIRED_OPTIONS_GEOCODE = [];
58
59
    /**
60
     * @var array
61
     */
62
    protected const DEFAULT_OPTIONS_REVERSE = [];
63
64
    /**
65
     * @var array
66
     */
67
    protected const REQUIRED_OPTIONS_REVERSE = [
68
        'type' => 'adres',
69
    ];
70
71
    /**
72
     * @var array
73
     */
74
    protected $options = [];
75
76
    /**
77
     * @param ClientInterface $client  An HTTP adapter
78
     * @param array           $options Extra query parameters (optional)
79
     */
80 144
    public function __construct(ClientInterface $client, array $options = [])
81
    {
82 144
        parent::__construct($client);
0 ignored issues
show
Deprecated Code introduced by
The function Geocoder\Http\Provider\A...Provider::__construct() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

82
        /** @scrutinizer ignore-deprecated */ parent::__construct($client);
Loading history...
83
84 144
        $this->setOptions($options);
85 48
    }
86
87
    /**
88
     * @return array
89
     */
90 12
    public function getOptions(): array
91
    {
92 12
        return $this->options;
93
    }
94
95
    /**
96
     * @param array $options
97
     */
98 144
    public function setOptions(array $options)
99
    {
100 144
        $this->options = array_diff_key($options, array_fill_keys(self::BLACKLISTED_OPTIONS, true));
101 48
    }
102
103
    /**
104
     * @param GeocodeQuery $query
105
     *
106
     * @throws InvalidServerResponse
107
     * @throws UnsupportedOperation
108
     *
109
     * @return Collection
110
     */
111 84
    public function geocodeQuery(GeocodeQuery $query): Collection
112
    {
113
        // This API doesn't handle IPs.
114 84
        if (filter_var($query->getText(), FILTER_VALIDATE_IP)) {
115 6
            throw new UnsupportedOperation('The NationaalGeoregister provider does not support IP addresses.');
116
        }
117
118 78
        return $this->executeQuery(sprintf(self::ENDPOINT_URL_FREE, http_build_query($this->getGeocodeOptions($query))));
119
    }
120
121
    /**
122
     * @param GeocodeQuery $query
123
     *
124
     * @return array
125
     */
126 78
    protected function getGeocodeOptions(GeocodeQuery $query): array
127
    {
128 78
        return array_merge(
129 78
            static::DEFAULT_OPTIONS,
130 78
            static::DEFAULT_OPTIONS_GEOCODE,
131 78
            $this->options,
132 78
            array_diff_key($query->getAllData(), array_fill_keys(self::BLACKLISTED_OPTIONS, true)),
133 78
            static::REQUIRED_OPTIONS_GEOCODE,
134 52
            [
135 78
                'rows' => $query->getLimit(),
136 78
                'q' => $query->getText(),
137 52
            ]
138 52
        );
139
    }
140
141
    /**
142
     * @param ReverseQuery $query
143
     *
144
     * @throws InvalidServerResponse
145
     *
146
     * @return Collection
147
     */
148 42
    public function reverseQuery(ReverseQuery $query): Collection
149
    {
150 42
        return $this->executeQuery(sprintf(self::ENDPOINT_URL_REVERSE, http_build_query($this->getReverseOptions($query))));
151
    }
152
153
    /**
154
     * @param ReverseQuery $query
155
     *
156
     * @return array
157
     */
158 42
    protected function getReverseOptions(ReverseQuery $query): array
159
    {
160 42
        return array_merge(
161 42
            static::DEFAULT_OPTIONS,
162 42
            static::DEFAULT_OPTIONS_REVERSE,
163 42
            $this->options,
164 42
            array_diff_key($query->getAllData(), array_fill_keys(self::BLACKLISTED_OPTIONS, true)),
165 42
            static::REQUIRED_OPTIONS_REVERSE,
166 28
            [
167 42
                'rows' => $query->getLimit(),
168 42
                'lat' => $query->getCoordinates()->getLatitude(),
169 42
                'lon' => $query->getCoordinates()->getLongitude(),
170 28
            ]
171 28
        );
172
    }
173
174
    /**
175
     * @return string
176
     */
177 54
    public function getName(): string
178
    {
179 54
        return 'nationaal_georegister';
180
    }
181
182
    /**
183
     * @param string $query
184
     *
185
     * @throws InvalidServerResponse
186
     *
187
     * @return AddressCollection
188
     */
189 120
    protected function executeQuery(string $query): AddressCollection
190
    {
191 120
        $results = $this->getResultsForQuery($query);
192
193 54
        $addresses = [];
194 54
        foreach ($results->response->docs as $doc) {
195 48
            $position = explode(' ', trim(str_replace(['POINT(', ')'], '', $doc->centroide_ll)));
196
197 48
            $builder = new AddressBuilder($this->getName());
198
199 48
            $builder->setCoordinates((float) $position[1], (float) $position[0]);
200 48
            $builder->setStreetNumber($doc->huis_nlt ?? $doc->huisnummer ?? null);
201 48
            $builder->setStreetName($doc->straatnaam ?? null);
202 48
            $builder->setPostalCode($doc->postcode ?? null);
203 48
            $builder->setLocality($doc->woonplaatsnaam ?? null);
204 48
            if (isset($doc->gemeentenaam)) {
205 42
                $builder->addAdminLevel(2, $doc->gemeentenaam, $doc->gemeentecode);
206
            }
207 48
            if (isset($doc->provincienaam)) {
208 48
                $builder->addAdminLevel(1, $doc->provincienaam, $doc->provinciecode);
209
            }
210 48
            $builder->setCountry('Netherlands');
211 48
            $builder->setCountryCode('NL');
212 48
            $builder->setTimezone('Europe/Amsterdam');
213
214 48
            $addresses[] = $builder->build();
215
        }
216
217 54
        return new AddressCollection($addresses);
218
    }
219
220
    /**
221
     * @param string $query
222
     *
223
     * @throws InvalidServerResponse
224
     *
225
     * @return \stdClass
226
     */
227 120
    protected function getResultsForQuery(string $query): \stdClass
228
    {
229 120
        $content = $this->getUrlContents($query);
230
231 54
        $result = json_decode($content);
232
233 54
        if (json_last_error() === JSON_ERROR_UTF8) {
234
            $result = json_decode(utf8_encode($content));
235
        }
236
237 54
        if (json_last_error() !== JSON_ERROR_NONE) {
238
            throw new InvalidServerResponse(sprintf('Could not execute query "%s": %s', $query, json_last_error_msg()));
239
        }
240
241 54
        return $result;
242
    }
243
}
244