or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

brushing.mddata-filtering.mdgeometry-operations.mdindex.mdlegacy-experimental.mdstyling.md
tile.json

data-filtering.mddocs/

Data Filtering

Advanced GPU-based data filtering system that allows layers to show/hide objects based on numeric values and categorical properties, with support for soft filtering transitions and real-time filtering counts.

Capabilities

DataFilterExtension

Adds GPU-based data filtering functionalities to layers, enabling show/hide of objects based on user-defined numeric and categorical properties.

/**
 * Adds GPU-based data filtering functionalities to layers
 * Allows layers to show/hide objects based on user-defined properties
 */
class DataFilterExtension extends LayerExtension<Required<DataFilterExtensionOptions>> {
  static extensionName: 'DataFilterExtension';
  static defaultProps: DataFilterExtensionDefaultProps;
  
  constructor(opts?: DataFilterExtensionOptions);
  
  getShaders(extension: this): any;
  initializeState(context: LayerContext, extension: this): void;
  updateState(params: UpdateParameters, extension: this): void;
  draw(params: any, extension: this): void;
  finalizeState(): void;
}

interface DataFilterExtensionProps<DataT = any> {
  /**
   * Accessor to retrieve the value for each object that it will be filtered by.
   * Returns either a number (if filterSize: 1) or an array of numbers.
   * @default {type: 'accessor', value: 0}
   */
  getFilterValue?: Accessor<DataT, number | number[]>;
  
  /**
   * Accessor to retrieve the category (number | string) for each object that it will be filtered by.
   * Returns either a single category (if categorySize: 1) or an array of categories.
   * @default {type: 'accessor', value: 0}
   */
  getFilterCategory?: Accessor<DataT, FilterCategory | FilterCategory[]>;
  
  /**
   * Enable/disable the data filter. If disabled, all objects are rendered.
   * @default true
   */
  filterEnabled?: boolean;
  
  /**
   * The [min, max] bounds which defines whether an object should be rendered.
   * If an object's filtered value is within the bounds, the object will be rendered.
   * Can be a single range or array of ranges for multi-dimensional filtering.
   * @default [-1, 1]
   */
  filterRange?: [number, number] | [number, number][];
  
  /**
   * If specified, objects will be faded in/out instead of abruptly shown/hidden.
   * When the filtered value is outside of filterSoftRange but within filterRange,
   * the object will be rendered as "faded."
   * @default null
   */
  filterSoftRange?: [number, number] | [number, number][] | null;
  
  /**
   * When an object is "faded", manipulate its size so that it appears smaller or thinner.
   * Only works if filterSoftRange is specified.
   * @default true
   */
  filterTransformSize?: boolean;
  
  /**
   * When an object is "faded", manipulate its opacity so that it appears more translucent.
   * Only works if filterSoftRange is specified.
   * @default true
   */
  filterTransformColor?: boolean;
  
  /**
   * The categories which define whether an object should be rendered.
   * Can be a single array or array of arrays for multi-dimensional category filtering.
   * @default [0]
   */
  filterCategories?: FilterCategory[] | FilterCategory[][];
  
  /**
   * Callback fired when the number of filtered objects changes.
   * Only called if the countItems option is enabled.
   */
  onFilteredItemsChange?: (evt: {
    /** The id of the source layer */
    id: string;
    /** The number of data objects that pass the filter */
    count: number;
  }) => void;
}

interface DataFilterExtensionOptions {
  /**
   * The size of the category filter (number of columns to filter by).
   * The category filter can show/hide data based on 1-4 properties of each object.
   * Set to 0 to disable category filtering.
   * @default 0
   */
  categorySize?: 0 | 1 | 2 | 3 | 4;
  
  /**
   * The size of the filter (number of columns to filter by).
   * The data filter can show/hide data based on 1-4 numeric properties of each object.
   * Set to 0 to disable numeric filtering.
   * @default 1
   */
  filterSize?: 0 | 1 | 2 | 3 | 4;
  
  /**
   * Use 64-bit precision instead of 32-bit for numeric filtering.
   * @default false
   */
  fp64?: boolean;
  
  /**
   * If true, reports the number of filtered objects with the onFilteredItemsChange callback.
   * @default false
   */
  countItems?: boolean;
}

interface DataFilterExtensionDefaultProps {
  getFilterValue: {type: 'accessor', value: number};
  getFilterCategory: {type: 'accessor', value: number};
  onFilteredItemsChange: {type: 'function', value: null, optional: boolean};
  filterEnabled: boolean;
  filterRange: [number, number];
  filterSoftRange: null;
  filterCategories: number[];
  filterTransformSize: boolean;
  filterTransformColor: boolean;
}

type FilterCategory = number | string;

Usage Examples:

