Фильтрация слоев

Фильтрация слоев в MapLibre GL JS представляет собой механизм управления видимостью и отображением географических объектов на основе их свойств. Она реализуется через filter слоя и позволяет динамически ограничивать набор отображаемых фич без необходимости пересоздания источника данных или слоя.

Фильтрация выполняется на уровне слоя и применяется к каждому объекту (feature) источника данных перед рендерингом.


Структура фильтра слоя

Каждый слой в стиле MapLibre может содержать свойство:

filter: ["==", "property_name", "value"]

Фильтр задаётся в виде выражения (expression), представляющего дерево логических и сравнительных операций.

Пример слоя с фильтром:

map.addLayer({
  id: "cities",
  type: "circle",
  source: "places",
  "source-layer": "city_points",
  filter: ["==", ["get", "type"], "city"],
  paint: {
    "circle-radius": 6,
    "circle-color": "#ff5722"
  }
});

В данном случае будут отображены только те объекты, у которых свойство type равно "city".


Метод setFilter

Основной способ динамической фильтрации — метод:

map.setFilter(layerId, filterExpression);

Пример динамического обновления фильтра

map.setFilter("cities", ["==", ["get", "country"], "Kazakhstan"]);

Фильтр применяется мгновенно, без перезагрузки источника данных.

Сброс фильтра:

map.setFilter("cities", null);

Синтаксис выражений фильтра

Фильтры в MapLibre GL JS основаны на JSON-выражениях. Это стековая система операторов.

Сравнения

Равенство

["==", ["get", "population"], 100000]

Неравенство

["!=", ["get", "status"], "inactive"]

Больше / меньше

[">", ["get", "population"], 500000]

["<=", ["get", "rank"], 10]

Логические операторы

AND

["all",
  ["==", ["get", "type"], "city"],
  [">", ["get", "population"], 100000]
]

OR

["any",
  ["==", ["get", "type"], "city"],
  ["==", ["get", "type"], "town"]
]

NOT

["!", ["==", ["get", "status"], "inactive"]]

Проверка наличия свойств

has

Проверяет наличие свойства:

["has", "name"]

!has

["!has", "population"]

Оператор in

Позволяет проверять вхождение значения в список:

["in", ["get", "type"], "city", "town", "village"]

Этот вариант часто используется вместо длинных цепочек any.


Работа с числовыми диапазонами

Фильтрация по диапазону значений:

["all",
  [">=", ["get", "population"], 100000],
  ["<", ["get", "population"], 1000000]
]

Такой подход применяется для сегментации данных по классам величин.


Фильтрация по типу геометрии

Если источник содержит разные типы геометрии, можно использовать фильтрацию по geometry-type:

["==", ["geometry-type"], "Point"]

Допустимые значения:

  • Point
  • LineString
  • Polygon

Комбинированные фильтры

Реальные задачи требуют сочетания условий.

Пример сложной фильтрации

Отображение крупных городов определённого типа:

["all",
  ["==", ["get", "type"], "city"],
  [">", ["get", "population"], 500000],
  ["in", ["get", "region"], "south", "east"]
]

Динамическая фильтрация по UI-состоянию

Фильтры часто изменяются в ответ на пользовательский интерфейс.

Пример переключения категорий

function filterByCategory(category) {
  if (category === "all") {
    map.setFilter("places", null);
    return;
  }

  map.setFilter("places", ["==", ["get", "category"], category]);
}

Множественные значения через массивы

В некоторых случаях данные уже представлены массивами:

["in", "landuse", ["get", "tags"]]

Однако MapLibre не всегда поддерживает глубокие операции с массивами в фильтрах напрямую, поэтому часто используется нормализация данных на этапе подготовки GeoJSON или vector tiles.


Влияние фильтра на производительность

Фильтрация выполняется на этапе рендеринга слоя. Основные особенности:

  • фильтр применяется к каждому feature;
  • сложные выражения увеличивают нагрузку на CPU;
  • предпочтительно использовать простые сравнения при большом количестве объектов;
  • фильтры не изменяют исходные данные источника.

Оптимизация достигается за счёт:

  • предварительной фильтрации данных на сервере;
  • разделения источников по категориям;
  • использования vector tiles с уже сегментированными слоями.

Взаимодействие фильтра с zoom и min/max zoom

Хотя это не часть filter, часто используется совместно:

map.addLayer({
  id: "labels",
  type: "symbol",
  source: "places",
  minzoom: 10,
  maxzoom: 16,
  filter: ["==", ["get", "type"], "city"]
});

minzoom и maxzoom ограничивают видимость слоя по масштабу, тогда как filter ограничивает по данным.


Обновление фильтра без перерисовки слоя

MapLibre автоматически пересчитывает отображение при вызове setFilter.

Внутренне происходит:

  • пересчёт соответствия features;
  • обновление render tree;
  • частичный re-render слоя.

Важно, что источник данных остаётся неизменным.


Условная фильтрация через свойства и выражения

MapLibre позволяет использовать арифметические и строковые операции.

Пример фильтра по длине строки

["<", ["length", ["get", "name"]], 10]

Пример проверки подстроки

["==", ["slice", ["get", "name"], 0, 3], "Alm"]

Фильтрация по категориям в стиле классификаторов

Часто данные содержат числовые коды категорий:

["in", ["get", "class"], 1, 2, 3]

Такой подход используется в тайловых наборах типа OpenStreetMap-derived vector tiles.


Использование фильтров в нескольких слоях

Один источник может иметь несколько слоёв с разными фильтрами:

map.addLayer({
  id: "cities-large",
  type: "circle",
  source: "places",
  filter: [">", ["get", "population"], 1000000]
});

map.addLayer({
  id: "cities-small",
  type: "circle",
  source: "places",
  filter: ["<=", ["get", "population"], 1000000]
});

Это позволяет разделять визуальные стили без дублирования данных.


Удаление фильтра и возврат к исходному состоянию

map.setFilter("cities", null);

После этого слой начинает отображать все объекты источника без ограничений.


Типичные ошибки при фильтрации

Несовпадение типов данных

["==", ["get", "population"], "100000"] // строка вместо числа

Фильтр не сработает корректно, если типы не совпадают.


Обращение к несуществующему полю

["==", ["get", "populaton"], 1000]

Ошибки в названии свойства приводят к пустому результату.


Слишком сложные вложенные выражения

Глубокая вложенность:

["all",
  ["any",
    ["all", ...],
    ["all", ...]
  ]
]

Такие конструкции снижают читаемость и усложняют отладку.


Практика построения фильтров

Фильтры рекомендуется строить по принципу:

  1. сначала ограничение по типу объекта;
  2. затем числовые диапазоны;
  3. затем категориальные условия;
  4. затем логические объединения.

Пример структурированного фильтра:

["all",
  ["==", ["get", "type"], "poi"],
  [">=", ["get", "rating"], 4],
  ["in", ["get", "category"], "restaurant", "cafe"]
]

Итеративное изменение фильтра в реальном времени

Фильтры часто обновляются в ответ на:

  • ввод пользователя;
  • переключение чекбоксов;
  • изменение диапазонов;
  • выбор географической области.

Пример объединения условий:

function updateFilter(type, minPop) {
  map.setFilter("cities", [
    "all",
    ["==", ["get", "type"], type],
    [">=", ["get", "population"], minPop]
  ]);
}

Фильтрация и архитектура приложений

Фильтры в MapLibre GL JS часто становятся основой архитектуры:

  • слой = визуальная категория;
  • фильтр = подкатегория;
  • источник = единый датасет.

Такой подход уменьшает количество источников данных и упрощает управление картой.