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

heatmap-layer.mddocs/

Heatmap Layer

HeatmapLayer visualizes spatial distribution of data as a continuous heatmap using Gaussian kernels. Unlike grid-based layers, it creates smooth color gradients that blend individual data points into a continuous surface, ideal for density visualization and hotspot analysis.

Capabilities

HeatmapLayer Class

Creates a layer that renders data points as a continuous heatmap with smooth color transitions.

/**
 * Visualizes spatial distribution of data as a heatmap
 * @param props - Configuration properties for the heatmap layer
 */
class HeatmapLayer<DataT = any> extends AggregationLayer<DataT, HeatmapLayerProps<DataT>> {
  constructor(props: HeatmapLayerProps<DataT>);
}

interface HeatmapLayerProps<DataT> extends LayerProps {
  /** Array of data objects to visualize */
  data: DataT[];
  /** Function to extract position from data object */
  getPosition: Accessor<DataT, Position>;
  /** Function to extract weight value for heatmap intensity */
  getWeight?: Accessor<DataT, number>;
  /** Radius of influence for each point in pixels */
  radiusPixels?: number;
  /** Intensity multiplier for the heatmap values */
  intensity?: number;
  /** Threshold below which pixels are not drawn (0-1) */
  threshold?: number;
  /** Domain for color scale [min, max] */
  colorDomain?: [number, number] | null;
  /** Array of colors for the gradient */
  colorRange?: Color[];
  /** Aggregation method for overlapping points */
  aggregation?: 'SUM' | 'MEAN';
  /** Size of the internal texture for weight calculation */
  weightsTextureSize?: number;
  /** Debounce timeout for updates in milliseconds */
  debounceTimeout?: number;
  /** Maximum zoom level for texture updates */
  maxZoom?: number;
  /** Minimum zoom level for texture updates */
  minZoom?: number;
  /** Whether the heatmap should update on viewport change */
  updateTriggers?: {
    getPosition?: any;
    getWeight?: any;
  };
}

Usage Examples:

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

// Basic crime hotspot visualization
const crimeHeatmap = new HeatmapLayer({
  id: "crime-heatmap",
  data: crimeIncidents,
  getPosition: (d) => [d.longitude, d.latitude],
  getWeight: (d) => d.severity, // Weight by crime severity
  radiusPixels: 50,
  intensity: 1,
  threshold: 0.03,
  colorRange: [
    [255, 255, 178, 0],     // Transparent yellow
    [254, 217, 118, 85],    // Light orange
    [254, 178, 76, 127],    // Orange
    [253, 141, 60, 170],    // Dark orange
    [240, 59, 32, 212],     // Red-orange
    [189, 0, 38, 255]       // Dark red
  ]
});

// WiFi signal strength heatmap
const wifiHeatmap = new HeatmapLayer({
  id: "wifi-coverage",
  data: signalMeasurements,
  getPosition: (d) => d.coordinates,
  getWeight: (d) => d.signalStrength,
  radiusPixels: 80,
  intensity: 2,
  threshold: 0.05,
  colorDomain: [-100, -30], // dBm range
  colorRange: [
    [255, 0, 0, 200],       // Red for weak signal
    [255, 165, 0, 200],     // Orange
    [255, 255, 0, 200],     // Yellow
    [144, 238, 144, 200],   // Light green
    [0, 255, 0, 200]        // Green for strong signal
  ],
  aggregation: 'MEAN',
  weightsTextureSize: 1024,
  debounceTimeout: 500
});

// Population density with smooth transitions
const populationHeatmap = new HeatmapLayer({
  id: "population-density",
  data: populationCenters,
  getPosition: (d) => [d.lng, d.lat],
  getWeight: (d) => Math.log(d.population), // Log scale for better visualization
  radiusPixels: 100,
  intensity: 0.8,
  threshold: 0.02,
  colorRange: [
    [255, 255, 204, 0],
    [199, 233, 180, 64],
    [127, 205, 187, 128],
    [65, 182, 196, 192],
    [29, 145, 192, 255],
    [34, 94, 168, 255],
    [12, 44, 132, 255]
  ],
  aggregation: 'SUM',
  weightsTextureSize: 2048,
  maxZoom: 15,
  minZoom: 5
});

Visual Configuration

Controls the appearance and rendering of the heatmap.

/** Radius of influence for each point in pixels (default: 50) */
radiusPixels?: number;

/** Intensity multiplier for the heatmap values (default: 1) */
intensity?: number;

/** Threshold below which pixels are not drawn, 0-1 (default: 0.05) */
threshold?: number;

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

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

Data Processing Configuration

Controls how data points are processed and aggregated.

/** Function to extract weight value for heatmap intensity */
getWeight?: Accessor<DataT, number>;

/** Aggregation method for overlapping points (default: 'SUM') */
aggregation?: 'SUM' | 'MEAN';

/** Size of the internal texture for weight calculation (default: 2048) */
weightsTextureSize?: number;

/** Debounce timeout for updates in milliseconds (default: 500) */
debounceTimeout?: number;

Performance Configuration

Controls rendering performance and update behavior.

/** Maximum zoom level for texture updates */
maxZoom?: number;

/** Minimum zoom level for texture updates */
minZoom?: number;

/** Whether the heatmap should update on viewport change */
updateTriggers?: {
  getPosition?: any;
  getWeight?: any;
};

/** Size of the internal texture for weight calculation (default: 2048) */
weightsTextureSize?: number;