import { ScatterplotLayer } from "@deck.gl/layers";
import { DataFilterExtension } from "@deck.gl/extensions";

// Basic numeric filtering
const numericFilterLayer = new ScatterplotLayer({
  id: "filtered-points",
  data: dataPoints,
  extensions: [new DataFilterExtension({ filterSize: 1 })],
  getPosition: d => d.coordinates,
  getFilterValue: d => d.temperature, // Filter by temperature
  filterRange: [20, 30], // Show only temperatures between 20-30
  filterEnabled: true
});

// Multi-dimensional numeric filtering
const multiFilterLayer = new ScatterplotLayer({
  id: "multi-filtered",
  data: dataPoints,
  extensions: [new DataFilterExtension({ filterSize: 2 })],
  getPosition: d => d.coordinates,
  getFilterValue: d => [d.temperature, d.humidity], // Filter by both
  filterRange: [[20, 30], [40, 80]], // Temperature 20-30, humidity 40-80
  filterEnabled: true
});

// Soft filtering with fade transitions
const softFilterLayer = new ScatterplotLayer({
  id: "soft-filtered",
  data: dataPoints,
  extensions: [new DataFilterExtension({ filterSize: 1 })],
  getPosition: d => d.coordinates,
  getFilterValue: d => d.score,
  filterRange: [0, 100], // Hard bounds
  filterSoftRange: [20, 80], // Objects outside this range are faded
  filterTransformSize: true, // Fade by making smaller
  filterTransformColor: true, // Fade by reducing opacity
  filterEnabled: true
});

// Category filtering
const categoryFilterLayer = new ScatterplotLayer({
  id: "category-filtered",
  data: dataPoints,
  extensions: [new DataFilterExtension({ categorySize: 1 })],
  getPosition: d => d.coordinates,
  getFilterCategory: d => d.type, // Filter by category
  filterCategories: ["typeA", "typeC"], // Show only these categories
  filterEnabled: true
});

// Combined numeric and category filtering with counting
const combinedFilterLayer = new ScatterplotLayer({
  id: "combined-filtered",
  data: dataPoints,
  extensions: [new DataFilterExtension({ 
    filterSize: 1, 
    categorySize: 1, 
    countItems: true 
  })],
  getPosition: d => d.coordinates,
  getFilterValue: d => d.value,
  getFilterCategory: d => d.status,
  filterRange: [0, 50],
  filterCategories: ["active", "pending"],
  onFilteredItemsChange: ({id, count}) => {
    console.log(`Layer ${id} has ${count} visible items`);
  },
  filterEnabled: true
});

Advanced Filtering Features

Multi-dimensional Filtering

// Filter by up to 4 numeric properties simultaneously
const advancedFilter = new DataFilterExtension({ filterSize: 4 });

const layer = new ScatterplotLayer({
  extensions: [advancedFilter],
  getFilterValue: d => [d.temp, d.humidity, d.pressure, d.windSpeed],
  filterRange: [[20, 30], [40, 80], [1000, 1020], [0, 15]]
});

High-Precision Filtering

// Use 64-bit precision for high-precision numeric filtering
const precisionFilter = new DataFilterExtension({ 
  filterSize: 1, 
  fp64: true 
});

const layer = new ScatterplotLayer({
  extensions: [precisionFilter],
  getFilterValue: d => d.preciseValue, // High-precision numeric value
  filterRange: [1000000.1, 1000000.9] // Small range requiring precision
});

Real-time Filter Counting

// Track the number of visible objects in real-time
const countingFilter = new DataFilterExtension({ 
  filterSize: 1, 
  countItems: true 
});

const layer = new ScatterplotLayer({
  extensions: [countingFilter],
  getFilterValue: d => d.value,
  filterRange: [0, 100],
  onFilteredItemsChange: ({id, count}) => {
    updateUI(`${count} items visible`);
  }
});

Performance Considerations

  • All filtering operations are performed on the GPU for maximum performance
  • Supports millions of data objects with real-time filtering
  • Soft filtering adds minimal performance overhead
  • Category filtering uses bit masking for efficient GPU operations
  • Filter counting requires additional GPU-CPU readback (use sparingly)

Shader Integration

The extension provides two shader module variants:

  • dataFilter: Standard 32-bit precision filtering
  • dataFilter64: High-precision 64-bit filtering

Both modules support:

  • Multi-dimensional numeric filtering (up to 4 dimensions)
  • Multi-category filtering (up to 4 categories)
  • Soft filtering with size and color transformations
  • Efficient bit-mask based category filtering

Compatibility

  • Works with all deck.gl layers that support extensions
  • Can be combined with other extensions (BrushingExtension, etc.)
  • Supports both WebGL and WebGPU rendering backends
  • Particularly effective with large datasets requiring real-time filtering