Работа со свойствами объектов (feature properties) в MapLibre GL JS
строится вокруг концепции GeoJSON, где каждый географический объект
представляет собой структуру Feature с набором координат и произвольным
набором атрибутов в поле properties. Эти свойства являются
основным источником динамики при визуализации данных, фильтрации,
стилизации и взаимодействии с картой.
GeoJSON Feature имеет следующую логическую форму:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [30.5, 50.5]
},
"properties": {
"name": "Объект A",
"category": "restaurant",
"rating": 4.6,
"isActive": true
}
}
Поле properties не ограничено по структуре, что делает
его универсальным контейнером для любых атрибутов: от числовых значений
до строк, булевых флагов и вложенных структур (хотя вложенность
поддерживается ограниченно в выражениях стилей).
В MapLibre GL JS свойства feature становятся ключевым источником данных для:
При работе с интерактивными слоями свойства feature часто извлекаются из событий карты:
map.on('click', 'restaurants-layer', (e) => {
const feature = e.features[0];
const props = feature.properties;
console.log(props.name);
console.log(props.rating);
});
Объект e.features содержит список всех объектов слоя в
точке клика. Каждый feature уже содержит поле properties,
полученное из источника данных.
Важно учитывать, что свойства доступны только если слой поддерживает queryable features (обычно vector sources или GeoJSON sources с включённой интерактивностью).
Одна из ключевых возможностей заключается в привязке визуальных параметров к значениям properties через expression API.
map.addLayer({
id: 'restaurants-layer',
type: 'circle',
source: 'restaurants',
paint: {
'circle-color': [
'match',
['get', 'category'],
'restaurant', '#ff4d4d',
'cafe', '#ffd24d',
'bar', '#8c4dff',
'#cccccc'
]
}
});
Здесь функция ['get', 'category'] извлекает значение
свойства category из feature.
'circle-radius': [
'interpolate',
['linear'],
['get', 'rating'],
0, 2,
5, 10
]
Такой подход позволяет строить непрерывные визуальные шкалы, где свойства напрямую управляют визуальными параметрами.
Фильтры позволяют ограничивать отображение объектов слоя на основе их свойств.
map.setFilter('restaurants-layer', [
'>=',
['get', 'rating'],
4
]);
Другой пример — фильтрация по категории:
map.setFilter('restaurants-layer', [
'in',
['get', 'category'],
'restaurant',
'cafe'
]);
Фильтры выполняются на уровне рендеринга, что делает их более эффективными, чем ручное скрытие объектов.
В MapLibre GL JS свойства feature не являются напрямую мутабельными. Изменение properties требует обновления источника данных.
Типичный подход при использовании GeoJSON source:
const source = map.getSource('restaurants');
const data = source._data;
data.features[0].properties.rating = 5.0;
source.setData(data);
После вызова setData происходит полная перерисовка
слоя.
Этот подход подходит для небольших наборов данных. При больших объёмах он становится затратным по производительности.
Вместо изменения самих properties часто используется механизм
feature-state, который позволяет хранить динамическое
состояние отдельно от GeoJSON.
map.setFeatureState(
{
source: 'restaurants',
id: 123
},
{
hovered: true
}
);
Использование в стиле:
'circle-color': [
'case',
['boolean', ['feature-state', 'hovered'], false],
'#ff0000',
'#0000ff'
]
Преимущество подхода заключается в том, что исходные properties остаются неизменными, а состояние можно обновлять без перезагрузки source.
Метод queryRenderedFeatures позволяет получать feature
прямо с карты в заданной области:
const features = map.queryRenderedFeatures(point, {
layers: ['restaurants-layer']
});
features.forEach(f => {
console.log(f.properties.name);
});
Этот метод часто используется для кастомных взаимодействий, таких как построение всплывающих окон или динамических панелей информации.
В properties допустимы базовые JSON-типы:
Однако при использовании expression API важно учитывать ограничения:
getПример допустимой структуры:
{
"temperature": 22.5,
"label": "station A",
"active": true
}
Пример проблемной структуры:
{
"meta": {
"nested": {
"value": 10
}
}
}
В таких случаях доступ к meta.nested.value через
expression невозможен напрямую.
properties часто применяются для генерации интерфейсов:
map.on('click', 'restaurants-layer', (e) => {
const f = e.features[0];
new maplibregl.Popup()
.setLngLat(f.geometry.coordinates)
.setHTML(`
<div>
<strong>${f.properties.name}</strong><br/>
Rating: ${f.properties.rating}
</div>
`)
.addTo(map);
});
Здесь properties становятся источником данных для UI-слоя поверх карты.
Важно различать:
feature.properties — данные конкретного объектаlayer.paint и layer.layout — правила
отображенияfeature-state — временное состояниеProperties статичны относительно GeoJSON, layer definitions описывают поведение, feature-state обеспечивает динамику.
При интенсивном использовании properties важно учитывать:
['get', ...] выполняются на
GPU/рендер-уровнеsetData приводят к перерасчёту слояОптимальная стратегия:
На практике встречаются типичные ошибки:
['==', ['get', 'rating'], '5'] // строка вместо числа
Если feature не содержит ключа, выражение вернёт null, что может привести к fallback-значениям.
Прямое изменение объекта feature не обновляет карту:
feature.properties.name = 'New'; // не сработает визуально
Требуется обновление источника.
Properties можно комбинировать:
[
'case',
['all',
['==', ['get', 'category'], 'restaurant'],
['>=', ['get', 'rating'], 4]
],
'#00ff00',
'#999999'
]
Так формируются сложные правила визуализации, основанные на нескольких атрибутах одновременно.
При использовании кластеров свойства исходных features агрегируются:
'cluster-radius': 40,
'cluster-properties': {
'maxRating': ['max', ['get', 'rating']]
}
Таким образом можно вычислять агрегированные характеристики кластеров на основе properties вложенных объектов.