or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aggregation-systems.mdcontour-layer.mdgrid-layer.mdheatmap-layer.mdhexagon-layer.mdindex.mdscreen-grid-layer.md
tile.json

grid-layer.mddocs/

Grid Layer

GridLayer aggregates data into a regular grid-based heatmap using square cells. It provides similar functionality to HexagonLayer but with rectangular binning, making it ideal for aligned geographic data visualization and cases where regular grid patterns are preferred.

Capabilities

GridLayer Class

Creates a layer that aggregates data points into rectangular grid cells with configurable cell size and optional 3D extrusion.

/**
 * Aggregate data into a grid-based heatmap
 * @param props - Configuration properties for the grid layer
 */
class GridLayer<DataT = any> extends AggregationLayer<DataT, GridLayerProps<DataT>> {
  constructor(props: GridLayerProps<DataT>);
}

interface GridLayerProps<DataT> extends LayerProps {
  /** Array of data objects to aggregate */
  data: DataT[];
  /** Function to extract position from data object (default: d => d.position) */
  getPosition?: Accessor<DataT, Position>;
  /** Function to extract weight value for color aggregation (default: 1) */
  getColorWeight?: Accessor<DataT, number>;
  /** Function to extract weight value for elevation aggregation (default: 1) */
  getElevationWeight?: Accessor<DataT, number>;
  /** After data objects are aggregated into cells, this accessor is called on each cell to get the value that its color is based on. Not supported by GPU aggregation. */
  getColorValue?: AggregateAccessor<DataT> | null;
  /** After data objects are aggregated into cells, this accessor is called on each cell to get the value that its elevation is based on. Not supported by GPU aggregation. */
  getElevationValue?: AggregateAccessor<DataT> | null;
  /** Size of grid cells in meters (default: 1000) */
  cellSize?: number;
  /** Cell size multiplier (0-1) (default: 1) */
  coverage?: number;
  /** Whether to extrude grid cells in 3D (default: false) */
  extruded?: boolean;
  /** Cell elevation multiplier (default: 1) */
  elevationScale?: number;
  /** Aggregation operation for color values (default: 'SUM') */
  colorAggregation?: AggregationOperation;
  /** Aggregation operation for elevation values (default: 'SUM') */
  elevationAggregation?: AggregationOperation;
  /** Array of colors for the color scale (default: 6-class YlOrRd) */
  colorRange?: Color[];
  /** Color scale domain, default is set to the extent of aggregated weights in each cell */
  colorDomain?: [number, number] | null;
  /** Scaling function used to determine the color of the grid cell (default: 'quantize') */
  colorScaleType?: 'quantize' | 'linear' | 'quantile' | 'ordinal';
  /** Elevation scale output range (default: [0, 1000]) */
  elevationRange?: [number, number];
  /** Elevation scale input domain, default is set to between 0 and the max of aggregated weights in each cell */
  elevationDomain?: [number, number] | null;
  /** Scaling function used to determine the elevation of the grid cell (default: 'linear') */
  elevationScaleType?: 'linear' | 'quantile';
  /** Whether to use GPU aggregation (default: true) */
  gpuAggregation?: boolean;
  /** Filter cells and re-calculate color by lowerPercentile (0-100) (default: 0) */
  lowerPercentile?: number;
  /** Filter cells and re-calculate color by upperPercentile (0-100) (default: 100) */
  upperPercentile?: number;
  /** Filter cells and re-calculate elevation by elevationLowerPercentile (0-100) (default: 0) */
  elevationLowerPercentile?: number;
  /** Filter cells and re-calculate elevation by elevationUpperPercentile (0-100) (default: 100) */
  elevationUpperPercentile?: number;
  /** Custom accessor to retrieve a grid bin index from each data object. Not supported by GPU aggregation. */
  gridAggregator?: ((position: number[], cellSize: number) => [number, number]) | null;
  /** Material settings for lighting effect. Applies if extruded: true */
  material?: Material;
  /** This callback will be called when bin color domain has been calculated */
  onSetColorDomain?: (minMax: [number, number]) => void;
  /** This callback will be called when bin elevation domain has been calculated */
  onSetElevationDomain?: (minMax: [number, number]) => void;
}

