В системе отображения карты Mapbox GL JS каждый кадр представляет собой результат преобразования географических координат в экранное пространство. Центральным элементом этой модели является прямоугольная область canvas, ограниченная четырьмя углами. Эти углы формируют границу видимой области и служат опорными точками для вычисления экстента, взаимодействия с объектами и синхронизации слоёв.
Угловые координаты в контексте Mapbox GL JS выражаются одновременно в двух системах:
Переход между ними осуществляется через проекцию Web Mercator и внутренний трансформационный слой карты.
Прямоугольная область карты всегда описывается четырьмя ключевыми углами:
Каждый угол одновременно существует в двух представлениях:
LngLat — долгота и широтаPoint — пиксельные координаты относительно canvasMapbox GL JS предоставляет удобный доступ к границам текущего вида через объект:
const bounds = map.getBounds();
Этот объект типа LngLatBounds содержит минимальные и
максимальные значения широты и долготы, которые соответствуют углам
текущего отображения.
Хотя getBounds() возвращает только географический
прямоугольник, углы могут быть вычислены явно:
const bounds = map.getBounds();
const nw = bounds.getNorthWest();
const ne = bounds.getNorthEast();
const sw = bounds.getSouthWest();
const se = bounds.getSouthEast();
Каждый метод возвращает объект LngLat, содержащий
координаты:
{
lng: number,
lat: number
}
Эти значения формируют географическую рамку текущего кадра, независимо от zoom, pitch и bearing.
Экранные координаты углов определяются через метод
project, преобразующий географическую точку в пиксельное
пространство canvas:
const nwPoint = map.project(bounds.getNorthWest());
const nePoint = map.project(bounds.getNorthEast());
const swPoint = map.project(bounds.getSouthWest());
const sePoint = map.project(bounds.getSouthEast());
Результат:
{
x: number,
y: number
}
Эта система координат имеет начало в левом верхнем углу canvas, где:
Таким образом, северо-западный угол всегда имеет минимальные значения x и y, а юго-восточный — максимальные.
Обратная операция выполняется через unproject,
позволяющий получить географические координаты из пиксельной точки:
const lngLat = map.unproject({ x: 0, y: 0 });
Для углов canvas это даёт:
Получение размеров canvas:
const canvas = map.getCanvas();
const width = canvas.width;
const height = canvas.height;
Mapbox GL JS использует тайловую систему, где карта разбита на квадратные фрагменты. Углы viewport определяют набор тайлов, необходимых для отрисовки текущего состояния.
Каждый угол влияет на:
При изменении углов пересчитывается набор загружаемых данных:
Объект LngLatBounds представляет минимальный bounding
box, который полностью охватывает текущую область карты.
const bounds = map.getBounds();
Структура включает:
В терминах углов:
west = sw.lng = nw.lng
east = se.lng = ne.lng
north = nw.lat = ne.lat
south = sw.lat = se.lat
Эта система упрощает геопространственные вычисления:
Алгоритм определения попадания координаты в текущий экранный прямоугольник:
const bounds = map.getBounds();
function isVisible(lngLat) {
return bounds.contains(lngLat);
}
Метод contains использует углы NW и SE как границы
прямоугольника.
При использовании свойств pitch и bearing
геометрия углов усложняется. Хотя географический bounding box остаётся
прямоугольным в координатах LngLat, экранное представление
становится трапецией.
В этом случае:
Пример вычисления экранных углов:
const corners = [
map.project(bounds.getNorthWest()),
map.project(bounds.getNorthEast()),
map.project(bounds.getSouthEast()),
map.project(bounds.getSouthWest())
];
Эти точки больше не образуют идеальный прямоугольник в пиксельном пространстве.
Центр viewport можно вычислить как среднее между угловыми координатами:
const nw = bounds.getNorthWest();
const se = bounds.getSouthEast();
const center = {
lng: (nw.lng + se.lng) / 2,
lat: (nw.lat + se.lat) / 2
};
Однако Mapbox GL JS предоставляет встроенный метод:
map.getCenter();
Который учитывает проекционные искажении и wrapping мира.
Углы используются для позиционирования интерфейсных элементов:
Пример закрепления элемента в северо-восточном углу:
const container = map.getContainer();
const element = document.createElement('div');
element.style.position = 'absolute';
element.style.top = '0';
element.style.right = '0';
container.appendChild(element);
Функция fitBounds напрямую использует углы для
вычисления zoom и center:
map.fitBounds([
[west, south],
[east, north]
]);
Алгоритм:
При кликах и перемещениях курсора координаты события могут быть преобразованы относительно углов viewport:
map.on('click', (e) => {
const lngLat = e.lngLat;
});
Для анализа положения относительно углов:
const bounds = map.getBounds();
const isTopHalf = lngLat.lat > (bounds.getSouth() + bounds.getNorth()) / 2;
const isLeftHalf = lngLat.lng < (bounds.getWest() + bounds.getEast()) / 2;
При мировом wrapping координаты углов могут выходить за диапазон [-180, 180], однако Mapbox GL JS нормализует значения.
Особенности:
Пример:
const bounds = map.getBounds();
if (bounds.getEast() < bounds.getWest()) {
// пересечение антимеридиана
}
Углы viewport применяются для:
Типичный запрос:
const bounds = map.getBounds();
fetch(`/data?bbox=${bounds.toArray().flat().join(',')}`);
Внутренний объект transform карты хранит ключевые параметры:
Углы являются производными от transform:
transform → projection → corners
Любое изменение transform мгновенно пересчитывает координаты всех углов.
Часто требуется обход всех углов viewport:
const corners = [
map.getBounds().getNorthWest(),
map.getBounds().getNorthEast(),
map.getBounds().getSouthEast(),
map.getBounds().getSouthWest()
];
corners.forEach(corner => {
console.log(corner.lng, corner.lat);
});
Этот подход используется для:
В идеализированной модели без проекции углы образуют:
Это различие критично при:
При диагностике отображения карты углы используются для:
Пример логирования:
console.log(map.getBounds().toString());
или детально:
console.table({
nw: map.getBounds().getNorthWest(),
ne: map.getBounds().getNorthEast(),
sw: map.getBounds().getSouthWest(),
se: map.getBounds().getSouthEast()
});