@sqlrooms/deck 0.29.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -0
- package/dist/ColorScaleLegend.d.ts +8 -0
- package/dist/ColorScaleLegend.d.ts.map +1 -0
- package/dist/ColorScaleLegend.js +11 -0
- package/dist/ColorScaleLegend.js.map +1 -0
- package/dist/DeckMap.d.ts +4 -0
- package/dist/DeckMap.d.ts.map +1 -0
- package/dist/DeckMap.js +157 -0
- package/dist/DeckMap.js.map +1 -0
- package/dist/datasets/normalizeDatasets.d.ts +5 -0
- package/dist/datasets/normalizeDatasets.d.ts.map +1 -0
- package/dist/datasets/normalizeDatasets.js +57 -0
- package/dist/datasets/normalizeDatasets.js.map +1 -0
- package/dist/datasets/usePreparedDeckDatasets.d.ts +3 -0
- package/dist/datasets/usePreparedDeckDatasets.d.ts.map +1 -0
- package/dist/datasets/usePreparedDeckDatasets.js +61 -0
- package/dist/datasets/usePreparedDeckDatasets.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/json/compileColorScale.d.ts +35 -0
- package/dist/json/compileColorScale.d.ts.map +1 -0
- package/dist/json/compileColorScale.js +527 -0
- package/dist/json/compileColorScale.js.map +1 -0
- package/dist/json/compileGeoArrowAccessor.d.ts +11 -0
- package/dist/json/compileGeoArrowAccessor.d.ts.map +1 -0
- package/dist/json/compileGeoArrowAccessor.js +74 -0
- package/dist/json/compileGeoArrowAccessor.js.map +1 -0
- package/dist/json/createDeckJsonConfiguration.d.ts +9 -0
- package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -0
- package/dist/json/createDeckJsonConfiguration.js +112 -0
- package/dist/json/createDeckJsonConfiguration.js.map +1 -0
- package/dist/json/defaultClasses.d.ts +24 -0
- package/dist/json/defaultClasses.d.ts.map +1 -0
- package/dist/json/defaultClasses.js +21 -0
- package/dist/json/defaultClasses.js.map +1 -0
- package/dist/json/extractColorScaleLegends.d.ts +8 -0
- package/dist/json/extractColorScaleLegends.d.ts.map +1 -0
- package/dist/json/extractColorScaleLegends.js +48 -0
- package/dist/json/extractColorScaleLegends.js.map +1 -0
- package/dist/json/layerCompatibility.d.ts +15 -0
- package/dist/json/layerCompatibility.d.ts.map +1 -0
- package/dist/json/layerCompatibility.js +38 -0
- package/dist/json/layerCompatibility.js.map +1 -0
- package/dist/json/layerConfig.d.ts +10 -0
- package/dist/json/layerConfig.d.ts.map +1 -0
- package/dist/json/layerConfig.js +43 -0
- package/dist/json/layerConfig.js.map +1 -0
- package/dist/json/rewriteGeoArrowAccessors.d.ts +7 -0
- package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -0
- package/dist/json/rewriteGeoArrowAccessors.js +53 -0
- package/dist/json/rewriteGeoArrowAccessors.js.map +1 -0
- package/dist/prepare/detectGeometryColumn.d.ts +10 -0
- package/dist/prepare/detectGeometryColumn.d.ts.map +1 -0
- package/dist/prepare/detectGeometryColumn.js +84 -0
- package/dist/prepare/detectGeometryColumn.js.map +1 -0
- package/dist/prepare/geoarrow.d.ts +18 -0
- package/dist/prepare/geoarrow.d.ts.map +1 -0
- package/dist/prepare/geoarrow.js +114 -0
- package/dist/prepare/geoarrow.js.map +1 -0
- package/dist/prepare/geometryDecoder.d.ts +9 -0
- package/dist/prepare/geometryDecoder.d.ts.map +1 -0
- package/dist/prepare/geometryDecoder.js +2 -0
- package/dist/prepare/geometryDecoder.js.map +1 -0
- package/dist/prepare/prepareDeckDataset.d.ts +11 -0
- package/dist/prepare/prepareDeckDataset.d.ts.map +1 -0
- package/dist/prepare/prepareDeckDataset.js +66 -0
- package/dist/prepare/prepareDeckDataset.js.map +1 -0
- package/dist/prepare/toGeoJsonBinary.d.ts +9 -0
- package/dist/prepare/toGeoJsonBinary.d.ts.map +1 -0
- package/dist/prepare/toGeoJsonBinary.js +25 -0
- package/dist/prepare/toGeoJsonBinary.js.map +1 -0
- package/dist/prepare/types.d.ts +25 -0
- package/dist/prepare/types.d.ts.map +1 -0
- package/dist/prepare/types.js +2 -0
- package/dist/prepare/types.js.map +1 -0
- package/dist/prepare/wkbDecoder.d.ts +3 -0
- package/dist/prepare/wkbDecoder.d.ts.map +1 -0
- package/dist/prepare/wkbDecoder.js +106 -0
- package/dist/prepare/wkbDecoder.js.map +1 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
Deck.gl integration for SQLRooms with JSON-driven map specs, dataset registry
|
|
2
|
+
binding, DuckDB-backed or in-memory Arrow datasets, and GeoArrow-first geometry
|
|
3
|
+
preparation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sqlrooms/deck @sqlrooms/duckdb @sqlrooms/ui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What This Package Does
|
|
12
|
+
|
|
13
|
+
`@sqlrooms/deck` is the JSON-spec bridge between SQLRooms data and deck.gl:
|
|
14
|
+
|
|
15
|
+
- render a DeckGL map from a serializable `DeckJsonMap` spec
|
|
16
|
+
- bind one or more datasets through a `datasets` registry
|
|
17
|
+
- generate starter JSON specs from datasets with `createDeckJsonSpecFromDatasets`
|
|
18
|
+
- validate SQLRooms-specific layer bindings under `_sqlroomsBinding`
|
|
19
|
+
- prepare geometry for GeoArrow-native layers from
|
|
20
|
+
[`@geoarrow/deck.gl-layers`](https://github.com/geoarrow/deck.gl-layers)
|
|
21
|
+
and GeoJSON fallback layers
|
|
22
|
+
- support shared declarative color scales through `@sqlrooms/color-scales`
|
|
23
|
+
|
|
24
|
+
Use this package when you want deck.gl layers to be driven by a JSON-like spec
|
|
25
|
+
instead of hand-constructing deck layer instances in React code.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import {DeckJsonMap} from '@sqlrooms/deck';
|
|
31
|
+
|
|
32
|
+
const spec = {
|
|
33
|
+
initialViewState: {
|
|
34
|
+
longitude: -122.4,
|
|
35
|
+
latitude: 37.74,
|
|
36
|
+
zoom: 10,
|
|
37
|
+
pitch: 0,
|
|
38
|
+
bearing: 0,
|
|
39
|
+
},
|
|
40
|
+
controller: true,
|
|
41
|
+
layers: [
|
|
42
|
+
{
|
|
43
|
+
'@@type': 'GeoArrowScatterplotLayer',
|
|
44
|
+
id: 'airports',
|
|
45
|
+
_sqlroomsBinding: {
|
|
46
|
+
dataset: 'airports',
|
|
47
|
+
geometryColumn: 'geom',
|
|
48
|
+
},
|
|
49
|
+
getFillColor: {
|
|
50
|
+
'@@function': 'colorScale',
|
|
51
|
+
field: 'scalerank',
|
|
52
|
+
type: 'sequential',
|
|
53
|
+
scheme: 'YlOrRd',
|
|
54
|
+
domain: 'auto',
|
|
55
|
+
},
|
|
56
|
+
getRadius: '@@=6',
|
|
57
|
+
radiusMinPixels: 2,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export function AirportsMap() {
|
|
63
|
+
return (
|
|
64
|
+
<DeckJsonMap
|
|
65
|
+
spec={spec}
|
|
66
|
+
datasets={{
|
|
67
|
+
airports: {
|
|
68
|
+
sqlQuery:
|
|
69
|
+
'SELECT name, abbrev, scalerank, ST_AsWKB(geom) AS geom FROM airports',
|
|
70
|
+
geometryColumn: 'geom',
|
|
71
|
+
geometryEncodingHint: 'wkb',
|
|
72
|
+
},
|
|
73
|
+
}}
|
|
74
|
+
mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Auto Spec Generation
|
|
81
|
+
|
|
82
|
+
If you want a starter JSON spec instead of writing every layer manually, use
|
|
83
|
+
`createDeckJsonSpecFromDatasets(...)`:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import {createDeckJsonSpecFromDatasets, DeckJsonMap} from '@sqlrooms/deck';
|
|
87
|
+
|
|
88
|
+
const datasets = {
|
|
89
|
+
earthquakes: {
|
|
90
|
+
arrowTable,
|
|
91
|
+
geometryColumn: 'geom',
|
|
92
|
+
geometryEncodingHint: 'wkb',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const spec = createDeckJsonSpecFromDatasets({datasets});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
By default, the helper is conservative:
|
|
100
|
+
|
|
101
|
+
- point / multipoint -> `GeoArrowScatterplotLayer`
|
|
102
|
+
- linestring / multilinestring -> `GeoArrowPathLayer`
|
|
103
|
+
- polygon / multipolygon -> `GeoArrowPolygonLayer`
|
|
104
|
+
- mixed, unknown, or unsupported -> `GeoJsonLayer`
|
|
105
|
+
|
|
106
|
+
You can provide semantic hints for special layers:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const spec = createDeckJsonSpecFromDatasets({
|
|
110
|
+
datasets,
|
|
111
|
+
hints: {
|
|
112
|
+
earthquakes: {prefer: 'heatmap'},
|
|
113
|
+
trips: {
|
|
114
|
+
type: 'GeoArrowTripsLayer',
|
|
115
|
+
timestampColumn: 'timestamps',
|
|
116
|
+
},
|
|
117
|
+
flows: {
|
|
118
|
+
type: 'GeoArrowArcLayer',
|
|
119
|
+
sourceGeometryColumn: 'source_geom',
|
|
120
|
+
targetGeometryColumn: 'target_geom',
|
|
121
|
+
},
|
|
122
|
+
hexes: {
|
|
123
|
+
type: 'GeoArrowH3HexagonLayer',
|
|
124
|
+
hexagonColumn: 'h3',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Core Concepts
|
|
131
|
+
|
|
132
|
+
### `DeckJsonMap`
|
|
133
|
+
|
|
134
|
+
`DeckJsonMap` is the main React component exported by this package. It takes:
|
|
135
|
+
|
|
136
|
+
- `spec`: a JSON-like deck.gl spec object or JSON string
|
|
137
|
+
- `datasets`: a dataset registry keyed by dataset id
|
|
138
|
+
- `deckProps`: runtime-only deck props such as `getTooltip`, `onHover`, `onClick`
|
|
139
|
+
- `mapProps`: runtime-only MapLibre props
|
|
140
|
+
- `showLegends`: whether SQLRooms-generated color legends should render
|
|
141
|
+
|
|
142
|
+
`spec` stays serializable; callbacks and runtime behavior belong in `deckProps`
|
|
143
|
+
or `mapProps`.
|
|
144
|
+
|
|
145
|
+
### Dataset Registry
|
|
146
|
+
|
|
147
|
+
Each SQLRooms-managed layer binds to exactly one dataset through
|
|
148
|
+
`_sqlroomsBinding.dataset`.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<DeckJsonMap
|
|
152
|
+
spec={spec}
|
|
153
|
+
datasets={{
|
|
154
|
+
earthquakes: {sqlQuery: 'SELECT * FROM earthquakes'},
|
|
155
|
+
faults: {sqlQuery: 'SELECT * FROM faults'},
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Dataset ids are layer-binding labels. Internally, prepared geometry is cached
|
|
161
|
+
by the resolved data identity, not by dataset id, so multiple maps or layers
|
|
162
|
+
can reuse the same preparation work when they point at the same table/query.
|
|
163
|
+
|
|
164
|
+
### Dataset Input Kinds
|
|
165
|
+
|
|
166
|
+
Each dataset entry is one of:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
datasets={{
|
|
170
|
+
airports: {
|
|
171
|
+
sqlQuery: 'SELECT * FROM airports',
|
|
172
|
+
geometryColumn: 'geom',
|
|
173
|
+
geometryEncodingHint: 'wkb',
|
|
174
|
+
},
|
|
175
|
+
preview: {
|
|
176
|
+
arrowTable,
|
|
177
|
+
geometryColumn: 'geom',
|
|
178
|
+
geometryEncodingHint: 'wkb',
|
|
179
|
+
},
|
|
180
|
+
}}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- `sqlQuery`
|
|
184
|
+
Runs through the DuckDB slice execution path.
|
|
185
|
+
- `arrowTable`
|
|
186
|
+
Uses an already available Apache Arrow table. This is the right input for
|
|
187
|
+
Arrow-native SQLRooms hooks such as `useSql` and `useMosaicClient`.
|
|
188
|
+
|
|
189
|
+
For in-memory Arrow datasets, `arrowTable` may be temporarily `undefined` while
|
|
190
|
+
data is still loading. `DeckJsonMap` will keep rendering the basemap and treat
|
|
191
|
+
that dataset as loading until a table is provided.
|
|
192
|
+
|
|
193
|
+
## SQLRooms Layer Bindings
|
|
194
|
+
|
|
195
|
+
SQLRooms-specific layer metadata lives under `_sqlroomsBinding`:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
{
|
|
199
|
+
'@@type': 'GeoArrowScatterplotLayer',
|
|
200
|
+
id: 'earthquakes',
|
|
201
|
+
_sqlroomsBinding: {
|
|
202
|
+
dataset: 'earthquakes',
|
|
203
|
+
geometryColumn: 'geom',
|
|
204
|
+
geometryEncodingHint: 'wkb',
|
|
205
|
+
},
|
|
206
|
+
getFillColor: {
|
|
207
|
+
'@@function': 'colorScale',
|
|
208
|
+
field: 'Magnitude',
|
|
209
|
+
type: 'sequential',
|
|
210
|
+
scheme: 'YlOrRd',
|
|
211
|
+
domain: 'auto',
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Currently supported SQLRooms binding fields are:
|
|
217
|
+
|
|
218
|
+
- `dataset`: binds the layer to one dataset id
|
|
219
|
+
- `geometryColumn`: overrides geometry column detection for that layer
|
|
220
|
+
- `geometryEncodingHint`: helps geometry detection when the source table needs it
|
|
221
|
+
- `sourceGeometryColumn`: source point geometry for `GeoArrowArcLayer`
|
|
222
|
+
- `targetGeometryColumn`: target point geometry for `GeoArrowArcLayer`
|
|
223
|
+
- `timestampColumn`: timestamp list column for `GeoArrowTripsLayer`
|
|
224
|
+
- `hexagonColumn`: H3 index column for `GeoArrowH3HexagonLayer`
|
|
225
|
+
|
|
226
|
+
The surrounding deck spec remains intentionally loose so normal deck.gl JSON
|
|
227
|
+
props still pass through, while `_sqlroomsBinding` is validated strictly.
|
|
228
|
+
|
|
229
|
+
## Color Scales and Legends
|
|
230
|
+
|
|
231
|
+
You can ask SQLRooms to derive colors from a field with the
|
|
232
|
+
`colorScale` JSON function instead of writing long `@@=` color
|
|
233
|
+
expressions:
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
getFillColor: {
|
|
237
|
+
'@@function': 'colorScale',
|
|
238
|
+
field: 'Magnitude',
|
|
239
|
+
type: 'sequential',
|
|
240
|
+
scheme: 'YlOrRd',
|
|
241
|
+
domain: 'auto',
|
|
242
|
+
clamp: true,
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Discrete numeric palettes are supported too:
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
getFillColor: {
|
|
250
|
+
'@@function': 'colorScale',
|
|
251
|
+
field: 'Magnitude',
|
|
252
|
+
type: 'quantize',
|
|
253
|
+
scheme: 'PuBuGn',
|
|
254
|
+
domain: [0, 8],
|
|
255
|
+
bins: 5,
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`DeckJsonMap` renders SQLRooms-generated legends by default for layers that use
|
|
260
|
+
`colorScale`. To disable them globally:
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
<DeckJsonMap spec={spec} datasets={datasets} showLegends={false} />
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
To override the title:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
getFillColor: {
|
|
270
|
+
'@@function': 'colorScale',
|
|
271
|
+
field: 'Magnitude',
|
|
272
|
+
type: 'sequential',
|
|
273
|
+
scheme: 'YlOrRd',
|
|
274
|
+
domain: 'auto',
|
|
275
|
+
legend: {
|
|
276
|
+
title: 'Magnitude (Mw)',
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Supported scale types come from `@sqlrooms/color-scales`:
|
|
282
|
+
|
|
283
|
+
- `sequential`
|
|
284
|
+
- `diverging`
|
|
285
|
+
- `quantize`
|
|
286
|
+
- `quantile`
|
|
287
|
+
- `threshold`
|
|
288
|
+
- `categorical`
|
|
289
|
+
|
|
290
|
+
When `domain` is set to `'auto'`, the domain is computed from the currently
|
|
291
|
+
bound dataset, so colors may shift as filters change. Use explicit domains when
|
|
292
|
+
you want colors to stay stable across filtering.
|
|
293
|
+
|
|
294
|
+
## Geometry Preparation
|
|
295
|
+
|
|
296
|
+
`prepareDeckDataset(...)` is the deck-specific preparation step behind the
|
|
297
|
+
scenes. It accepts resolved Arrow tables with geometry stored as:
|
|
298
|
+
|
|
299
|
+
- native GeoArrow
|
|
300
|
+
- WKB / GeoArrow WKB
|
|
301
|
+
- WKT / GeoArrow WKT
|
|
302
|
+
|
|
303
|
+
It then produces canonical deck-facing geometry outputs for:
|
|
304
|
+
|
|
305
|
+
- GeoArrow-native layers such as `GeoArrowScatterplotLayer`
|
|
306
|
+
- GeoJSON-binary fallback layers such as `GeoJsonLayer`
|
|
307
|
+
|
|
308
|
+
Specialized layers such as `GeoArrowArcLayer`, `GeoArrowTripsLayer`, and
|
|
309
|
+
`GeoArrowH3HexagonLayer` reuse the prepared table but bind additional
|
|
310
|
+
configured columns on top for source/target geometry,
|
|
311
|
+
timestamps, or index cells.
|
|
312
|
+
|
|
313
|
+
This work is cached internally in a module-global prepared dataset store. That
|
|
314
|
+
cache is separate from any upstream query cache:
|
|
315
|
+
|
|
316
|
+
- Mosaic-driven queries already benefit from Mosaic's own query cache
|
|
317
|
+
- DuckDB SQL datasets still use the DuckDB slice execution path
|
|
318
|
+
|
|
319
|
+
Deck caches only the expensive geometry preparation layer on top.
|
|
320
|
+
|
|
321
|
+
## Supported Layers
|
|
322
|
+
|
|
323
|
+
The current curated layer set is:
|
|
324
|
+
|
|
325
|
+
- `GeoArrowScatterplotLayer`
|
|
326
|
+
- `GeoArrowHeatmapLayer`
|
|
327
|
+
- `GeoArrowColumnLayer`
|
|
328
|
+
- `GeoArrowPathLayer`
|
|
329
|
+
- `GeoArrowPolygonLayer`
|
|
330
|
+
- `GeoArrowSolidPolygonLayer`
|
|
331
|
+
- `GeoArrowArcLayer`
|
|
332
|
+
- `GeoArrowTripsLayer`
|
|
333
|
+
- `GeoArrowH3HexagonLayer`
|
|
334
|
+
- `GeoJsonLayer`
|
|
335
|
+
|
|
336
|
+
GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls
|
|
337
|
+
back to decoding and GeoJSON-binary preparation, with point promotion available
|
|
338
|
+
for point-focused GeoArrow layers such as `GeoArrowScatterplotLayer`,
|
|
339
|
+
`GeoArrowHeatmapLayer`, and `GeoArrowColumnLayer`.
|
|
340
|
+
|
|
341
|
+
The GeoArrow layer implementations themselves come from
|
|
342
|
+
[`@geoarrow/deck.gl-layers`](https://github.com/geoarrow/deck.gl-layers).
|
|
343
|
+
|
|
344
|
+
When querying DuckDB spatial `GEOMETRY` columns directly, convert them first
|
|
345
|
+
with `ST_AsWKB(...)` or `ST_AsText(...)`. DuckDB's internal geometry payload is
|
|
346
|
+
not the same as standard WKB.
|
|
347
|
+
|
|
348
|
+
## Runtime Props and Children
|
|
349
|
+
|
|
350
|
+
Keep the spec serializable, then pass runtime behavior separately:
|
|
351
|
+
|
|
352
|
+
- `deckProps` for deck callbacks such as `getTooltip`, `onHover`, `onClick`
|
|
353
|
+
- `mapProps` for MapLibre props such as `projection`
|
|
354
|
+
- `children` for controls, overlays, and popups rendered inside the map
|
|
355
|
+
|
|
356
|
+
This lets the spec stay stable for storage, validation, and future AI-assisted
|
|
357
|
+
generation while still supporting interactive React behavior at runtime.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ResolvedColorLegend } from './json/compileColorScale';
|
|
2
|
+
type ColorScaleLegendProps = {
|
|
3
|
+
legends: ResolvedColorLegend[];
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function ColorScaleLegend({ legends, className }: ColorScaleLegendProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=ColorScaleLegend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorScaleLegend.d.ts","sourceRoot":"","sources":["../src/ColorScaleLegend.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,0BAA0B,CAAC;AAElE,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAC,OAAO,EAAE,SAAS,EAAC,EAAE,qBAAqB,kDA8D3E"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '@sqlrooms/ui';
|
|
3
|
+
export function ColorScaleLegend({ legends, className }) {
|
|
4
|
+
if (!legends.length) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
return (_jsx("div", { className: cn('pointer-events-none absolute bottom-2 left-2 z-10 flex max-w-56 flex-col gap-3', className), children: legends.map((legend) => (_jsxs("div", { className: "bg-background/70 rounded-md px-3 py-2 shadow-sm backdrop-blur-sm", children: [_jsx("div", { className: "text-foreground mb-2 text-xs font-semibold", children: legend.title }), legend.type === 'continuous' ? (_jsxs("div", { children: [_jsx("div", { className: "h-3 rounded-sm border", style: { background: legend.gradient } }), _jsx("div", { className: "text-muted-foreground mt-1 flex justify-between gap-2 text-[0.6rem]", children: legend.ticks.map((tick) => (_jsx("span", { className: cn(tick.offset === 50 ? 'text-center' : '', tick.offset === 100 ? 'text-right' : ''), children: tick.label }, `${legend.title}-${tick.offset}-${tick.label}`))) })] })) : (_jsx("div", { className: "space-y-1.5", children: legend.items.map((item) => (_jsxs("div", { className: "text-foreground flex items-center gap-2 text-xs", children: [_jsx("span", { className: "h-3 w-3 rounded-sm border", style: {
|
|
8
|
+
backgroundColor: `rgba(${item.color[0]}, ${item.color[1]}, ${item.color[2]}, ${item.color[3] / 255})`,
|
|
9
|
+
} }), _jsx("span", { className: "truncate", children: item.label })] }, `${legend.title}-${item.label}`))) }))] }, `${legend.title}-${legend.type}`))) }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ColorScaleLegend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorScaleLegend.js","sourceRoot":"","sources":["../src/ColorScaleLegend.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAQhC,MAAM,UAAU,gBAAgB,CAAC,EAAC,OAAO,EAAE,SAAS,EAAwB;IAC1E,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CACX,gFAAgF,EAChF,SAAS,CACV,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,eAEE,SAAS,EAAC,kEAAkE,aAE5E,cAAK,SAAS,EAAC,4CAA4C,YACxD,MAAM,CAAC,KAAK,GACT,EACL,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,CAC9B,0BACE,cACE,SAAS,EAAC,uBAAuB,EACjC,KAAK,EAAE,EAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAC,GACpC,EACF,cAAK,SAAS,EAAC,qEAAqE,YACjF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC1B,eAEE,SAAS,EAAE,EAAE,CACX,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EACvC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CACxC,YAEA,IAAI,CAAC,KAAK,IANN,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAO9C,CACR,CAAC,GACE,IACF,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,aAAa,YACzB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC1B,eAEE,SAAS,EAAC,iDAAiD,aAE3D,eACE,SAAS,EAAC,2BAA2B,EACrC,KAAK,EAAE;oCACL,eAAe,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG;iCACtG,GACD,EACF,eAAM,SAAS,EAAC,UAAU,YAAE,IAAI,CAAC,KAAK,GAAQ,KATzC,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAUhC,CACP,CAAC,GACE,CACP,KA3CI,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,EAAE,CA4CjC,CACP,CAAC,GACE,CACP,CAAC;AACJ,CAAC","sourcesContent":["import {cn} from '@sqlrooms/ui';\nimport type {ResolvedColorLegend} from './json/compileColorScale';\n\ntype ColorScaleLegendProps = {\n legends: ResolvedColorLegend[];\n className?: string;\n};\n\nexport function ColorScaleLegend({legends, className}: ColorScaleLegendProps) {\n if (!legends.length) {\n return null;\n }\n\n return (\n <div\n className={cn(\n 'pointer-events-none absolute bottom-2 left-2 z-10 flex max-w-56 flex-col gap-3',\n className,\n )}\n >\n {legends.map((legend) => (\n <div\n key={`${legend.title}-${legend.type}`}\n className=\"bg-background/70 rounded-md px-3 py-2 shadow-sm backdrop-blur-sm\"\n >\n <div className=\"text-foreground mb-2 text-xs font-semibold\">\n {legend.title}\n </div>\n {legend.type === 'continuous' ? (\n <div>\n <div\n className=\"h-3 rounded-sm border\"\n style={{background: legend.gradient}}\n />\n <div className=\"text-muted-foreground mt-1 flex justify-between gap-2 text-[0.6rem]\">\n {legend.ticks.map((tick) => (\n <span\n key={`${legend.title}-${tick.offset}-${tick.label}`}\n className={cn(\n tick.offset === 50 ? 'text-center' : '',\n tick.offset === 100 ? 'text-right' : '',\n )}\n >\n {tick.label}\n </span>\n ))}\n </div>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {legend.items.map((item) => (\n <div\n key={`${legend.title}-${item.label}`}\n className=\"text-foreground flex items-center gap-2 text-xs\"\n >\n <span\n className=\"h-3 w-3 rounded-sm border\"\n style={{\n backgroundColor: `rgba(${item.color[0]}, ${item.color[1]}, ${item.color[2]}, ${item.color[3] / 255})`,\n }}\n />\n <span className=\"truncate\">{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
2
|
+
import type { DeckMapProps } from './types';
|
|
3
|
+
export declare function DeckMap({ spec, datasets, sqlQuery, arrowTable, queryResult, geometryColumn, geometryEncodingHint, mapStyle, deckProps, mapProps, className, children, }: DeckMapProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=DeckMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeckMap.d.ts","sourceRoot":"","sources":["../src/DeckMap.tsx"],"names":[],"mappings":"AAGA,OAAO,kCAAkC,CAAC;AAU1C,OAAO,KAAK,EAAC,YAAY,EAA2B,MAAM,SAAS,CAAC;AA6GpE,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,YAAY,2CAgId"}
|
package/dist/DeckMap.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { JSONConverter } from '@deck.gl/json';
|
|
3
|
+
import DeckGL from '@deck.gl/react';
|
|
4
|
+
import { cn } from '@sqlrooms/ui';
|
|
5
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
6
|
+
import { useEffect, useMemo } from 'react';
|
|
7
|
+
import Map from 'react-map-gl/maplibre';
|
|
8
|
+
import { normalizeDatasets } from './datasets/normalizeDatasets';
|
|
9
|
+
import { usePreparedDeckDatasets } from './datasets/usePreparedDeckDatasets';
|
|
10
|
+
import { ColorScaleLegend } from './ColorScaleLegend';
|
|
11
|
+
import { createDeckJsonConfiguration } from './json/createDeckJsonConfiguration';
|
|
12
|
+
import { extractColorScaleLegends } from './json/extractColorScaleLegends';
|
|
13
|
+
import { resolveDatasetId } from './json/layerConfig';
|
|
14
|
+
import { getLayerCompatibility } from './json/layerCompatibility';
|
|
15
|
+
const DEFAULT_MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
|
|
16
|
+
function parseSpec(spec) {
|
|
17
|
+
try {
|
|
18
|
+
return {
|
|
19
|
+
spec: typeof spec === 'string' ? JSON.parse(spec) : spec,
|
|
20
|
+
error: null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return {
|
|
25
|
+
spec: null,
|
|
26
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function extractFallbackDeckProps(spec) {
|
|
31
|
+
if (!spec) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
const fallbackProps = {};
|
|
35
|
+
for (const key of ['initialViewState', 'viewState', 'controller']) {
|
|
36
|
+
if (key in spec) {
|
|
37
|
+
fallbackProps[key] = spec[key];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return fallbackProps;
|
|
41
|
+
}
|
|
42
|
+
function filterUnavailableLayers(spec, datasetIds, datasetStates) {
|
|
43
|
+
const layers = Array.isArray(spec.layers) ? spec.layers : [];
|
|
44
|
+
const filteredLayers = layers.filter((layer) => {
|
|
45
|
+
if (!layer || typeof layer !== 'object') {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const layerProps = layer;
|
|
49
|
+
const layerName = String(layerProps['@@type'] ?? '');
|
|
50
|
+
const compatibility = getLayerCompatibility(layerName);
|
|
51
|
+
if (!compatibility) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
const datasetId = resolveDatasetId(layerProps, datasetIds);
|
|
55
|
+
if (!datasetId) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const datasetState = datasetStates[datasetId];
|
|
59
|
+
if (!datasetState) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return datasetState.status === 'ready';
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
...spec,
|
|
66
|
+
layers: filteredLayers,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function renderDatasetStatusOverlay(datasetStates) {
|
|
70
|
+
const loadingDatasets = Object.entries(datasetStates)
|
|
71
|
+
.filter(([, state]) => state.status === 'loading')
|
|
72
|
+
.map(([datasetId]) => datasetId);
|
|
73
|
+
const failedDatasets = Object.entries(datasetStates).filter((entry) => entry[1].status === 'error');
|
|
74
|
+
if (!loadingDatasets.length && !failedDatasets.length) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return (_jsxs("div", { className: "pointer-events-none absolute inset-x-4 top-4 z-10 space-y-2", children: [loadingDatasets.length > 0 ? (_jsxs("div", { className: "rounded-md border border-black/10 bg-white/90 px-3 py-2 text-sm text-slate-700 shadow-sm", children: ["Loading datasets: ", loadingDatasets.join(', ')] })) : null, failedDatasets.map(([datasetId, state]) => (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50/95 px-3 py-2 text-sm text-red-700 shadow-sm", children: `Dataset "${datasetId}" failed: ${state.error.message}` }, datasetId)))] }));
|
|
78
|
+
}
|
|
79
|
+
export function DeckMap({ spec, datasets, sqlQuery, arrowTable, queryResult, geometryColumn, geometryEncodingHint, mapStyle, deckProps, mapProps, className, children, }) {
|
|
80
|
+
const normalizedDatasets = useMemo(() => normalizeDatasets({
|
|
81
|
+
datasets,
|
|
82
|
+
sqlQuery,
|
|
83
|
+
arrowTable,
|
|
84
|
+
queryResult,
|
|
85
|
+
geometryColumn,
|
|
86
|
+
geometryEncodingHint,
|
|
87
|
+
}), [
|
|
88
|
+
datasets,
|
|
89
|
+
sqlQuery,
|
|
90
|
+
arrowTable,
|
|
91
|
+
queryResult,
|
|
92
|
+
geometryColumn,
|
|
93
|
+
geometryEncodingHint,
|
|
94
|
+
]);
|
|
95
|
+
const datasetIds = useMemo(() => Object.keys(normalizedDatasets), [normalizedDatasets]);
|
|
96
|
+
const datasetStates = usePreparedDeckDatasets(normalizedDatasets);
|
|
97
|
+
const { spec: parsedSpec, error: specError } = useMemo(() => parseSpec(spec), [spec]);
|
|
98
|
+
const availableSpec = useMemo(() => parsedSpec
|
|
99
|
+
? filterUnavailableLayers(parsedSpec, datasetIds, datasetStates)
|
|
100
|
+
: null, [parsedSpec, datasetIds, datasetStates]);
|
|
101
|
+
const converter = useMemo(() => new JSONConverter({
|
|
102
|
+
configuration: createDeckJsonConfiguration({
|
|
103
|
+
datasetStates,
|
|
104
|
+
datasetIds,
|
|
105
|
+
}),
|
|
106
|
+
onJSONChange: () => { },
|
|
107
|
+
}), [datasetIds, datasetStates]);
|
|
108
|
+
const convertedDeckPropsResult = useMemo(() => {
|
|
109
|
+
if (!availableSpec) {
|
|
110
|
+
return { props: null, error: specError };
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
return {
|
|
114
|
+
props: converter.convert(availableSpec),
|
|
115
|
+
error: null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
return {
|
|
120
|
+
props: null,
|
|
121
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}, [availableSpec, converter, specError]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (convertedDeckPropsResult.error) {
|
|
127
|
+
console.error(convertedDeckPropsResult.error);
|
|
128
|
+
}
|
|
129
|
+
}, [convertedDeckPropsResult.error]);
|
|
130
|
+
const fallbackDeckProps = useMemo(() => extractFallbackDeckProps(availableSpec), [availableSpec]);
|
|
131
|
+
const convertedDeckProps = (convertedDeckPropsResult.props ??
|
|
132
|
+
fallbackDeckProps ??
|
|
133
|
+
{});
|
|
134
|
+
const extraDeckProps = (deckProps ?? {});
|
|
135
|
+
const extraMapProps = (mapProps ?? {});
|
|
136
|
+
const hasRenderingError = Boolean(convertedDeckPropsResult.error);
|
|
137
|
+
const mergedDeckProps = {
|
|
138
|
+
...convertedDeckProps,
|
|
139
|
+
...extraDeckProps,
|
|
140
|
+
layers: hasRenderingError
|
|
141
|
+
? []
|
|
142
|
+
: (deckProps?.layers ??
|
|
143
|
+
convertedDeckProps.layers ??
|
|
144
|
+
[]),
|
|
145
|
+
};
|
|
146
|
+
const mergedMapProps = {
|
|
147
|
+
...extraMapProps,
|
|
148
|
+
mapStyle: mapStyle ?? mapProps?.mapStyle ?? DEFAULT_MAP_STYLE,
|
|
149
|
+
};
|
|
150
|
+
const legends = useMemo(() => extractColorScaleLegends({
|
|
151
|
+
spec: availableSpec,
|
|
152
|
+
datasetIds,
|
|
153
|
+
datasetStates,
|
|
154
|
+
}), [availableSpec, datasetIds, datasetStates]);
|
|
155
|
+
return (_jsxs("div", { className: cn('relative h-full w-full', className), children: [hasRenderingError ? (_jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center p-4", children: _jsx("div", { className: "max-w-sm rounded-md border border-red-200 bg-red-50/95 p-4 text-sm text-red-700 shadow-sm", children: `Map couldn't be rendered. Check the console for details.` }) })) : null, _jsx(DeckGL, { ...mergedDeckProps, children: _jsx(Map, { ...mergedMapProps, children: children }) }), renderDatasetStatusOverlay(datasetStates), !hasRenderingError ? _jsx(ColorScaleLegend, { legends: legends }) : null] }));
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=DeckMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeckMap.js","sourceRoot":"","sources":["../src/DeckMap.tsx"],"names":[],"mappings":"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","sourcesContent":["import {JSONConverter} from '@deck.gl/json';\nimport DeckGL from '@deck.gl/react';\nimport {cn} from '@sqlrooms/ui';\nimport 'maplibre-gl/dist/maplibre-gl.css';\nimport {useEffect, useMemo} from 'react';\nimport Map from 'react-map-gl/maplibre';\nimport {normalizeDatasets} from './datasets/normalizeDatasets';\nimport {usePreparedDeckDatasets} from './datasets/usePreparedDeckDatasets';\nimport {ColorScaleLegend} from './ColorScaleLegend';\nimport {createDeckJsonConfiguration} from './json/createDeckJsonConfiguration';\nimport {extractColorScaleLegends} from './json/extractColorScaleLegends';\nimport {resolveDatasetId} from './json/layerConfig';\nimport {getLayerCompatibility} from './json/layerCompatibility';\nimport type {DeckMapProps, PreparedDeckDatasetState} from './types';\n\nconst DEFAULT_MAP_STYLE =\n 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';\n\nfunction parseSpec(spec: DeckMapProps['spec']) {\n try {\n return {\n spec: typeof spec === 'string' ? JSON.parse(spec) : spec,\n error: null,\n };\n } catch (error) {\n return {\n spec: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction extractFallbackDeckProps(spec: Record<string, unknown> | null) {\n if (!spec) {\n return {};\n }\n\n const fallbackProps: Record<string, unknown> = {};\n for (const key of ['initialViewState', 'viewState', 'controller']) {\n if (key in spec) {\n fallbackProps[key] = spec[key];\n }\n }\n\n return fallbackProps;\n}\n\nfunction filterUnavailableLayers(\n spec: Record<string, unknown>,\n datasetIds: string[],\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const layers = Array.isArray(spec.layers) ? spec.layers : [];\n const filteredLayers = layers.filter((layer) => {\n if (!layer || typeof layer !== 'object') {\n return true;\n }\n\n const layerProps = layer as Record<string, unknown>;\n const layerName = String(layerProps['@@type'] ?? '');\n const compatibility = getLayerCompatibility(layerName);\n if (!compatibility) {\n return true;\n }\n\n const datasetId = resolveDatasetId(layerProps, datasetIds);\n if (!datasetId) {\n return true;\n }\n\n const datasetState = datasetStates[datasetId];\n if (!datasetState) {\n return false;\n }\n\n return datasetState.status === 'ready';\n });\n\n return {\n ...spec,\n layers: filteredLayers,\n };\n}\n\nfunction renderDatasetStatusOverlay(\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const loadingDatasets = Object.entries(datasetStates)\n .filter(([, state]) => state.status === 'loading')\n .map(([datasetId]) => datasetId);\n const failedDatasets = Object.entries(datasetStates).filter(\n (\n entry,\n ): entry is [\n string,\n Extract<PreparedDeckDatasetState, {status: 'error'}>,\n ] => entry[1].status === 'error',\n );\n\n if (!loadingDatasets.length && !failedDatasets.length) {\n return null;\n }\n\n return (\n <div className=\"pointer-events-none absolute inset-x-4 top-4 z-10 space-y-2\">\n {loadingDatasets.length > 0 ? (\n <div className=\"rounded-md border border-black/10 bg-white/90 px-3 py-2 text-sm text-slate-700 shadow-sm\">\n Loading datasets: {loadingDatasets.join(', ')}\n </div>\n ) : null}\n {failedDatasets.map(([datasetId, state]) => (\n <div\n key={datasetId}\n className=\"rounded-md border border-red-200 bg-red-50/95 px-3 py-2 text-sm text-red-700 shadow-sm\"\n >\n {`Dataset \"${datasetId}\" failed: ${state.error.message}`}\n </div>\n ))}\n </div>\n );\n}\n\nexport function DeckMap({\n spec,\n datasets,\n sqlQuery,\n arrowTable,\n queryResult,\n geometryColumn,\n geometryEncodingHint,\n mapStyle,\n deckProps,\n mapProps,\n className,\n children,\n}: DeckMapProps) {\n const normalizedDatasets = useMemo(\n () =>\n normalizeDatasets({\n datasets,\n sqlQuery,\n arrowTable,\n queryResult,\n geometryColumn,\n geometryEncodingHint,\n }),\n [\n datasets,\n sqlQuery,\n arrowTable,\n queryResult,\n geometryColumn,\n geometryEncodingHint,\n ],\n );\n const datasetIds = useMemo(\n () => Object.keys(normalizedDatasets),\n [normalizedDatasets],\n );\n const datasetStates = usePreparedDeckDatasets(normalizedDatasets);\n\n const {spec: parsedSpec, error: specError} = useMemo(\n () => parseSpec(spec),\n [spec],\n );\n\n const availableSpec = useMemo(\n () =>\n parsedSpec\n ? filterUnavailableLayers(parsedSpec, datasetIds, datasetStates)\n : null,\n [parsedSpec, datasetIds, datasetStates],\n );\n\n const converter = useMemo(\n () =>\n new JSONConverter({\n configuration: createDeckJsonConfiguration({\n datasetStates,\n datasetIds,\n }),\n onJSONChange: () => {},\n }),\n [datasetIds, datasetStates],\n );\n\n const convertedDeckPropsResult = useMemo(() => {\n if (!availableSpec) {\n return {props: null, error: specError};\n }\n\n try {\n return {\n props: converter.convert(availableSpec) as Record<string, unknown>,\n error: null,\n };\n } catch (error) {\n return {\n props: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }, [availableSpec, converter, specError]);\n\n useEffect(() => {\n if (convertedDeckPropsResult.error) {\n console.error(convertedDeckPropsResult.error);\n }\n }, [convertedDeckPropsResult.error]);\n\n const fallbackDeckProps = useMemo(\n () => extractFallbackDeckProps(availableSpec),\n [availableSpec],\n );\n const convertedDeckProps = (convertedDeckPropsResult.props ??\n fallbackDeckProps ??\n {}) as Record<string, unknown>;\n const extraDeckProps = (deckProps ?? {}) as Record<string, unknown>;\n const extraMapProps = (mapProps ?? {}) as Record<string, unknown>;\n const hasRenderingError = Boolean(convertedDeckPropsResult.error);\n\n const mergedDeckProps = {\n ...convertedDeckProps,\n ...extraDeckProps,\n layers: hasRenderingError\n ? []\n : (deckProps?.layers ??\n (convertedDeckProps.layers as unknown[] | undefined) ??\n []),\n };\n\n const mergedMapProps = {\n ...extraMapProps,\n mapStyle: mapStyle ?? mapProps?.mapStyle ?? DEFAULT_MAP_STYLE,\n };\n const legends = useMemo(\n () =>\n extractColorScaleLegends({\n spec: availableSpec,\n datasetIds,\n datasetStates,\n }),\n [availableSpec, datasetIds, datasetStates],\n );\n\n return (\n <div className={cn('relative h-full w-full', className)}>\n {hasRenderingError ? (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center p-4\">\n <div className=\"max-w-sm rounded-md border border-red-200 bg-red-50/95 p-4 text-sm text-red-700 shadow-sm\">\n {`Map couldn't be rendered. Check the console for details.`}\n </div>\n </div>\n ) : null}\n\n <DeckGL {...(mergedDeckProps as object)}>\n <Map {...(mergedMapProps as object)}>{children}</Map>\n </DeckGL>\n\n {renderDatasetStatusOverlay(datasetStates)}\n {!hasRenderingError ? <ColorScaleLegend legends={legends} /> : null}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type * as arrow from 'apache-arrow';
|
|
2
|
+
import type { DeckDatasetInput, DeckMapProps } from '../types';
|
|
3
|
+
export declare function resolveArrowTable(input: DeckDatasetInput): arrow.Table | undefined;
|
|
4
|
+
export declare function normalizeDatasets(props: Pick<DeckMapProps, 'datasets' | 'sqlQuery' | 'arrowTable' | 'queryResult' | 'geometryColumn' | 'geometryEncodingHint'>): Record<string, DeckDatasetInput>;
|
|
5
|
+
//# sourceMappingURL=normalizeDatasets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeDatasets.d.ts","sourceRoot":"","sources":["../../src/datasets/normalizeDatasets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAC,gBAAgB,EAAE,YAAY,EAAsB,MAAM,UAAU,CAAC;AA8ClF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,gBAAgB,GACtB,KAAK,CAAC,KAAK,GAAG,SAAS,CAEzB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,CACT,YAAY,EACV,UAAU,GACV,UAAU,GACV,YAAY,GACZ,aAAa,GACb,gBAAgB,GAChB,sBAAsB,CACzB,GACA,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CA2BlC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function getArrowTableFromQueryResult(queryResult) {
|
|
2
|
+
return queryResult?.arrowTable;
|
|
3
|
+
}
|
|
4
|
+
function countDefinedInputs(input) {
|
|
5
|
+
return Number(Boolean(input.sqlQuery)) +
|
|
6
|
+
Number(Boolean(input.arrowTable)) +
|
|
7
|
+
Number(Boolean(input.queryResult));
|
|
8
|
+
}
|
|
9
|
+
function assertArrowTable(value, datasetId) {
|
|
10
|
+
if (!value) {
|
|
11
|
+
throw new Error(`Dataset "${datasetId}" queryResult did not expose an arrowTable.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function normalizeDatasetEntry(datasetId, input) {
|
|
15
|
+
const normalized = {
|
|
16
|
+
sqlQuery: input.sqlQuery,
|
|
17
|
+
arrowTable: input.arrowTable,
|
|
18
|
+
queryResult: input.queryResult,
|
|
19
|
+
geometryColumn: input.geometryColumn,
|
|
20
|
+
geometryEncodingHint: input.geometryEncodingHint,
|
|
21
|
+
};
|
|
22
|
+
const definedInputs = countDefinedInputs(normalized);
|
|
23
|
+
if (definedInputs !== 1) {
|
|
24
|
+
throw new Error(`Dataset "${datasetId}" must provide exactly one of sqlQuery, arrowTable, or queryResult.`);
|
|
25
|
+
}
|
|
26
|
+
if (normalized.queryResult) {
|
|
27
|
+
assertArrowTable(getArrowTableFromQueryResult(normalized.queryResult), datasetId);
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
export function resolveArrowTable(input) {
|
|
32
|
+
return input.arrowTable ?? getArrowTableFromQueryResult(input.queryResult);
|
|
33
|
+
}
|
|
34
|
+
export function normalizeDatasets(props) {
|
|
35
|
+
if (props.datasets) {
|
|
36
|
+
const entries = Object.entries(props.datasets).map(([datasetId, input]) => [
|
|
37
|
+
datasetId,
|
|
38
|
+
normalizeDatasetEntry(datasetId, input),
|
|
39
|
+
]);
|
|
40
|
+
return Object.fromEntries(entries);
|
|
41
|
+
}
|
|
42
|
+
const singleInput = {
|
|
43
|
+
sqlQuery: props.sqlQuery,
|
|
44
|
+
arrowTable: props.arrowTable,
|
|
45
|
+
queryResult: props.queryResult,
|
|
46
|
+
geometryColumn: props.geometryColumn,
|
|
47
|
+
geometryEncodingHint: props.geometryEncodingHint,
|
|
48
|
+
};
|
|
49
|
+
const definedInputs = countDefinedInputs(singleInput);
|
|
50
|
+
if (definedInputs === 0) {
|
|
51
|
+
throw new Error('DeckMap requires either datasets or one of sqlQuery, arrowTable, or queryResult.');
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
default: normalizeDatasetEntry('default', singleInput),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=normalizeDatasets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeDatasets.js","sourceRoot":"","sources":["../../src/datasets/normalizeDatasets.ts"],"names":[],"mappings":"AAGA,SAAS,4BAA4B,CAAC,WAA4C;IAChF,OAAO,WAAW,EAAE,UAAU,CAAC;AACjC,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAuB;IACjD,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAA8B,EAAE,SAAiB;IACzE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,YAAY,SAAS,6CAA6C,CACnE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAC5B,SAAiB,EACjB,KAAuB;IAEvB,MAAM,UAAU,GAAqB;QACnC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;KACjD,CAAC;IAEF,MAAM,aAAa,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IACrD,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,YAAY,SAAS,qEAAqE,CAC3F,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;QAC3B,gBAAgB,CAAC,4BAA4B,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC;IACpF,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAuB;IAEvB,OAAO,KAAK,CAAC,UAAU,IAAI,4BAA4B,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAQC;IAED,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;YACzE,SAAS;YACT,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC;SACxC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,WAAW,GAAqB;QACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;KACjD,CAAC;IAEF,MAAM,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACtD,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,qBAAqB,CAAC,SAAS,EAAE,WAAW,CAAC;KACvD,CAAC;AACJ,CAAC","sourcesContent":["import type * as arrow from 'apache-arrow';\nimport type {DeckDatasetInput, DeckMapProps, DeckQueryResultLike} from '../types';\n\nfunction getArrowTableFromQueryResult(queryResult: DeckQueryResultLike | undefined) {\n return queryResult?.arrowTable;\n}\n\nfunction countDefinedInputs(input: DeckDatasetInput) {\n return Number(Boolean(input.sqlQuery)) +\n Number(Boolean(input.arrowTable)) +\n Number(Boolean(input.queryResult));\n}\n\nfunction assertArrowTable(value: arrow.Table | undefined, datasetId: string) {\n if (!value) {\n throw new Error(\n `Dataset \"${datasetId}\" queryResult did not expose an arrowTable.`,\n );\n }\n}\n\nfunction normalizeDatasetEntry(\n datasetId: string,\n input: DeckDatasetInput,\n): DeckDatasetInput {\n const normalized: DeckDatasetInput = {\n sqlQuery: input.sqlQuery,\n arrowTable: input.arrowTable,\n queryResult: input.queryResult,\n geometryColumn: input.geometryColumn,\n geometryEncodingHint: input.geometryEncodingHint,\n };\n\n const definedInputs = countDefinedInputs(normalized);\n if (definedInputs !== 1) {\n throw new Error(\n `Dataset \"${datasetId}\" must provide exactly one of sqlQuery, arrowTable, or queryResult.`,\n );\n }\n\n if (normalized.queryResult) {\n assertArrowTable(getArrowTableFromQueryResult(normalized.queryResult), datasetId);\n }\n\n return normalized;\n}\n\nexport function resolveArrowTable(\n input: DeckDatasetInput,\n): arrow.Table | undefined {\n return input.arrowTable ?? getArrowTableFromQueryResult(input.queryResult);\n}\n\nexport function normalizeDatasets(\n props: Pick<\n DeckMapProps,\n | 'datasets'\n | 'sqlQuery'\n | 'arrowTable'\n | 'queryResult'\n | 'geometryColumn'\n | 'geometryEncodingHint'\n >,\n): Record<string, DeckDatasetInput> {\n if (props.datasets) {\n const entries = Object.entries(props.datasets).map(([datasetId, input]) => [\n datasetId,\n normalizeDatasetEntry(datasetId, input),\n ]);\n return Object.fromEntries(entries);\n }\n\n const singleInput: DeckDatasetInput = {\n sqlQuery: props.sqlQuery,\n arrowTable: props.arrowTable,\n queryResult: props.queryResult,\n geometryColumn: props.geometryColumn,\n geometryEncodingHint: props.geometryEncodingHint,\n };\n\n const definedInputs = countDefinedInputs(singleInput);\n if (definedInputs === 0) {\n throw new Error(\n 'DeckMap requires either datasets or one of sqlQuery, arrowTable, or queryResult.',\n );\n }\n\n return {\n default: normalizeDatasetEntry('default', singleInput),\n };\n}\n"]}
|