Определение местоположения

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

В основе механизма лежит объект navigator.geolocation, который предоставляет доступ к данным GPS, Wi-Fi и сетевого позиционирования устройства. Mapbox GL JS не выполняет геолокацию самостоятельно, а интегрируется с этим API через контролы и пользовательскую логику.


GeolocateControl как основной инструмент

В Mapbox GL JS предусмотрен встроенный компонент управления геолокацией — GeolocateControl. Он обеспечивает:

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

Базовая инициализация выполняется добавлением контролла к экземпляру карты.

mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';

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

const geolocate = new mapboxgl.GeolocateControl({
  positionOptions: {
    enableHighAccuracy: true
  },
  trackUserLocation: true,
  showAccuracyCircle: true,
  showUserHeading: true
});

map.addControl(geolocate);

Параметры GeolocateControl

Поведение контроля геолокации определяется набором параметров, влияющих на точность, производительность и UX.

positionOptions

Объект, передаваемый напрямую в Geolocation API:

  • enableHighAccuracy — использование более точных методов определения координат (GPS вместо сетевых данных);
  • timeout — максимальное время ожидания получения координат;
  • maximumAge — допустимое время кэширования позиции.
positionOptions: {
  enableHighAccuracy: true,
  timeout: 6000,
  maximumAge: 0
}

Высокая точность увеличивает энергопотребление и время получения данных, но критична для навигационных сценариев.


trackUserLocation

Режим непрерывного отслеживания положения. При включении карта автоматически обновляет позицию при каждом изменении координат.

trackUserLocation: true

Механизм основан на watchPosition, что позволяет получать поток обновлений вместо одноразового запроса.


showAccuracyCircle

Отображение окружности погрешности определения координат. Радиус зависит от качества сигнала и источника данных.

showAccuracyCircle: true

Этот элемент визуально интерпретирует точность позиционирования, особенно важную при использовании сетевых методов определения координат.


showUserHeading

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

showUserHeading: true

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


Программное управление геолокацией

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

Основные события

  • geolocate — успешное получение позиции;
  • error — ошибка геолокации;
  • trackuserlocationstart — начало отслеживания;
  • trackuserlocationend — завершение отслеживания.
geolocate.on('geolocate', (position) => {
  const { longitude, latitude, accuracy } = position.coords;
  console.log(longitude, latitude, accuracy);
});

geolocate.on('error', (error) => {
  console.error(error.message);
});

Использование Geolocation API без контролла

В ряде случаев требуется прямой доступ к navigator.geolocation без использования встроенного UI-компонента Mapbox GL JS. Это даёт полный контроль над логикой обновления данных и их отображением.

Однократное получение позиции

navigator.geolocation.getCurrentPosition(
  (position) => {
    const { longitude, latitude } = position.coords;

    map.flyTo({
      center: [longitude, latitude],
      zoom: 14
    });
  },
  (error) => {
    console.error(error.message);
  },
  {
    enableHighAccuracy: true
  }
);

Непрерывное отслеживание

const watchId = navigator.geolocation.watchPosition(
  (position) => {
    const { longitude, latitude } = position.coords;

    userMarker.setLngLat([longitude, latitude]);
  },
  (error) => {
    console.error(error);
  },
  {
    enableHighAccuracy: true,
    maximumAge: 1000
  }
);

// остановка отслеживания
navigator.geolocation.clearWatch(watchId);

Отображение пользователя на карте

Для визуализации позиции обычно используется слой или маркер. Mapbox GL JS предоставляет два основных подхода: Marker и GeoJSON source.

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

const userMarker = new mapboxgl.Marker({
  color: '#2E86AB'
})
  .setLngLat([0, 0])
  .addTo(map);

При обновлении координат маркер перемещается без пересоздания объекта:

userMarker.setLngLat([longitude, latitude]);

Использование источника данных GeoJSON

Более гибкий способ — создание источника и слоя.

map.on('load', () => {
  map.addSource('user-location', {
    type: 'geojson',
    data: {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [0, 0]
      }
    }
  });

  map.addLayer({
    id: 'user-location-layer',
    type: 'circle',
    source: 'user-location',
    paint: {
      'circle-radius': 8,
      'circle-color': '#007cbf'
    }
  });
});

Обновление координат:

map.getSource('user-location').setData({
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [longitude, latitude]
  }
});

Работа с точностью координат

Поле accuracy в объекте coords задаёт радиус неопределённости в метрах. Чем выше значение, тем менее точное определение.

Типичные сценарии интерпретации:

  • 5–20 м — GPS высокого качества;
  • 20–100 м — смешанные источники (Wi-Fi, сотовые сети);
  • 100+ м — базовое сетевое позиционирование.

Визуализация точности обычно реализуется через окружность вокруг маркера пользователя.


Центрирование карты на пользователе

Автоматическое перемещение камеры реализуется через методы flyTo или easeTo.

map.flyTo({
  center: [longitude, latitude],
  zoom: 15,
  speed: 1.2,
  curve: 1
});

Для плавного сопровождения перемещения используется обновление центра при каждом событии watchPosition.


Обработка ошибок геолокации

Geolocation API возвращает структурированные ошибки с кодами:

  • 1 — доступ запрещён пользователем;
  • 2 — позиция недоступна;
  • 3 — превышен таймаут.

Пример обработки:

navigator.geolocation.getCurrentPosition(
  successCallback,
  (error) => {
    switch (error.code) {
      case error.PERMISSION_DENIED:
        console.log('Доступ к геолокации запрещён');
        break;
      case error.POSITION_UNAVAILABLE:
        console.log('Позиция недоступна');
        break;
      case error.TIMEOUT:
        console.log('Превышено время ожидания');
        break;
    }
  }
);

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

Поведение геолокации зависит от платформы:

  • на мобильных устройствах предпочтение отдаётся GPS;
  • на десктопах используются Wi-Fi и IP-геолокация;
  • HTTPS обязателен для доступа к API;
  • разрешение на доступ к местоположению запрашивается один раз и может быть изменено в настройках браузера.

При отсутствии разрешения GeolocateControl не активирует отслеживание и возвращает событие ошибки.


Интеграция с состоянием карты Mapbox GL JS

Геолокация часто комбинируется с состоянием карты:

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

Пример привязки к радиусу поиска:

map.on('geolocate', (e) => {
  const center = [e.coords.longitude, e.coords.latitude];

  map.setFilter('points-of-interest', [
    'distance',
    center,
    5000
  ]);
});

Оптимизация частоты обновлений

При использовании watchPosition важно учитывать частоту обновлений:

  • высокая частота увеличивает нагрузку на устройство;
  • низкая частота снижает точность трекинга.

Практика заключается в балансировке параметров maximumAge и обработки событий с throttling:

let lastUpdate = 0;

navigator.geolocation.watchPosition((pos) => {
  const now = Date.now();

  if (now - lastUpdate < 1000) return;

  lastUpdate = now;

  userMarker.setLngLat([
    pos.coords.longitude,
    pos.coords.latitude
  ]);
});