Turf.js — это библиотека пространственного анализа для JavaScript, предназначенная для выполнения геометрических вычислений непосредственно в браузере или на сервере. В связке с MapLibre GL JS она позволяет не только отображать геоданные, но и выполнять над ними сложные операции: измерения, поиск пересечений, буферизацию, кластерный анализ, построение маршрутов, генерацию изолиний и многое другое.
MapLibre GL JS отвечает за визуализацию и взаимодействие с картой, тогда как Turf.js берет на себя вычислительную часть геоинформационной обработки.
Типичная архитектура выглядит следующим образом:
GeoJSON → Turf.js → Обработанный GeoJSON → MapLibre GL JS
Например:
const buffered = turf.buffer(feature, 1, {
units: 'kilometers'
});
map.getSource('zones').setData(buffered);
В данном случае Turf.js создает буферную зону вокруг объекта, а MapLibre отображает результат на карте.
Установка через npm:
npm install @turf/turf
Импорт всей библиотеки:
import * as turf from '@turf/turf';
Импорт отдельных модулей:
import buffer from '@turf/buffer';
import distance from '@turf/distance';
В крупных проектах предпочтителен импорт отдельных пакетов, поскольку это уменьшает итоговый размер бандла.
MapLibre GL JS и Turf.js используют единый формат данных — GeoJSON.
Пример точки:
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [37.6176, 55.7558]
},
properties: {}
};
Пример полигона:
const polygon = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [[
[37.60, 55.75],
[37.65, 55.75],
[37.65, 55.78],
[37.60, 55.78],
[37.60, 55.75]
]]
},
properties: {}
};
Любой объект, полученный от Turf.js, может быть напрямую передан в источник данных MapLibre:
map.addSource('data', {
type: 'geojson',
data: turfResult
});
Одной из наиболее востребованных функций является расчет расстояний.
Создание точек:
const p1 = turf.point([37.6176, 55.7558]);
const p2 = turf.point([30.3141, 59.9386]);
Расчет расстояния:
const distance = turf.distance(p1, p2, {
units: 'kilometers'
});
console.log(distance);
Поддерживаемые единицы измерения:
Отображение результата:
document.getElementById('distance').textContent =
`${distance.toFixed(2)} км`;
Для линий используется функция length().
Создание маршрута:
const route = turf.lineString([
[37.61, 55.75],
[37.65, 55.77],
[37.72, 55.80]
]);
Вычисление длины:
const routeLength = turf.length(route, {
units: 'kilometers'
});
console.log(routeLength);
Это особенно полезно для:
Площадь рассчитывается функцией area().
const area = turf.area(polygon);
console.log(area);
Результат возвращается в квадратных метрах.
Преобразование в гектары:
const hectares = area / 10000;
Преобразование в квадратные километры:
const km2 = area / 1000000;
Типичный сценарий:
map.on('click', e => {
const selectedPolygon = getPolygon();
const area = turf.area(selectedPolygon);
console.log(`Площадь: ${area} м²`);
});
Буферизация является одной из важнейших операций ГИС.
Буфер создает область вокруг объекта на заданном расстоянии.
Создание буфера вокруг точки:
const point = turf.point([37.6176, 55.7558]);
const buffer = turf.buffer(point, 2, {
units: 'kilometers'
});
Отображение:
map.addSource('buffer', {
type: 'geojson',
data: buffer
});
Добавление слоя:
map.addLayer({
id: 'buffer-fill',
type: 'fill',
source: 'buffer',
paint: {
'fill-color': '#0080ff',
'fill-opacity': 0.3
}
});
Буферизация применяется для:
Для определения принадлежности объекта определенной территории
используется booleanPointInPolygon().
const inside = turf.booleanPointInPolygon(
point,
polygon
);
console.log(inside);
Интеграция с кликом по карте:
map.on('click', e => {
const point = turf.point([
e.lngLat.lng,
e.lngLat.lat
]);
const result =
turf.booleanPointInPolygon(
point,
polygon
);
console.log(result);
});
Возможные результаты:
true
или
false
Полигоны часто необходимо сравнивать между собой.
Проверка пересечения:
const intersects =
turf.booleanIntersects(
polygon1,
polygon2
);
Результат:
true
или
false
Это востребовано в системах:
Если требуется не просто узнать факт пересечения, а получить саму
область пересечения, используется intersect().
const intersection =
turf.intersect(
turf.featureCollection([
polygon1,
polygon2
])
);
После получения результата его можно сразу отобразить:
map.getSource('intersection')
.setData(intersection);
Для объединения нескольких территорий применяется
union().
const merged =
turf.union(
turf.featureCollection([
polygon1,
polygon2
])
);
Результатом будет единый полигон.
Пример отображения:
map.getSource('merged')
.setData(merged);
Центроид представляет геометрический центр объекта.
const center =
turf.centroid(polygon);
Полученные координаты:
console.log(
center.geometry.coordinates
);
Отображение маркера:
new maplibregl.Marker()
.setLngLat(
center.geometry.coordinates
)
.addTo(map);
Использование:
Для определения границ объекта используется bbox().
const bbox =
turf.bbox(feature);
Результат:
[
minLng,
minLat,
maxLng,
maxLat
]
Автоматическое масштабирование карты:
map.fitBounds([
[bbox[0], bbox[1]],
[bbox[2], bbox[3]]
]);
Полученный bbox можно преобразовать в геометрию:
const boundsPolygon =
turf.bboxPolygon(
turf.bbox(feature)
);
Отображение:
map.getSource('bounds')
.setData(boundsPolygon);
Такой подход полезен для отладки и визуального анализа данных.
Функция nearestPoint() позволяет находить ближайший
объект из набора точек.
const nearest =
turf.nearestPoint(
targetPoint,
pointsCollection
);
Пример:
const nearestHospital =
turf.nearestPoint(
userLocation,
hospitals
);
Подобная логика используется в:
Большие полигоны могут содержать тысячи вершин.
Для оптимизации применяется simplify().
const simplified =
turf.simplify(
polygon,
{
tolerance: 0.001,
highQuality: false
}
);
Преимущества:
Выпуклая оболочка строится вокруг множества точек.
const hull =
turf.convex(points);
Пример отображения:
map.getSource('hull')
.setData(hull);
Типичные области применения:
Создание линии:
const line =
turf.lineString([
pointA.geometry.coordinates,
pointB.geometry.coordinates
]);
Отображение:
map.getSource('line')
.setData(line);
Используется для:
Для анализа маршрутов применяется lineChunk().
const chunks =
turf.lineChunk(
route,
1,
{
units: 'kilometers'
}
);
Каждый сегмент будет отдельным объектом GeoJSON.
Это удобно для:
Поиск точки на определенном расстоянии:
const point =
turf.along(
route,
5,
{
units: 'kilometers'
}
);
Полученная точка находится на расстоянии 5 километров от начала линии.
Применение:
Для тестирования часто требуется искусственный набор данных.
Случайные точки:
const randomPoints =
turf.randomPoint(100);
Случайные полигоны:
const randomPolygons =
turf.randomPolygon(20);
Отображение:
map.getSource('random')
.setData(randomPoints);
Такой подход полезен при нагрузочном тестировании интерфейса.
Создание регулярной сетки:
const grid =
turf.pointGrid(
[
37.5,
55.6,
37.8,
55.9
],
1,
{
units: 'kilometers'
}
);
Полученная коллекция может использоваться для:
Практический сценарий объединяет MapLibre и Turf.js.
map.on('click', e => {
const point = turf.point([
e.lngLat.lng,
e.lngLat.lat
]);
const inside =
turf.booleanPointInPolygon(
point,
cityBoundary
);
if (inside) {
console.log(
'Точка находится внутри города'
);
}
});
Здесь MapLibre предоставляет координаты события, а Turf.js выполняет пространственный анализ.
После выполнения вычислений данные могут сразу отображаться на карте.
const result =
turf.buffer(
selectedObject,
3,
{
units: 'kilometers'
}
);
map.getSource('analysis')
.setData(result);
Преимущество такого подхода заключается в отсутствии необходимости обращаться к серверу после каждого действия пользователя.
При работе с большими объемами данных рекомендуется:
Пример кэширования:
const cache = new Map();
function getBuffer(id, feature) {
if (cache.has(id)) {
return cache.get(id);
}
const result =
turf.buffer(
feature,
2,
{
units: 'kilometers'
}
);
cache.set(id, result);
return result;
}
Практика показывает, что наиболее удобной является схема разделения ответственности:
MapLibre GL JS
↓
Событие пользователя
↓
Turf.js
↓
GeoJSON результат
↓
Источник данных MapLibre
↓
Отрисовка слоя
Такой подход обеспечивает:
Совместное использование MapLibre GL JS и Turf.js превращает браузерное картографическое приложение в полноценную геоинформационную систему, способную не только отображать данные, но и выполнять пространственный анализ в режиме реального времени непосредственно на стороне клиента.