@sqlrooms/deck 0.29.0-rc.12 → 0.29.0-rc.13
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 +144 -22
- package/dist/DeckJsonMap.d.ts.map +1 -1
- package/dist/DeckJsonMap.js +58 -12
- package/dist/DeckJsonMap.js.map +1 -1
- package/dist/DeckMapBasemapSelect.d.ts +9 -0
- package/dist/DeckMapBasemapSelect.d.ts.map +1 -0
- package/dist/DeckMapBasemapSelect.js +20 -0
- package/dist/DeckMapBasemapSelect.js.map +1 -0
- package/dist/DeckMapDefaultStylesProvider.d.ts +13 -19
- package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -1
- package/dist/DeckMapDefaultStylesProvider.js +11 -18
- package/dist/DeckMapDefaultStylesProvider.js.map +1 -1
- package/dist/DeckMapsSlice.d.ts +5 -0
- package/dist/DeckMapsSlice.d.ts.map +1 -1
- package/dist/DeckMapsSlice.js +6 -3
- package/dist/DeckMapsSlice.js.map +1 -1
- package/dist/MapSettings.d.ts.map +1 -1
- package/dist/MapSettings.js +3 -2
- package/dist/MapSettings.js.map +1 -1
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +15 -10
- package/dist/ai.js.map +1 -1
- package/dist/basemap.d.ts +23 -0
- package/dist/basemap.d.ts.map +1 -0
- package/dist/basemap.js +23 -0
- package/dist/basemap.js.map +1 -0
- package/dist/block.d.ts.map +1 -1
- package/dist/block.js +12 -8
- package/dist/block.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/mapAiSharedInstructions.d.ts.map +1 -1
- package/dist/mapAiSharedInstructions.js +1 -0
- package/dist/mapAiSharedInstructions.js.map +1 -1
- package/dist/mapConfig.d.ts +5 -0
- package/dist/mapConfig.d.ts.map +1 -1
- package/dist/mapConfig.js +28 -3
- package/dist/mapConfig.js.map +1 -1
- package/dist/mapConfigUtils.d.ts.map +1 -1
- package/dist/mapConfigUtils.js +7 -2
- package/dist/mapConfigUtils.js.map +1 -1
- package/dist/mapStyles.d.ts +16 -0
- package/dist/mapStyles.d.ts.map +1 -0
- package/dist/mapStyles.js +32 -0
- package/dist/mapStyles.js.map +1 -0
- package/dist/protomapsStyles.d.ts +18 -0
- package/dist/protomapsStyles.d.ts.map +1 -0
- package/dist/protomapsStyles.js +38 -0
- package/dist/protomapsStyles.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
# @sqlrooms/deck
|
|
2
|
+
|
|
1
3
|
Deck.gl integration for SQLRooms with JSON-driven map specs, dataset registry
|
|
2
4
|
binding, DuckDB-backed or in-memory Arrow datasets, and GeoArrow-first geometry
|
|
3
5
|
preparation.
|
|
4
6
|
|
|
7
|
+
## Package entry points
|
|
8
|
+
|
|
9
|
+
| Import | Use |
|
|
10
|
+
| ----------------------- | ----------------------------------------------------------------------------------------- |
|
|
11
|
+
| `@sqlrooms/deck` | Host-neutral maps, durable map resources, block-document integration, and authoring tools |
|
|
12
|
+
| `@sqlrooms/deck/mosaic` | Opt-in Mosaic dashboard renderers, configuration helpers, and AI tools |
|
|
13
|
+
|
|
14
|
+
The [Deck.gl example](https://github.com/sqlrooms/examples/tree/main/deckgl)
|
|
15
|
+
shows direct DuckDB-backed maps. The
|
|
16
|
+
[Deck.gl + Mosaic example](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
|
|
17
|
+
shows custom cross-filter integration.
|
|
18
|
+
|
|
5
19
|
## Map resources and dashboard adapters
|
|
6
20
|
|
|
7
21
|
Document maps are first-class `deckMaps` resources. The root package export
|
|
@@ -39,9 +53,13 @@ resource ownership.
|
|
|
39
53
|
## Installation
|
|
40
54
|
|
|
41
55
|
```bash
|
|
42
|
-
npm install @sqlrooms/deck @sqlrooms/
|
|
56
|
+
npm install @sqlrooms/deck @sqlrooms/room-shell apache-arrow
|
|
43
57
|
```
|
|
44
58
|
|
|
59
|
+
For Mosaic dashboard integration, also install `@sqlrooms/mosaic`,
|
|
60
|
+
`@uwdata/mosaic-core`, and `@uwdata/mosaic-sql`, then import the adapter from
|
|
61
|
+
`@sqlrooms/deck/mosaic`.
|
|
62
|
+
|
|
45
63
|
## What This Package Does
|
|
46
64
|
|
|
47
65
|
`@sqlrooms/deck` is the JSON-spec bridge between SQLRooms data and deck.gl:
|
|
@@ -60,6 +78,10 @@ instead of hand-constructing deck layer instances in React code.
|
|
|
60
78
|
|
|
61
79
|
## Quick Start
|
|
62
80
|
|
|
81
|
+
`DeckJsonMap` resolves SQL and table datasets through the current SQLRooms
|
|
82
|
+
store. Render it inside `RoomShell` or `RoomStateProvider` with a store that
|
|
83
|
+
includes DuckDB state. The example below assumes that host is already mounted.
|
|
84
|
+
|
|
63
85
|
```tsx
|
|
64
86
|
import {DeckJsonMap} from '@sqlrooms/deck';
|
|
65
87
|
|
|
@@ -87,7 +109,8 @@ const spec = {
|
|
|
87
109
|
scheme: 'YlOrRd',
|
|
88
110
|
domain: 'auto',
|
|
89
111
|
},
|
|
90
|
-
getRadius:
|
|
112
|
+
getRadius: 6,
|
|
113
|
+
radiusUnits: 'pixels',
|
|
91
114
|
radiusMinPixels: 2,
|
|
92
115
|
},
|
|
93
116
|
],
|
|
@@ -105,12 +128,78 @@ export function AirportsMap() {
|
|
|
105
128
|
geometryEncodingHint: 'wkb',
|
|
106
129
|
},
|
|
107
130
|
}}
|
|
108
|
-
mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
|
|
109
131
|
/>
|
|
110
132
|
);
|
|
111
133
|
}
|
|
112
134
|
```
|
|
113
135
|
|
|
136
|
+
## Basemaps
|
|
137
|
+
|
|
138
|
+
Deck maps use [OpenFreeMap](https://openfreemap.org/) vector tiles by default:
|
|
139
|
+
**Positron** for light mode and **Dark** for dark mode. No API key or registration
|
|
140
|
+
is required. The hosted styles include attribution, which MapLibre displays.
|
|
141
|
+
Maps work immediately with `createDeckMapsSlice()` or `DeckJsonMap`, without
|
|
142
|
+
additional configuration.
|
|
143
|
+
|
|
144
|
+
New map resources and dashboard panels save `mapStyle: 'light'` or
|
|
145
|
+
`'dark'` using the app theme at creation. Changing the app theme later
|
|
146
|
+
does not change existing maps. The **Basemap** dropdown in map settings selects
|
|
147
|
+
Light or Dark, including for maps with custom layer configurations. The selection
|
|
148
|
+
survives dataset changes, config updates, and saved-workspace reloads; keys and
|
|
149
|
+
generated style objects are not stored in map resources. Applications creating
|
|
150
|
+
configs outside the browser can use `getDefaultDeckMapStyle(theme)` explicitly.
|
|
151
|
+
|
|
152
|
+
Bare `DeckJsonMap` instances and older saved maps without a style continue to
|
|
153
|
+
follow the app theme. Choose a basemap in settings to persist it in an older map.
|
|
154
|
+
Explicit custom `mapStyle` URLs and `mapProps.mapStyle` objects remain supported;
|
|
155
|
+
the selector displays **Custom** until a built-in style is selected.
|
|
156
|
+
|
|
157
|
+
For custom basemaps, supply a `DeckMapBasemapProvider` callback:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
createDeckMapsSlice({basemapProvider: (theme) => customStyles[theme]});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Return stable MapLibre style objects or URLs. Direct `DeckJsonMap` callers
|
|
164
|
+
can pass `basemapProvider` as a prop, overriding the room's provider without
|
|
165
|
+
requiring the Deck maps slice. The existing room store is still required for
|
|
166
|
+
dataset preparation. Explicit custom map styles take precedence over
|
|
167
|
+
the provider.
|
|
168
|
+
|
|
169
|
+
`DeckMapDefaultStylesProvider` and `useDeckMapDefaultStyles` are deprecated.
|
|
170
|
+
To migrate an existing `styles={{light, dark}}` wrapper, keep the style objects
|
|
171
|
+
and pass `basemapProvider: (theme) => styles[theme]` to `createDeckMapsSlice`,
|
|
172
|
+
then remove the wrapper. For individual map overrides, pass the same callback
|
|
173
|
+
to `DeckJsonMap`. Code that reads `useDeckMapDefaultStyles` should use the room's
|
|
174
|
+
`deckMaps.basemapProvider` or the supplied callback instead.
|
|
175
|
+
The context API remains functional for backward compatibility, as a fallback
|
|
176
|
+
when no callback style is available. Removal is reserved for a future breaking
|
|
177
|
+
release.
|
|
178
|
+
|
|
179
|
+
Protomaps remains an optional provider for applications supplying their own key:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import {
|
|
183
|
+
createDeckMapsSlice,
|
|
184
|
+
createProtomapsBasemapProvider,
|
|
185
|
+
} from '@sqlrooms/deck';
|
|
186
|
+
|
|
187
|
+
createDeckMapsSlice({
|
|
188
|
+
basemapProvider: createProtomapsBasemapProvider(protomapsApiKey),
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The helper creates and reuses the light/dark style objects. Its callback lives
|
|
193
|
+
outside `deckMaps.config`, so its key is not persisted with maps. Obtain a
|
|
194
|
+
browser-visible key from [Protomaps](https://protomaps.com/api) and configure
|
|
195
|
+
allowed origins. A missing or blank key falls back to OpenFreeMap.
|
|
196
|
+
Document maps, dashboard maps, and `DeckJsonMap` inherit the room's provider.
|
|
197
|
+
|
|
198
|
+
`createProtomapsStyle(flavor, apiKey)` creates a MapLibre style object, and
|
|
199
|
+
`createProtomapsDefaultStyles(apiKey)` creates the Protomaps light/dark pair.
|
|
200
|
+
`DECK_MAP_BASEMAP_STYLES` lists the provider-neutral IDs and labels. Existing
|
|
201
|
+
`protomaps-light`/`protomaps-dark` selections are recognized as light/dark aliases.
|
|
202
|
+
|
|
114
203
|
## Auto Spec Generation
|
|
115
204
|
|
|
116
205
|
If you want a starter JSON spec instead of writing every layer manually, use
|
|
@@ -164,10 +253,11 @@ const spec = createDeckJsonSpecFromDatasets({
|
|
|
164
253
|
|
|
165
254
|
## Mosaic Dashboard Renderer
|
|
166
255
|
|
|
167
|
-
`@sqlrooms/deck`
|
|
256
|
+
`@sqlrooms/deck/mosaic` contributes a `deck-json-map` panel renderer to
|
|
168
257
|
`@sqlrooms/mosaic` dashboards without making the Mosaic package depend on
|
|
169
|
-
deck.gl or MapLibre.
|
|
170
|
-
|
|
258
|
+
deck.gl or MapLibre. `createDeckMapDashboardSliceOptions()` installs the map
|
|
259
|
+
renderer and add-panel action alongside the default Mosaic renderers, actions,
|
|
260
|
+
and chart types.
|
|
171
261
|
|
|
172
262
|
The dashboard renderer exposes `DeckMapDashboardSettings` through its renderer
|
|
173
263
|
definition. `DeckMapBlockSettings` is also exported for block-document hosts
|
|
@@ -176,20 +266,13 @@ that embed maps as stateful blocks.
|
|
|
176
266
|
```tsx
|
|
177
267
|
import {
|
|
178
268
|
createDeckMapDashboardPanelConfig,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
} from '@sqlrooms/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
} from '@sqlrooms/mosaic';
|
|
187
|
-
|
|
188
|
-
const dashboardSlice = createMosaicDashboardSlice({
|
|
189
|
-
panelRenderers: createDefaultMosaicDashboardPanelRenderers({
|
|
190
|
-
[DECK_MAP_DASHBOARD_PANEL_TYPE]: deckMapDashboardPanelRenderer,
|
|
191
|
-
}),
|
|
192
|
-
});
|
|
269
|
+
createDeckMapDashboardSliceOptions,
|
|
270
|
+
} from '@sqlrooms/deck/mosaic';
|
|
271
|
+
import {createMosaicDashboardSlice, MosaicDashboard} from '@sqlrooms/mosaic';
|
|
272
|
+
|
|
273
|
+
const dashboardSlice = createMosaicDashboardSlice(
|
|
274
|
+
createDeckMapDashboardSliceOptions(),
|
|
275
|
+
);
|
|
193
276
|
|
|
194
277
|
function Dashboard() {
|
|
195
278
|
return <MosaicDashboard dashboardId="geo" />;
|
|
@@ -264,6 +347,10 @@ as durable resources without creating a dashboard. Compose
|
|
|
264
347
|
`ensureDeckMapResourceState(...)` for a durable map id, and render it with
|
|
265
348
|
`DeckMapBlockRenderer`.
|
|
266
349
|
|
|
350
|
+
See [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
|
|
351
|
+
for the surrounding artifact, renderer-provider, persistence, and ownership
|
|
352
|
+
setup.
|
|
353
|
+
|
|
267
354
|
Runtime issue recovery can call `deckMaps.clearMapIssue(mapId, kind)` to clear
|
|
268
355
|
only a matching issue kind; omit `kind` when the map state should clear any
|
|
269
356
|
stale issue. Replacing a map config clears its prior render issue, while data
|
|
@@ -409,6 +496,24 @@ layers (e.g. render points under map labels) and reduces WebGL context usage.
|
|
|
409
496
|
Set `interleaved` to `false` to render deck layers in a separate overlay canvas
|
|
410
497
|
on top of all basemap layers (uses an additional WebGL context per map).
|
|
411
498
|
|
|
499
|
+
MapLibre's drawing buffer is preserved by default so DOM image capture can
|
|
500
|
+
include the basemap and interleaved deck layers after a frame finishes.
|
|
501
|
+
In separate-overlay mode, deck.gl/luma.gl also preserves its drawing buffer by
|
|
502
|
+
default. Both map canvases are identified for capture validation, including
|
|
503
|
+
overlays with a custom deck ID. A lost context or disabled preservation on
|
|
504
|
+
either canvas causes the CLI rendering tools to return an actionable error.
|
|
505
|
+
Capture readiness also tracks dataset preparation, MapLibre tile rendering,
|
|
506
|
+
and deck layers' asynchronous resources. While any map in the requested surface
|
|
507
|
+
is still loading, the CLI rendering tools return an error asking the caller to
|
|
508
|
+
wait and retry, instead of returning an incomplete image.
|
|
509
|
+
Preserving the buffer can increase GPU memory use and reduce rendering
|
|
510
|
+
performance. Hosts that do not need image capture can opt out by setting
|
|
511
|
+
`mapProps.canvasContextAttributes.preserveDrawingBuffer` to `false`.
|
|
512
|
+
Changing this context option requires remounting the map.
|
|
513
|
+
To opt out for a separate deck overlay, set
|
|
514
|
+
`deckProps.deviceProps.webgl.preserveDrawingBuffer` to `false` and remount
|
|
515
|
+
the map; DOM image capture will then be unavailable.
|
|
516
|
+
|
|
412
517
|
```tsx
|
|
413
518
|
{
|
|
414
519
|
/* Default (interleaved): */
|
|
@@ -653,7 +758,7 @@ Deck caches only the expensive geometry preparation layer on top.
|
|
|
653
758
|
|
|
654
759
|
## Supported Layers
|
|
655
760
|
|
|
656
|
-
The
|
|
761
|
+
The map-resource validator and settings UI support this curated layer set:
|
|
657
762
|
|
|
658
763
|
- `GeoArrowScatterplotLayer`
|
|
659
764
|
- `GeoArrowHeatmapLayer`
|
|
@@ -665,6 +770,10 @@ The current curated layer set is:
|
|
|
665
770
|
- `GeoArrowH3HexagonLayer`
|
|
666
771
|
- `GeoJsonLayer`
|
|
667
772
|
|
|
773
|
+
The lower-level `DeckJsonMap` JSON converter also registers
|
|
774
|
+
`GeoArrowSolidPolygonLayer`. Durable resource and AI authoring intentionally do
|
|
775
|
+
not accept it; use `GeoArrowPolygonLayer` for authored map resources.
|
|
776
|
+
|
|
668
777
|
GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls
|
|
669
778
|
back to decoding and GeoJSON-binary preparation, with promotion available
|
|
670
779
|
for point-focused GeoArrow layers such as `GeoArrowScatterplotLayer`,
|
|
@@ -684,7 +793,7 @@ not rewritten — the wrap is an outer pipeline query. Prefer writing
|
|
|
684
793
|
`ST_AsWKB(...)` in transforms when you control the SQL; the pipeline covers
|
|
685
794
|
bare `ST_Point(...)` / table `GEOMETRY` columns for every authoring surface.
|
|
686
795
|
|
|
687
|
-
## AI map
|
|
796
|
+
## Prepare AI-authored map configs
|
|
688
797
|
|
|
689
798
|
AI and host tooling should prepare authored Deck map configs through the shared
|
|
690
799
|
helpers exported from `@sqlrooms/deck`:
|
|
@@ -708,6 +817,11 @@ helpers exported from `@sqlrooms/deck`:
|
|
|
708
817
|
Skips unknown-field rejection when `transformSql` / `sqlQuery` is present
|
|
709
818
|
(aliases may be transform-only).
|
|
710
819
|
|
|
820
|
+
Mosaic-specific AI helpers such as `createDeckMapDashboardTool()`,
|
|
821
|
+
`createDashboardWithDeckMapAiTools()`, and
|
|
822
|
+
`createDashboardAgentToolWithDeckMaps()` are exported from
|
|
823
|
+
`@sqlrooms/deck/mosaic`.
|
|
824
|
+
|
|
711
825
|
Durable resource writes also run `getDeckMapResourceConfigIssues` /
|
|
712
826
|
`assertDeckMapResourceConfig` for syntax, supported layer types, and type/scheme
|
|
713
827
|
compatibility (e.g. `quantile` + `Viridis` is rejected; layer `@@type` must be
|
|
@@ -724,3 +838,11 @@ Keep the spec serializable, then pass runtime behavior separately:
|
|
|
724
838
|
|
|
725
839
|
This lets the spec stay stable for storage, validation, and future AI-assisted
|
|
726
840
|
generation while still supporting interactive React behavior at runtime.
|
|
841
|
+
|
|
842
|
+
## More resources
|
|
843
|
+
|
|
844
|
+
- [`@sqlrooms/deck` API reference](https://sqlrooms.org/api/deck/)
|
|
845
|
+
- [Deck.gl example source](https://github.com/sqlrooms/examples/tree/main/deckgl)
|
|
846
|
+
- [Deck.gl + Mosaic example source](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
|
|
847
|
+
- [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
|
|
848
|
+
- [Commands](https://sqlrooms.org/commands.html)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAaA,OAAO,kCAAkC,CAAC;AAyB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AA8SjB,eAAO,MAAM,WAAW,gHAsXvB,CAAC"}
|
package/dist/DeckJsonMap.js
CHANGED
|
@@ -2,14 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { JSONConverter } from '@deck.gl/json';
|
|
3
3
|
import { MapboxOverlay } from '@deck.gl/mapbox';
|
|
4
4
|
import { ColorScaleLegend } from '@sqlrooms/color-scales';
|
|
5
|
+
import { useBaseRoomStore } from '@sqlrooms/room-store';
|
|
5
6
|
import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger, useTheme, } from '@sqlrooms/ui';
|
|
6
7
|
import { ChevronRightIcon } from 'lucide-react';
|
|
7
8
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
8
|
-
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
9
|
+
import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
9
10
|
import { forwardRef } from 'react';
|
|
10
11
|
import Map, { useControl } from 'react-map-gl/maplibre';
|
|
11
12
|
import { DeckJsonMapSpec } from './DeckJsonMapSpec';
|
|
12
|
-
import { resolveDeckMapStyle
|
|
13
|
+
import { resolveDeckMapStyle } from './basemap';
|
|
14
|
+
import { useDeckMapDefaultStyles } from './DeckMapDefaultStylesProvider';
|
|
13
15
|
import { normalizeDatasets } from './datasets/normalizeDatasets';
|
|
14
16
|
import { usePreparedDatasetStates } from './datasets/usePreparedDatasetStates';
|
|
15
17
|
import { createDeckJsonConfiguration } from './json/createDeckJsonConfiguration';
|
|
@@ -18,8 +20,8 @@ import { getLayerCompatibility } from './json/layerCompatibility';
|
|
|
18
20
|
import { resolveDatasetId } from './json/layerConfig';
|
|
19
21
|
import { buildDeckMapJumpToOptions } from './mapFit';
|
|
20
22
|
const DEFAULT_MAP_STYLES = {
|
|
21
|
-
light: 'https://
|
|
22
|
-
dark: 'https://
|
|
23
|
+
light: 'https://tiles.openfreemap.org/styles/positron',
|
|
24
|
+
dark: 'https://tiles.openfreemap.org/styles/dark',
|
|
23
25
|
};
|
|
24
26
|
function parseSpec(spec) {
|
|
25
27
|
try {
|
|
@@ -121,7 +123,13 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
|
|
|
121
123
|
const overlay = useControl(() => new MapboxOverlay({ interleaved }));
|
|
122
124
|
const prevLayerKeyRef = useRef('');
|
|
123
125
|
const [clearing, setClearing] = useState(false);
|
|
126
|
+
const captureStatusRef = useRef(null);
|
|
124
127
|
const layers = deckProps.layers;
|
|
128
|
+
const captureLayers = useMemo(() => (layers?.flat(Infinity).filter(Boolean) ?? []), [layers]);
|
|
129
|
+
useLayoutEffect(() => {
|
|
130
|
+
// A previous frame no longer represents the current layers.
|
|
131
|
+
captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', 'true');
|
|
132
|
+
}, [layers, clearing]);
|
|
125
133
|
const layerKey = Array.isArray(layers)
|
|
126
134
|
? layers.map(getDeckLayerTypeName).join(',')
|
|
127
135
|
: '';
|
|
@@ -135,23 +143,44 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
|
|
|
135
143
|
}, [layerKey, clearing]);
|
|
136
144
|
useEffect(() => {
|
|
137
145
|
if (clearing && overlay) {
|
|
138
|
-
|
|
146
|
+
// The temporary empty frame must not advertise capture readiness.
|
|
147
|
+
overlay.setProps({ layers: [], onAfterRender: () => { } });
|
|
139
148
|
requestAnimationFrame(() => setClearing(false));
|
|
140
149
|
}
|
|
141
150
|
}, [clearing, overlay]);
|
|
142
|
-
|
|
143
|
-
overlay
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
useLayoutEffect(() => {
|
|
152
|
+
if (clearing || !overlay)
|
|
153
|
+
return;
|
|
154
|
+
overlay.setProps({
|
|
155
|
+
...deckProps,
|
|
156
|
+
onLoad: () => {
|
|
157
|
+
// The separate canvas is created asynchronously. Mark it once deck
|
|
158
|
+
// initializes, including when the host supplies a custom canvas ID.
|
|
159
|
+
overlay.getCanvas()?.setAttribute('data-sqlrooms-deck-canvas', '');
|
|
160
|
+
if (typeof deckProps.onLoad === 'function')
|
|
161
|
+
deckProps.onLoad();
|
|
162
|
+
},
|
|
163
|
+
onAfterRender: (context) => {
|
|
164
|
+
const loaded = captureLayers.every((layer) => layer.isLoaded);
|
|
165
|
+
captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', String(!loaded));
|
|
166
|
+
if (typeof deckProps.onAfterRender === 'function') {
|
|
167
|
+
deckProps.onAfterRender(context);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}, [clearing, overlay, deckProps, captureLayers]);
|
|
172
|
+
return (_jsx("span", { hidden: true, ref: captureStatusRef, "data-sqlrooms-map-loading": "true" }));
|
|
146
173
|
}
|
|
147
174
|
function DeckMapRenderingErrorOverlay({ error }) {
|
|
148
175
|
return (_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: _jsxs(Collapsible, { children: [_jsxs("div", { className: "flex items-start gap-1", children: [_jsx("span", { children: "Map couldn't be rendered" }), _jsx(CollapsibleTrigger, { className: "group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100", "aria-label": "Show error details", children: _jsx(ChevronRightIcon, { className: "h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90" }) })] }), _jsx(CollapsibleContent, { children: _jsx("pre", { className: "mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap", children: error.message }) })] }) }) }));
|
|
149
176
|
}
|
|
150
|
-
export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, mapStyle, interleaved = true, deckProps, mapProps, showLegends = true, className, children, onDatasetStatesChange, onRenderingError, }, ref) {
|
|
177
|
+
export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, mapStyle, basemapProvider, interleaved = true, deckProps, mapProps, showLegends = true, className, children, onDatasetStatesChange, onRenderingError, }, ref) {
|
|
151
178
|
const normalizedDatasets = useMemo(() => normalizeDatasets(datasets), [datasets]);
|
|
152
179
|
const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());
|
|
153
180
|
const datasetIds = useMemo(() => JSON.parse(datasetIdKey), [datasetIdKey]);
|
|
154
181
|
const datasetStates = usePreparedDatasetStates(normalizedDatasets);
|
|
182
|
+
const [mapReady, setMapReady] = useState(false);
|
|
183
|
+
const datasetsLoading = datasetIds.some((id) => !datasetStates[id] || datasetStates[id]?.status === 'loading');
|
|
155
184
|
const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);
|
|
156
185
|
const { spec: parsedSpec, error: specError } = useMemo(() => parseSpec(spec), [spec]);
|
|
157
186
|
const availableSpec = useMemo(() => parsedSpec
|
|
@@ -348,11 +377,19 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
|
|
|
348
377
|
}, [extraMapProps]);
|
|
349
378
|
const { resolvedTheme } = useTheme();
|
|
350
379
|
const hostDefaultMapStyles = useDeckMapDefaultStyles();
|
|
380
|
+
const roomBasemapProvider = useBaseRoomStore((state) => state.deckMaps?.basemapProvider);
|
|
351
381
|
const mergedMapProps = {
|
|
352
382
|
...extraMapProps,
|
|
383
|
+
// DOM image capture reads the canvas after the render frame has ended.
|
|
384
|
+
// Interleaved deck layers share this buffer with the basemap.
|
|
385
|
+
canvasContextAttributes: {
|
|
386
|
+
preserveDrawingBuffer: true,
|
|
387
|
+
...mapProps?.canvasContextAttributes,
|
|
388
|
+
},
|
|
353
389
|
mapStyle: resolveDeckMapStyle({
|
|
354
390
|
mapStyle,
|
|
355
391
|
mapPropsMapStyle: mapProps?.mapStyle,
|
|
392
|
+
basemapProvider: basemapProvider ?? roomBasemapProvider,
|
|
356
393
|
hostDefaultStyles: hostDefaultMapStyles,
|
|
357
394
|
resolvedTheme,
|
|
358
395
|
fallbackStyles: DEFAULT_MAP_STYLES,
|
|
@@ -365,8 +402,17 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
|
|
|
365
402
|
datasetStates,
|
|
366
403
|
})
|
|
367
404
|
: [], [availableSpec, datasetIds, showLegends, datasetStates]);
|
|
368
|
-
return (_jsxs("div", { className: cn('relative h-full w-full', className), children: [finalDeckPropsResult.error ? (_jsx(DeckMapRenderingErrorOverlay, { error: finalDeckPropsResult.error })) : null, _jsxs(Map, { ref: mapRef, ...mergedMapProps, ...(initialViewState
|
|
405
|
+
return (_jsxs("div", { className: cn('relative h-full w-full', className), "data-sqlrooms-map-loading": !mapReady || datasetsLoading, children: [finalDeckPropsResult.error ? (_jsx(DeckMapRenderingErrorOverlay, { error: finalDeckPropsResult.error })) : null, _jsxs(Map, { ref: mapRef, ...mergedMapProps, ...(initialViewState
|
|
369
406
|
? { initialViewState: initialViewState }
|
|
370
|
-
: {}), onLoad: handleMapLoad,
|
|
407
|
+
: {}), onLoad: handleMapLoad, onData: (event) => {
|
|
408
|
+
setMapReady(false);
|
|
409
|
+
mapProps?.onData?.(event);
|
|
410
|
+
}, onMoveStart: (event) => {
|
|
411
|
+
setMapReady(false);
|
|
412
|
+
mapProps?.onMoveStart?.(event);
|
|
413
|
+
}, onRender: (event) => {
|
|
414
|
+
setMapReady(event.target.loaded());
|
|
415
|
+
mapProps?.onRender?.(event);
|
|
416
|
+
}, style: { width: '100%', height: '100%', ...mapProps?.style }, children: [_jsx(DeckOverlayControl, { interleaved: interleaved, ...overlayDeckProps }), children] }), renderDatasetErrorOverlay(datasetStates), !hasRenderingError && showLegends ? (_jsxs("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10 max-w-56", children: [hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "mb-1", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null, _jsx(ColorScaleLegend, { legends: legends })] })) : hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null] }));
|
|
371
417
|
});
|
|
372
418
|
//# sourceMappingURL=DeckJsonMap.js.map
|
package/dist/DeckJsonMap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeckJsonMap.js","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"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","sourcesContent":["import {JSONConverter} from '@deck.gl/json';\nimport {MapboxOverlay} from '@deck.gl/mapbox';\nimport {ColorScaleLegend} from '@sqlrooms/color-scales';\nimport {\n cn,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n ResolvedTheme,\n useTheme,\n} from '@sqlrooms/ui';\nimport {ChevronRightIcon} from 'lucide-react';\nimport 'maplibre-gl/dist/maplibre-gl.css';\nimport {\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type FC,\n} from 'react';\nimport {forwardRef} from 'react';\nimport Map, {useControl} from 'react-map-gl/maplibre';\nimport {ZodError} from 'zod';\nimport {DeckJsonMapSpec} from './DeckJsonMapSpec';\nimport {\n resolveDeckMapStyle,\n useDeckMapDefaultStyles,\n} from './DeckMapDefaultStylesProvider';\nimport {normalizeDatasets} from './datasets/normalizeDatasets';\nimport {usePreparedDatasetStates} from './datasets/usePreparedDatasetStates';\nimport {createDeckJsonConfiguration} from './json/createDeckJsonConfiguration';\nimport {extractColorScaleLegends} from './json/extractColorScaleLegends';\nimport {getLayerCompatibility} from './json/layerCompatibility';\nimport {resolveDatasetId} from './json/layerConfig';\nimport {buildDeckMapJumpToOptions} from './mapFit';\nimport type {\n DeckJsonMapHandle,\n DeckJsonMapProps,\n PreparedDeckDatasetState,\n} from './types';\n\nconst DEFAULT_MAP_STYLES: Record<ResolvedTheme, string> = {\n light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',\n dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',\n};\n\nfunction parseSpec(spec: DeckJsonMapProps['spec']) {\n try {\n const parsedValue = typeof spec === 'string' ? JSON.parse(spec) : spec;\n const validatedSpec = DeckJsonMapSpec.safeParse(parsedValue);\n if (!validatedSpec.success) {\n return {\n spec: null,\n error: new Error(formatSpecValidationError(validatedSpec.error)),\n };\n }\n\n return {\n spec: validatedSpec.data,\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 formatSpecValidationError(error: ZodError) {\n const issues = error.issues.map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : 'spec';\n return `${path}: ${issue.message}`;\n });\n\n return `Invalid DeckJsonMap spec. ${issues.join('; ')}`;\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 !== 'error';\n });\n\n return {\n ...spec,\n layers: filteredLayers,\n };\n}\n\nfunction renderDatasetErrorOverlay(\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\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 (!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 {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\nconst TripsTimeControl: FC<{\n currentTime: number;\n maxTime: number;\n playing: boolean;\n onPlayPause: () => void;\n onSeek: (time: number) => void;\n}> = ({currentTime, maxTime, playing, onPlayPause, onSeek}) => {\n const [collapsed, setCollapsed] = useState(false);\n const progress = maxTime > 0 ? currentTime / maxTime : 0;\n\n if (collapsed) {\n return (\n <button\n type=\"button\"\n onClick={() => setCollapsed(false)}\n className=\"border-border/70 bg-background/90 text-muted-foreground hover:text-foreground pointer-events-auto flex w-fit items-center gap-1 rounded-md border px-2 py-1 text-[10px] leading-none font-medium shadow-sm backdrop-blur-sm transition-colors\"\n >\n Timeline\n </button>\n );\n }\n\n return (\n <div className=\"bg-background/90 border-border/70 pointer-events-auto relative flex items-center gap-1.5 rounded-md border px-2 py-1.5 pr-7 shadow-sm backdrop-blur-sm\">\n <button\n type=\"button\"\n onClick={onPlayPause}\n className=\"text-foreground hover:bg-accent flex h-5 w-5 shrink-0 items-center justify-center rounded\"\n title={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <rect x=\"1\" y=\"1\" width=\"3\" height=\"8\" />\n <rect x=\"6\" y=\"1\" width=\"3\" height=\"8\" />\n </svg>\n ) : (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <polygon points=\"2,1 9,5 2,9\" />\n </svg>\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1000}\n value={Math.round(progress * 1000)}\n onChange={(e) => onSeek((Number(e.target.value) / 1000) * maxTime)}\n className=\"h-1 w-24 flex-1 cursor-pointer\"\n />\n <button\n type=\"button\"\n onClick={() => setCollapsed(true)}\n className=\"text-muted-foreground hover:text-foreground absolute top-1.5 right-1.5 flex h-4 w-4 items-center justify-center rounded-sm transition-colors\"\n title=\"Collapse\"\n >\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n <path\n d=\"M1 1L7 7M7 1L1 7\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n );\n};\n\nfunction getDeckLayerTypeName(layer: unknown): string {\n if (!layer || typeof layer !== 'object') return '?';\n const ctor = (\n layer as {\n constructor?: {layerName?: string; name?: string};\n }\n ).constructor;\n // Prefer static layerName: table-to-RecordBatch adapters all share\n // constructor.name \"Adapter\", which would miss heatmap ↔ scatterplot swaps.\n return ctor?.layerName ?? ctor?.name ?? '?';\n}\n\n// Workaround for deck.gl HeatmapLayer not releasing its WebGL framebuffer\n// when replaced by another layer type (e.g. switching heatmap → scatterplot).\n// We detect layer class changes and clear layers for one animation frame,\n// giving deck.gl a chance to finalize the old layer's resources before\n// initializing the new one.\nfunction DeckOverlayControl({\n interleaved,\n ...deckProps\n}: {interleaved: boolean} & Record<string, unknown>) {\n const overlay = useControl<MapboxOverlay>(\n () => new MapboxOverlay({interleaved}),\n );\n const prevLayerKeyRef = useRef<string>('');\n const [clearing, setClearing] = useState(false);\n\n const layers = deckProps.layers as unknown[] | undefined;\n const layerKey = Array.isArray(layers)\n ? layers.map(getDeckLayerTypeName).join(',')\n : '';\n\n useEffect(() => {\n if (\n prevLayerKeyRef.current &&\n prevLayerKeyRef.current !== layerKey &&\n !clearing\n ) {\n setClearing(true);\n }\n prevLayerKeyRef.current = layerKey;\n }, [layerKey, clearing]);\n\n useEffect(() => {\n if (clearing && overlay) {\n overlay.setProps({layers: []});\n requestAnimationFrame(() => setClearing(false));\n }\n }, [clearing, overlay]);\n\n if (!clearing && overlay) {\n overlay.setProps(deckProps);\n }\n\n return null;\n}\n\nfunction DeckMapRenderingErrorOverlay({error}: {error: Error}) {\n return (\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 <Collapsible>\n <div className=\"flex items-start gap-1\">\n <span>{\"Map couldn't be rendered\"}</span>\n <CollapsibleTrigger\n className=\"group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100\"\n aria-label=\"Show error details\"\n >\n <ChevronRightIcon className=\"h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90\" />\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <pre className=\"mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap\">\n {error.message}\n </pre>\n </CollapsibleContent>\n </Collapsible>\n </div>\n </div>\n );\n}\n\nexport const DeckJsonMap = forwardRef<DeckJsonMapHandle, DeckJsonMapProps>(\n function DeckJsonMap(\n {\n spec,\n datasets,\n mapStyle,\n interleaved = true,\n deckProps,\n mapProps,\n showLegends = true,\n className,\n children,\n onDatasetStatesChange,\n onRenderingError,\n },\n ref,\n ) {\n const normalizedDatasets = useMemo(\n () => normalizeDatasets(datasets),\n [datasets],\n );\n const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());\n const datasetIds = useMemo(\n () => JSON.parse(datasetIdKey) as string[],\n [datasetIdKey],\n );\n const datasetStates = usePreparedDatasetStates(normalizedDatasets);\n const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);\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 // Animation for TripsLayer — update currentTime on each frame.\n // Only activate when the trips dataset is ready to avoid spinning the RAF\n // loop while data is still loading.\n const hasTripsLayer = useMemo(() => {\n if (!availableSpec || !Array.isArray(availableSpec.layers)) return false;\n return availableSpec.layers.some((l: unknown) => {\n if (\n !l ||\n typeof l !== 'object' ||\n (l as {'@@type'?: string})['@@type'] !== 'GeoArrowTripsLayer'\n )\n return false;\n const dsId = resolveDatasetId(l as Record<string, unknown>, datasetIds);\n if (!dsId) return true;\n const state = datasetStates[dsId];\n return state?.status === 'ready';\n });\n }, [availableSpec, datasetIds, datasetStates]);\n\n const [tripsTime, setTripsTime] = useState(0);\n const [tripsPlaying, setTripsPlaying] = useState(true);\n const tripsTimeRef = useRef(0);\n\n useEffect(() => {\n if (!hasTripsLayer || !tripsPlaying) return;\n let raf: number;\n let lastFrame = Date.now();\n const tick = () => {\n const now = Date.now();\n tripsTimeRef.current += now - lastFrame;\n lastFrame = now;\n setTripsTime(tripsTimeRef.current);\n raf = requestAnimationFrame(tick);\n };\n lastFrame = Date.now();\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [hasTripsLayer, tripsPlaying]);\n\n const handleTripsPlayPause = useCallback(() => {\n setTripsPlaying((p) => !p);\n }, []);\n\n const handleTripsSeek = useCallback((time: number) => {\n const rawTime = time * 30;\n tripsTimeRef.current = rawTime;\n setTripsTime(rawTime);\n }, []);\n\n const finalDeckPropsResult = convertedDeckPropsResult;\n\n const onRenderingErrorRef = useRef(onRenderingError);\n useEffect(() => {\n onRenderingErrorRef.current = onRenderingError;\n }, [onRenderingError]);\n\n useEffect(() => {\n if (finalDeckPropsResult.error) {\n console.error(finalDeckPropsResult.error);\n onRenderingErrorRef.current?.(finalDeckPropsResult.error);\n }\n }, [finalDeckPropsResult.error]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current = onDatasetStatesChange;\n }, [onDatasetStatesChange]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current?.(datasetStates);\n }, [datasetStates]);\n\n const fallbackDeckProps = useMemo(\n () => extractFallbackDeckProps(availableSpec),\n [availableSpec],\n );\n const convertedDeckProps = (finalDeckPropsResult.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(finalDeckPropsResult.error);\n\n // Separate stable layer analysis from per-frame animation to avoid\n // re-running .map() over all layers 60 times per second.\n const baseLayers = useMemo(() => {\n const mergedLayers = hasRenderingError\n ? []\n : (deckProps?.layers ??\n (convertedDeckProps.layers as unknown[] | undefined) ??\n []);\n if (!hasTripsLayer || !Array.isArray(mergedLayers))\n return {layers: mergedLayers, maxTs: 0, tripsIndices: [] as number[]};\n let computedMaxTs = 0;\n const tripsIndices: number[] = [];\n for (let i = 0; i < mergedLayers.length; i++) {\n const layer = mergedLayers[i];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {props?: Record<string, unknown>};\n const maxTs = (layerObj.props as Record<string, unknown> | undefined)\n ?._tripsMaxTimestamp as number | undefined;\n if (maxTs && maxTs > 0) {\n computedMaxTs = Math.max(computedMaxTs, maxTs);\n tripsIndices.push(i);\n }\n }\n return {layers: mergedLayers, maxTs: computedMaxTs, tripsIndices};\n }, [\n hasTripsLayer,\n hasRenderingError,\n deckProps?.layers,\n convertedDeckProps.layers,\n ]);\n\n // Per-frame: only clone trips layers with updated currentTime\n const animatedLayers = useMemo(() => {\n const {layers, tripsIndices} = baseLayers;\n if (!tripsIndices.length) return layers;\n if (!Array.isArray(layers)) return layers;\n const result = layers.slice();\n for (const idx of tripsIndices) {\n const layer = result[idx];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {\n props?: Record<string, unknown>;\n clone?: (props: Record<string, unknown>) => unknown;\n };\n if (!layerObj.clone) continue;\n const props = layerObj.props as Record<string, unknown> | undefined;\n const maxTs = (props?._tripsMaxTimestamp as number | undefined) ?? 0;\n const trailFraction =\n (props?._trailLengthFactor as number | undefined) ?? 0.4;\n const widthPx = (props?.widthMinPixels as number | undefined) ?? 3;\n const animationSpeed = 30;\n const currentTime = (tripsTime / animationSpeed) % maxTs;\n result[idx] = layerObj.clone({\n currentTime,\n trailLength: maxTs * trailFraction,\n widthMinPixels: widthPx,\n });\n }\n return result;\n }, [baseLayers, tripsTime]);\n const tripsMaxTimeValue = baseLayers.maxTs;\n\n const tripsCurrentTime = useMemo(() => {\n if (!tripsMaxTimeValue || tripsMaxTimeValue <= 0) return 0;\n return (tripsTime / 30) % tripsMaxTimeValue;\n }, [tripsTime, tripsMaxTimeValue]);\n\n const mergedDeckProps = {\n ...convertedDeckProps,\n ...extraDeckProps,\n layers: animatedLayers,\n };\n\n // overlayDeckProps should not contain viewState/initialViewState\n const {\n initialViewState,\n viewState: _vs,\n onViewStateChange: _onViewStateChange,\n ...overlayDeckProps\n } = mergedDeckProps as Record<string, unknown>;\n void _vs;\n void _onViewStateChange;\n\n const mapRef = useRef<{jumpTo: (opts: object) => void} | null>(null);\n const pendingJumpRef = useRef<object | null>(null);\n\n useImperativeHandle(\n ref,\n () => ({\n jumpTo(opts) {\n const jumpOpts = buildDeckMapJumpToOptions(opts);\n if (mapRef.current) {\n mapRef.current.jumpTo(jumpOpts);\n } else {\n pendingJumpRef.current = jumpOpts;\n }\n },\n }),\n [],\n );\n\n const handleMapLoad = useCallback(\n (event?: unknown) => {\n if (pendingJumpRef.current && mapRef.current) {\n mapRef.current.jumpTo(pendingJumpRef.current);\n pendingJumpRef.current = null;\n }\n const externalOnLoad = (\n extraMapProps as {onLoad?: (e?: unknown) => void}\n ).onLoad;\n externalOnLoad?.(event);\n },\n [extraMapProps],\n );\n\n const {resolvedTheme} = useTheme();\n const hostDefaultMapStyles = useDeckMapDefaultStyles();\n\n const mergedMapProps = {\n ...extraMapProps,\n mapStyle: resolveDeckMapStyle({\n mapStyle,\n mapPropsMapStyle: mapProps?.mapStyle,\n hostDefaultStyles: hostDefaultMapStyles,\n resolvedTheme,\n fallbackStyles: DEFAULT_MAP_STYLES,\n }),\n };\n const legends = useMemo(\n () =>\n showLegends\n ? extractColorScaleLegends({\n spec: availableSpec,\n datasetIds,\n datasetStates,\n })\n : [],\n [availableSpec, datasetIds, showLegends, datasetStates],\n );\n\n return (\n <div className={cn('relative h-full w-full', className)}>\n {finalDeckPropsResult.error ? (\n <DeckMapRenderingErrorOverlay error={finalDeckPropsResult.error} />\n ) : null}\n\n <Map\n ref={mapRef as any}\n {...(mergedMapProps as object)}\n {...(initialViewState\n ? {initialViewState: initialViewState as object}\n : {})}\n onLoad={handleMapLoad}\n style={{width: '100%', height: '100%', ...mapProps?.style}}\n >\n <DeckOverlayControl interleaved={interleaved} {...overlayDeckProps} />\n {children}\n </Map>\n\n {renderDatasetErrorOverlay(datasetStates)}\n {!hasRenderingError && showLegends ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10 max-w-56\">\n {hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"mb-1\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n <ColorScaleLegend legends={legends} />\n </div>\n ) : hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n </div>\n );\n },\n);\n"]}
|
|
1
|
+
{"version":3,"file":"DeckJsonMap.js","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"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","sourcesContent":["import type {Layer} from '@deck.gl/core';\nimport {JSONConverter} from '@deck.gl/json';\nimport {MapboxOverlay} from '@deck.gl/mapbox';\nimport {ColorScaleLegend} from '@sqlrooms/color-scales';\nimport {useBaseRoomStore} from '@sqlrooms/room-store';\nimport {\n cn,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n useTheme,\n} from '@sqlrooms/ui';\nimport {ChevronRightIcon} from 'lucide-react';\nimport 'maplibre-gl/dist/maplibre-gl.css';\nimport {\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type FC,\n} from 'react';\nimport {forwardRef} from 'react';\nimport Map, {useControl} from 'react-map-gl/maplibre';\nimport {ZodError} from 'zod';\nimport {DeckJsonMapSpec} from './DeckJsonMapSpec';\nimport {resolveDeckMapStyle, type DeckMapBasemapProvider} from './basemap';\nimport {useDeckMapDefaultStyles} from './DeckMapDefaultStylesProvider';\nimport type {DeckMapsSliceState} from './DeckMapsSlice';\nimport {normalizeDatasets} from './datasets/normalizeDatasets';\nimport {usePreparedDatasetStates} from './datasets/usePreparedDatasetStates';\nimport {createDeckJsonConfiguration} from './json/createDeckJsonConfiguration';\nimport {extractColorScaleLegends} from './json/extractColorScaleLegends';\nimport {getLayerCompatibility} from './json/layerCompatibility';\nimport {resolveDatasetId} from './json/layerConfig';\nimport {buildDeckMapJumpToOptions} from './mapFit';\nimport type {\n DeckJsonMapHandle,\n DeckJsonMapProps,\n PreparedDeckDatasetState,\n} from './types';\n\nconst DEFAULT_MAP_STYLES = {\n light: 'https://tiles.openfreemap.org/styles/positron',\n dark: 'https://tiles.openfreemap.org/styles/dark',\n} as const;\n\nfunction parseSpec(spec: DeckJsonMapProps['spec']) {\n try {\n const parsedValue = typeof spec === 'string' ? JSON.parse(spec) : spec;\n const validatedSpec = DeckJsonMapSpec.safeParse(parsedValue);\n if (!validatedSpec.success) {\n return {\n spec: null,\n error: new Error(formatSpecValidationError(validatedSpec.error)),\n };\n }\n\n return {\n spec: validatedSpec.data,\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 formatSpecValidationError(error: ZodError) {\n const issues = error.issues.map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : 'spec';\n return `${path}: ${issue.message}`;\n });\n\n return `Invalid DeckJsonMap spec. ${issues.join('; ')}`;\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 !== 'error';\n });\n\n return {\n ...spec,\n layers: filteredLayers,\n };\n}\n\nfunction renderDatasetErrorOverlay(\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\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 (!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 {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\nconst TripsTimeControl: FC<{\n currentTime: number;\n maxTime: number;\n playing: boolean;\n onPlayPause: () => void;\n onSeek: (time: number) => void;\n}> = ({currentTime, maxTime, playing, onPlayPause, onSeek}) => {\n const [collapsed, setCollapsed] = useState(false);\n const progress = maxTime > 0 ? currentTime / maxTime : 0;\n\n if (collapsed) {\n return (\n <button\n type=\"button\"\n onClick={() => setCollapsed(false)}\n className=\"border-border/70 bg-background/90 text-muted-foreground hover:text-foreground pointer-events-auto flex w-fit items-center gap-1 rounded-md border px-2 py-1 text-[10px] leading-none font-medium shadow-sm backdrop-blur-sm transition-colors\"\n >\n Timeline\n </button>\n );\n }\n\n return (\n <div className=\"bg-background/90 border-border/70 pointer-events-auto relative flex items-center gap-1.5 rounded-md border px-2 py-1.5 pr-7 shadow-sm backdrop-blur-sm\">\n <button\n type=\"button\"\n onClick={onPlayPause}\n className=\"text-foreground hover:bg-accent flex h-5 w-5 shrink-0 items-center justify-center rounded\"\n title={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <rect x=\"1\" y=\"1\" width=\"3\" height=\"8\" />\n <rect x=\"6\" y=\"1\" width=\"3\" height=\"8\" />\n </svg>\n ) : (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <polygon points=\"2,1 9,5 2,9\" />\n </svg>\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1000}\n value={Math.round(progress * 1000)}\n onChange={(e) => onSeek((Number(e.target.value) / 1000) * maxTime)}\n className=\"h-1 w-24 flex-1 cursor-pointer\"\n />\n <button\n type=\"button\"\n onClick={() => setCollapsed(true)}\n className=\"text-muted-foreground hover:text-foreground absolute top-1.5 right-1.5 flex h-4 w-4 items-center justify-center rounded-sm transition-colors\"\n title=\"Collapse\"\n >\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n <path\n d=\"M1 1L7 7M7 1L1 7\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n );\n};\n\nfunction getDeckLayerTypeName(layer: unknown): string {\n if (!layer || typeof layer !== 'object') return '?';\n const ctor = (\n layer as {\n constructor?: {layerName?: string; name?: string};\n }\n ).constructor;\n // Prefer static layerName: table-to-RecordBatch adapters all share\n // constructor.name \"Adapter\", which would miss heatmap ↔ scatterplot swaps.\n return ctor?.layerName ?? ctor?.name ?? '?';\n}\n\n// Workaround for deck.gl HeatmapLayer not releasing its WebGL framebuffer\n// when replaced by another layer type (e.g. switching heatmap → scatterplot).\n// We detect layer class changes and clear layers for one animation frame,\n// giving deck.gl a chance to finalize the old layer's resources before\n// initializing the new one.\nfunction DeckOverlayControl({\n interleaved,\n ...deckProps\n}: {interleaved: boolean} & Record<string, unknown>) {\n const overlay = useControl<MapboxOverlay>(\n () => new MapboxOverlay({interleaved}),\n );\n const prevLayerKeyRef = useRef<string>('');\n const [clearing, setClearing] = useState(false);\n const captureStatusRef = useRef<HTMLSpanElement>(null);\n\n const layers = deckProps.layers as unknown[] | undefined;\n const captureLayers = useMemo(\n () => (layers?.flat(Infinity).filter(Boolean) ?? []) as Layer[],\n [layers],\n );\n useLayoutEffect(() => {\n // A previous frame no longer represents the current layers.\n captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', 'true');\n }, [layers, clearing]);\n const layerKey = Array.isArray(layers)\n ? layers.map(getDeckLayerTypeName).join(',')\n : '';\n\n useEffect(() => {\n if (\n prevLayerKeyRef.current &&\n prevLayerKeyRef.current !== layerKey &&\n !clearing\n ) {\n setClearing(true);\n }\n prevLayerKeyRef.current = layerKey;\n }, [layerKey, clearing]);\n\n useEffect(() => {\n if (clearing && overlay) {\n // The temporary empty frame must not advertise capture readiness.\n overlay.setProps({layers: [], onAfterRender: () => {}});\n requestAnimationFrame(() => setClearing(false));\n }\n }, [clearing, overlay]);\n\n useLayoutEffect(() => {\n if (clearing || !overlay) return;\n overlay.setProps({\n ...deckProps,\n onLoad: () => {\n // The separate canvas is created asynchronously. Mark it once deck\n // initializes, including when the host supplies a custom canvas ID.\n overlay.getCanvas()?.setAttribute('data-sqlrooms-deck-canvas', '');\n if (typeof deckProps.onLoad === 'function') deckProps.onLoad();\n },\n onAfterRender: (context) => {\n const loaded = captureLayers.every((layer) => layer.isLoaded);\n captureStatusRef.current?.setAttribute(\n 'data-sqlrooms-map-loading',\n String(!loaded),\n );\n if (typeof deckProps.onAfterRender === 'function') {\n deckProps.onAfterRender(context);\n }\n },\n });\n }, [clearing, overlay, deckProps, captureLayers]);\n\n return (\n <span hidden ref={captureStatusRef} data-sqlrooms-map-loading=\"true\" />\n );\n}\n\nfunction DeckMapRenderingErrorOverlay({error}: {error: Error}) {\n return (\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 <Collapsible>\n <div className=\"flex items-start gap-1\">\n <span>{\"Map couldn't be rendered\"}</span>\n <CollapsibleTrigger\n className=\"group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100\"\n aria-label=\"Show error details\"\n >\n <ChevronRightIcon className=\"h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90\" />\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <pre className=\"mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap\">\n {error.message}\n </pre>\n </CollapsibleContent>\n </Collapsible>\n </div>\n </div>\n );\n}\n\nexport const DeckJsonMap = forwardRef<DeckJsonMapHandle, DeckJsonMapProps>(\n function DeckJsonMap(\n {\n spec,\n datasets,\n mapStyle,\n basemapProvider,\n interleaved = true,\n deckProps,\n mapProps,\n showLegends = true,\n className,\n children,\n onDatasetStatesChange,\n onRenderingError,\n },\n ref,\n ) {\n const normalizedDatasets = useMemo(\n () => normalizeDatasets(datasets),\n [datasets],\n );\n const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());\n const datasetIds = useMemo(\n () => JSON.parse(datasetIdKey) as string[],\n [datasetIdKey],\n );\n const datasetStates = usePreparedDatasetStates(normalizedDatasets);\n const [mapReady, setMapReady] = useState(false);\n const datasetsLoading = datasetIds.some(\n (id) => !datasetStates[id] || datasetStates[id]?.status === 'loading',\n );\n const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);\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 // Animation for TripsLayer — update currentTime on each frame.\n // Only activate when the trips dataset is ready to avoid spinning the RAF\n // loop while data is still loading.\n const hasTripsLayer = useMemo(() => {\n if (!availableSpec || !Array.isArray(availableSpec.layers)) return false;\n return availableSpec.layers.some((l: unknown) => {\n if (\n !l ||\n typeof l !== 'object' ||\n (l as {'@@type'?: string})['@@type'] !== 'GeoArrowTripsLayer'\n )\n return false;\n const dsId = resolveDatasetId(l as Record<string, unknown>, datasetIds);\n if (!dsId) return true;\n const state = datasetStates[dsId];\n return state?.status === 'ready';\n });\n }, [availableSpec, datasetIds, datasetStates]);\n\n const [tripsTime, setTripsTime] = useState(0);\n const [tripsPlaying, setTripsPlaying] = useState(true);\n const tripsTimeRef = useRef(0);\n\n useEffect(() => {\n if (!hasTripsLayer || !tripsPlaying) return;\n let raf: number;\n let lastFrame = Date.now();\n const tick = () => {\n const now = Date.now();\n tripsTimeRef.current += now - lastFrame;\n lastFrame = now;\n setTripsTime(tripsTimeRef.current);\n raf = requestAnimationFrame(tick);\n };\n lastFrame = Date.now();\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [hasTripsLayer, tripsPlaying]);\n\n const handleTripsPlayPause = useCallback(() => {\n setTripsPlaying((p) => !p);\n }, []);\n\n const handleTripsSeek = useCallback((time: number) => {\n const rawTime = time * 30;\n tripsTimeRef.current = rawTime;\n setTripsTime(rawTime);\n }, []);\n\n const finalDeckPropsResult = convertedDeckPropsResult;\n\n const onRenderingErrorRef = useRef(onRenderingError);\n useEffect(() => {\n onRenderingErrorRef.current = onRenderingError;\n }, [onRenderingError]);\n\n useEffect(() => {\n if (finalDeckPropsResult.error) {\n console.error(finalDeckPropsResult.error);\n onRenderingErrorRef.current?.(finalDeckPropsResult.error);\n }\n }, [finalDeckPropsResult.error]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current = onDatasetStatesChange;\n }, [onDatasetStatesChange]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current?.(datasetStates);\n }, [datasetStates]);\n\n const fallbackDeckProps = useMemo(\n () => extractFallbackDeckProps(availableSpec),\n [availableSpec],\n );\n const convertedDeckProps = (finalDeckPropsResult.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(finalDeckPropsResult.error);\n\n // Separate stable layer analysis from per-frame animation to avoid\n // re-running .map() over all layers 60 times per second.\n const baseLayers = useMemo(() => {\n const mergedLayers = hasRenderingError\n ? []\n : (deckProps?.layers ??\n (convertedDeckProps.layers as unknown[] | undefined) ??\n []);\n if (!hasTripsLayer || !Array.isArray(mergedLayers))\n return {layers: mergedLayers, maxTs: 0, tripsIndices: [] as number[]};\n let computedMaxTs = 0;\n const tripsIndices: number[] = [];\n for (let i = 0; i < mergedLayers.length; i++) {\n const layer = mergedLayers[i];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {props?: Record<string, unknown>};\n const maxTs = (layerObj.props as Record<string, unknown> | undefined)\n ?._tripsMaxTimestamp as number | undefined;\n if (maxTs && maxTs > 0) {\n computedMaxTs = Math.max(computedMaxTs, maxTs);\n tripsIndices.push(i);\n }\n }\n return {layers: mergedLayers, maxTs: computedMaxTs, tripsIndices};\n }, [\n hasTripsLayer,\n hasRenderingError,\n deckProps?.layers,\n convertedDeckProps.layers,\n ]);\n\n // Per-frame: only clone trips layers with updated currentTime\n const animatedLayers = useMemo(() => {\n const {layers, tripsIndices} = baseLayers;\n if (!tripsIndices.length) return layers;\n if (!Array.isArray(layers)) return layers;\n const result = layers.slice();\n for (const idx of tripsIndices) {\n const layer = result[idx];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {\n props?: Record<string, unknown>;\n clone?: (props: Record<string, unknown>) => unknown;\n };\n if (!layerObj.clone) continue;\n const props = layerObj.props as Record<string, unknown> | undefined;\n const maxTs = (props?._tripsMaxTimestamp as number | undefined) ?? 0;\n const trailFraction =\n (props?._trailLengthFactor as number | undefined) ?? 0.4;\n const widthPx = (props?.widthMinPixels as number | undefined) ?? 3;\n const animationSpeed = 30;\n const currentTime = (tripsTime / animationSpeed) % maxTs;\n result[idx] = layerObj.clone({\n currentTime,\n trailLength: maxTs * trailFraction,\n widthMinPixels: widthPx,\n });\n }\n return result;\n }, [baseLayers, tripsTime]);\n const tripsMaxTimeValue = baseLayers.maxTs;\n\n const tripsCurrentTime = useMemo(() => {\n if (!tripsMaxTimeValue || tripsMaxTimeValue <= 0) return 0;\n return (tripsTime / 30) % tripsMaxTimeValue;\n }, [tripsTime, tripsMaxTimeValue]);\n\n const mergedDeckProps = {\n ...convertedDeckProps,\n ...extraDeckProps,\n layers: animatedLayers,\n };\n\n // overlayDeckProps should not contain viewState/initialViewState\n const {\n initialViewState,\n viewState: _vs,\n onViewStateChange: _onViewStateChange,\n ...overlayDeckProps\n } = mergedDeckProps as Record<string, unknown>;\n void _vs;\n void _onViewStateChange;\n\n const mapRef = useRef<{jumpTo: (opts: object) => void} | null>(null);\n const pendingJumpRef = useRef<object | null>(null);\n\n useImperativeHandle(\n ref,\n () => ({\n jumpTo(opts) {\n const jumpOpts = buildDeckMapJumpToOptions(opts);\n if (mapRef.current) {\n mapRef.current.jumpTo(jumpOpts);\n } else {\n pendingJumpRef.current = jumpOpts;\n }\n },\n }),\n [],\n );\n\n const handleMapLoad = useCallback(\n (event?: unknown) => {\n if (pendingJumpRef.current && mapRef.current) {\n mapRef.current.jumpTo(pendingJumpRef.current);\n pendingJumpRef.current = null;\n }\n const externalOnLoad = (\n extraMapProps as {onLoad?: (e?: unknown) => void}\n ).onLoad;\n externalOnLoad?.(event);\n },\n [extraMapProps],\n );\n\n const {resolvedTheme} = useTheme();\n const hostDefaultMapStyles = useDeckMapDefaultStyles();\n const roomBasemapProvider = useBaseRoomStore<\n Partial<DeckMapsSliceState>,\n DeckMapBasemapProvider | undefined\n >((state) => state.deckMaps?.basemapProvider);\n\n const mergedMapProps = {\n ...extraMapProps,\n // DOM image capture reads the canvas after the render frame has ended.\n // Interleaved deck layers share this buffer with the basemap.\n canvasContextAttributes: {\n preserveDrawingBuffer: true,\n ...mapProps?.canvasContextAttributes,\n },\n mapStyle: resolveDeckMapStyle({\n mapStyle,\n mapPropsMapStyle: mapProps?.mapStyle,\n basemapProvider: basemapProvider ?? roomBasemapProvider,\n hostDefaultStyles: hostDefaultMapStyles,\n resolvedTheme,\n fallbackStyles: DEFAULT_MAP_STYLES,\n }),\n };\n const legends = useMemo(\n () =>\n showLegends\n ? extractColorScaleLegends({\n spec: availableSpec,\n datasetIds,\n datasetStates,\n })\n : [],\n [availableSpec, datasetIds, showLegends, datasetStates],\n );\n\n return (\n <div\n className={cn('relative h-full w-full', className)}\n data-sqlrooms-map-loading={!mapReady || datasetsLoading}\n >\n {finalDeckPropsResult.error ? (\n <DeckMapRenderingErrorOverlay error={finalDeckPropsResult.error} />\n ) : null}\n\n <Map\n ref={mapRef as any}\n {...(mergedMapProps as object)}\n {...(initialViewState\n ? {initialViewState: initialViewState as object}\n : {})}\n onLoad={handleMapLoad}\n onData={(event) => {\n setMapReady(false);\n mapProps?.onData?.(event);\n }}\n onMoveStart={(event) => {\n setMapReady(false);\n mapProps?.onMoveStart?.(event);\n }}\n onRender={(event) => {\n setMapReady(event.target.loaded());\n mapProps?.onRender?.(event);\n }}\n style={{width: '100%', height: '100%', ...mapProps?.style}}\n >\n <DeckOverlayControl interleaved={interleaved} {...overlayDeckProps} />\n {children}\n </Map>\n\n {renderDatasetErrorOverlay(datasetStates)}\n {!hasRenderingError && showLegends ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10 max-w-56\">\n {hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"mb-1\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n <ColorScaleLegend legends={legends} />\n </div>\n ) : hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n </div>\n );\n },\n);\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { DeckMapConfig } from './mapConfig';
|
|
3
|
+
/** Selects a persistent basemap independently of the app's current theme. */
|
|
4
|
+
export declare const DeckMapBasemapSelect: FC<{
|
|
5
|
+
config: DeckMapConfig;
|
|
6
|
+
onConfigChange: (config: DeckMapConfig) => void;
|
|
7
|
+
readOnly?: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=DeckMapBasemapSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeckMapBasemapSelect.d.ts","sourceRoot":"","sources":["../src/DeckMapBasemapSelect.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAQ/C,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAqCA,CAAC"}
|