Разница в координатах

Любой объект на карте в Mapbox GL JS привязывается к определённым координатам. Маркеры, всплывающие окна, линии, полигоны, маршруты и пользовательские слои используют географические координаты для определения своего положения.

Основой координатной системы служит стандарт WGS84, в котором местоположение задаётся двумя значениями:

  • долгота (longitude, lng);
  • широта (latitude, lat).

Пример координат города Алматы:

[76.886, 43.238]

Здесь:

  • 76.886 — долгота;
  • 43.238 — широта.

В Mapbox GL JS порядок координат всегда следующий:

[longitude, latitude]

Это одно из наиболее важных правил при работе с картой.


Долгота и широта

Долгота

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

Диапазон значений:

-180 ... 180

Примеры:

-74.0060   // Нью-Йорк
37.6173    // Москва
139.6917   // Токио

Отрицательные значения находятся западнее нулевого меридиана, положительные — восточнее.


Широта

Широта определяет положение точки относительно экватора.

Диапазон значений:

-90 ... 90

Примеры:

40.7128   // Нью-Йорк
55.7558   // Москва
35.6895   // Токио

Отрицательные значения располагаются южнее экватора, положительные — севернее.


Почему порядок координат имеет значение

Очень распространённая ошибка — перепутать широту и долготу местами.

Правильно:

const coordinates = [37.6173, 55.7558];

Неправильно:

const coordinates = [55.7558, 37.6173];

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

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

Создание маркера:

new mapboxgl.Marker()
    .setLngLat([37.6173, 55.7558])
    .addTo(map);

Метод setLngLat() всегда ожидает сначала долготу, затем широту.


Координаты центра карты

Карта также использует географические координаты для определения центра отображаемой области.

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

Здесь центр карты находится в Москве.

Получение текущего центра:

const center = map.getCenter();

console.log(center.lng);
console.log(center.lat);

Результат:

LngLat {
    lng: 37.6173,
    lat: 55.7558
}

Класс LngLat

Mapbox GL JS предоставляет специальный класс LngLat.

Создание объекта:

const point = new mapboxgl.LngLat(
    37.6173,
    55.7558
);

Доступ к значениям:

console.log(point.lng);
console.log(point.lat);

Использование при центрировании карты:

map.setCenter(point);

Координаты GeoJSON

GeoJSON является основным форматом пространственных данных в Mapbox GL JS.

Точка:

{
    "type": "Point",
    "coordinates": [37.6173, 55.7558]
}

Линия:

{
    "type": "LineString",
    "coordinates": [
        [37.6173, 55.7558],
        [37.6500, 55.7800]
    ]
}

Полигон:

{
    "type": "Polygon",
    "coordinates": [[
        [37.60, 55.75],
        [37.70, 55.75],
        [37.70, 55.85],
        [37.60, 55.85],
        [37.60, 55.75]
    ]]
}

Во всех случаях сохраняется одинаковый порядок:

[longitude, latitude]

Географические и экранные координаты

При работе с картой существуют два различных типа координат:

Географические координаты

Описывают положение объекта на Земле.

Пример:

[37.6173, 55.7558]

Используются для:

  • маркеров;
  • маршрутов;
  • GeoJSON;
  • центрирования карты.

Экранные координаты

Описывают положение пикселя внутри контейнера карты.

Пример:

{x: 400, y: 300}

Используются для:

  • обработки кликов;
  • определения положения курсора;
  • преобразования координат.

Преобразование координат карты в пиксели

Метод project() переводит географические координаты в экранные.

const pixel = map.project([37.6173, 55.7558]);

console.log(pixel);

Результат:

Point {
    x: 512,
    y: 384
}

Теперь известна точка на экране, соответствующая заданному месту на карте.


Преобразование пикселей в координаты карты

Обратное преобразование выполняет метод unproject().

const lngLat = map.unproject([512, 384]);

console.log(lngLat);

Результат:

LngLat {
    lng: 37.6173,
    lat: 55.7558
}

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


Координаты при клике мышью

Во время клика Mapbox GL JS предоставляет оба типа координат.

