init(); $this->apiKey = $apiKey ?? $this->providerConfig('key'); } /** * Provider unique key * @return string */ public function providerKey(): string { return 'yandex'; } /** * Provider title * @return string */ public function providerTitle(): string { return 'Yandex Maps'; } /** * Config settings form * @param Form $form */ public function providerSettingsForm($form) { $form->text($this->providerConfigKey('key'), _t('geo', 'API key'), '', false) ->htmlAfter(_t('geo', 'Get API key [a]here[/a]', ['a' => '', '/a' => ''])) ->visibleIf('geo.geocode.provider', $this->providerKey()) ->sysAdmin($this->providerConfigKey('key')); } /** * Geocode query * @param string $query address * @param array $opts * @return array */ public function geocodeQuery($query, array $opts = []) { $query = $this->defaults($opts['query'] ?? [], [ 'geocode' => $query, ]); $response = $this->geocodeRequest($query, $opts); $i = 0; $locations = []; foreach ($response as $item) { # "lng lat" => [lat,lng] $coords = array_reverse(explode(' ', $item['GeoObject']['Point']['pos'] ?? '0 0')); $locations[] = [ 'lat' => $coords[0], 'lng' => $coords[1], ]; if (++$i >= $this->limit) { break; } } if ($this->limit === 1) { return reset($locations); } return $locations; } /** * Reverse geocode query * @see https://tech.yandex.ru/maps/jsapi/doc/2.1/ref/reference/geocode-docpage/ * @param string|float $latitude * @param string|float $longitude * @param array|Closure $format * @param array $opts * @return array|string */ public function reverseQuery($latitude, $longitude, $format = self::FORMAT_DEFAULT, array $opts = []) { $query = $this->defaults($opts['query'] ?? [], [ 'geocode' => join(',', [$latitude, $longitude]), 'sco' => 'latlong', ]); $response = $this->geocodeRequest($query, $opts); $i = 0; $address = []; foreach ($response as $item) { $address[] = $this->formatAddress( $item['GeoObject']['metaDataProperty']['GeocoderMetaData']['Address'], $format ); if (++$i >= $this->limit) { break; } } if ($this->limit === 1) { return reset($address); } return $address; } /** * Format address to specified type * @param array $data * @param array|Closure $format * @return string */ protected function formatAddress(array $data, $format): string { if ($format instanceof Closure) { return $format($data); } if ($format === static::FORMAT_FULL || empty($format) || !is_array($format)) { return $data['formatted']; } $known = []; foreach ($data['Components'] as $value) { switch ($value['kind']) { case 'country': $known[static::ADDRESS_COUNTRY] = $value['name']; break; case 'province': $known[static::ADDRESS_REGION] = $value['name']; break; case 'locality': $known[static::ADDRESS_CITY] = $value['name']; break; case 'district': $known[static::ADDRESS_DISTRICT] = $value['name']; break; case 'street': $known[static::ADDRESS_STREET] = $value['name']; break; case 'house': $known[static::ADDRESS_HOUSE] = $value['name']; break; default: $known[$value['kind']] = $value['name']; break; } } $address = []; foreach ($format as $type) { if (! empty($known[$type])) { $address[] = $known[$type]; } } return join(', ', $address); } /** * Send geocode request to API * @param array $query * @param array $opts * @return array | bool */ protected function geocodeRequest(array $query, array $opts = []) { $query['apikey'] = $this->apiKey; $query['format'] = 'json'; $query['results'] = $this->limit; $version = $opts['version'] ?? $this->providerConfig('version', '1.x'); $response = $this->request('https://geocode-maps.yandex.ru/' . $version . '/', $query); if ($response === false) { return []; } $json = $response->json(); if ($json === null) { $this->log('Invalid response'); return false; } if ( empty($json) || isset($json['error']) || ( isset($json['response']) && $json['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['found'] === '0' ) ) { return []; } return $json['response']['GeoObjectCollection']['featureMember']; } }