Passed
Push — master ( d6584b...36df1b )
by
unknown
04:09
created

Api::getMetadata()   B

Complexity

Conditions 11
Paths 11

Size

Total Lines 66
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 11

Importance

Changes 0
Metric Value
cc 11
eloc 31
nc 11
nop 1
dl 0
loc 66
ccs 31
cts 31
cp 1
crap 11
rs 7.3166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Defro\Google\StreetView;
4
5
use Defro\Google\StreetView\Exception\BadStatusCodeException;
6
use Defro\Google\StreetView\Exception\RequestException;
7
use Defro\Google\StreetView\Exception\UnexpectedStatusException;
8
use Defro\Google\StreetView\Exception\UnexpectedValueException;
9
use GuzzleHttp\Client;
10
use GuzzleHttp\Exception\GuzzleException;
11
12
class Api
13
{
14
    /** @var string default source */
15
    const SOURCE_DEFAULT = 'default';
16
17
    /** @var string outdoor source */
18
    const SOURCE_OUTDOOR = 'outdoor';
19
20
    /** @var string */
21
    private $endpointImage = 'https://maps.googleapis.com/maps/api/streetview';
22
23
    /** @var string */
24
    private $endpointMetadata = 'https://maps.googleapis.com/maps/api/streetview/metadata';
25
26
    /** @var \GuzzleHttp\Client */
27
    private $client;
28
29
    /** @var string */
30
    private $apiKey;
31
32
    /** @var string */
33
    private $signature;
34
35
    /** @var int */
36
    private $imageWidth = 600;
37
38
    /** @var int */
39
    private $imageHeight = 600;
40
41
    /** @var int */
42
    private $heading;
43
44
    /** @var int */
45
    private $cameraFov = 90;
46
47
    /** @var int */
48
    private $cameraPitch = 0;
49
50
    /** @var int */
51
    private $radius = 50;
52
53
    /** @var string */
54
    private $source = 'default';
55
56
57
    /**
58
     * Api constructor.
59
     *
60
     * @param Client $client
61
     */
62 29
    public function __construct(Client $client)
63
    {
64 29
        $this->client = $client;
65 29
    }
66
67
    /**
68
     * API key from your Google console
69
     *
70
     * @param string $apiKey
71
     * @return $this
72
     */
73 1
    public function setApiKey(string $apiKey): self
74
    {
75 1
        $this->apiKey = $apiKey;
76
77 1
        return $this;
78
    }
79
80
    /**
81
     * Digital signature used to verify that any site generating requests.
82
     *
83
     * @param string $signature
84
     * @return $this
85
     */
86 2
    public function setSignature(string $signature): self
87
    {
88 2
        $this->signature = $signature;
89
90 2
        return $this;
91
    }
92
93
    /**
94
     * Determines the horizontal field of view of the image.
95
     * The field of view is expressed in degrees, with a maximum allowed value of 120.
96
     * When dealing with a fixed-size viewport, as with a Street View image of a set size,
97
     * field of view in essence represents zoom, with smaller numbers indicating a higher level of zoom.
98
     *
99
     * @param int $cameraFov
100
     * @return $this
101
     * @throws UnexpectedValueException
102
     */
103 2
    public function setCameraFov(int $cameraFov): self
104
    {
105 2
        if ($cameraFov > 120) {
106 1
            throw new UnexpectedValueException(
107 1
                'Camera FOV value cannot exceed 120 degrees.'
108
            );
109
        }
110
111 1
        $this->cameraFov = $cameraFov;
112
113 1
        return $this;
114
    }
115
116
    /**
117
     * Specifies the up or down angle of the camera relative to the Street View vehicle.
118
     * This is often, but not always, flat horizontal.
119
     * Positive values angle the camera up (with 90 degrees indicating straight up);
120
     * negative values angle the camera down (with -90 indicating straight down).
121
     *
122
     * @param int $cameraPitch
123
     * @return $this
124
     * @throws UnexpectedValueException
125
     */
126 3
    public function setCameraPitch(int $cameraPitch):self
127
    {
128 3
        if ($cameraPitch > 90) {
129 1
            throw new UnexpectedValueException(
130 1
                'Camera pitch value for Google Street View cannot exceed 90 degrees.'
131
            );
132
        }
133 2
        if ($cameraPitch < -90) {
134 1
            throw new UnexpectedValueException(
135 1
                'Camera pitch value for Google Street View cannot be inferior of -90 degrees.'
136
            );
137
        }
138
139 1
        $this->cameraPitch = $cameraPitch;
140
141 1
        return $this;
142
    }
143
144
    /**
145
     * Sets a radius, specified in meters,
146
     * in which to search for a panorama, centered on the given latitude and longitude.
147
     * Valid values are non-negative integers.
148
     *
149
     * @param int $radius
150
     * @return $this
151
     * @throws UnexpectedValueException
152
     */
153 2
    public function setRadius(int $radius): self
154
    {
155 2
        if ($radius < 0) {
156 1
            throw new UnexpectedValueException(
157 1
                'Radius value cannot be negative.'
158
            );
159
        }
160
161 1
        $this->radius = $radius;
162
163 1
        return $this;
164
    }
165
166
    /**
167
     * Indicates the compass heading of the camera.
168
     * Accepted values are from 0 to 360 (both values indicating North, with 90 indicating East, and 180 South).
169
     *
170
     * @param int $heading
171
     * @return $this
172
     * @throws UnexpectedValueException
173
     */
174 4
    public function setHeading(int $heading): self
175
    {
176 4
        if ($heading < 0) {
177 1
            throw new UnexpectedValueException(
178 1
                'Heading value cannot be inferior to zero degree.'
179
            );
180
        }
181 3
        if ($heading > 360) {
182 1
            throw new UnexpectedValueException(
183 1
                'Heading value cannot exceed 360 degrees.'
184
            );
185
        }
186
187 2
        $this->heading = $heading;
188
189 2
        return $this;
190
    }
191
192
    /**
193
     * Limits Street View searches to selected sources. Valid values are:
194
     *  - default uses the default sources for Street View; searches are not limited to specific sources.
195
     *  - outdoor limits searches to outdoor collections.
196
     *
197
     * @param string $source
198
     * @return $this
199
     * @throws UnexpectedValueException
200
     */
201 2
    public function setSource(string $source): self
202
    {
203 2
        if (!in_array($source, [self::SOURCE_DEFAULT, self::SOURCE_OUTDOOR], true)) {
204 1
            throw new UnexpectedValueException(sprintf(
205 1
                'Source value "%s" is unknown, only "%s" or "%s" values expected.',
206 1
                $source, self::SOURCE_DEFAULT, self::SOURCE_OUTDOOR
207
            ));
208
        }
209
210 1
        $this->source = $source;
211
212 1
        return $this;
213
    }
214
215
    /**
216
     * @param int $height
217
     * @return $this
218
     * @throws UnexpectedValueException
219
     */
220 2
    public function setImageHeight(int $height): self
221
    {
222 2
        if ($height < 1) {
223 1
            throw new UnexpectedValueException(
224 1
                'Image height value cannot be negative or equal to zero.'
225
            );
226
        }
227
228 1
        $this->imageHeight = $height;
229
230 1
        return $this;
231
    }
232
233
    /**
234
     * @param int $width
235
     * @return $this
236
     * @throws UnexpectedValueException
237
     */
238 2
    public function setImageWidth(int $width): self
239
    {
240 2
        if ($width < 1) {
241 1
            throw new UnexpectedValueException(
242 1
                'Image height value cannot be negative or equal to zero.'
243
            );
244
        }
245
246 1
        $this->imageWidth = $width;
247
248 1
        return $this;
249
    }
250
251
    /**
252
     * Returns URL to a static (non-interactive) Street View panorama or thumbnail.
253
     *
254
     * @param string $location
255
     * @return string
256
     * @throws BadStatusCodeException
257
     */
258
    public function getImageUrlByLocation(string $location): string
259
    {
260
        $parameters = $this->getRequestParameters([
261
            'location' => $location
262
        ]);
263
264
        return $this->getImageUrl($parameters);
265
    }
266
267
    /**
268
     * Returns URL to a static (non-interactive) Street View panorama or thumbnail
269
     *
270
     * @param float $latitude
271
     * @param float $longitude
272
     * @return string
273
     * @throws BadStatusCodeException
274
     */
275
    public function getImageUrlByLatitudeAndLongitude(float $latitude, float $longitude): string
276
    {
277
        $parameters = $this->getRequestParameters([
278
            'location' => $latitude . ',' . $longitude
279
        ]);
280
281
        return $this->getImageUrl($parameters);
282
    }
283
284
    /**
285
     * Returns URL to a static (non-interactive) Street View panorama or thumbnail
286
     *
287
     * @param string $panoramaId
288
     * @return string
289
     * @throws BadStatusCodeException
290
     */
291
    public function getImageUrlByPanoramaId(string $panoramaId): string
292
    {
293
        $parameters = $this->getRequestParameters([
294
            'pano' => $panoramaId
295
        ]);
296
297
        return $this->getImageUrl($parameters);
298
    }
299
300
    /**
301
     * Returns URL to a static (non-interactive) Street View panorama or thumbnail
302
     * The viewport is defined with URL parameters sent through a standard HTTP request, and is returned as a static image.
303
     *
304
     * @param array $parameters
305
     * @return string
306
     * @throws BadStatusCodeException
307
     */
308
    private function getImageUrl(array $parameters): string
309
    {
310
        $uri = $this->endpointImage . '?' . http_build_query($parameters);
311
312
        $response = $this->client->get($uri);
313
314
        if ($response->getStatusCode() !== 200) {
315
            throw new BadStatusCodeException(
316
                'Could not connect to ' . $this->endpointImage,
317
                $response->getStatusCode()
318
            );
319
        }
320
321
        return $uri;
322
    }
323
324
    /**
325
     * Requests provide data about Street View panoramas.
326
     * Using the metadata, you can find out if a Street View image is available at a given location,
327
     * as well as getting programmatic access to the latitude and longitude,
328
     * the panorama ID, the date the photo was taken, and the copyright information for the image.
329
     * Accessing this metadata allows you to customize error behavior in your application.
330
     * Street View API metadata requests are free to use. No quota is consumed when you request metadata.
331
     *
332
     * @param string $location
333
     * @return array
334
     * @throws UnexpectedValueException
335
     * @throws RequestException
336
     * @throws BadStatusCodeException
337
     * @throws UnexpectedStatusException
338
     */
339 11
    public function getMetadata(string $location): array
340
    {
341 11
        $location = trim($location);
342
343 11
        if (empty($location)) {
344 1
            throw new UnexpectedValueException(
345 1
                'Location argument cannot be empty to request Google Street view API Metadata.'
346
            );
347
        }
348
349 10
        $payload = $this->getRequestPayload(compact('location'));
350
351
        try {
352 10
            $response = $this->client->request('GET', $this->endpointMetadata, $payload);
353
        }
354 1
        catch (GuzzleException $e) {
355 1
            throw new RequestException(
356 1
                'Guzzle http client request failed.', $e
357
            );
358
        }
359
360 9
        if ($response->getStatusCode() !== 200) {
361 1
            throw new BadStatusCodeException(
362 1
                'Could not connect to ' . $this->endpointMetadata,
363 1
                $response->getStatusCode()
364
            );
365
        }
366
367 8
        $response = json_decode($response->getBody());
368
369
        // Indicates that no errors occurred; a panorama is found and metadata is returned.
370 8
        if ($response->status === 'OK') {
371 1
            return $this->formatMetadataResponse($response);
372
        }
373
        // Indicates that no panorama could be found near the provided location.
374
        // This may occur if a non-existent or invalid panorama ID is given.
375 7
        if ($response->status === 'ZERO_RESULTS') {
376 1
            throw new UnexpectedStatusException('Google Street view return zero results.');
377
        }
378
        // Indicates that the address string provided in the location parameter could not be found.
379
        // This may occur if a non-existent address is given.
380 6
        if ($response->status === 'NOT_FOUND') {
381 1
            throw new UnexpectedStatusException('No Google Street view result found.');
382
        }
383
        // Indicates that you have exceeded your daily quota or per-second quota for this API.
384 5
        if ($response->status === 'OVER_QUERY_LIMIT') {
385 1
            throw new UnexpectedStatusException('Google Street view API quota exceed.');
386
        }
387
        // Indicates that your request was denied.
388
        // This may occur if you did not use an API key or client ID, or
389
        // if the Street View API is not activated in the Google Cloud Platform Console project containing your API key.
390 4
        if ($response->status === 'REQUEST_DENIED') {
391 1
            throw new UnexpectedStatusException('Google Street view denied the request.');
392
        }
393
        // Generally indicates that the query parameters (address or latlng or components) are missing.
394 3
        if ($response->status === 'INVALID_REQUEST') {
395 1
            throw new UnexpectedStatusException('Google Street view request is invalid.');
396
        }
397
        // Indicates that the request could not be processed due to a server error.
398
        // This is often a temporary status. The request may succeed if you try again.
399 2
        if ($response->status === 'UNKNOWN_ERROR') {
400 1
            throw new UnexpectedStatusException('Google Street view unknown error occurred. Please try again.');
401
        }
402
403 1
        throw new UnexpectedStatusException(
404 1
            'Google Street view respond an unknown status response : "' . $response->status . '".'
405
        );
406
    }
407
408 1
    protected function formatMetadataResponse($response): array
409
    {
410
        return [
411 1
            'lat'           => $response->location->lat,
412 1
            'lng'           => $response->location->lng,
413 1
            'date'          => $response->date,
414 1
            'copyright'     => $response->copyright,
415 1
            'panoramaId'    => $response->pano_id
416
        ];
417
    }
418
419 10
    private function getRequestPayload(array $parameters): array
420
    {
421 10
        return ['query' => $this->getRequestParameters($parameters)];
422
    }
423
424 10
    private function getRequestParameters(array $parameters): array
425
    {
426
        $defaultParameters = [
427 10
            'key'       => $this->apiKey,
428 10
            'size'      => $this->imageWidth . 'x' . $this->imageHeight,
429 10
            'fov'       => $this->cameraFov,
430 10
            'pitch'     => $this->cameraPitch,
431 10
            'radius'    => $this->radius,
432 10
            'source'    => $this->source
433
        ];
434
435
        // optional parameters which have not default value
436 10
        if ($this->heading) {
437 1
            $defaultParameters['heading'] = $this->heading;
438
        }
439 10
        if ($this->signature) {
440 1
            $defaultParameters['signature'] = $this->signature;
441
        }
442
443 10
        return array_merge($defaultParameters, $parameters);
444
    }
445
446
}
447