Some visualizations animate, pulse, and flash. If you're sensitive to motion or flashing, turn on Reduce motion in your system settings.

poopdeck.gl
deck.gl Layers

AnimatedColumnLayer

The AnimatedColumnLayer renders extruded 3D columns at point features — a bar chart laid over the map, where each column's height comes from a numeric property. It draws through deck.gl's ColumnLayer (@deck.gl/layers), one binary sublayer per tile, animated window-mode by the shared TimeFilterExtension.

It extends SpatioTemporalLayer and is instanced at points exactly like AnimatedPointLayer: the elevation column is baked into a per-feature getElevation attribute (zero-copy), NOT per-vertex. Any point archive with a numeric column works — no rebuild needed.

Installation#

import { AnimatedColumnLayer } from '@poopdeck.gl/layers';

Usage#

const layer = new AnimatedColumnLayer({
id: 'quake-columns',
data: '/data/earthquakes-v2/manifest.json',
currentTime,
timeWindow: 30 * 24 * 3600 * 1000,
elevation: 'magnitude', // numeric column → column height
elevationScale: 12000, // metres per unit
radius: 6000, // metres
fillColor: [251, 106, 74, 220],
});

Properties#

Inherits all properties from SpatioTemporalLayer.

PropertyTypeDefaultDescription
elevation / getElevationnumber | string1000Column height — constant or a numeric property-column name.
elevationScalenumber1Multiplier on every elevation (e.g. metres per unit).
radiusnumber100Column disk radius, in radiusUnits. Diverges from deck.gl's ColumnLayer default of 1000 — see Deliberate default drift.
radiusUnits'meters' | 'pixels' | 'common''meters'Radius units.
diskResolutionnumber20Number of sides on the column cross-section.
extrudedbooleantrue3D extrusion.
fillColor / getFillColorColor | string[255,140,0,255]Fill — constant RGBA or a categorical property-column name. See Categorical fill and lighting for where the palette is resolved.
colorPaletteColor[]10-stopPalette for a categorical fillColor column (by first-seen category index).
colorMappingRecord<string, Color> | nullnullExplicit category-string → color map. Keeps a category's color stable across tiles whose dictionaries differ in order or subset, which the bare colorPalette cannot. Always CPU-expands (a category string can't hash to a palette slot on the GPU).
colorMappingDefaultColor[0, 0, 0, 0]Fallback for categories absent from colorMapping (transparent by default).
wireframebooleanfalseDraw the column edges as a wireframe — ColumnLayer pass-through.
filledbooleantrueFill the column disks/extrusions — ColumnLayer pass-through.
strokedbooleanfalseDraw an outline stroke on each column's disk — ColumnLayer pass-through. Gates the lineColor / lineWidth / lineWidth* props below.
flatShadingbooleanfalseFlat rather than smooth shading on the extrusion — ColumnLayer pass-through.
anglenumber0Disk rotation (degrees, counter-clockwise) — ColumnLayer pass-through.
verticesPosition[] | nullnullCustom disk cross-section replacing the regular polygon — ColumnLayer pass-through.
offset[number, number][0, 0]Disk offset from the anchor, in radius multiples — ColumnLayer pass-through.
coveragenumber1Radius multiplier [0, 1] shrinking each column within its footprint.
lineColor / getLineColorColor[0,0,0,255]Outline stroke color (constant only) — drawn when stroked is true.
lineWidth / getLineWidthnumber1Outline stroke width (constant only), in lineWidthUnits.
lineWidthUnits'meters' | 'pixels' | 'common''meters'Outline stroke width units.
lineWidthScalenumber1Outline width multiplier — applies when stroked is true.
lineWidthMinPixelsnumber0Minimum outline width in pixels — applies when stroked is true.
lineWidthMaxPixelsnumberNumber.MAX_SAFE_INTEGERMaximum outline width in pixels — applies when stroked is true.
materialMaterialtrueLighting material for the extrusion.
fadeInDuration / fadeOutDurationnumber300Window fade ramps (ms).
reducedMotionbooleanfalseHonor prefers-reduced-motion: forces the inherited timeHeightScale space-time-cube lift to 0 so the columns stay ground-anchored. Time playback and fades are unaffected.
filterPropertystring | nullnullName of a baked numeric column for a GPU range filter (STTDataFilterExtension). Accessor-alias of deck's getFilterValue — a column NAME, not a function. Unset ⇒ the extension is not installed at all.
filterRange[number, number] | nullnullInclusive [min, max] bounds for filterProperty. null idles the filter (renders all) while keeping the column bound. Note this differs from upstream deck's [-1, 1] default.
filterSoftRange[number, number] | nullnullOptional soft [min, max] for a fade instead of a hard clip.
filterEnabledbooleantrueEnable/disable the filter without dropping the bound attribute.

Deliberate default drift#

radius defaults to 100, where deck.gl's own ColumnLayer defaults to 1000. The STT default is sized for the point archives this layer renders (a metro-scale bar chart, not a continental hex grid). Porting a deck config that relied on the upstream default therefore yields columns 10× narrower — pass radius explicitly. This is the only value in the column surface that diverges from upstream.

Categorical fill and lighting#

Where a categorical fillColor palette is resolved depends on extruded:

  • extruded: true (the default) — the palette is expanded on the CPU into a per-feature RGBA getFillColor buffer.
  • extruded: false (flat disks) — the palette lifts to the GPU via CategoryColorExtension.
  • A colorMapping always CPU-expands, extruded or not.

The split is forced by the shader. ColumnLayer computes lighting before DECKGL_FILTER_COLOR — gouraud into vColor in the vertex stage, phong into fragColor under flatShading — and the extension's hook replaces rgb, so a GPU palette write on an extruded column discards the lit color and every bar renders as a flat single-tone silhouette. AnimatedPointLayer can use the GPU path unconditionally because its markers are unlit; AnimatedPointCloudLayer, also lit, makes the same CPU choice.

Behavior notes#

  • Point tiles only. The layer checks each tile layer's geometryType and skips any layer that is not Point, emitting one named console warning rather than misreading a linestring/polygon vertex run as one position per feature.
  • Elevation is per-feature (instanced), unlike AnimatedPolygonLayer's per-vertex elevation expansion.
  • Columns are best read at a tilted, regional zoom; at low zoom a metre-radius column falls sub-pixel (the deliberate "render by space" tradeoff).
  • There is no cumulative-slab path: columns are an overview/aggregate primitive, so the per-tile sublayer count never climbs the way a cumulative point reveal does.
  • The sublayer short id for _subLayerProps overrides is columns.

Source#

packages/layers/src/layers/core/animated-column-layer.ts