Налаштування геолокаційного визначення найближчого магазину 1С-Bitrix
Користувач відкриває сторінку «Наші магазини» та бачить список з 30 адрес без якого-небудь порядку. Він живе в Мінську, а перші п'ять магазинів у списку — Москва. Здавалося б, банальна завдання — показати найближчі — на практиці упирається в точність геолокації, розрахунок відстаней та зберігання координат магазинів в Bitrix.
Зберігання даних магазинів
В Bitrix магазини зберігаються у двох місцях залежно від використовуваного функціоналу:
Модуль sale, торгові точки: таблиця b_sale_store з полями ID, TITLE, ADDRESS, GPS_N (широта), GPS_S (довгота). Це стандартна структура для точок самовивозу.
Інфоблок магазинів: якщо магазини оформлені як елементи інфоблоку, координати зазвичай зберігаються в користувацьких властивостях типу String або в спеціалізованих полях типу Map (при наявності сторонніх модулів).
Для розрахунку відстаней потрібні числові координати. Якщо вони зберігаються в рядковій властивості інфоблоку у форматі «53.9045, 27.5615» — при виборці їх доведеться парсити, що незручно. Правильне рішення: зберігати широту та довготу у двох окремих числових властивостях або використовувати b_sale_store.GPS_N / b_sale_store.GPS_S.
Визначення координат користувача
Два підходи:
Браузерна геолокація (Geolocation API): точна, до 50 метрів, але вимагає дозволу користувача. Асинхронна — не можна використовувати при серверному рендерингу.
IP-геолокація: працює без запиту дозволу, точність — до міста/району. В Bitrix вбудований модуль location з базою GeoIP (b_geocode_city, b_geocode_country). Метод \Bitrix\Location\Service\FormatService::getInstance() працює з адресами, для IP-геолокації використовується \Bitrix\Main\Service\GeoIp\Manager::getLocationByIp().
$location = \Bitrix\Main\Service\GeoIp\Manager::getLocationByIp(
\Bitrix\Main\Context::getCurrent()->getRequest()->getRemoteAddress()
);
$userLat = $location['LATITUDE'] ?? null;
$userLon = $location['LONGITUDE'] ?? null;
Розрахунок відстаней: формула Хаверсина у SQL
Найефективніший підхід — рахувати відстані прямо у SQL-запиті. Формула Хаверсина для PostgreSQL:
SELECT
id,
title,
gps_n AS lat,
gps_s AS lon,
(
6371 * acos(
cos(radians(:user_lat)) * cos(radians(gps_n)) *
cos(radians(gps_s) - radians(:user_lon)) +
sin(radians(:user_lat)) * sin(radians(gps_n))
)
) AS distance_km
FROM b_sale_store
WHERE active = 'Y'
AND gps_n IS NOT NULL
AND gps_s IS NOT NULL
ORDER BY distance_km
LIMIT 5;
Для MySQL синтаксис аналогічний. На PostgreSQL додатково можна використовувати розширення earthdistance з cube, що швидше для великих наборів точок.
Через ORM Bitrix прямий SQL викликається через \Bitrix\Main\Application::getConnection()->query(). Готового вираження Хаверсина в D7 ORM немає — доведеться використовувати ExpressionField з raw SQL або нативний запит.
Фронтенд: два кроки
- При завантаженні сторінки — показуємо магазини, відсортовані за IP-геолокацією (серверна сортування, миттєво).
- Після отримання точних координат через
navigator.geolocation.getCurrentPosition()— пересортовуємо через AJAX-запит до компонента з параметрамиlatтаlon.
navigator.geolocation.getCurrentPosition(function(pos) {
fetch('/ajax/nearest-stores/?lat=' + pos.coords.latitude + '&lon=' + pos.coords.longitude)
.then(r => r.json())
.then(stores => renderStoreList(stores));
});
Компонент-обробник AJAX читає lat/lon з GET, виконує SQL з Хаверсином, повертає JSON. В Bitrix це реалізується через компонент з параметром ajax_mode = Y або через власний endpoint у /local/ajax/.
Що ми налаштовуємо
- Перевірку наявності координат у
b_sale_storeабо інфоблоці магазинів, їх заповнення - SQL-запит з формулою Хаверсина через
Application::getConnection() - IP-геолокацію через
GeoIp\Managerдля первинної сортування без дозволу користувача - AJAX-endpoint для пересортування після отримання точних координат браузера
- Кешування результату для кожної пари координат (округленої до 0.01 градуса)







