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\Yandex;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\UnsupportedOperation;
17
use Geocoder\Http\Provider\AbstractHttpProvider;
18
use Geocoder\Model\AddressBuilder;
19
use Geocoder\Model\AddressCollection;
20
use Geocoder\Provider\Provider;
21
use Geocoder\Provider\Yandex\Model\YandexAddress;
22
use Geocoder\Query\GeocodeQuery;
23
use Geocoder\Query\ReverseQuery;
24
use Http\Client\HttpClient;
25
26
/**
27
 * @author Antoine Corcy <[email protected]>
28
 */
29
final class Yandex extends AbstractHttpProvider implements Provider
30
{
31
    /**
32
     * @var string
33
     */
34
    const GEOCODE_ENDPOINT_URL = 'https://geocode-maps.yandex.ru/1.x/?format=json&geocode=%s';
35
36
    /**
37
     * @var string
38
     */
39
    const REVERSE_ENDPOINT_URL = 'https://geocode-maps.yandex.ru/1.x/?format=json&geocode=%F,%F';
40
41
    /**
42
     * @var string
43
     */
44
    private $toponym;
45
46
    /**
47
     * @var string|null
48
     */
49
    private $apiKey;
50
51
    /**
52
     * @param HttpClient  $client  an HTTP adapter
53
     * @param string      $toponym toponym biasing only for reverse geocoding (optional)
54
     * @param string|null $apiKey  API Key
55
     */
56 26
    public function __construct(HttpClient $client, string $toponym = null, string $apiKey = null)
57
    {
58 26
        parent::__construct($client);
59
60 26
        $this->toponym = $toponym;
61 26
        $this->apiKey = $apiKey;
62 26
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 14
    public function geocodeQuery(GeocodeQuery $query): Collection
68
    {
69 14
        $address = $query->getText();
70
71
        // This API doesn't handle IPs
72 14
        if (filter_var($address, FILTER_VALIDATE_IP)) {
73 2
            throw new UnsupportedOperation('The Yandex provider does not support IP addresses, only street addresses.');
74
        }
75
76 12
        $url = sprintf(self::GEOCODE_ENDPOINT_URL, urlencode($address));
77
78 12
        return $this->executeQuery($url, $query->getLimit(), $query->getLocale());
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 11
    public function reverseQuery(ReverseQuery $query): Collection
85
    {
86 11
        $coordinates = $query->getCoordinates();
87 11
        $longitude = $coordinates->getLongitude();
88 11
        $latitude = $coordinates->getLatitude();
89 11
        $url = sprintf(self::REVERSE_ENDPOINT_URL, $longitude, $latitude);
90
91 11
        if (null !== $toponym = $query->getData('toponym', $this->toponym)) {
92 4
            $url = sprintf('%s&kind=%s', $url, $toponym);
93
        }
94
95 11
        return $this->executeQuery($url, $query->getLimit(), $query->getLocale());
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 11
    public function getName(): string
102
    {
103 11
        return 'yandex';
104
    }
105
106
    /**
107
     * @param string $url
108
     * @param int    $limit
109
     * @param string $locale
110
     *
111
     * @return AddressCollection
112
     */
113 23
    private function executeQuery(string $url, int $limit, string $locale = null): AddressCollection
114
    {
115 23
        if (null !== $locale) {
116 8
            $url = sprintf('%s&lang=%s', $url, str_replace('_', '-', $locale));
117
        }
118
119 23
        if (null !== $this->apiKey) {
120
            $url = sprintf('%s&apikey=%s', $url, $this->apiKey);
121
        }
122
123 23
        $url = sprintf('%s&results=%d', $url, $limit);
124 23
        $content = $this->getUrlContents($url);
125 13
        $json = json_decode($content, true);
126
127 13
        if (empty($json) || isset($json['error']) ||
128 13
            (isset($json['response']) && '0' === $json['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['found'])
129
        ) {
130 3
            return new AddressCollection([]);
131
        }
132
133 10
        $data = $json['response']['GeoObjectCollection']['featureMember'];
134
135 10
        $locations = [];
136 10
        foreach ($data as $item) {
137 10
            $builder = new AddressBuilder($this->getName());
138 10
            $bounds = null;
139 10
            $flatArray = ['pos' => ' '];
140
141 10
            array_walk_recursive(
142 10
                $item['GeoObject'],
143
144
                /**
145
                 * @param string $value
146
                 */
147 10
                function ($value, $key) use (&$flatArray) {
148 10
                    $flatArray[$key] = $value;
149 10
                }
150
            );
151
152 10
            if (!empty($flatArray['lowerCorner']) && !empty($flatArray['upperCorner'])) {
153 10
                $lowerCorner = explode(' ', $flatArray['lowerCorner']);
154 10
                $upperCorner = explode(' ', $flatArray['upperCorner']);
155 10
                $builder->setBounds(
156 10
                    (float) $lowerCorner[1],
157 10
                    (float) $lowerCorner[0],
158 10
                    (float) $upperCorner[1],
159 10
                    (float) $upperCorner[0]
160
                );
161
            }
162
163 10
            $coordinates = explode(' ', $flatArray['pos']);
164 10
            $builder->setCoordinates((float) $coordinates[1], (float) $coordinates[0]);
165
166 10
            foreach (['AdministrativeAreaName', 'SubAdministrativeAreaName'] as $i => $name) {
167 10
                if (isset($flatArray[$name])) {
168 10
                    $builder->addAdminLevel($i + 1, $flatArray[$name], null);
169
                }
170
            }
171
172 10
            $builder->setStreetNumber(isset($flatArray['PremiseNumber']) ? $flatArray['PremiseNumber'] : null);
173 10
            $builder->setStreetName(isset($flatArray['ThoroughfareName']) ? $flatArray['ThoroughfareName'] : null);
174 10
            $builder->setSubLocality(isset($flatArray['DependentLocalityName']) ? $flatArray['DependentLocalityName'] : null);
175 10
            $builder->setLocality(isset($flatArray['LocalityName']) ? $flatArray['LocalityName'] : null);
176 10
            $builder->setCountry(isset($flatArray['CountryName']) ? $flatArray['CountryName'] : null);
177 10
            $builder->setCountryCode(isset($flatArray['CountryNameCode']) ? $flatArray['CountryNameCode'] : null);
178
179
            /** @var YandexAddress $location */
180 10
            $location = $builder->build(YandexAddress::class);
181 10
            $location = $location->withPrecision(isset($flatArray['precision']) ? $flatArray['precision'] : null);
182 10
            $location = $location->withName(isset($flatArray['name']) ? $flatArray['name'] : null);
183 10
            $location = $location->withKind($flatArray['kind'] ?? null);
184 10
            $locations[] = $location;
185
        }
186
187 10
        return new AddressCollection($locations);
188
    }
189
}
190