interface GridLayerPickingInfo<DataT = any> extends PickingInfo<DataT> {
  /** The aggregated bin data */
  object: AggregatedBin | null;
  /** Grid cell coordinates [col, row] */
  index: [number, number];
  /** Total count of points in the bin */
  count: number;
  /** Aggregated color value for the bin */
  colorValue?: number;
  /** Aggregated elevation value for the bin */
  elevationValue?: number;
  /** Array indices of points in this bin (CPU aggregation only) */
  pointIndices?: number[];
}

Usage Examples:

import { GridLayer } from "@deck.gl/aggregation-layers";

// Basic grid layer for retail locations
const retailLayer = new GridLayer({
  id: "retail-grid",
  data: storeData,
  getPosition: (d) => [d.longitude, d.latitude],
  getColorWeight: (d) => d.revenue,
  getElevationWeight: (d) => d.customers,
  cellSize: 2000, // 2km grid cells
  extruded: true,
  elevationScale: 5,
  colorRange: [
    [65, 182, 196],
    [127, 205, 187],
    [199, 233, 180],
    [237, 248, 177],
    [255, 255, 204]
  ],
  pickable: true
});

// Urban analysis with separate metrics
const urbanLayer = new GridLayer({
  id: "urban-analysis",
  data: urbanData,
  getPosition: (d) => d.location,
  getColorWeight: (d) => d.buildingDensity,
  getElevationWeight: (d) => d.populationDensity,
  cellSize: 1000, // 1km cells
  coverage: 0.9,
  extruded: true,
  elevationScale: 8,
  colorAggregation: 'MEAN',
  elevationAggregation: 'MEAN',
  colorRange: [
    [255, 255, 178, 200],
    [254, 204, 92, 200],
    [253, 141, 60, 200],
    [240, 59, 32, 200],
    [189, 0, 38, 200]
  ],
  elevationRange: [0, 200],
  colorScaleType: 'quantize',
  elevationScaleType: 'linear',
  gpuAggregation: true,
  pickable: true,
  onHover: (info) => {
    if (info.object) {
      console.log(`Cell [${info.index[0]}, ${info.index[1]}]: Buildings: ${info.colorValue}, Population: ${info.elevationValue}`);
    }
  }
});

Grid Configuration

Controls the size and appearance of grid cells.

/** Size of grid cells in meters (default: 1000) */
cellSize?: number;

/** Cell size multiplier, 0-1 where 1 = full size (default: 1) */
coverage?: number;

/** Whether to extrude grid cells in 3D (default: false) */
extruded?: boolean;

/** Custom grid aggregator function for advanced binning */
gridAggregator?: ((position: number[], cellSize: number) => [number, number]) | null;

Color Configuration

Controls color mapping for grid cells based on aggregated values.

/** Function to extract weight value for color aggregation */
getColorWeight?: Accessor<DataT, number>;

/** Aggregation operation for color values (default: 'SUM') */
colorAggregation?: AggregationOperation;

/** Array of colors for the color scale */
colorRange?: Color[];

/** Domain for color scale [min, max] */
colorDomain?: [number, number] | null;

/** Scaling function used to determine the color of the grid cell (default: 'quantize') */
colorScaleType?: 'quantize' | 'linear' | 'quantile' | 'ordinal';

/** Filter cells and re-calculate color by lowerPercentile (0-100) (default: 0) */
lowerPercentile?: number;

/** Filter cells and re-calculate color by upperPercentile (0-100) (default: 100) */
upperPercentile?: number;

Elevation Configuration

Controls 3D height of grid cells when extruded is true.

/** Function to extract weight value for elevation aggregation */
getElevationWeight?: Accessor<DataT, number>;

/** Aggregation operation for elevation values (default: 'SUM') */
elevationAggregation?: AggregationOperation;

