Линия в геопространственных данных представляется как объект типа
LineString в формате GeoJSON. Такой объект состоит из
упорядоченного массива координат, где каждая точка задаётся парой
[долгота, широта] или тройкой
[долгота, широта, высота].
Базовая структура:
{
"type": "LineString",
"coordinates": [
[30.5, 50.5],
[31.0, 50.7],
[31.5, 51.0]
]
}
Ключевой аспект при работе с линиями — строгий порядок точек. Геометрия интерпретируется как последовательное соединение сегментов, поэтому перестановка координат меняет форму линии.
Turf.js предоставляет набор инструментов для создания, преобразования и анализа таких объектов, включая построение линий из набора точек, вычисление длины, интерполяцию и разбиение.
Основная функция для создания линии — turf.lineString.
Она принимает массив координат и возвращает GeoJSON-объект типа
LineString.
import { lineString } fr om "@turf/turf";
const points = [
[30.5, 50.5],
[31.0, 50.7],
[31.5, 51.0]
];
const line = lineString(points);
Результатом является стандартный GeoJSON-объект, который может быть использован в Mapbox, Leaflet и других GIS-библиотеках.
В реальных задачах точки часто представлены как
Feature<Point>. В таком случае требуется извлечение
координат перед построением линии.
import { lineString } from "@turf/turf";
const pointFeatures = [
{ type: "Feature", geometry: { type: "Point", coordinates: [30.5, 50.5] } },
{ type: "Feature", geometry: { type: "Point", coordinates: [31.0, 50.7] } },
{ type: "Feature", geometry: { type: "Point", coordinates: [31.5, 51.0] } }
];
const coordinates = pointFeatures.map(p => p.geometry.coordinates);
const line = lineString(coordinates);
Такой подход характерен для обработки данных, полученных из API или базы пространственных объектов.
Корректность линии зависит от порядка координат. При отсутствии явного порядка часто применяется сортировка по атрибутам: времени, индексу, расстоянию или координатам.
Пример сортировки по индексу:
const points = [
{ coords: [31.5, 51.0], index: 3 },
{ coords: [30.5, 50.5], index: 1 },
{ coords: [31.0, 50.7], index: 2 }
];
points.sort((a, b) => a.index - b.index);
const line = lineString(points.map(p => p.coords));
При географической сортировке необходимо учитывать сферическую природу координат, поскольку простое сравнение широты и долготы может давать искажённый порядок.
Turf.js позволяет добавлять свойства к линии, которые сохраняются в
properties.
const line = lineString(
[
[30.5, 50.5],
[31.0, 50.7],
[31.5, 51.0]
],
{
name: "Маршрут 1",
type: "delivery-route",
speedLim it: 60
}
);
Эти свойства используются в визуализации и аналитике, но не влияют на геометрию.
При редких точках линия может выглядеть «ломаной». Turf.js предоставляет инструменты для добавления промежуточных точек.
Используется подход через turf.along и
turf.lineDistance.
import { lineString, lineDistance, along } from "@turf/turf";
const line = lineString([
[30.5, 50.5],
[31.0, 50.7],
[31.5, 51.0]
]);
const distance = lineDistance(line, { units: "kilometers" });
const segmentStep = 5;
const interpolated = [];
for (let i = 0; i <= distance; i += segmentStep) {
interpolated.push(along(line, i, { units: "kilometers" }).geometry.coordinates);
}
const smoothLine = lineString(interpolated);
Такой метод позволяет создавать более плотную геометрию для визуализации и анализа.
GPS-данные часто содержат шум и требуют предварительной обработки.
Типичная структура трека:
const track = [
[30.5001, 50.5002],
[30.5005, 50.5006],
[30.5010, 50.5010],
[30.5100, 50.5100]
];
Прямое построение:
const route = lineString(track);
Однако при анализе движения часто применяется фильтрация выбросов. Простая эвристика — удаление точек с аномально большим расстоянием между соседями.
Если точки заданы в хаотичном порядке, требуется алгоритмическое восстановление маршрута. Один из подходов — построение через ближайшего соседа.
function buildOrderedLine(points) {
const unused = [...points];
const ordered = [unused.shift()];
while (unused.length) {
const last = ordered[ordered.length - 1];
let nearestIndex = 0;
let nearestDist = Infinity;
unused.forEach((p, i) => {
const dx = p[0] - last[0];
const dy = p[1] - last[1];
const d = dx * dx + dy * dy;
if (d < nearestDist) {
nearestDist = d;
nearestIndex = i;
}
});
ordered.push(unused.splice(nearestIndex, 1)[0]);
}
return lineString(ordered);
}
Этот метод не гарантирует оптимальность маршрута, но часто используется как быстрый способ реконструкции последовательности.
При наличии нескольких линий используется
turf.lineMerge.
import { lineMerge, featureCollection, lineString } from "@turf/turf";
const lines = featureCollection([
lineString([[30, 50], [31, 51]]),
lineString([[31, 51], [32, 52]])
]);
const merged = lineMerge(lines);
Результат объединяет смежные сегменты в одну непрерывную геометрию.
Корректная линия должна удовлетворять нескольким условиям:
null или undefinedПростейшая валидация:
function validateLine(coords) {
return (
Array.isArray(coords) &&
coords.length >= 2 &&
coords.every(
p =>
Array.isArray(p) &&
typeof p[0] === "number" &&
typeof p[1] === "number" &&
p[1] >= -90 &&
p[1] <= 90 &&
p[0] >= -180 &&
p[0] <= 180
)
);
}
После построения линии становятся доступны геометрические операции:
turf.lengthturf.lineSliceturf.alongturf.simplifyПример получения длины:
import { length, lineString } from "@turf/turf";
const line = lineString([
[30.5, 50.5],
[31.0, 50.7],
[31.5, 51.0]
]);
const dist = length(line, { units: "kilometers" });
При визуализации большого количества точек применяется упрощение геометрии. Это снижает нагрузку на рендеринг без существенной потери формы.
import { simplify, lineString } from "@turf/turf";
const line = lineString(hugeCoordinateArray);
const simplified = simplify(line, {
tolerance: 0.001,
highQuality: true
});
Параметр tolerance определяет степень упрощения: чем
выше значение, тем меньше точек остаётся.
При объединении данных из разных источников часто встречаются сегменты с разной плотностью точек. Перед объединением применяется нормализация:
function removeDuplicates(coords) {
return coords.filter((p, i, arr) => {
if (i === 0) return true;
const prev = arr[i - 1];
return p[0] !== prev[0] || p[1] !== prev[1];
});
}
const clean = removeDuplicates(rawCoords);
const line = lineString(clean);