При работе с геопространственными объектами часто требуется хранить не только геометрию, но и дополнительную информацию: названия объектов, идентификаторы, категории, статистические показатели, сведения о владельцах, временные метки и другие атрибуты. В стандарте GeoJSON такие данные размещаются внутри свойства properties.
Пример объекта с метаданными:
const point = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [37.6176, 55.7558]
},
properties: {
name: "Москва",
population: 13000000,
capital: true
}
};
Turf.js рассматривает метаданные как неотъемлемую часть объекта GeoJSON и предоставляет набор инструментов для их создания, чтения, изменения и передачи между объектами.
Практически все функции Turf.js работают с объектами типа Feature.
Общая структура:
{
type: "Feature",
geometry: {
...
},
properties: {
...
}
}
Секция geometry описывает пространственную форму объекта.
Секция properties содержит произвольный набор данных:
properties: {
id: 101,
city: "Москва",
district: "Центральный",
population: 13000000,
isCapital: true
}
Turf.js не накладывает ограничений на содержимое properties. Допускаются:
null.Пример:
properties: {
name: "Парк Горького",
tags: ["park", "recreation"],
contacts: {
phone: "+7 999 123-45-67",
website: "example.com"
}
}
Большинство фабричных функций Turf.js принимает объект свойств вторым аргументом.
import * as turf from "@turf/turf";
const point = turf.point(
[37.6176, 55.7558],
{
name: "Москва",
population: 13000000
}
);
Результат:
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [37.6176, 55.7558]
},
properties: {
name: "Москва",
population: 13000000
}
}
const road = turf.lineString(
[
[37.61, 55.75],
[37.65, 55.77]
],
{
roadType: "highway",
lanes: 4
}
);
const district = turf.polygon(
[[
[37.60, 55.74],
[37.64, 55.74],
[37.64, 55.78],
[37.60, 55.78],
[37.60, 55.74]
]],
{
name: "Центральный район",
code: "CTR"
}
);
Доступ к свойствам осуществляется через объект properties.
const cityName = point.properties.name;
console.log(cityName);
Результат:
Москва
Получение нескольких значений:
const population = point.properties.population;
const capital = point.properties.capital;
Поскольку свойства представлены обычным JavaScript-объектом, их можно изменять напрямую.
point.properties.population = 13500000;
point.properties.country = "Россия";
Результат:
properties: {
name: "Москва",
population: 13500000,
country: "Россия"
}
delete point.properties.population;
После удаления:
properties: {
name: "Москва"
}
Для обработки большого количества объектов удобно применять функцию
propEach.
Она последовательно перебирает свойства всех объектов коллекции.
Исходные данные:
const cities = turf.featureCollection([
turf.point([37.61, 55.75], {
name: "Москва"
}),
turf.point([30.31, 59.93], {
name: "Санкт-Петербург"
})
]);
Обход свойств:
turf.propEach(cities, (properties) => {
console.log(properties.name);
});
Результат:
Москва
Санкт-Петербург
Функция propReduce работает аналогично
Array.reduce().
Пример получения списка названий:
const names = turf.propReduce(
cities,
(result, properties) => {
result.push(properties.name);
return result;
},
[]
);
console.log(names);
Результат:
[
"Москва",
"Санкт-Петербург"
]
Часто требуется работать одновременно с геометрией и атрибутами.
Для этого используется featureEach.
turf.featureEach(cities, (feature) => {
console.log(feature.properties.name);
});
Можно анализировать и координаты, и свойства:
turf.featureEach(cities, (feature) => {
console.log(
feature.geometry.coordinates,
feature.properties.name
);
});
Нередко после геометрических преобразований необходимо сохранить исходные свойства.
const source = turf.point(
[37.61, 55.75],
{
city: "Москва",
region: "Центральный"
}
);
const target = turf.point([38, 56]);
target.properties = {
...source.properties
};
Результат:
{
city: "Москва",
region: "Центральный"
}
Если свойства содержат вложенные структуры:
const copiedProperties =
JSON.parse(
JSON.stringify(source.properties)
);
Такой подход предотвращает совместное использование ссылок.
Большинство функций Turf.js автоматически переносит свойства исходного объекта.
Пример буферизации:
const point = turf.point(
[37.61, 55.75],
{
city: "Москва"
}
);
const buffer = turf.buffer(
point,
5,
{ units: "kilometers" }
);
Полученный объект:
buffer.properties
Содержит:
{
city: "Москва"
}
Это позволяет выполнять сложные геометрические преобразования без потери атрибутивной информации.
Результаты пространственного анализа часто записываются в properties.
Например, вычисление площади полигона:
const polygon = turf.polygon([
[
[0, 0],
[10, 0],
[10, 10],
[0, 10],
[0, 0]
]
]);
polygon.properties = {};
polygon.properties.area =
turf.area(polygon);
Теперь объект содержит вычисленное значение:
{
area: 1232921098571.292
}
Метаданные могут вычисляться на основе существующих значений.
Исходный объект:
const city = turf.point(
[37.61, 55.75],
{
population: 13000000,
areaKm2: 2561
}
);
Вычисление плотности населения:
city.properties.density =
city.properties.population /
city.properties.areaKm2;
Результат:
{
population: 13000000,
areaKm2: 2561,
density: 5076.14
}
Очень распространённая задача — отбор объектов по свойствам.
Исходная коллекция:
const collection = turf.featureCollection([
turf.point([0, 0], {
type: "capital"
}),
turf.point([1, 1], {
type: "city"
}),
turf.point([2, 2], {
type: "capital"
})
]);
Фильтрация:
const capitals =
collection.features.filter(
feature =>
feature.properties.type ===
"capital"
);
Результат:
[
Feature,
Feature
]
Пример группировки по категории.
Исходные данные:
const places = [
{
properties: {
category: "park"
}
},
{
properties: {
category: "park"
}
},
{
properties: {
category: "museum"
}
}
];
Группировка:
const groups = {};
for (const place of places) {
const category =
place.properties.category;
if (!groups[category]) {
groups[category] = [];
}
groups[category].push(place);
}
Результат:
{
park: [...],
museum: [...]
}
Коллекции объектов могут содержать тысячи элементов.
Пример:
const collection =
turf.featureCollection([
turf.point([0, 0], {
city: "A"
}),
turf.point([1, 1], {
city: "B"
})
]);
Получение списка городов:
const cities =
collection.features.map(
feature =>
feature.properties.city
);
Результат:
["A", "B"]
Для дальнейшей обработки часто используются уникальные идентификаторы.
turf.featureEach(
collection,
(feature, index) => {
feature.properties.id =
index + 1;
}
);
После обработки:
{
city: "A",
id: 1
}
{
city: "B",
id: 2
}
GeoJSON допускает хранение сложных объектов внутри properties.
const building = turf.point(
[37.61, 55.75],
{
address: {
city: "Москва",
street: "Тверская",
house: 10
},
owner: {
name: "Иван Иванов",
type: "private"
}
}
);
Доступ:
console.log(
building.properties.address.city
);
Результат:
Москва
Иногда необходимо соединить свойства из нескольких источников.
const baseProperties = {
city: "Москва",
population: 13000000
};
const analyticsProperties = {
density: 5076,
region: "Центральный"
};
Объединение:
const merged = {
...baseProperties,
...analyticsProperties
};
Результат:
{
city: "Москва",
population: 13000000,
density: 5076,
region: "Центральный"
}
Перед использованием атрибутов рекомендуется выполнять проверки.
if (
point.properties &&
point.properties.population
) {
console.log(
point.properties.population
);
}
Более современный вариант:
const population =
point.properties?.population;
При объединении данных из разных источников названия полей могут отличаться.
Исходные данные:
{
cityName: "Москва"
}
{
name: "Санкт-Петербург"
}
Нормализация:
feature.properties.city =
feature.properties.cityName ||
feature.properties.name;
После обработки:
{
city: "Москва"
}
{
city: "Санкт-Петербург"
}
Создаётся коллекция населённых пунктов:
const cities =
turf.featureCollection([
turf.point([37.61, 55.75], {
name: "Москва"
}),
turf.point([30.31, 59.93], {
name: "Санкт-Петербург"
})
]);
Для каждого объекта вычисляется расстояние до условного центра:
const center =
turf.point([35, 57]);
turf.featureEach(
cities,
(feature) => {
const distance =
turf.distance(
center,
feature,
{
units: "kilometers"
}
);
feature.properties.distanceToCenter =
distance;
}
);
Полученные объекты содержат одновременно:
Подобный подход является основой большинства геоинформационных систем, где геометрия описывает пространственное положение объекта, а метаданные содержат всю прикладную информацию, необходимую для хранения, поиска, фильтрации, анализа и визуализации данных.