Code

< 40 %
40-60 %
> 60 %
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Geocoder package.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT License
11
 */
12
13
namespace Geocoder\Provider\BingMaps;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\InvalidCredentials;
17
use Geocoder\Exception\UnsupportedOperation;
18
use Geocoder\Http\Provider\AbstractHttpProvider;
19
use Geocoder\Model\AddressBuilder;
20
use Geocoder\Model\AddressCollection;
21
use Geocoder\Provider\Provider;
22
use Geocoder\Query\GeocodeQuery;
23
use Geocoder\Query\ReverseQuery;
24
use Psr\Http\Client\ClientInterface;
25
26
/**
27
 * @author David Guyon <[email protected]>
28
 */
29
final class BingMaps extends AbstractHttpProvider implements Provider
30
{
31
    /**
32
     * @var string
33
     */
34
    public const GEOCODE_ENDPOINT_URL = 'https://dev.virtualearth.net/REST/v1/Locations/?maxResults=%d&q=%s&key=%s&incl=ciso2';
35
36
    /**
37
     * @var string
38
     */
39
    public const REVERSE_ENDPOINT_URL = 'https://dev.virtualearth.net/REST/v1/Locations/%F,%F?key=%s&incl=ciso2';
40
41
    /**
42
     * @var string
43
     */
44
    private $apiKey;
45
46
    /**
47
     * @param ClientInterface $client An HTTP adapter
48
     * @param string          $apiKey An API key
49
     */
50 24
    public function __construct(ClientInterface $client, string $apiKey)
51
    {
52 24
        if (empty($apiKey)) {
53
            throw new InvalidCredentials('No API key provided.');
54
        }
55
56 24
        $this->apiKey = $apiKey;
57 24
        parent::__construct($client);
58
    }
59
60 14
    public function geocodeQuery(GeocodeQuery $query): Collection
61
    {
62
        // This API doesn't handle IPs
63 14
        if (filter_var($query->getText(), FILTER_VALIDATE_IP)) {
64 4
            throw new UnsupportedOperation('The BingMaps provider does not support IP addresses, only street addresses.');
65
        }
66
67 10
        $url = sprintf(self::GEOCODE_ENDPOINT_URL, $query->getLimit(), urlencode($query->getText()), $this->apiKey);
68
69 10
        return $this->executeQuery($url, $query->getLocale(), $query->getLimit());
70
    }
71
72 9
    public function reverseQuery(ReverseQuery $query): Collection
73
    {
74 9
        $coordinates = $query->getCoordinates();
75 9
        $url = sprintf(self::REVERSE_ENDPOINT_URL, $coordinates->getLatitude(), $coordinates->getLongitude(), $this->apiKey);
76
77 9
        return $this->executeQuery($url, $query->getLocale(), $query->getLimit());
78
    }
79
80 7
    public function getName(): string
81
    {
82 7
        return 'bing_maps';
83
    }
84
85
    /**
86
     * @param string $locale
87
     */
88 19
    private function executeQuery(string $url, string $locale = null, int $limit): Collection
89
    {
90 19
        if (null !== $locale) {
91 4
            $url = sprintf('%s&culture=%s', $url, str_replace('_', '-', $locale));
92
        }
93
94 19
        $content = $this->getUrlContents($url);
95 8
        $json = json_decode($content);
96
97 8
        if (!isset($json->resourceSets[0]) || !isset($json->resourceSets[0]->resources)) {
98
            return new AddressCollection([]);
99
        }
100
101 8
        $data = (array) $json->resourceSets[0]->resources;
102
103 8
        $results = [];
104 8
        foreach ($data as $item) {
105 6
            $builder = new AddressBuilder($this->getName());
106 6
            $coordinates = (array) $item->geocodePoints[0]->coordinates;
107 6
            $builder->setCoordinates($coordinates[0], $coordinates[1]);
108
109 6
            if (isset($item->bbox) && is_array($item->bbox) && count($item->bbox) > 0) {
110 6
                $builder->setBounds($item->bbox[0], $item->bbox[1], $item->bbox[2], $item->bbox[3]);
111
            }
112
113 6
            $builder->setStreetName($item->address->addressLine ?? null);
114 6
            $builder->setPostalCode($item->address->postalCode ?? null);
115 6
            $builder->setLocality($item->address->locality ?? null);
116 6
            $builder->setCountry($item->address->countryRegion ?? null);
117 6
            $builder->setCountryCode($item->address->countryRegionIso2 ?? null);
118
119 6
            foreach (['adminDistrict', 'adminDistrict2'] as $i => $property) {
120 6
                if (property_exists($item->address, $property)) {
121 6
                    $builder->addAdminLevel($i + 1, $item->address->{$property}, null);
122
                }
123
            }
124
125 6
            $results[] = $builder->build();
126
127 6
            if (count($results) >= $limit) {
128 1
                break;
129
            }
130
        }
131
132 8
        return new AddressCollection($results);
133
    }
134
}
135