В основе всех пространственных операций в Leaflet лежит преобразование между географическими координатами и экранной системой координат. Географическая модель использует пары широта/долгота (LatLng), тогда как рендеринг карты опирается на пиксельные координаты в проекции мира.
Ключевая идея заключается в разделении двух пространств:
Все математические функции Leaflet обеспечивают согласованное преобразование между этими системами.
Объект координат представляет собой фундаментальную единицу геометрии:
L.latLng(51.505, -0.09)
Он поддерживает нормализацию значений и базовые преобразования.
Leaflet автоматически приводит значения к допустимым диапазонам:
Математическая нормализация долготы:
function wrapLng(lng) {
return ((lng + 180) % 360 + 360) % 360 - 180;
}
Это обеспечивает непрерывность карты при переходе через меридиан ±180°.
Leaflet предоставляет геодезическое расстояние между координатами на сфере.
Используется приближённая модель Земли (сфера), а не эллипсоид.
L.latLng(lat1, lng1).distanceTo(L.latLng(lat2, lng2))
Внутренне применяется классическая тригонометрия:
[ a = ^2() + _1 _2 ^2()]
[ c = 2 (, )]
[ d = R c]
где (R) — радиус Земли (6378137 м в WGS84).
Leaflet использует систему проекций, где основной является Spherical Mercator (EPSG:3857).
map.project(L.latLng(51.5, -0.09), zoom)
Математически:
[ x = R ]
[ y = R (( + ))]
где:
map.unproject(point, zoom)
В Leaflet масштаб зависит от уровня зума:
[ scale = 2^{zoom}]
Каждый следующий уровень увеличивает детализацию в два раза.
worldSize = tileSize * Math.pow(2, zoom)
При стандартном размере тайла 256:
При анимации перемещения карты и плавных переходах используется интерполяция:
function lerp(a, b, t) {
return a + (b - a) * t;
}
Применение:
Для координат:
lat = lat1 + (lat2 - lat1) * t;
lng = lng1 + (lng2 - lng1) * t;
Leaflet активно использует перевод между:
map.latLngToLayerPoint(latlng)
map.layerPointToLatLng(point)
Объект границ используется для описания прямоугольных областей.
bounds.extend(latlng)
bounds.contains(latlng)
bounds.intersects(otherBounds)
function extendBounds(bounds, latlng) {
bounds.minLat = Math.min(bounds.minLat, latlng.lat);
bounds.maxLat = Math.max(bounds.maxLat, latlng.lat);
bounds.minLng = Math.min(bounds.minLng, latlng.lng);
bounds.maxLng = Math.max(bounds.maxLng, latlng.lng);
}
Проблема мировых карт — непрерывность по оси X.
Leaflet решает это через wrapping:
lng = ((lng + 180) % 360 + 360) % 360 - 180;
При зуме координаты должны оставаться стабильными:
function normalizePoint(point, worldSize) {
return {
x: ((point.x % worldSize) + worldSize) % worldSize,
y: point.y
};
}
Это предотвращает “разрыв” карты при перемещении.
Leaflet использует систему CRS как слой математических преобразований.
Основные методы:
latLngToPointpointToLatLngprojectunprojectscaleL.CRS.EPSG3857.project(latlng)
Фактически CRS инкапсулирует:
Leaflet содержит набор низкоуровневых математических утилит.
function clamp(x, min, max) {
return Math.max(min, Math.min(max, x));
}
Используется для:
function toRad(deg) {
return deg * Math.PI / 180;
}
function toDeg(rad) {
return rad * 180 / Math.PI;
}
При каждом движении карты происходит перерасчёт:
Формула смещения:
offset = containerCenter.subtract(mapCenterInPixels);
Размер объектов часто зависит от zoom:
size = baseSize * Math.pow(2, zoom - baseZoom);
Это позволяет:
Для оптимизации отображения используется проверка попадания точки в границы:
function contains(bounds, latlng) {
return (
latlng.lat >= bounds._southWest.lat &&
latlng.lat <= bounds._northEast.lat &&
latlng.lng >= bounds._southWest.lng &&
latlng.lng <= bounds._northEast.lng
);
}
Leaflet оптимизирует вычисления за счёт:
Особенно дорогостоящие операции:
Во время pan/zoom применяется цикл пересчёта:
Leaflet избегает накопления ошибок:
Математическая подсистема Leaflet строится вокруг трёх уровней:
Каждый уровень строго изолирован и связан через функции преобразования, что позволяет поддерживать стабильность при высокой интерактивности карты.