В MapLibre GL JS фильтрация применяется на уровне слоя и определяет, какие объекты из источника данных будут визуализированы. Каждый слой может содержать выражение фильтра, которое вычисляется для каждого фичи и возвращает логическое значение. Если результат истинный — объект отображается, иначе исключается из рендеринга.
Фильтры работают только с данными источника и не изменяют сам источник. Это ключевой принцип: визуализация отделена от данных.
Функция установки фильтра:
map.setFilter(layerId, filterExpression);
Удаление фильтра:
map.setFilter(layerId, null);
Фильтрация применяется мгновенно и пересчитывается на GPU-пайплайне рендеринга.
Фильтры в MapLibre GL JS представляют собой массивы в стиле expression syntax.
Базовый формат:
["==", ["get", "propertyName"], value]
Пример фильтра по типу объекта:
map.setFilter("points-layer", ["==", ["get", "type"], "restaurant"]);
Фильтр по числовому диапазону:
map.setFilter("points-layer", [
">=",
["get", "population"],
100000
]);
Фильтр по наличию значения:
map.setFilter("points-layer", ["has", "name"]);
Для построения сложных условий используются логические операторы:
["all",
["==", ["get", "type"], "restaurant"],
[">", ["get", "rating"], 4]
]
["any",
["==", ["get", "type"], "cafe"],
["==", ["get", "type"], "bar"]
]
["!", ["==", ["get", "status"], "closed"]]
Комбинирование операторов позволяет строить сложные динамические правила отображения без изменения данных источника.
Фильтры часто изменяются в ответ на пользовательские действия или события интерфейса. Типичный сценарий — переключение категорий через UI.
const categories = ["restaurant", "cafe", "bar"];
function setCategory(category) {
map.setFilter("places-layer", [
"==",
["get", "type"],
category
]);
}
Изменение может происходить при клике:
button.addEventListener("click", () => {
setCategory("cafe");
});
При каждом вызове setFilter слой пересчитывает видимость
объектов без перезагрузки источника.
Для выбора нескольких категорий используется оператор in
или any.
anymap.setFilter("places-layer", [
"any",
["==", ["get", "type"], "restaurant"],
["==", ["get", "type"], "bar"]
]);
inmap.setFilter("places-layer", [
"in",
["get", "type"],
"restaurant",
"bar",
"cafe"
]);
Второй вариант предпочтительнее при динамическом формировании списка категорий.
Часто фильтры зависят от набора параметров: чекбоксов, слайдеров, диапазонов дат.
const activeTypes = new Set(["restaurant", "cafe"]);
function updateFilter() {
map.setFilter("places-layer", [
"in",
["get", "type"],
...Array.from(activeTypes)
]);
}
Изменение состояния:
checkbox.addEventListener("change", (e) => {
if (e.target.checked) {
activeTypes.add(e.target.value);
} else {
activeTypes.delete(e.target.value);
}
updateFilter();
});
Слайдеры и числовые фильтры часто используются для динамической настройки отображения.
function setRatingFilter(minRating) {
map.setFilter("places-layer", [
">=",
["get", "rating"],
minRating
]);
}
Двусторонний диапазон:
function setRange(min, max) {
map.setFilter("places-layer", [
"all",
[">=", ["get", "rating"], min],
["<=", ["get", "rating"], max]
]);
}
Фильтрация может зависеть от масштаба карты.
map.on("zoom", () => {
const zoom = map.getZoom();
if (zoom < 10) {
map.setFilter("places-layer", ["<", ["get", "importance"], 5]);
} else {
map.setFilter("places-layer", ["has", "id"]);
}
});
Такой подход позволяет снижать плотность объектов на малых масштабах.
Иногда требуется не только визуальная фильтрация, но и вычисление объектов, попадающих под условия.
const features = map.queryRenderedFeatures({
layers: ["places-layer"],
filter: ["==", ["get", "type"], "restaurant"]
});
Это позволяет синхронизировать UI-логику с текущей визуализацией.
При сложных сценариях фильтрация слоя заменяется обновлением данных источника.
const source = map.getSource("places");
source.setData(filteredGeojson);
Такой подход применяется, когда фильтрация зависит от внешних API или тяжёлых вычислений, которые нецелесообразно выполнять на стороне выражений MapLibre.
Feature-state используется для динамических визуальных изменений без изменения данных и фильтров слоя.
map.setFeatureState(
{ source: "places", id: 123 },
{ selected: true }
);
В фильтре можно учитывать состояние:
map.setFilter("places-layer", [
"==",
["feature-state", "selected"],
true
]);
Этот подход позволяет реализовывать интерактивное выделение объектов.
Частое обновление фильтров может приводить к нагрузке на рендеринг, особенно при больших GeoJSON-источниках.
Ключевые принципы оптимизации:
setFilterin вместо множества ==Пример debounce:
let timeout;
function updateDebounced(filter) {
clearTimeout(timeout);
timeout = setTimeout(() => {
map.setFilter("places-layer", filter);
}, 100);
}
Использование набора переключателей для типов объектов:
["in", ["get", "category"], "park", "museum", "theater"]
map.setFilter("events-layer", [
">=",
["get", "timestamp"],
currentTimestamp
]);
map.setFilter("places-layer", [
"all",
["in", ["get", "type"], ...selectedTypes],
[">=", ["get", "rating"], minRating],
["!=", ["get", "status"], "closed"]
]);