Фильтрация данных

Фильтрация данных в Mapbox GL JS является одним из ключевых механизмов управления отображением векторных слоёв, позволяющим динамически контролировать видимость объектов на карте без изменения исходного источника данных.

Основной принцип фильтрации заключается в применении выражений (filter expressions) к слоям карты. Каждый слой может содержать правило filter, которое определяет, какие фичи из источника будут визуализированы. Фильтры применяются на уровне слоя и не изменяют исходный набор данных, что делает их эффективным инструментом для интерактивных приложений.


Фильтр слоя задаётся в объекте слоя и представляет собой массив, где первый элемент — оператор, а последующие — аргументы:

map.addLayer({
  id: 'cities-layer',
  type: 'circle',
  source: 'cities',
  'source-layer': 'cities-data',
  filter: ['==', 'type', 'city']
});

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


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

Фильтры в Mapbox GL JS используют выражения, основанные на логике S-expression. Это обеспечивает гибкость и читаемость.

Операторы сравнения

['==', 'property', value]   // равенство
['!=', 'property', value]   // не равно
['>', 'property', value]    // больше
['>=', 'property', value]   // больше или равно
['<', 'property', value]    // меньше
['<=', 'property', value]   // меньше или равно

Пример фильтрации объектов по числовому полю:

filter: ['>=', 'population', 1000000]

Отображаются только города с населением более одного миллиона.


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

Для построения сложных условий используются логические операторы:

['all', expr1, expr2, ...]   // И
['any', expr1, expr2, ...]   // ИЛИ
['none', expr1, expr2, ...]  // НЕ (ни одно условие)

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

filter: [
  'all',
  ['==', 'type', 'city'],
  ['>=', 'population', 500000]
]

Работа с принадлежностью и массивами

Mapbox GL JS поддерживает проверку вхождения значений в список.

Оператор in

filter: ['in', 'country', 'KZ', 'RU', 'UZ']

Фильтрует объекты, принадлежащие указанным странам.


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

filter: ['has', 'name']

Отображаются только объекты, у которых существует свойство name.


Проверка отсутствия

filter: ['!', ['has', 'population']]

Отбираются объекты без поля population.


Типизация и геометрия

Фильтрация может учитывать тип геометрии:

filter: ['==', '$type', 'Point']

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

  • Point
  • LineString
  • Polygon

Это позволяет разделять отображение различных типов объектов из одного источника.


Изменение фильтров во время выполнения

Одним из наиболее мощных механизмов является динамическое обновление фильтров через метод setFilter.

map.setFilter('cities-layer', ['>=', 'population', 1000000]);

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


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

function updateFilter(minPopulation) {
  map.setFilter('cities-layer', [
    '>=',
    'population',
    minPopulation
  ]);
}

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


Условные выражения и case

Для более сложной логики используется выражение case, позволяющее задавать цепочку условий:

filter: [
  'case',
  ['>=', 'population', 1000000],
  true,
  false
]

Хотя такой подход чаще применяется в стилях, он также может использоваться в фильтрах для формирования логических ветвлений.


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

Строковые фильтры часто применяются для категорий:

filter: ['==', 'category', 'park']

или:

filter: ['in', 'category', 'park', 'forest', 'lake']

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


Регистрозависимость и нормализация данных

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

filter: ['==', ['downcase', ['get', 'name']], 'almaty']

Такой подход позволяет унифицировать сравнение строковых значений.


Фильтрация и масштаб карты

Фильтры могут зависеть от уровня масштаба, но чаще это реализуется через zoom-based styling. Тем не менее, комбинированный подход возможен:

filter: [
  'all',
  ['>=', ['zoom'], 5],
  ['<=', ['zoom'], 10]
]

Хотя чаще масштаб контролируется через minzoom и maxzoom слоя, фильтрация позволяет более тонкую настройку логики отображения.


Работа с GeoJSON источниками

При использовании GeoJSON фильтрация применяется к каждому объекту в источнике:

map.addSource('points', {
  type: 'geojson',
  data: '/data/points.geojson'
});

map.addLayer({
  id: 'points-layer',
  type: 'circle',
  source: 'points',
  filter: ['==', ['get', 'active'], true]
});

Здесь используется выражение ['get', 'active'], позволяющее извлекать свойства объектов.


Комбинированные выражения с вычислениями

Фильтры могут включать арифметические и логические операции:

filter: [
  '>',
  ['+', ['get', 'a'], ['get', 'b']],
  10
]

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


Практика сегментации данных

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

  • разделения категорий объектов (жилые, коммерческие, промышленные)
  • выделения активных/неактивных элементов
  • отображения данных по регионам
  • построения интерактивных аналитических карт

Пример сегментации по типу:

const types = ['school', 'hospital', 'shop'];

types.forEach(type => {
  map.addLayer({
    id: `${type}-layer`,
    type: 'circle',
    source: 'places',
    filter: ['==', 'type', type]
  });
});

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

Фильтрация выполняется на уровне рендеринга WebGL и оптимизирована для работы с большими наборами данных. Однако сложные выражения могут влиять на производительность.

Критические факторы:

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

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

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

Ограничения фильтрации

Фильтры применяются только к данным, уже загруженным в источник. Они не заменяют серверную фильтрацию и не уменьшают объём передаваемых данных.

Также фильтры:

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

Взаимодействие фильтров со стилями

Фильтрация тесно связана со стилями отображения. Часто один и тот же атрибут используется одновременно в фильтре и в стиле:

map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'transport',
  filter: ['==', 'highway', 'primary'],
  paint: {
    'line-width': 2,
    'line-color': '#ff0000'
  }
});

Это обеспечивает согласованное поведение визуализации и логики отбора данных.