CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-deck-gl--extensions

Plug-and-play extension system providing advanced functionalities for deck.gl layers including brushing, filtering, styling, clipping, collision detection, masking, and terrain rendering capabilities.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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

Install with Tessl CLI

npx tessl i tessl/npm-deck-gl--extensions

docs

brushing.md

data-filtering.md

geometry-operations.md

index.md

legacy-experimental.md

styling.md

tile.json