Прямое геокодирование

Прямое геокодирование (Forward Geocoding) — процесс преобразования текстового описания места в географические координаты. В качестве входных данных могут выступать адреса, названия городов, стран, улиц, достопримечательностей, организаций и других объектов. Результатом работы геокодера становятся координаты широты и долготы, а также дополнительная информация о найденном объекте.

В экосистеме Mapbox прямое геокодирование выполняется через Geocoding API. В приложениях на базе Mapbox GL JS этот механизм позволяет:

  • искать адреса и населённые пункты;
  • создавать поисковые строки на карте;
  • автоматически перемещать карту к найденному месту;
  • отображать результаты поиска маркерами;
  • получать сведения об объектах и их категориях;
  • реализовывать автодополнение адресов.

Типичный сценарий работы выглядит следующим образом:

  1. Пользователь вводит текстовый запрос.
  2. Запрос отправляется в Geocoding API.
  3. API возвращает список подходящих объектов.
  4. Приложение выбирает нужный результат.
  5. Карта перемещается к найденным координатам.

Подключение Geocoding API

Для выполнения запросов требуется access token Mapbox.

Создание карты:

mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';

const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v12',
    center: [37.6176, 55.7558],
    zoom: 10
});

После инициализации карты можно выполнять геокодирование через HTTP-запросы или готовый плагин поиска.


Структура запроса геокодирования

Базовый URL имеет следующий формат:

https://api.mapbox.com/geocoding/v5/mapbox.places/{search_text}.json

Пример запроса:

const query = 'Moscow';

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/${query}.json?access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(data => {
    console.log(data);
});

После выполнения запроса сервер возвращает JSON-документ с массивом найденных объектов.


Структура ответа

Типичный ответ содержит поле features.

Пример сокращённого результата:

{
  "type": "FeatureCollection",
  "query": ["moscow"],
  "features": [
    {
      "id": "place.123456",
      "type": "Feature",
      "place_name": "Moscow, Russia",
      "center": [37.6176, 55.7558],
      "geometry": {
        "type": "Point",
        "coordinates": [37.6176, 55.7558]
      }
    }
  ]
}

Наиболее важные поля:

Поле Назначение
id Уникальный идентификатор объекта
place_name Полное название объекта
center Координаты центра
geometry Геометрия объекта
text Основное название
properties Дополнительные свойства
context Информация о регионе и стране

Получение координат объекта

Самая распространённая задача — извлечение координат найденного места.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/London.json?access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(data => {

    const place = data.features[0];

    const longitude = place.center[0];
    const latitude = place.center[1];

    console.log(longitude, latitude);

});

Массив координат всегда содержит:

[longitude, latitude]

Порядок элементов критически важен. Ошибка в порядке координат приводит к отображению точки в неверном месте.


Перемещение карты к найденному объекту

После получения координат карта может автоматически перейти к нужному месту.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/Berlin.json?access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(data => {

    const coordinates = data.features[0].center;

    map.flyTo({
        center: coordinates,
        zoom: 13
    });

});

Метод flyTo() создаёт плавную анимацию перемещения.

Параметры:

map.flyTo({
    center: [13.405, 52.52],
    zoom: 13,
    speed: 1.5,
    curve: 1.2,
    essential: true
});

Отображение найденной точки маркером

После геокодирования часто требуется показать результат на карте.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/Paris.json?access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(data => {

    const coordinates = data.features[0].center;

    new mapboxgl.Marker()
        .setLngLat(coordinates)
        .addTo(map);

});

Дополнительно можно отобразить всплывающее окно.

const popup = new mapboxgl.Popup({
    offset: 25
}).setText('Paris');

new mapboxgl.Marker()
    .setLngLat(coordinates)
    .setPopup(popup)
    .addTo(map);

Использование асинхронной функции

Современный подход предполагает применение async/await.

async function geocode(placeName) {

    const response = await fetch(
        `https://api.mapbox.com/geocoding/v5/mapbox.places/${placeName}.json?access_token=${mapboxgl.accessToken}`
    );

    const data = await response.json();

    return data.features[0];
}

Использование:

const place = await geocode('Rome');

console.log(place.place_name);
console.log(place.center);

Такой код легче читать и поддерживать.


Ограничение области поиска

По умолчанию поиск выполняется по всему миру.

Для повышения точности можно ограничить поиск конкретной страной.

Поиск только в Германии:

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/Berlin.json?country=de&access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(console.log);

Поиск сразу по нескольким странам:

country=de,fr,it

Пример:

https://api.mapbox.com/geocoding/v5/mapbox.places/Main%20Street.json?country=us,ca

Подобный подход уменьшает количество ложных совпадений.


Поиск объектов определённого типа

Geocoding API умеет фильтровать результаты по категориям.

Поддерживаются типы:

Тип Описание
country Страна
region Регион
district Район
place Город
locality Населённый пункт
neighborhood Микрорайон
postcode Почтовый индекс
address Адрес
poi Точка интереса

Поиск только городов:

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/York.json?types=place&access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(console.log);

Поиск только адресов:

types=address