/** Range of elevation values [min, max] (default: [0, 1000]) */
elevationRange?: [number, number];

/** Domain for elevation scale [min, max] */
elevationDomain?: [number, number] | null;

/** Scale factor for elevation values (default: 1) */
elevationScale?: number;

/** Scaling function used to determine the elevation of the grid cell (default: 'linear') */
elevationScaleType?: 'linear' | 'quantile';

/** Filter cells and re-calculate elevation by elevationLowerPercentile (0-100) (default: 0) */
elevationLowerPercentile?: number;

/** Filter cells and re-calculate elevation by elevationUpperPercentile (0-100) (default: 100) */
elevationUpperPercentile?: number;

/** Material properties for 3D rendering */
material?: Material;

Grid Binning Algorithm

The layer uses a rectangular grid binning algorithm that:

  • Maps geographic coordinates to grid cell coordinates
  • Aligns cells to a regular grid pattern
  • Supports custom aggregator functions for specialized use cases
  • Optimizes for both CPU and GPU computation
// Default grid binning function
function defaultGridAggregator(position: [number, number], cellSize: number): [number, number] {
  const col = Math.floor(position[0] / cellSize);
  const row = Math.floor(position[1] / cellSize);
  return [col, row];
}

Performance Considerations

  • Cell Size: Smaller cells create more bins and require more computation
  • GPU vs CPU: GPU aggregation recommended for datasets with >100K points
  • Extrusion: 3D rendering has higher GPU memory requirements
  • Coverage: Lower coverage values reduce overlapping geometry and improve performance

Common Patterns

// Real estate price analysis
const realEstateLayer = new GridLayer({
  id: "real-estate",
  data: propertyData,
  getPosition: (d) => [d.lng, d.lat],
  getColorWeight: (d) => d.price,
  getElevationWeight: (d) => d.squareFootage,
  cellSize: 5000, // 5km cells for neighborhood analysis
  extruded: true,
  elevationScale: 0.1, // Scale down for building heights
  colorAggregation: 'MEAN', // Average price per area
  elevationAggregation: 'MEAN', // Average size per area
  colorRange: [
    [0, 128, 0],     // Green for lower prices
    [255, 255, 0],   // Yellow for medium
    [255, 0, 0]      // Red for high prices
  ]
});

// Environmental monitoring
const environmentalLayer = new GridLayer({
  id: "environmental",
  data: sensorData,
  getPosition: (d) => d.coordinates,
  getColorWeight: (d) => d.airQuality,
  getElevationWeight: (d) => d.noiseLevel,
  cellSize: 500, // 500m fine-grained analysis
  coverage: 0.8,
  extruded: true,
  elevationScale: 20,
  colorAggregation: 'MEAN',
  elevationAggregation: 'MAX', // Peak noise levels
  colorDomain: [0, 100], // Air quality index
  elevationDomain: [0, 80], // Decibel levels
  gpuAggregation: true
});

// Transportation flow analysis
const transportLayer = new GridLayer({
  id: "transport",
  data: vehicleData,
  getPosition: (d) => d.currentLocation,
  getColorWeight: (d) => d.speed,
  getElevationWeight: (d) => 1, // Count vehicles
  cellSize: 200, // 200m for traffic analysis
  extruded: true,
  colorAggregation: 'MEAN', // Average speed
  elevationAggregation: 'COUNT', // Vehicle count
  colorRange: [
    [255, 0, 0],     // Red for slow traffic
    [255, 255, 0],   // Yellow for medium
    [0, 255, 0]      // Green for fast traffic
  ],
  elevationScale: 10
});

Grid vs Hexagon Comparison

FeatureGridLayerHexagonLayer
Cell ShapeSquare/RectangleHexagon
AlignmentGrid-alignedNatural clustering
Edge EffectsMore pronouncedMinimized
ComputationSlightly fasterMore complex
Visual AppealRegular/structuredOrganic/natural
Use CasesUrban planning, aligned dataPopulation studies, natural phenomena