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

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


Основы фильтрации слоя

Каждый слой в стиле MapLibre GL JS может содержать поле filter, которое представляет собой выражение в формате JSON-массива. Это выражение проверяется для каждой геометрии, поступающей из источника (vector tile или GeoJSON).

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

map.addLayer({
  id: 'cities',
  type: 'circle',
  source: 'points',
  paint: {
    'circle-radius': 6,
    'circle-color': '#1978c8'
  },
  filter: ['==', ['get', 'type'], 'city']
});

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


Метод setFilter

Фильтрацию слоя можно изменять динамически без пересоздания слоя. Для этого используется метод setFilter.

map.setFilter('cities', ['==', ['get', 'type'], 'town']);

При вызове:

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

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

map.setFilter('cities', null);

Логические операторы фильтрации

MapLibre GL JS использует выражения, схожие с Mapbox Style Specification. Основные операторы:

Сравнение

['==', value1, value2]
['!=', value1, value2]
['>', value1, value2]
['>=', value1, value2]
['<', value1, value2]
['<=', value1, value2]

Пример:

filter: ['>=', ['get', 'population'], 100000]

Логические комбинации

AND

['all',
  ['==', ['get', 'type'], 'city'],
  ['>=', ['get', 'population'], 500000]
]

OR

['any',
  ['==', ['get', 'type'], 'city'],
  ['==', ['get', 'type'], 'capital']
]

NOT

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

Работа с массивами и строками

Проверка вхождения

['in', 'capital', ['get', 'tags']]

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

['has', 'population']

Фильтрация по диапазонам и категориям

Часто используется комбинированная логика для сегментации данных:

map.setFilter('cities', [
  'all',
  ['>=', ['get', 'population'], 100000],
  ['<=', ['get', 'population'], 1000000],
  ['==', ['get', 'country'], 'Kazakhstan']
]);

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


Динамическая фильтрация через UI

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

function applyFilters(type) {
  if (type === 'all') {
    map.setFilter('cities', null);
    return;
  }

  map.setFilter('cities', [
    '==',
    ['get', 'type'],
    type
  ]);
}

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


Масштаб-зависимая фильтрация

Фильтрация может учитывать текущий zoom через выражение zoom().

map.setFilter('cities', [
  '>=',
  ['zoom'],
  6
]);

Более сложный пример:

map.setFilter('cities', [
  'any',
  ['<', ['zoom'], 5],
  ['>', ['get', 'population'], 500000]
]);

Фильтрация через свойства feature-state

Feature-state позволяет хранить временные состояния объектов без изменения исходных данных.

map.setFeatureState(
  { source: 'points', id: 123 },
  { selected: true }
);

Фильтр:

map.setFilter('cities', [
  '==',
  ['feature-state', 'selected'],
  true
]);

Такой подход используется для интерактивного выделения объектов.


Разница между filter и queryRenderedFeatures

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

const features = map.queryRenderedFeatures({
  layers: ['cities'],
  filter: ['>', ['get', 'population'], 100000]
});

Однако важно различать:

  • filter — управляет отображением слоя
  • queryRenderedFeatures — выбирает уже отрисованные элементы

Производительность фильтрации

Фильтры в MapLibre GL JS вычисляются для каждой фичи в GPU-ускоренном пайплайне, но сложные выражения могут влиять на производительность.

Факторы, влияющие на скорость:

  • глубина вложенности выражений
  • количество логических операторов any/all
  • использование in на больших массивах
  • частые вызовы setFilter

Рекомендуется:

  • минимизировать пересчёт фильтров
  • избегать избыточных условий
  • выносить часть логики в preprocessing GeoJSON

Типовые сценарии фильтрации

Фильтр по категории

map.setFilter('places', [
  '==',
  ['get', 'category'],
  'restaurant'
]);

Фильтр по нескольким категориям

map.setFilter('places', [
  'in',
  ['get', 'category'],
  'restaurant',
  'cafe',
  'bar'
]);

Переключение слоёв данных

const filters = {
  traffic: ['==', ['get', 'type'], 'traffic'],
  transit: ['==', ['get', 'type'], 'transit']
};

map.setFilter('transport', filters['traffic']);

Сложная бизнес-логика

map.setFilter('points', [
  'all',
  ['any',
    ['==', ['get', 'status'], 'active'],
    ['==', ['get', 'status'], 'pending']
  ],
  ['>=', ['get', 'rating'], 4],
  ['!', ['==', ['get', 'archived'], true]]
]);

Особенности работы с GeoJSON источниками

При использовании GeoJSON фильтрация происходит на уровне слоя, но данные остаются в источнике. Это означает:

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

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

Фильтрация часто используется вместе с динамическими стилями:

map.setPaintProperty('cities', 'circle-color', [
  'case',
  ['==', ['get', 'type'], 'capital'],
  '#ff0000',
  '#1978c8'
]);

Таким образом фильтр определяет видимость, а стили — визуальное представление.


Итоговые паттерны применения

Фильтрация в MapLibre GL JS формирует основу интерактивной работы с картографическими данными:

  • разделение данных по слоям через filter
  • динамическое управление через setFilter
  • сложные логические выражения
  • интеграция с UI-состояниями
  • масштаб-зависимая логика отображения
  • взаимодействие с feature-state для интерактивности