Data Filter Extension

Deck.gl содержит систему расширений, предназначенных для модификации поведения слоёв без изменения их базовой реализации. Data Filter Extension относится к классу GPU-ускоренных фильтров, работающих на уровне шейдеров и позволяющих эффективно отбрасывать элементы набора данных до стадии рендеринга. Основная цель расширения заключается в обеспечении высокопроизводительной фильтрации больших массивов пространственных и временных данных с минимальной нагрузкой на CPU.

Data Filter Extension внедряется в pipeline слоя и модифицирует этап подготовки атрибутов и фрагментного/вершинного шейдера. В отличие от классической фильтрации на JavaScript-уровне, где массив данных предварительно обрабатывается через Array.filter, здесь используется передача фильтрующих значений в GPU, где каждая геометрическая сущность получает вычисляемый флаг видимости.

Основной принцип работы:

  • данные передаются в виде атрибутов вершин или инстансов;
  • для каждого элемента вычисляется filterValue;
  • filterValue сравнивается с заданным диапазоном filterRange;
  • результат сравнения определяет discard или render на уровне шейдера.

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

Подключение расширения

Data Filter Extension подключается через механизм extensions слоя:

import {DataFilterExtension} from '@deck.gl/extensions';

const layer = new ScatterplotLayer({
  id: 'points',
  data,
  getPosition: d => d.position,
  getRadius: d => d.radius,
  getFilterValue: d => d.year,
  extensions: [new DataFilterExtension({filterSize: 1})]
});

Параметр filterSize определяет количество независимых измерений фильтрации. При filterSize: 1 используется один диапазон значений, при увеличении до 2–4 появляется возможность многомерной фильтрации (например, время + категория + интенсивность).

Основные параметры Data Filter Extension

getFilterValue

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

getFilterValue: d => d.timestamp

или при многомерной фильтрации:

getFilterValue: d => [d.year, d.severity]

Значения передаются в GPU как атрибуты инстансов.

filterRange

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

filterRange: [2010, 2020]

При многомерности:

filterRange: [
  [2010, 2020],
  [1, 5]
]

Каждое измерение фильтра сопоставляется с соответствующим элементом getFilterValue.

extensions configuration

new DataFilterExtension({
  filterSize: 2,
  fp64: false
})

Параметр fp64 влияет на точность вычислений, особенно при работе с временными рядами или координатами с высокой точностью.

GPU-модель фильтрации

Фильтрация реализуется через добавление логики в vertex shader. Для каждого инстанса вычисляется маска:

  • если значение попадает в диапазон → filter = 1.0
  • иначе → filter = 0.0

В фрагментном шейдере происходит discard:

if (filter < 0.5) {
  discard;
}

Эта схема позволяет GPU полностью исключать пиксели до растеризации, снижая нагрузку на пайплайн.

CPU-фильтрация vs GPU-фильтрация

Классическая фильтрация на CPU:

  • требует пересоздания массива данных;
  • вызывает повторный upload в GPU буферы;
  • плохо масштабируется при интерактивных изменениях.

GPU-фильтрация через Data Filter Extension:

  • изменения filterRange не требуют пересборки data;
  • обновляется только uniform buffer;
  • подходит для интерактивных таймлайнов и слайдеров.

Критическое различие проявляется при объёмах данных от сотен тысяч объектов и выше, где CPU-подход становится узким местом.

Многомерная фильтрация

При filterSize > 1 каждый объект получает вектор фильтрующих значений. Это позволяет реализовать сложные сценарии:

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

Пример:

new DataFilterExtension({filterSize: 3})

getFilterValue: d => [d.year, d.month, d.value]
filterRange: [
  [2015, 2025],
  [1, 12],
  [0, 100]
]

Каждое измерение обрабатывается независимо, а итоговая видимость определяется пересечением условий.

Интеграция с слоями

Data Filter Extension совместим с большинством инстансных слоёв:

  • ScatterplotLayer
  • ColumnLayer
  • ArcLayer
  • LineLayer
  • PathLayer

Во всех случаях фильтрация применяется до стадии геометрической сборки инстансов.

Пример с ColumnLayer:

new ColumnLayer({
  data,
  getPosition: d => d.position,
  getElevation: d => d.value,
  getFilterValue: d => d.category,
  filterRange: [2, 5],
  extensions: [new DataFilterExtension({filterSize: 1})]
});

Поведение при динамическом обновлении

Изменение filterRange вызывает только обновление uniform-параметров:

layer.setProps({
  filterRange: [2018, 2022]
});

Пересборка геометрии не происходит, что критично для интерактивных дашбордов.

Производительность и оптимизация

Ключевые факторы производительности:

1. Размер filterSize

Увеличение размерности фильтра приводит к росту вычислений в шейдере. Практическое ограничение обычно лежит в диапазоне 1–3.

2. Тип данных filterValue

Использование числовых значений предпочтительнее категориальных строк. Категории должны быть заранее закодированы в числа.

3. Минимизация branching в шейдере

Условия фильтрации должны быть линейными, без сложной логики, чтобы избежать деградации GPU pipeline.

4. Батчинг данных

Data Filter Extension наиболее эффективен при больших батчах однородных данных. Разделение на множество мелких слоёв снижает эффективность.

Сценарии использования

Временные фильтры

Отображение событий по диапазону дат без пересборки данных:

filterRange: [startYear, endYear]

Интерактивные панели аналитики

Связка slider UI → filterRange позволяет обновлять визуализацию в реальном времени.

Геопространственная аналитика

Фильтрация объектов по интенсивности, плотности или классификации в больших GIS наборах.

Многослойная аналитика

Несколько слоёв используют одинаковый filterSize, но разные semantics фильтрации, что позволяет синхронизировать поведение визуализации.

Внутреннее представление данных

При компиляции слоя Data Filter Extension добавляет:

  • attribute buffer для filter values;
  • uniforms для filterRange;
  • define macros в shader preprocessor;
  • ветку логики discard в fragment shader.

Эта модификация происходит на уровне layer lifecycle и не требует изменения пользовательского data model.

Ограничения подхода

Несмотря на высокую производительность, существуют ограничения:

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

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

Поведение при комбинировании с другими расширениями

Data Filter Extension может использоваться совместно с:

  • Mask Extension (пространственные маски);
  • Brushing Extension (интерактивное выделение);
  • Aggregation Layers.

При комбинировании порядок применения определяется стадиями shader compilation pipeline, где фильтрация обычно выполняется до агрегации и отрисовки.