Rendering Algorithm

The HeatmapLayer uses a multi-pass rendering approach:

  1. Weight Accumulation: Points are rendered to a texture with Gaussian kernels
  2. Maximum Value Pass: Finds the maximum value across the texture
  3. Color Mapping: Maps normalized values to colors using the specified gradient
  4. Final Render: Applies threshold and blends with the scene
// Internal rendering components (from heatmap-layer implementation)
interface HeatmapRenderingState {
  weightsTexture: Texture;
  maxTexture: Texture;
  colorTexture: Texture;
  aggregationTransform: Transform;
  triangleLayer: TriangleLayer;
}

// Shader components
const weightsVertexShader: string;
const weightsFragmentShader: string;
const maxVertexShader: string;
const maxFragmentShader: string;

Performance Considerations

  • Radius Size: Larger radii create smoother gradients but require more computation
  • Texture Size: Larger weightsTextureSize provides higher resolution but uses more GPU memory
  • Debouncing: Higher debounceTimeout reduces updates during interaction but may feel less responsive
  • Zoom Limits: Setting minZoom and maxZoom can improve performance by avoiding unnecessary updates
  • Data Size: Performance degrades with datasets over 1M points; consider data filtering or clustering

Common Patterns

// Real-time sensor data heatmap
const sensorHeatmap = new HeatmapLayer({
  id: "sensors",
  data: sensorReadings,
  getPosition: (d) => d.location,
  getWeight: (d) => d.temperature,
  radiusPixels: 60,
  intensity: 1.2,
  threshold: 0.01,
  colorRange: [
    [0, 0, 255, 100],       // Blue for cold
    [0, 255, 255, 150],     // Cyan
    [0, 255, 0, 200],       // Green
    [255, 255, 0, 250],     // Yellow for hot
    [255, 0, 0, 255]        // Red for very hot
  ],
  aggregation: 'MEAN',
  debounceTimeout: 100, // Quick updates for real-time data
  weightsTextureSize: 512 // Smaller texture for performance
});

// Customer foot traffic analysis
const footTrafficHeatmap = new HeatmapLayer({
  id: "foot-traffic", 
  data: customerVisits,
  getPosition: (d) => [d.x, d.y], // Store coordinates
  getWeight: (d) => d.duration, // Weight by visit duration
  radiusPixels: 30, // Small radius for detailed view
  intensity: 2,
  threshold: 0.05,
  colorDomain: [0, 3600], // 0 to 1 hour in seconds
  colorRange: [
    [255, 255, 255, 0],     // Transparent for no activity
    [255, 255, 0, 100],     // Yellow for light activity
    [255, 165, 0, 150],     // Orange for medium activity
    [255, 0, 0, 200]        // Red for high activity
  ],
  aggregation: 'SUM'
});

// Environmental monitoring with temporal data
const environmentalHeatmap = new HeatmapLayer({
  id: "pollution",
  data: airQualityReadings.filter(d => d.timestamp > Date.now() - 86400000), // Last 24 hours
  getPosition: (d) => [d.longitude, d.latitude],
  getWeight: (d) => d.pm25 * d.reliability, // Weight by PM2.5 and sensor reliability
  radiusPixels: 120,
  intensity: 0.7,
  threshold: 0.02,
  colorRange: [
    [0, 255, 0, 100],       // Green for good air quality
    [255, 255, 0, 150],     // Yellow for moderate
    [255, 165, 0, 200],     // Orange for unhealthy for sensitive
    [255, 0, 0, 250],       // Red for unhealthy
    [128, 0, 128, 255]      // Purple for very unhealthy
  ],
  aggregation: 'MEAN',
  weightsTextureSize: 1024,
  debounceTimeout: 1000,
  updateTriggers: {
    getWeight: [Date.now()] // Update when time changes
  }
});

Integration with Other Layers

Heatmap layers work well as base layers with other overlay layers:

const layerStack = [
  // Base heatmap
  new HeatmapLayer({
    id: "temperature-heatmap",
    data: temperatureData,
    getPosition: (d) => [d.lon, d.lat],
    getWeight: (d) => d.temp,
    radiusPixels: 80,
    colorRange: temperatureColors
  }),
  
  // Overlay with actual sensor locations
  new ScatterplotLayer({
    id: "sensor-locations",
    data: sensors,
    getPosition: (d) => [d.lon, d.lat],
    getRadius: 100,
    getFillColor: [255, 255, 255, 200],
    getLineColor: [0, 0, 0, 255],
    lineWidthMinPixels: 2,
    pickable: true
  }),
  
  // Text labels for major readings
  new TextLayer({
    id: "temperature-labels",
    data: sensors.filter(d => d.temp > 35), // Only show high temperatures
    getPosition: (d) => [d.lon, d.lat],
    getText: (d) => `${d.temp}°C`,
    getSize: 12,
    getColor: [255, 255, 255],
    getAngle: 0,
    getTextAnchor: 'middle',
    getAlignmentBaseline: 'center'
  })
];

Comparison with Grid-Based Layers

FeatureHeatmapLayerGridLayer/HexagonLayer
Visual StyleSmooth, continuousDiscrete bins
PerformanceLower for large datasetsBetter for large datasets
PrecisionPixel-levelCell-level
Memory UsageHigher (textures)Lower (geometry)
Use CasesDensity visualization, hotspotsStatistical analysis, binning
InteractionLimited (continuous surface)Rich (individual bins)