Внутри картографического движка одновременно существуют несколько пространств координат, каждое из которых решает собственный класс задач. Понимание различий между ними критично для корректного преобразования пикселей в географические координаты и обратно.
Географические координаты (LatLng) описывают положение на поверхности Земли в градусах широты и долготы. Они независимы от масштаба и устройства отображения.
Пиксельные координаты описывают положение точки уже в плоскости карты, где единицей измерения выступает пиксель. Здесь важно различать несколько уровней:
Именно переход между этими пространствами лежит в основе всех интерактивных операций: кликов, перетаскиваний, оверлеев и кастомных слоёв.
Leaflet использует проекцию Web Mercator (EPSG:3857) как стандартную,
через объект CRS.EPSG3857.
Проекция выполняет преобразование:
На этом уровне применяется масштабирование, зависящее от zoom-уровня.
Базовая идея:
world size = tileSize * 2^zoom
При стандартном размере тайла 256 px:
Внутри карты используются два ключевых метода:
map.latLngToLayerPoint(latlng)
Результат — координата точки в пикселях относительно текущего слоя карты с учётом zoom и центра.
Пример:
const point = map.latLngToLayerPoint([55.751244, 37.618423]);
console.log(point.x, point.y);
Эти координаты удобны для позиционирования кастомных DOM-элементов внутри карты.
map.layerPointToLatLng(point)
Используется для получения географической координаты из пиксельной позиции слоя:
const latlng = map.layerPointToLatLng([200, 300]);
console.log(latlng.lat, latlng.lng);
Помимо layerPoint существует ещё один уровень — координаты контейнера карты.
map.containerPointToLatLng(point)
Используется, когда координаты приходят напрямую из событий DOM:
map.on('click', (e) => {
const latlng = map.containerPointToLatLng(e.containerPoint);
});
map.latLngToContainerPoint(latlng)
Позволяет вычислить положение элемента внутри видимой области карты:
const p = map.latLngToContainerPoint([55.75, 37.61]);
Разница между containerPoint и layerPoint проявляется при смещении карты: containerPoint зависит от текущего viewport, layerPoint — от общей системы слоя.
Leaflet предоставляет прямой доступ к проекции:
map.project(latlng, zoom)
map.unproject(point, zoom)
Преобразует LatLng в координаты мировой проекции:
const projected = map.project([55.75, 37.61], 10);
Обратное преобразование:
const latlng = map.unproject([1200, 1500], 10);
Эти методы работают без привязки к текущему состоянию карты, что делает их полезными для вычислений вне UI-цикла.
События карты содержат сразу несколько представлений координат:
map.on('click', (e) => {
e.latlng; // географические координаты
e.containerPoint; // пиксели внутри контейнера
e.layerPoint; // пиксели слоя
});
Каждое поле соответствует уровню преобразования и может использоваться для разных задач:
При работе с чистыми DOM-событиями вне Leaflet необходимо вручную переводить координаты:
map.on('mousemove', (e) => {
const rect = map.getContainer().getBoundingClientRect();
const point = {
x: e.originalEvent.clientX - rect.left,
y: e.originalEvent.clientY - rect.top
};
const latlng = map.containerPointToLatLng(point);
});
Здесь важно учитывать, что браузерные координаты не учитывают внутренние смещения карты, zoom и трансформации.
Любое преобразование пикселей напрямую связано с уровнем масштабирования:
1 пиксель ≈ (360° / 256) / 2^zoom
При увеличении zoom:
Это влияет на точность обратных преобразований pixel → LatLng, особенно при работе с высокими zoom-уровнями.
Пиксельные координаты активно используются при создании пользовательских overlay-слоёв.
Пример ручного позиционирования элемента:
const div = document.createElement('div');
div.className = 'marker';
map.getPanes().overlayPane.appendChild(div);
function update() {
const point = map.latLngToLayerPoint([55.75, 37.61]);
L.DomUtil.setPosition(div, point);
}
При каждом движении карты необходимо пересчитывать позицию через
latLngToLayerPoint, так как слой постоянно
трансформируется.
Любое из следующих событий требует повторного преобразования пикселей:
zoommoveresizerotate (если используется кастомная реализация)viewresetТиповой обработчик:
map.on('move zoom', () => {
updateMarkers();
});
Работа в layerPoint координатах позволяет выполнять быстрые вычисления:
Пример расстояния:
const p1 = map.latLngToLayerPoint(a);
const p2 = map.latLngToLayerPoint(b);
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
Такие вычисления быстрее, чем работа в географических координатах, так как исключают сферическую геометрию.
Карта постоянно имеет внутреннее смещение относительно глобальной системы координат. Оно зависит от:
При изменении центра:
newLayerPoint = worldPoint - centerOffset
Именно поэтому прямое использование pixel координат без пересчёта приводит к некорректному позиционированию.
Внутренний конвейер координат можно представить как цепочку:
LatLng
↓ project
World coordinates
↓ scale by zoom
Pixel world space
↓ translate by center
Layer point
↓ DOM offset
Container point
Каждый этап имеет обратное преобразование, что обеспечивает двунаправленную синхронизацию между географией и интерфейсом карты.