Поиск нескольких категорий:

types=address,poi

Использование языковой локализации

Результаты могут возвращаться на разных языках.

Пример русского языка:

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/London.json?language=ru&access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(console.log);

Пример английского языка:

language=en

Несколько языков:

language=ru,en

Это особенно полезно для международных проектов.


Ограничение количества результатов

Параметр limit определяет максимальное число найденных объектов.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/New.json?limit=5&access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(console.log);

Результат будет содержать не более пяти объектов.


Поиск рядом с заданной точкой

Иногда необходимо учитывать текущее положение пользователя.

Для этого используется параметр proximity.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/Main%20Street.json?proximity=37.6176,55.7558&access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(console.log);

Результаты, расположенные ближе к указанным координатам, получают более высокий приоритет.

Это значительно улучшает качество поиска адресов.


Обработка нескольких результатов

Запрос может вернуть множество объектов.

fetch(
    `https://api.mapbox.com/geocoding/v5/mapbox.places/Springfield.json?access_token=${mapboxgl.accessToken}`
)
.then(response => response.json())
.then(data => {

    data.features.forEach(place => {

        console.log(place.place_name);

    });

});

Создание маркеров для каждого результата:

data.features.forEach(place => {

    new mapboxgl.Marker()
        .setLngLat(place.center)
        .addTo(map);

});

Подход удобен для отображения списка совпадений.


Реализация поисковой строки

Простейший пример поиска по нажатию кнопки.

HTML:

<input id="search">
<button id="btn">Найти</button>

Jav * aScript:

document
    .getElementById('btn')
    .addEventListener('click', async () => {

        const value =
            document.getElementById('search').value;

        const response = await fetch(
            `https://api.mapbox.com/geocoding/v5/mapbox.places/${value}.json?access_token=${mapboxgl.accessToken}`
        );

        const data = await response.json();

        if (!data.features.length) {
            return;
        }

        const place = data.features[0];

        map.flyTo({
            center: place.center,
            zoom: 14
        });

    });

После ввода адреса карта автоматически переходит к найденному месту.


Использование Mapbox Geocoder Plugin

Mapbox предоставляет готовый интерфейс поиска.

Подключение стилей:

<link
    href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css"
    rel="stylesheet">

Подключение скрипта:

<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>

Создание поискового элемента:

const geocoder = new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl
});

map.addControl(geocoder);

После добавления контрола появляются:

  • поле ввода;
  • выпадающий список подсказок;
  • автоматическое геокодирование;
  • переход карты к результату.

Обработка события выбора результата

Плагин генерирует событие result.

geocoder.on('result', event => {

    console.log(event.result);

});

Получение координат:

geocoder.on('result', event => {

    const coordinates =
        event.result.center;

    console.log(coordinates);

});

Получение полного адреса:

geocoder.on('result', event => {

    console.log(
        event.result.place_name
    );

});

Настройка геокодера

Ограничение поиска странами:

const geocoder = new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl,
    countries: 'us,ca'
});

Поиск только адресов:

const geocoder = new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl,
    types: 'address'
});

Ограничение числа результатов:

const geocoder = new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl,
    limit: 3
});

Установка языка:

const geocoder = new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl,
    language: 'ru'
});

Обработка ошибок

Сетевые запросы должны сопровождаться обработкой исключений.

async function geocode(query) {

    try {

        const response = await fetch(
            `https://api.mapbox.com/geocoding/v5/mapbox.places/${query}.json?access_token=${mapboxgl.accessToken}`
        );

        if (!response.ok) {
            throw new Error('Ошибка API');
        }

        const data = await response.json();

        return data.features;

    } catch (error) {

        console.error(error);

        return [];

    }

}

Подобная проверка предотвращает аварийное завершение приложения.


Оптимизация запросов

При поиске в режиме реального времени желательно уменьшать количество обращений к серверу.

Для этого применяется техника debounce.

function debounce(callback, delay) {

    let timeout;

    return (...args) => {

        clearTimeout(timeout);

        timeout = setTimeout(() => {
            callback(...args);
        }, delay);

    };

}

Использование:

const search = debounce(async value => {

    const response = await fetch(
        `https://api.mapbox.com/geocoding/v5/mapbox.places/${value}.json?access_token=${mapboxgl.accessToken}`
    );

    const data = await response.json();

    console.log(data);

}, 300);

Такой подход уменьшает нагрузку на API и повышает производительность интерфейса.


Практические сценарии применения

Прямое геокодирование активно используется в различных типах картографических приложений:

  • навигационные сервисы;
  • системы доставки;
  • логистические платформы;
  • сервисы недвижимости;
  • туристические порталы;
  • корпоративные геоинформационные системы;
  • системы мониторинга транспорта;
  • приложения поиска ближайших объектов;
  • административные панели с картографическими данными;
  • CRM-системы, работающие с адресами клиентов.

Во всех этих сценариях прямое геокодирование служит связующим звеном между текстовыми данными пользователя и пространственными координатами, позволяя быстро находить объекты и отображать их на интерактивной карте Mapbox GL JS.