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.
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
});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;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;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;The HeatmapLayer uses a multi-pass rendering approach:
// 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;weightsTextureSize provides higher resolution but uses more GPU memorydebounceTimeout reduces updates during interaction but may feel less responsiveminZoom and maxZoom can improve performance by avoiding unnecessary updates// 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
}
});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'
})
];| Feature | HeatmapLayer | GridLayer/HexagonLayer |
|---|---|---|
| Visual Style | Smooth, continuous | Discrete bins |
| Performance | Lower for large datasets | Better for large datasets |
| Precision | Pixel-level | Cell-level |
| Memory Usage | Higher (textures) | Lower (geometry) |
| Use Cases | Density visualization, hotspots | Statistical analysis, binning |
| Interaction | Limited (continuous surface) | Rich (individual bins) |