@sqlrooms/deck
Deck.gl integration for SQLRooms with JSON-driven map specs, dataset registry binding, DuckDB-backed or in-memory Arrow datasets, and GeoArrow-first geometry preparation.
Installation
npm install @sqlrooms/deck @sqlrooms/duckdb @sqlrooms/uiWhat This Package Does
@sqlrooms/deck is the JSON-spec bridge between SQLRooms data and deck.gl:
- render a DeckGL map from a serializable
DeckJsonMapspec - bind one or more datasets through a
datasetsregistry - generate starter JSON specs from datasets with
createDeckJsonSpecFromDatasets - validate SQLRooms-specific layer bindings under
_sqlroomsBinding - prepare geometry for GeoArrow-native layers from
@geoarrow/deck.gl-layersand GeoJSON fallback layers - support shared declarative color scales through
@sqlrooms/color-scales
Use this package when you want deck.gl layers to be driven by a JSON-like spec instead of hand-constructing deck layer instances in React code.
Quick Start
import {DeckJsonMap} from '@sqlrooms/deck';
const spec = {
initialViewState: {
longitude: -122.4,
latitude: 37.74,
zoom: 10,
pitch: 0,
bearing: 0,
},
controller: true,
layers: [
{
'@@type': 'GeoArrowScatterplotLayer',
id: 'airports',
_sqlroomsBinding: {
dataset: 'airports',
geometryColumn: 'geom',
},
getFillColor: {
'@@function': 'colorScale',
field: 'scalerank',
type: 'sequential',
scheme: 'YlOrRd',
domain: 'auto',
},
getRadius: '@@=6',
radiusMinPixels: 2,
},
],
};
export function AirportsMap() {
return (
<DeckJsonMap
spec={spec}
datasets={{
airports: {
sqlQuery:
'SELECT name, abbrev, scalerank, ST_AsWKB(geom) AS geom FROM airports',
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
}}
mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
/>
);
}Auto Spec Generation
If you want a starter JSON spec instead of writing every layer manually, use createDeckJsonSpecFromDatasets(...):
import {createDeckJsonSpecFromDatasets, DeckJsonMap} from '@sqlrooms/deck';
const datasets = {
earthquakes: {
arrowTable,
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
};
const spec = createDeckJsonSpecFromDatasets({datasets});By default, the helper is conservative:
- point / multipoint ->
GeoArrowScatterplotLayer - linestring / multilinestring ->
GeoArrowPathLayer - polygon / multipolygon ->
GeoArrowPolygonLayer - mixed, unknown, or unsupported ->
GeoJsonLayer
You can provide semantic hints for special layers:
const spec = createDeckJsonSpecFromDatasets({
datasets,
hints: {
earthquakes: {prefer: 'heatmap'},
trips: {
type: 'GeoArrowTripsLayer',
timestampColumn: 'timestamps',
},
flows: {
type: 'GeoArrowArcLayer',
sourceGeometryColumn: 'source_geom',
targetGeometryColumn: 'target_geom',
},
hexes: {
type: 'GeoArrowH3HexagonLayer',
hexagonColumn: 'h3',
},
},
});Mosaic Dashboard Renderer
@sqlrooms/deck can contribute a deck-json-map panel renderer to @sqlrooms/mosaic dashboards without making the Mosaic package depend on deck.gl or MapLibre. Pass the renderer when creating the Mosaic dashboard slice.
import {
createDeckMapDashboardPanelConfig,
DECK_MAP_DASHBOARD_PANEL_TYPE,
deckMapDashboardPanelRenderer,
} from '@sqlrooms/deck';
import {
createDefaultMosaicDashboardPanelRenderers,
createMosaicDashboardSlice,
MosaicDashboard,
} from '@sqlrooms/mosaic';
const dashboardSlice = createMosaicDashboardSlice({
panelRenderers: createDefaultMosaicDashboardPanelRenderers({
[DECK_MAP_DASHBOARD_PANEL_TYPE]: deckMapDashboardPanelRenderer,
}),
});
function Dashboard() {
return <MosaicDashboard dashboardId="geo" />;
}
const mapPanel = createDeckMapDashboardPanelConfig({
title: 'Earthquakes map',
spec: {
initialViewState: {longitude: -119.5, latitude: 37, zoom: 4.5},
layers: [
{
'@@type': 'GeoArrowScatterplotLayer',
id: 'earthquakes',
_sqlroomsBinding: {dataset: 'earthquakes'},
},
],
},
datasets: {
earthquakes: {
source: {
sqlQuery:
'SELECT *, ST_AsWKB(ST_Point(Longitude, Latitude)) AS geom FROM earthquakes',
},
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
},
fitToData: {
dataset: 'earthquakes',
longitudeColumn: 'Longitude',
latitudeColumn: 'Latitude',
padding: 40,
maxZoom: 12,
},
});The dashboard renderer uses useMosaicClient, receives Arrow tables directly, and passes them to DeckJsonMap as Arrow-backed datasets. Dataset sources fall back from dataset-level source, to panel source, to the dashboard selected table. When fitToData is provided, the renderer asks DuckDB Spatial for the dataset extent using the declared longitude/latitude columns and fits the initial map view once, instead of inferring bounds from the loaded Arrow payload in React.
Core Concepts
DeckJsonMap
DeckJsonMap is the main React component exported by this package. It takes:
spec: a JSON-like deck.gl spec object or JSON stringdatasets: a dataset registry keyed by dataset iddeckProps: runtime-only deck props such asgetTooltip,onHover,onClickmapProps: runtime-only MapLibre propsshowLegends: whether SQLRooms-generated color legends should render
spec stays serializable; callbacks and runtime behavior belong in deckProps or mapProps.
Dataset Registry
Each SQLRooms-managed layer binds to exactly one dataset through _sqlroomsBinding.dataset.
<DeckJsonMap
spec={spec}
datasets={{
earthquakes: {sqlQuery: 'SELECT * FROM earthquakes'},
faults: {sqlQuery: 'SELECT * FROM faults'},
}}
/>Dataset ids are layer-binding labels. Internally, prepared geometry is cached by the resolved data identity, not by dataset id, so multiple maps or layers can reuse the same preparation work when they point at the same table/query.
Dataset Input Kinds
Each dataset entry is one of:
datasets={{
airports: {
sqlQuery: 'SELECT * FROM airports',
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
preview: {
arrowTable,
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
}}sqlQueryRuns through the DuckDB slice execution path.arrowTableUses an already available Apache Arrow table. This is the right input for Arrow-native SQLRooms hooks such asuseSqlanduseMosaicClient.
For in-memory Arrow datasets, arrowTable may be temporarily undefined while data is still loading. DeckJsonMap will keep rendering the basemap and treat that dataset as loading until a table is provided.
SQLRooms Layer Bindings
SQLRooms-specific layer metadata lives under _sqlroomsBinding:
{
'@@type': 'GeoArrowScatterplotLayer',
id: 'earthquakes',
_sqlroomsBinding: {
dataset: 'earthquakes',
geometryColumn: 'geom',
geometryEncodingHint: 'wkb',
},
getFillColor: {
'@@function': 'colorScale',
field: 'Magnitude',
type: 'sequential',
scheme: 'YlOrRd',
domain: 'auto',
},
}Currently supported SQLRooms binding fields are:
dataset: binds the layer to one dataset idgeometryColumn: overrides geometry column detection for that layergeometryEncodingHint: helps geometry detection when the source table needs itsourceGeometryColumn: source point geometry forGeoArrowArcLayertargetGeometryColumn: target point geometry forGeoArrowArcLayertimestampColumn: timestamp list column forGeoArrowTripsLayerhexagonColumn: H3 index column forGeoArrowH3HexagonLayer
The surrounding deck spec remains intentionally loose so normal deck.gl JSON props still pass through, while _sqlroomsBinding is validated strictly.
Color Scales and Legends
You can ask SQLRooms to derive colors from a field with the colorScale JSON function instead of writing long @@= color expressions:
getFillColor: {
'@@function': 'colorScale',
field: 'Magnitude',
type: 'sequential',
scheme: 'YlOrRd',
domain: 'auto',
clamp: true,
}Discrete numeric palettes are supported too:
getFillColor: {
'@@function': 'colorScale',
field: 'Magnitude',
type: 'quantize',
scheme: 'PuBuGn',
domain: [0, 8],
bins: 5,
}DeckJsonMap renders SQLRooms-generated legends by default for layers that use colorScale. To disable them globally:
<DeckJsonMap spec={spec} datasets={datasets} showLegends={false} />To override the title:
getFillColor: {
'@@function': 'colorScale',
field: 'Magnitude',
type: 'sequential',
scheme: 'YlOrRd',
domain: 'auto',
legend: {
title: 'Magnitude (Mw)',
},
}Supported scale types come from @sqlrooms/color-scales:
sequentialdivergingquantizequantilethresholdcategorical
When domain is set to 'auto', the domain is computed from the currently bound dataset, so colors may shift as filters change. Use explicit domains when you want colors to stay stable across filtering.
Geometry Preparation
prepareDeckDataset(...) is the deck-specific preparation step behind the scenes. It accepts resolved Arrow tables with geometry stored as:
- native GeoArrow
- WKB / GeoArrow WKB
- WKT / GeoArrow WKT
It then produces canonical deck-facing geometry outputs for:
- GeoArrow-native layers such as
GeoArrowScatterplotLayer - GeoJSON-binary fallback layers such as
GeoJsonLayer
Specialized layers such as GeoArrowArcLayer, GeoArrowTripsLayer, and GeoArrowH3HexagonLayer reuse the prepared table but bind additional configured columns on top for source/target geometry, timestamps, or index cells.
This work is cached internally in a module-global prepared dataset store. That cache is separate from any upstream query cache:
- Mosaic-driven queries already benefit from Mosaic's own query cache
- DuckDB SQL datasets still use the DuckDB slice execution path
Deck caches only the expensive geometry preparation layer on top.
Supported Layers
The current curated layer set is:
GeoArrowScatterplotLayerGeoArrowHeatmapLayerGeoArrowColumnLayerGeoArrowPathLayerGeoArrowPolygonLayerGeoArrowSolidPolygonLayerGeoArrowArcLayerGeoArrowTripsLayerGeoArrowH3HexagonLayerGeoJsonLayer
GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls back to decoding and GeoJSON-binary preparation, with point promotion available for point-focused GeoArrow layers such as GeoArrowScatterplotLayer, GeoArrowHeatmapLayer, and GeoArrowColumnLayer.
The GeoArrow layer implementations themselves come from @geoarrow/deck.gl-layers.
When querying DuckDB spatial GEOMETRY columns directly, convert them first with ST_AsWKB(...) or ST_AsText(...). DuckDB's internal geometry payload is not the same as standard WKB.
Runtime Props and Children
Keep the spec serializable, then pass runtime behavior separately:
deckPropsfor deck callbacks such asgetTooltip,onHover,onClickmapPropsfor MapLibre props such asprojectionchildrenfor controls, overlays, and popups rendered inside the map
This lets the spec stay stable for storage, validation, and future AI-assisted generation while still supporting interactive React behavior at runtime.
Type Aliases
- ColorLegendConfig
- ColorLegendConfig
- ColorScaleConfig
- ColorScaleConfig
- DeckGeometryEncodingHint
- ColorScaleFunction
- LayerBindingConfig
- LayerBindingProps
- DeckJsonMapLayerSpec
- DeckJsonMapSpec
- DeckMapDashboardDatasetConfig
- DeckMapDashboardInteractionConfig
- DeckMapDashboardFitToDataConfig
- DeckMapDashboardPanelConfig
- CreateDeckMapDashboardPanelConfigOptions
- DeckMapDashboardDatasetClientState
- GeometryEncodingHint
- ResolvedGeometryEncoding
- ResolvedGeometryColumn
- PreparedGeoArrowLayerData
- PreparedDeckDataset
- DeckAutoLayerType
- DeckSqlDatasetInput
- DeckTable
- DeckArrowTableDatasetInput
- DeckDatasetInput
- DeckJsonSpecDatasetHint
- CreateDeckJsonSpecFromDatasetsOptions
- PreparedDeckDatasetState
- DeckJsonMapProps
Variables
- DeckGeometryEncodingHint
- ColorScaleFunction
- LayerBindingConfig
- LayerBindingProps
- DeckJsonMapLayerSpec
- DeckJsonMapSpec
- deckMapDashboardPanelRenderer
- deckMapDashboardAddPanelAction
- DECK_MAP_DASHBOARD_PANEL_TYPE
