Yandex Metrika
sanches.free

Координаты по текстовому адресу в PHP: Geocoding API и запись lat/lng в таблицу

Задача и поля в БД

В каталогах и справочниках магазинов часто хранят текстовый адрес, а для карты на сайте нужны числовые координаты. Типичная схема в пользовательской таблице (b_hlbd_* или обычный InnoDB‑таблица рядом с проектом): колонки lat и lng типа числа с достаточной точностью, плюс флаг вроде is_loc_checked, чтобы помечать строки уже обработанные геокодером (включая неудачные попытки, чтобы не долбить API бесконечно).

Старый приём и почему его обновили

Раньше можно было дергать http://maps.googleapis.com/maps/api/geocode/json без ключа на простых задачах. Сейчас для продакшена нужен включённый Google Geocoding API, ограниченный ключ (по referrer или IP вашего cron‑хоста) и только HTTPS. Квота и платные вызовы — обычная эксплуатация: лимиты и биллинг смотреть в Google Cloud Console; в ответах по-прежнему бывает статус вроде OVER_QUERY_LIMIT — его стоит логировать и останавливать пакет, а не падать немым die().

Каркас клиента без жёсткой привязки к БД

Ниже — выделенная функция, которая по адресу возвращает широту и долготу или null. Имя ключа и URL взяты параметрами, чтобы ключ не светился в репозитории: передайте его из переменной окружения, .settings.php или параметра модуля на стороне «1С‑Битрикс».

declare(strict_types=1);

/**
 * @return array{lat: float, lng: float}|null
 */
function resolve_address_geocode(string $address, string $apiKey): ?array
{
    $base = 'https://maps.googleapis.com/maps/api/geocode/json';
    $query = http_build_query([
        'address' => $address,
        'key' => $apiKey,
        'language' => 'ru',
    ], '', '&', PHP_QUERY_RFC3986);

    $ch = curl_init($base . '?' . $query);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 8,
        CURLOPT_TIMEOUT => 20,
    ]);
    $payload = curl_exec($ch);
    if ($payload === false) {
        return null;
    }
    curl_close($ch);

    $decoded = json_decode($payload, true);
    if (!is_array($decoded) || !isset($decoded['status'])) {
        return null;
    }

    if ($decoded['status'] === 'OVER_QUERY_LIMIT') {
        throw new RuntimeException('Geocoding quota exceeded');
    }
    if ($decoded['status'] !== 'OK' || empty($decoded['results'][0]['geometry']['location'])) {
        return null;
    }

    $loc = $decoded['results'][0]['geometry']['location'];
    if (!isset($loc['lat'], $loc['lng'])) {
        return null;
    }

    return [
        'lat' => (float) $loc['lat'],
        'lng' => (float) $loc['lng'],
    ];
}

Цикл по «необработанным» точкам и запись координат

После успешного геокодирования сохраняйте числа как числа — без addslashes над вещественными литералами сниппетом из двухтысячных. В PDO или соединении Битрикса используйте плейсхолдеры; для иллюстрации ниже упрощённый UPDATE с явным приведением (int) для первичного ключа.

$apiKey = getenv('GOOGLE_MAPS_SERVER_KEY');
if ($apiKey === false || $apiKey === '') {
    throw new RuntimeException('Missing GOOGLE_MAPS_SERVER_KEY');
}

$addresses = []; // здесь загрузите SELECT из своей таблицы stores

foreach ($addresses as $row) {
    $id = (int) $row['id'];
    $line = trim((string) $row['address']);
    if ($line === '') {
        continue;
    }

    try {
        $coords = resolve_address_geocode($line, $apiKey);
    } catch (Throwable $e) {
        error_log('[geocode] stop: ' . $e->getMessage());
        break;
    }

    if ($coords === null) {
        continue;
    }

    // Пример только для понятности; на бою лучше prepare/execute
    printf("UPDATE shops_geo SET lat = %f, lng = %f, is_loc_checked = 1 WHERE id = %d;\n",
        $coords['lat'],
        $coords['lng'],
        $id
    );
}

Связь с типовым кодом Bitrix

Такие скрипты обычно крутят из cron с однократным подключением пролога ядра, если нужен контекст сайта или COption для секрета. Для высокочастотного импорта разумнее отложить геокодирование из фонового агента с паузой между запросами, чтобы не упереться в лимиты поставщика и не блокировать интернет‑магазин.

Кратко

  • Координаты по адресу — через официальный Geocoding API поверх HTTPS и с ограниченным ключом.
  • Флаг is_loc_checked экономит повторные вызовы и помогает отделить «ещё не пробовали» от «пробовали и не нашли».
  • Широта и долгота — тип float/decimal и параметризованный запрос, не строки с экранированием.
  • Превышение квоты обрабатывайте явно: лог, отложенный повтор, алерт.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии