В веб-картографии расстояние между двумя точками на карте нельзя корректно вычислять через обычную евклидову формулу координат. Причина в том, что координаты долготы и широты задаются на сфере (или эллипсоиде), а не на плоскости. Поэтому прямое применение формулы Пифагора даёт искажённый результат, особенно на больших расстояниях.
В Mapbox GL JS работа с расстояниями обычно строится вокруг
геодезических вычислений. Библиотека предоставляет объект
LngLat, который уже учитывает географическую природу
координат и позволяет вычислять расстояние между точками через метод
distanceTo.
Любая точка в Mapbox GL JS задаётся в формате:
const point = new mapboxgl.LngLat(37.6173, 55.7558);
Ключевой момент: это не декартова система координат. Даже если визуально карта выглядит плоской, под капотом используется проекция Web Mercator, которая искажает расстояния, особенно ближе к полюсам.
Поэтому вычисление расстояния должно опираться на геодезические формулы.
В Mapbox GL JS встроен метод:
const a = new mapboxgl.LngLat(37.6173, 55.7558);
const b = new mapboxgl.LngLat(30.3351, 59.9343);
const distance = a.distanceTo(b);
Результат возвращается в метрах.
Внутри используется сферическая модель Земли и формула, близкая к гаверсинусной:
Приближение достаточно точное для большинства задач интерфейса и аналитики.
Иногда требуется полный контроль над вычислением, например при серверной синхронизации или работе вне Mapbox API.
function getDistance(lat1, lon1, lat2, lon2) {
const R = 6371000; // радиус Земли в метрах
const toRad = (value) => (value * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
Этот подход полностью независим от библиотеки и часто используется в связке с геоданными.
В экосистеме геопространственных вычислений широко применяется Turf.js.
Она предоставляет готовую функцию:
import { distance } from '@turf/turf';
const from = [37.6173, 55.7558];
const to = [30.3351, 59.9343];
const result = distance(from, to, { units: 'kilometers' });
Особенности:
Типовой сценарий в Mapbox GL JS — измерение расстояния между кликами пользователя.
const coordinates = [];
map.on('click', (e) => {
coordinates.push([e.lngLat.lng, e.lngLat.lat]);
if (coordinates.length === 2) {
const start = new mapboxgl.LngLat(coordinates[0][0], coordinates[0][1]);
const end = new mapboxgl.LngLat(coordinates[1][0], coordinates[1][1]);
const dist = start.distanceTo(end);
console.log(`Расстояние: ${dist} м`);
}
});
Такой подход применяется в:
Чтобы визуализировать расстояние, используется слой типа
line:
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: []
}
}
});
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
paint: {
'line-color': '#3b82f6',
'line-width': 3
}
});
Обновление данных при выборе точек:
const source = map.getSource('route');
source.setData({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates
}
});
Важно различать два типа расстояний:
Mapbox GL JS оперирует обоими типами, но методы вроде
distanceTo всегда относятся к географическому
пространству.
На практике часто встречаются типичные ошибки:
1. Использование евклидовой формулы
Math.sqrt(dx * dx + dy * dy)
Корректно только для локальных плоскостей.
2. Перепутанный порядок координат
Mapbox использует порядок:
[lng, lat]
а не наоборот.
3. Игнорирование проекции
Web Mercator сильно искажает расстояния ближе к полюсам.
При работе с большим количеством точек:
distanceTo быстрее и безопаснее ручных
реализацийДля кластеризации или heatmap лучше заранее считать матрицы расстояний на сервере.
Распространённый кейс — накопление маршрута:
const route = [];
map.on('click', (e) => {
route.push([e.lngLat.lng, e.lngLat.lat]);
let total = 0;
for (let i = 1; i < route.length; i++) {
const a = new mapboxgl.LngLat(route[i - 1][0], route[i - 1][1]);
const b = new mapboxgl.LngLat(route[i][0], route[i][1]);
total += a.distanceTo(b);
}
console.log('Общая длина маршрута:', total);
});
Такой подход используется в: