В экосистеме Leaflet ключевую роль при работе с векторными слоями
играет механизм хранения и обработки свойств объектов. Геометрия (точки,
линии, полигоны) почти всегда сопровождается набором метаданных,
описанных в формате GeoJSON через поле properties. Именно
эти данные формируют основу для визуализации, фильтрации, интерактивного
поведения и динамического стиля объектов на карте.
GeoJSON-объект стандартно включает три основных компонента:
type — тип геометрии (Feature,
FeatureCollection)geometry — координаты объектаproperties — произвольный набор атрибутовПример базового объекта:
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [76.8897, 43.2389]
},
properties: {
name: "Объект A",
category: "station",
capacity: 1200,
active: true
}
};
Поле properties не имеет фиксированной схемы. Это
приводит к необходимости аккуратной обработки данных при отображении на
карте, поскольку структура может отличаться даже внутри одной
коллекции.
При использовании L.geoJSON каждый объект передаётся в
обработчики, где доступ к properties осуществляется
напрямую через feature.
L.geoJSON(data, {
onEachFeature: function (feature, layer) {
console.log(feature.properties.name);
}
}).addTo(map);
Ключевым механизмом взаимодействия с атрибутами является функция
onEachFeature, которая позволяет привязывать поведение к
каждому объекту индивидуально.
Наиболее частое использование properties — формирование
содержимого popup.
function onEachFeature(feature, layer) {
const props = feature.properties;
layer.bindPopup(
`<strong>${props.name}</strong><br>
Категория: ${props.category}<br>
Вместимость: ${props.capacity}`
);
}
L.geoJSON(data, {
onEachFeature
}).addTo(map);
При этом структура данных может быть расширена без изменения логики отображения, что делает подход гибким и масштабируемым.
В реальных наборах данных свойства часто неполные. Отсутствие ключей требует дефолтной логики:
function getProp(props, key, fallback = "—") {
return props && props[key] != null ? props[key] : fallback;
}
function onEachFeature(feature, layer) {
const p = feature.properties;
layer.bindPopup(`
<strong>${getProp(p, "name")}</strong><br>
Статус: ${getProp(p, "status", "неизвестен")}
`);
}
Такая стратегия предотвращает ошибки при обращении к
undefined и делает отображение стабильным даже при неполных
данных.
Свойства объектов часто используются для динамического определения
стиля через функцию style.
function getColor(category) {
switch (category) {
case "station": return "blue";
case "warehouse": return "orange";
case "park": return "green";
default: return "gray";
}
}
L.geoJSON(data, {
style: function (feature) {
return {
color: getColor(feature.properties.category),
weight: 2,
fillOpacity: 0.5
};
}
}).addTo(map);
Таким образом геометрия отделяется от визуальной логики, а свойства становятся источником конфигурации отображения.
Свойства позволяют ограничивать отображаемые элементы через
filter.
L.geoJSON(data, {
filter: function (feature) {
return feature.properties.active === true;
}
}).addTo(map);
Фильтрация выполняется до создания слоя, что влияет на производительность при больших наборах данных.
Расширенный вариант фильтрации:
function isValid(feature) {
const p = feature.properties;
return p && p.capacity > 500 && p.category !== "deprecated";
}
Свойства могут использоваться для определения поведения объекта при взаимодействии.
function onEachFeature(feature, layer) {
layer.on("click", function () {
if (feature.properties.link) {
window.open(feature.properties.link);
}
});
}
Такой подход позволяет хранить логику навигации и взаимодействия прямо в данных, а не в коде интерфейса.
В ряде случаев требуется нормализация структуры данных до использования:
function normalizeFeature(feature) {
const p = feature.properties || {};
return {
...feature,
properties: {
name: p.name || "Без названия",
category: (p.category || "unknown").toLowerCase(),
capacity: Number(p.capacity) || 0,
active: Boolean(p.active)
}
};
}
const normalizedData = {
type: "FeatureCollection",
features: data.features.map(normalizeFeature)
};
Подобная трансформация снижает количество проверок в дальнейшем коде.
При работе с большими массивами геоданных часто требуется логическая группировка.
function groupByCategory(features) {
return features.reduce((acc, f) => {
const key = f.properties.category || "unknown";
if (!acc[key]) acc[key] = [];
acc[key].push(f);
return acc;
}, {});
}
После группировки можно создавать отдельные слои или управлять их отображением независимо.
Изменение свойств объекта может отражаться на карте без пересоздания всего слоя.
layer.feature.properties.active = false;
layer.setStyle({
opacity: 0.3,
fillOpacity: 0.1
});
При наличии привязки логики к properties можно
синхронизировать визуальное состояние с данными.
GeoJSON допускает произвольные поля внутри properties,
что позволяет внедрять доменную модель:
properties: {
id: 1023,
title: "Склад №5",
metrics: {
load: 0.78,
temperature: 18.5
},
tags: ["logistics", "cold-storage"]
}
Работа с вложенными структурами требует осторожного доступа:
const load = feature.properties.metrics?.load ?? 0;
При использовании кластеров свойства могут агрегироваться:
const markers = L.geoJSON(data, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.name);
}
});
markerClusterGroup.addLayer(markers);
Дополнительно возможно вычисление агрегированных метрик по кластерам
на основе исходных properties.
Логика отображения может зависеть от комбинации атрибутов:
function shouldHighlight(feature) {
const p = feature.properties;
return p.active && p.priority > 5;
}
L.geoJSON(data, {
style: function (feature) {
return shouldHighlight(feature)
? { weight: 3, color: "red" }
: { weight: 1, color: "gray" };
}
});
При сохранении состояния карты свойства объектов часто используются для восстановления интерфейса:
const serialized = JSON.stringify(geoJsonData);
const restored = JSON.parse(serialized);
При этом важно сохранять целостность структуры
properties, поскольку именно она определяет поведение слоя
после загрузки.
Некоторые атрибуты могут не храниться в данных, а вычисляться на лету:
function enrichFeature(feature) {
const p = feature.properties;
p.label = `${p.name} (${p.category})`;
p.isLarge = p.capacity > 1000;
return feature;
}
Такая модель объединяет исходные данные и производные характеристики в одном объекте, упрощая дальнейшее использование в слоях Leaflet.