map.on('click', (e) => {
    console.log(e.lngLat);
    console.log(e.point);
});

Объект:

e.lngLat

содержит географические координаты:

LngLat {
    lng: 37.61,
    lat: 55.75
}

Объект:

e.point

содержит экранные координаты:

Point {
    x: 450,
    y: 320
}

Разница между LngLat и Point

Эти объекты часто путают, хотя они решают разные задачи.

LngLat

LngLat {
    lng: 37.6173,
    lat: 55.7558
}

Используется для описания реального положения на поверхности Земли.


Point

Point {
    x: 300,
    y: 200
}

Используется для описания положения внутри окна карты.


Сравнение:

Характеристика LngLat Point
Тип данных Географические координаты Экранные координаты
Единицы измерения Градусы Пиксели
Используется для Карты мира DOM-контейнера
Зависит от масштаба Нет Да

Координаты и масштаб карты

Одна и та же географическая точка остаётся неизменной независимо от масштаба.

Например:

[37.6173, 55.7558]

Однако экранные координаты изменяются.

При масштабе:

zoom: 5

точка может находиться здесь:

{x: 420, y: 310}

При масштабе:

zoom: 15

уже здесь:

{x: 1500, y: 980}

Причина заключается в том, что карта увеличивается, а географическое положение остаётся постоянным.


Координаты и перемещение карты

После панорамирования экранные координаты объекта изменяются.

Географическая точка:

[37.6173, 55.7558]

остаётся прежней.

Но результат:

map.project(...)

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


Разница между координатами объекта и координатами события

Координаты объекта описывают постоянное положение элемента на карте.

Пример маркера:

const marker = new mapboxgl.Marker()
    .setLngLat([37.6173, 55.7558])
    .addTo(map);

Координаты события отражают текущую позицию курсора.

map.on('mousemove', (e) => {
    console.log(e.lngLat);
});

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


Работа с массивами координат

Многие методы принимают массивы координат.

Точка:

[37.6173, 55.7558]

Маршрут:

[
    [37.6173, 55.7558],
    [37.6300, 55.7600],
    [37.6500, 55.7800]
]

Обработка массива:

route.forEach(coord => {
    console.log(
        coord[0],
        coord[1]
    );
});

Где:

coord[0] // longitude
coord[1] // latitude

Проверка корректности координат

Перед отображением объектов полезно проверять диапазон значений.

Пример:

function isValidCoordinate(lng, lat) {
    return (
        lng >= -180 &&
        lng <= 180 &&
        lat >= -90 &&
        lat <= 90
    );
}

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

if (isValidCoordinate(37.6173, 55.7558)) {
    console.log('Координаты корректны');
}

Подобная проверка предотвращает ошибки при загрузке данных из внешних источников.


Типичные ошибки при работе с координатами

Перепутан порядок значений

[55.7558, 37.6173]

вместо:

[37.6173, 55.7558]

Использование строк вместо чисел

Нежелательно:

["37.6173", "55.7558"]

Предпочтительно:

[37.6173, 55.7558]

Выход за допустимые диапазоны

Некорректно:

[250, 95]

Корректно:

[120, 45]

Смешивание пикселей и географических координат

Неверно:

map.setCenter([500, 300]);

Здесь используются экранные координаты вместо географических.

Правильный вариант:

map.setCenter([37.6173, 55.7558]);

Практическое сравнение типов координат

Рассмотрим обработчик клика:

map.on('click', (e) => {
    console.log('География:', e.lngLat);
    console.log('Пиксели:', e.point);
});

Если пользователь нажал на карту, результат может выглядеть следующим образом:

География:
LngLat {
    lng: 37.621,
    lat: 55.751
}
Пиксели:
Point {
    x: 684,
    y: 412
}

Первое значение показывает реальное местоположение на Земле, второе — положение внутри HTML-контейнера карты. Понимание этой разницы является фундаментальным для разработки интерактивных карт, поскольку Mapbox GL JS постоянно выполняет преобразования между географическими координатами и экранными пикселями при масштабировании, повороте, наклоне и перемещении карты.