@sqlrooms/deck 0.29.0-rc.7 → 0.29.0-rc.9
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/LICENSE.md +2 -1
- package/README.md +31 -6
- package/dist/DeckJsonMap.d.ts +2 -2
- package/dist/DeckJsonMap.d.ts.map +1 -1
- package/dist/DeckJsonMap.js +204 -17
- package/dist/DeckJsonMap.js.map +1 -1
- package/dist/DeckJsonMapSpec.d.ts +36 -36
- package/dist/DeckJsonMapSpec.d.ts.map +1 -1
- package/dist/DeckJsonMapSpec.js +1 -0
- package/dist/DeckJsonMapSpec.js.map +1 -1
- package/dist/MapSettings.d.ts.map +1 -1
- package/dist/MapSettings.js +203 -25
- package/dist/MapSettings.js.map +1 -1
- package/dist/ai.d.ts +55 -8
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +190 -41
- package/dist/ai.js.map +1 -1
- package/dist/block.d.ts +20 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/block.js +119 -0
- package/dist/block.js.map +1 -0
- package/dist/dashboard.d.ts +2 -0
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/dashboard.js +194 -285
- package/dist/dashboard.js.map +1 -1
- package/dist/dashboardConfig.d.ts +11 -3
- package/dist/dashboardConfig.d.ts.map +1 -1
- package/dist/dashboardConfig.js +82 -7
- package/dist/dashboardConfig.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/json/aggregateWaypoints.d.ts +14 -0
- package/dist/json/aggregateWaypoints.d.ts.map +1 -0
- package/dist/json/aggregateWaypoints.js +190 -0
- package/dist/json/aggregateWaypoints.js.map +1 -0
- package/dist/json/compileColorScale.d.ts +1 -1
- package/dist/json/compileColorScale.d.ts.map +1 -1
- package/dist/json/compileColorScale.js +42 -20
- package/dist/json/compileColorScale.js.map +1 -1
- package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -1
- package/dist/json/createDeckJsonConfiguration.js +106 -8
- package/dist/json/createDeckJsonConfiguration.js.map +1 -1
- package/dist/json/defaultClasses.d.ts +5 -4
- package/dist/json/defaultClasses.d.ts.map +1 -1
- package/dist/json/defaultClasses.js +9 -4
- package/dist/json/defaultClasses.js.map +1 -1
- package/dist/json/extractColorScaleLegends.d.ts.map +1 -1
- package/dist/json/extractColorScaleLegends.js +43 -5
- package/dist/json/extractColorScaleLegends.js.map +1 -1
- package/dist/json/heatmapDefaults.d.ts +8 -0
- package/dist/json/heatmapDefaults.d.ts.map +1 -0
- package/dist/json/heatmapDefaults.js +14 -0
- package/dist/json/heatmapDefaults.js.map +1 -0
- package/dist/json/layerCompatibility.d.ts.map +1 -1
- package/dist/json/layerCompatibility.js +5 -1
- package/dist/json/layerCompatibility.js.map +1 -1
- package/dist/json/layers/DeckColumnLayer.d.ts +12 -0
- package/dist/json/layers/DeckColumnLayer.d.ts.map +1 -0
- package/dist/json/layers/DeckColumnLayer.js +26 -0
- package/dist/json/layers/DeckColumnLayer.js.map +1 -0
- package/dist/json/layers/DeckH3HexagonLayer.d.ts +37 -0
- package/dist/json/layers/DeckH3HexagonLayer.d.ts.map +1 -0
- package/dist/json/layers/DeckH3HexagonLayer.js +96 -0
- package/dist/json/layers/DeckH3HexagonLayer.js.map +1 -0
- package/dist/json/layers/DeckTripsLayer.d.ts +45 -0
- package/dist/json/layers/DeckTripsLayer.d.ts.map +1 -0
- package/dist/json/layers/DeckTripsLayer.js +169 -0
- package/dist/json/layers/DeckTripsLayer.js.map +1 -0
- package/dist/json/layers/index.d.ts +4 -0
- package/dist/json/layers/index.d.ts.map +1 -0
- package/dist/json/layers/index.js +4 -0
- package/dist/json/layers/index.js.map +1 -0
- package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -1
- package/dist/json/rewriteGeoArrowAccessors.js +5 -0
- package/dist/json/rewriteGeoArrowAccessors.js.map +1 -1
- package/dist/mapConfigUtils.d.ts +102 -1
- package/dist/mapConfigUtils.d.ts.map +1 -1
- package/dist/mapConfigUtils.js +21 -3
- package/dist/mapConfigUtils.js.map +1 -1
- package/dist/mapLayerConfigUtils.d.ts +15 -0
- package/dist/mapLayerConfigUtils.d.ts.map +1 -1
- package/dist/mapLayerConfigUtils.js +109 -7
- package/dist/mapLayerConfigUtils.js.map +1 -1
- package/dist/prepare/detectGeometryColumn.d.ts +10 -1
- package/dist/prepare/detectGeometryColumn.d.ts.map +1 -1
- package/dist/prepare/detectGeometryColumn.js +48 -6
- package/dist/prepare/detectGeometryColumn.js.map +1 -1
- package/dist/prepare/wkbDecoder.d.ts.map +1 -1
- package/dist/prepare/wkbDecoder.js +88 -1
- package/dist/prepare/wkbDecoder.js.map +1 -1
- package/dist/prepare/wkbParser.d.ts +5 -0
- package/dist/prepare/wkbParser.d.ts.map +1 -1
- package/dist/prepare/wkbParser.js +24 -0
- package/dist/prepare/wkbParser.js.map +1 -1
- package/dist/types.d.ts +14 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/useDeckMapDatasets.d.ts +17 -0
- package/dist/useDeckMapDatasets.d.ts.map +1 -0
- package/dist/useDeckMapDatasets.js +69 -0
- package/dist/useDeckMapDatasets.js.map +1 -0
- package/dist/useDeckMapFitToBounds.d.ts +29 -0
- package/dist/useDeckMapFitToBounds.d.ts.map +1 -0
- package/dist/useDeckMapFitToBounds.js +333 -0
- package/dist/useDeckMapFitToBounds.js.map +1 -0
- package/package.json +7 -6
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { WebMercatorViewport } from '@deck.gl/core';
|
|
2
|
+
import { escapeId, getColValAsNumber, quoteTableReference, useStoreWithDuckDb, } from '@sqlrooms/duckdb';
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { asDeckJsonMapConfig, resolveDeckMapDashboardDatasetSource, } from './dashboardConfig';
|
|
5
|
+
function createInitialFitState(key) {
|
|
6
|
+
return {
|
|
7
|
+
key,
|
|
8
|
+
didAutoFit: false,
|
|
9
|
+
fitRequestVersion: 0,
|
|
10
|
+
handledFitRequestVersion: 0,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function isDeckMapFitToDataValid(fitToData) {
|
|
14
|
+
return Boolean(fitToData?.dataset);
|
|
15
|
+
}
|
|
16
|
+
function resolveFitToData(fitToDataRaw, datasets, interaction, spec) {
|
|
17
|
+
if (!fitToDataRaw)
|
|
18
|
+
return null;
|
|
19
|
+
const dataset = datasets?.[fitToDataRaw.dataset];
|
|
20
|
+
if (fitToDataRaw.longitudeColumn && fitToDataRaw.latitudeColumn) {
|
|
21
|
+
return fitToDataRaw;
|
|
22
|
+
}
|
|
23
|
+
const geomCol = fitToDataRaw.geometryColumn ?? dataset?.geometryColumn;
|
|
24
|
+
if (geomCol) {
|
|
25
|
+
return { ...fitToDataRaw, geometryColumn: geomCol };
|
|
26
|
+
}
|
|
27
|
+
const inter = interaction;
|
|
28
|
+
if (inter?.longitudeColumn && inter?.latitudeColumn) {
|
|
29
|
+
return {
|
|
30
|
+
...fitToDataRaw,
|
|
31
|
+
geometryColumn: undefined,
|
|
32
|
+
longitudeColumn: inter.longitudeColumn,
|
|
33
|
+
latitudeColumn: inter.latitudeColumn,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Detect H3 layer as last resort: check layers bound to this dataset
|
|
37
|
+
// for hexagonColumn when no other bounds method is available.
|
|
38
|
+
const specObj = spec;
|
|
39
|
+
const specLayers = Array.isArray(specObj?.layers) ? specObj.layers : [];
|
|
40
|
+
for (const layer of specLayers) {
|
|
41
|
+
if (!layer || typeof layer !== 'object')
|
|
42
|
+
continue;
|
|
43
|
+
const binding = layer._sqlroomsBinding;
|
|
44
|
+
if (binding?.dataset === fitToDataRaw.dataset && binding?.hexagonColumn) {
|
|
45
|
+
return {
|
|
46
|
+
...fitToDataRaw,
|
|
47
|
+
h3Column: binding.hexagonColumn,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Strip unverifiable geometryColumn; the bounds query will use
|
|
52
|
+
// DuckDB case-insensitive column resolution as a fallback.
|
|
53
|
+
const { geometryColumn: _geom, ...safeConfig } = fitToDataRaw;
|
|
54
|
+
void _geom;
|
|
55
|
+
return safeConfig;
|
|
56
|
+
}
|
|
57
|
+
export function createDeckMapBoundsQuery(options) {
|
|
58
|
+
const { source, fitToData } = options;
|
|
59
|
+
if (!isDeckMapFitToDataValid(fitToData)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const baseSourceSql = source.sqlQuery
|
|
63
|
+
? `SELECT * FROM (${source.sqlQuery}) AS "__sqlrooms_dashboard_map_source"`
|
|
64
|
+
: `SELECT * FROM ${quoteTableReference(source.tableName ?? '')}`;
|
|
65
|
+
if (fitToData.h3Column) {
|
|
66
|
+
const h3Col = escapeId(fitToData.h3Column);
|
|
67
|
+
return `
|
|
68
|
+
SELECT
|
|
69
|
+
MIN(h3_cell_to_lng(${h3Col})) AS min_longitude,
|
|
70
|
+
MIN(h3_cell_to_lat(${h3Col})) AS min_latitude,
|
|
71
|
+
MAX(h3_cell_to_lng(${h3Col})) AS max_longitude,
|
|
72
|
+
MAX(h3_cell_to_lat(${h3Col})) AS max_latitude
|
|
73
|
+
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_h3"
|
|
74
|
+
WHERE ${h3Col} IS NOT NULL
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
if (fitToData.geometryColumn) {
|
|
78
|
+
const geometryCol = escapeId(fitToData.geometryColumn);
|
|
79
|
+
const isWkb = source.sqlQuery && source.sqlQuery.toLowerCase().includes('st_aswkb');
|
|
80
|
+
const geomExpr = isWkb
|
|
81
|
+
? `ST_GeomFromWKB(${geometryCol})`
|
|
82
|
+
: `${geometryCol}::GEOMETRY`;
|
|
83
|
+
return `
|
|
84
|
+
SELECT
|
|
85
|
+
ST_XMin(extent) AS min_longitude,
|
|
86
|
+
ST_YMin(extent) AS min_latitude,
|
|
87
|
+
ST_XMax(extent) AS max_longitude,
|
|
88
|
+
ST_YMax(extent) AS max_latitude
|
|
89
|
+
FROM (
|
|
90
|
+
SELECT ST_Extent_Agg(${geomExpr}) AS extent
|
|
91
|
+
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_geom"
|
|
92
|
+
WHERE ${geometryCol} IS NOT NULL
|
|
93
|
+
) AS "__sqlrooms_dashboard_map_extent"
|
|
94
|
+
WHERE extent IS NOT NULL
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
if (fitToData.longitudeColumn && fitToData.latitudeColumn) {
|
|
98
|
+
const longitudeColumn = escapeId(fitToData.longitudeColumn);
|
|
99
|
+
const latitudeColumn = escapeId(fitToData.latitudeColumn);
|
|
100
|
+
return `
|
|
101
|
+
SELECT
|
|
102
|
+
ST_XMin(extent) AS min_longitude,
|
|
103
|
+
ST_YMin(extent) AS min_latitude,
|
|
104
|
+
ST_XMax(extent) AS max_longitude,
|
|
105
|
+
ST_YMax(extent) AS max_latitude
|
|
106
|
+
FROM (
|
|
107
|
+
SELECT ST_Extent_Agg(ST_Point(${longitudeColumn}, ${latitudeColumn})) AS extent
|
|
108
|
+
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_points"
|
|
109
|
+
WHERE ${longitudeColumn} IS NOT NULL AND ${latitudeColumn} IS NOT NULL
|
|
110
|
+
) AS "__sqlrooms_dashboard_map_extent"
|
|
111
|
+
WHERE extent IS NOT NULL
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
// Fallback: use DuckDB unquoted identifiers for case-insensitive resolution
|
|
115
|
+
// of common coordinate column names (longitude, lat, etc.)
|
|
116
|
+
return `
|
|
117
|
+
SELECT
|
|
118
|
+
ST_XMin(extent) AS min_longitude,
|
|
119
|
+
ST_YMin(extent) AS min_latitude,
|
|
120
|
+
ST_XMax(extent) AS max_longitude,
|
|
121
|
+
ST_YMax(extent) AS max_latitude
|
|
122
|
+
FROM (
|
|
123
|
+
SELECT ST_Extent_Agg(ST_Point(Longitude, Latitude)) AS extent
|
|
124
|
+
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_points"
|
|
125
|
+
WHERE Longitude IS NOT NULL AND Latitude IS NOT NULL
|
|
126
|
+
) AS "__sqlrooms_dashboard_map_extent"
|
|
127
|
+
WHERE extent IS NOT NULL
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
function readBoundsFromExtentResult(result) {
|
|
131
|
+
const minLongitude = getColValAsNumber(result, 'min_longitude');
|
|
132
|
+
const minLatitude = getColValAsNumber(result, 'min_latitude');
|
|
133
|
+
const maxLongitude = getColValAsNumber(result, 'max_longitude');
|
|
134
|
+
const maxLatitude = getColValAsNumber(result, 'max_latitude');
|
|
135
|
+
if (!Number.isFinite(minLongitude) ||
|
|
136
|
+
!Number.isFinite(minLatitude) ||
|
|
137
|
+
!Number.isFinite(maxLongitude) ||
|
|
138
|
+
!Number.isFinite(maxLatitude)) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return [
|
|
142
|
+
[
|
|
143
|
+
minLongitude === maxLongitude ? minLongitude - 0.01 : minLongitude,
|
|
144
|
+
minLatitude === maxLatitude ? minLatitude - 0.01 : minLatitude,
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
minLongitude === maxLongitude ? maxLongitude + 0.01 : maxLongitude,
|
|
148
|
+
minLatitude === maxLatitude ? maxLatitude + 0.01 : maxLatitude,
|
|
149
|
+
],
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
function fitViewStateToBounds(options) {
|
|
153
|
+
const { bounds, width, height, padding = 40, maxZoom = 18 } = options;
|
|
154
|
+
const viewport = new WebMercatorViewport({
|
|
155
|
+
width: Math.max(width, 1),
|
|
156
|
+
height: Math.max(height, 1),
|
|
157
|
+
});
|
|
158
|
+
const fitted = viewport.fitBounds([
|
|
159
|
+
[bounds[0][0], bounds[0][1]],
|
|
160
|
+
[bounds[1][0], bounds[1][1]],
|
|
161
|
+
], { padding });
|
|
162
|
+
return {
|
|
163
|
+
longitude: fitted.longitude,
|
|
164
|
+
latitude: fitted.latitude,
|
|
165
|
+
zoom: Math.min(fitted.zoom, maxZoom),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const deckMapDashboardFitRequestTarget = new EventTarget();
|
|
169
|
+
export function emitDeckMapDashboardFitRequest(panelId) {
|
|
170
|
+
deckMapDashboardFitRequestTarget.dispatchEvent(new CustomEvent('fit-view', { detail: { panelId } }));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Manages the "fit to bounds" state machine for a deck map panel.
|
|
174
|
+
* Handles auto-fit on mount, manual fit requests, and bounds query execution.
|
|
175
|
+
*/
|
|
176
|
+
export function useDeckMapFitToBounds(options) {
|
|
177
|
+
const { panelId, dashboard, panel, containerSize, deckMapRef } = options;
|
|
178
|
+
const mapConfig = asDeckJsonMapConfig(panel.config);
|
|
179
|
+
const executeSql = useStoreWithDuckDb((state) => state.db.executeSql);
|
|
180
|
+
const fitToDataRaw = mapConfig?.fitToData ?? null;
|
|
181
|
+
const fitToData = useMemo(() => resolveFitToData(fitToDataRaw, mapConfig?.datasets, mapConfig?.interaction, mapConfig?.spec), [
|
|
182
|
+
fitToDataRaw,
|
|
183
|
+
mapConfig?.datasets,
|
|
184
|
+
mapConfig?.interaction,
|
|
185
|
+
mapConfig?.spec,
|
|
186
|
+
]);
|
|
187
|
+
const fitToDataSource = useMemo(() => fitToData
|
|
188
|
+
? resolveDeckMapDashboardDatasetSource({
|
|
189
|
+
dashboard,
|
|
190
|
+
panel,
|
|
191
|
+
dataset: mapConfig?.datasets[fitToData.dataset],
|
|
192
|
+
})
|
|
193
|
+
: undefined, [dashboard, fitToData, mapConfig?.datasets, panel]);
|
|
194
|
+
const fitToDataKey = useMemo(() => fitToData && fitToDataSource
|
|
195
|
+
? JSON.stringify({
|
|
196
|
+
source: fitToDataSource,
|
|
197
|
+
fitToData,
|
|
198
|
+
selectedTable: dashboard.selectedTable,
|
|
199
|
+
})
|
|
200
|
+
: null, [dashboard.selectedTable, fitToData, fitToDataSource]);
|
|
201
|
+
const fitStateKey = useMemo(() => JSON.stringify({ panelId, fitToDataKey }), [fitToDataKey, panelId]);
|
|
202
|
+
const [fitState, setFitState] = useState(() => createInitialFitState(fitStateKey));
|
|
203
|
+
const activeFitState = fitState.key === fitStateKey
|
|
204
|
+
? fitState
|
|
205
|
+
: createInitialFitState(fitStateKey);
|
|
206
|
+
const { didAutoFit, fitRequestVersion, handledFitRequestVersion } = activeFitState;
|
|
207
|
+
// Listen for manual fit requests
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
const handleFitRequest = (event) => {
|
|
210
|
+
const detail = event.detail;
|
|
211
|
+
if (detail?.panelId === panelId) {
|
|
212
|
+
setFitState((current) => {
|
|
213
|
+
const scoped = current.key === fitStateKey
|
|
214
|
+
? current
|
|
215
|
+
: createInitialFitState(fitStateKey);
|
|
216
|
+
return {
|
|
217
|
+
...scoped,
|
|
218
|
+
fitRequestVersion: scoped.fitRequestVersion + 1,
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
deckMapDashboardFitRequestTarget.addEventListener('fit-view', handleFitRequest);
|
|
224
|
+
return () => {
|
|
225
|
+
deckMapDashboardFitRequestTarget.removeEventListener('fit-view', handleFitRequest);
|
|
226
|
+
};
|
|
227
|
+
}, [fitStateKey, panelId]);
|
|
228
|
+
// Execute fit-to-bounds query
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
const hasManualFitRequest = fitRequestVersion > handledFitRequestVersion;
|
|
231
|
+
if (!isDeckMapFitToDataValid(fitToData) ||
|
|
232
|
+
!fitToDataSource ||
|
|
233
|
+
containerSize.width <= 0 ||
|
|
234
|
+
containerSize.height <= 0 ||
|
|
235
|
+
(!hasManualFitRequest && didAutoFit)) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
let isCancelled = false;
|
|
239
|
+
const fitToDataBounds = async () => {
|
|
240
|
+
try {
|
|
241
|
+
if (fitToData.h3Column) {
|
|
242
|
+
try {
|
|
243
|
+
await executeSql('INSTALL h3 FROM community');
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// May already be installed
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
await executeSql('LOAD h3');
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// May already be loaded
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const boundsQuery = createDeckMapBoundsQuery({
|
|
256
|
+
source: fitToDataSource,
|
|
257
|
+
fitToData,
|
|
258
|
+
});
|
|
259
|
+
if (!boundsQuery)
|
|
260
|
+
return;
|
|
261
|
+
const handle = await executeSql(boundsQuery);
|
|
262
|
+
const result = handle ? await handle : null;
|
|
263
|
+
if (isCancelled || !result)
|
|
264
|
+
return;
|
|
265
|
+
const bounds = readBoundsFromExtentResult(result);
|
|
266
|
+
if (!bounds) {
|
|
267
|
+
setFitState((current) => {
|
|
268
|
+
const scoped = current.key === fitStateKey
|
|
269
|
+
? current
|
|
270
|
+
: createInitialFitState(fitStateKey);
|
|
271
|
+
return {
|
|
272
|
+
...scoped,
|
|
273
|
+
didAutoFit: true,
|
|
274
|
+
handledFitRequestVersion: fitRequestVersion,
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const nextViewState = fitViewStateToBounds({
|
|
280
|
+
bounds,
|
|
281
|
+
width: containerSize.width,
|
|
282
|
+
height: containerSize.height,
|
|
283
|
+
padding: fitToData.padding,
|
|
284
|
+
maxZoom: fitToData.maxZoom,
|
|
285
|
+
});
|
|
286
|
+
if (!isCancelled && deckMapRef.current) {
|
|
287
|
+
deckMapRef.current.jumpTo(nextViewState);
|
|
288
|
+
}
|
|
289
|
+
setFitState((current) => {
|
|
290
|
+
const scoped = current.key === fitStateKey
|
|
291
|
+
? current
|
|
292
|
+
: createInitialFitState(fitStateKey);
|
|
293
|
+
return {
|
|
294
|
+
...scoped,
|
|
295
|
+
didAutoFit: true,
|
|
296
|
+
handledFitRequestVersion: fitRequestVersion,
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
if (!isCancelled) {
|
|
302
|
+
setFitState((current) => {
|
|
303
|
+
const scoped = current.key === fitStateKey
|
|
304
|
+
? current
|
|
305
|
+
: createInitialFitState(fitStateKey);
|
|
306
|
+
return {
|
|
307
|
+
...scoped,
|
|
308
|
+
didAutoFit: true,
|
|
309
|
+
handledFitRequestVersion: fitRequestVersion,
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
void fitToDataBounds();
|
|
316
|
+
return () => {
|
|
317
|
+
isCancelled = true;
|
|
318
|
+
};
|
|
319
|
+
}, [
|
|
320
|
+
containerSize.height,
|
|
321
|
+
containerSize.width,
|
|
322
|
+
deckMapRef,
|
|
323
|
+
didAutoFit,
|
|
324
|
+
executeSql,
|
|
325
|
+
fitRequestVersion,
|
|
326
|
+
fitStateKey,
|
|
327
|
+
fitToData,
|
|
328
|
+
fitToDataSource,
|
|
329
|
+
handledFitRequestVersion,
|
|
330
|
+
]);
|
|
331
|
+
return { fitToData };
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=useDeckMapFitToBounds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDeckMapFitToBounds.js","sourceRoot":"","sources":["../src/useDeckMapFitToBounds.ts"],"names":[],"mappings":"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","sourcesContent":["import {WebMercatorViewport} from '@deck.gl/core';\nimport {\n escapeId,\n getColValAsNumber,\n quoteTableReference,\n useStoreWithDuckDb,\n} from '@sqlrooms/duckdb';\nimport type {\n MosaicDashboardEntryType,\n MosaicDashboardPanelConfigType,\n} from '@sqlrooms/mosaic';\nimport type {Table as ArrowTable} from 'apache-arrow';\nimport {useEffect, useMemo, useState, type RefObject} from 'react';\nimport {\n asDeckJsonMapConfig,\n resolveDeckMapDashboardDatasetSource,\n type DeckMapDashboardFitToDataConfig,\n} from './dashboardConfig';\nimport type {DeckJsonMapHandle} from './types';\n\ntype DeckMapDashboardFitState = {\n key: string;\n didAutoFit: boolean;\n fitRequestVersion: number;\n handledFitRequestVersion: number;\n};\n\nfunction createInitialFitState(key: string): DeckMapDashboardFitState {\n return {\n key,\n didAutoFit: false,\n fitRequestVersion: 0,\n handledFitRequestVersion: 0,\n };\n}\n\nfunction isDeckMapFitToDataValid(\n fitToData: DeckMapDashboardFitToDataConfig | null | undefined,\n): fitToData is DeckMapDashboardFitToDataConfig {\n return Boolean(fitToData?.dataset);\n}\n\nfunction resolveFitToData(\n fitToDataRaw: DeckMapDashboardFitToDataConfig | null,\n datasets: Record<string, unknown> | undefined,\n interaction: Record<string, unknown> | undefined,\n spec: unknown,\n): DeckMapDashboardFitToDataConfig | null {\n if (!fitToDataRaw) return null;\n const dataset = (\n datasets as\n | Record<\n string,\n {geometryColumn?: string; source?: {sqlQuery?: string}} | undefined\n >\n | undefined\n )?.[fitToDataRaw.dataset];\n\n if (fitToDataRaw.longitudeColumn && fitToDataRaw.latitudeColumn) {\n return fitToDataRaw;\n }\n\n const geomCol = fitToDataRaw.geometryColumn ?? dataset?.geometryColumn;\n\n if (geomCol) {\n return {...fitToDataRaw, geometryColumn: geomCol};\n }\n\n const inter = interaction as\n | {longitudeColumn?: string; latitudeColumn?: string}\n | undefined;\n if (inter?.longitudeColumn && inter?.latitudeColumn) {\n return {\n ...fitToDataRaw,\n geometryColumn: undefined,\n longitudeColumn: inter.longitudeColumn,\n latitudeColumn: inter.latitudeColumn,\n };\n }\n\n // Detect H3 layer as last resort: check layers bound to this dataset\n // for hexagonColumn when no other bounds method is available.\n const specObj = spec as Record<string, unknown> | undefined;\n const specLayers = Array.isArray(specObj?.layers) ? specObj.layers : [];\n for (const layer of specLayers) {\n if (!layer || typeof layer !== 'object') continue;\n const binding = (layer as Record<string, unknown>)._sqlroomsBinding as\n | Record<string, unknown>\n | undefined;\n if (binding?.dataset === fitToDataRaw.dataset && binding?.hexagonColumn) {\n return {\n ...fitToDataRaw,\n h3Column: binding.hexagonColumn as string,\n };\n }\n }\n\n // Strip unverifiable geometryColumn; the bounds query will use\n // DuckDB case-insensitive column resolution as a fallback.\n const {geometryColumn: _geom, ...safeConfig} = fitToDataRaw;\n void _geom;\n return safeConfig;\n}\n\nexport function createDeckMapBoundsQuery(options: {\n source: {tableName?: string; sqlQuery?: string};\n fitToData: DeckMapDashboardFitToDataConfig;\n}) {\n const {source, fitToData} = options;\n if (!isDeckMapFitToDataValid(fitToData)) {\n return null;\n }\n const baseSourceSql = source.sqlQuery\n ? `SELECT * FROM (${source.sqlQuery}) AS \"__sqlrooms_dashboard_map_source\"`\n : `SELECT * FROM ${quoteTableReference(source.tableName ?? '')}`;\n\n if (fitToData.h3Column) {\n const h3Col = escapeId(fitToData.h3Column);\n return `\n SELECT\n MIN(h3_cell_to_lng(${h3Col})) AS min_longitude,\n MIN(h3_cell_to_lat(${h3Col})) AS min_latitude,\n MAX(h3_cell_to_lng(${h3Col})) AS max_longitude,\n MAX(h3_cell_to_lat(${h3Col})) AS max_latitude\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_h3\"\n WHERE ${h3Col} IS NOT NULL\n `;\n }\n\n if (fitToData.geometryColumn) {\n const geometryCol = escapeId(fitToData.geometryColumn);\n const isWkb =\n source.sqlQuery && source.sqlQuery.toLowerCase().includes('st_aswkb');\n const geomExpr = isWkb\n ? `ST_GeomFromWKB(${geometryCol})`\n : `${geometryCol}::GEOMETRY`;\n return `\n SELECT\n ST_XMin(extent) AS min_longitude,\n ST_YMin(extent) AS min_latitude,\n ST_XMax(extent) AS max_longitude,\n ST_YMax(extent) AS max_latitude\n FROM (\n SELECT ST_Extent_Agg(${geomExpr}) AS extent\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_geom\"\n WHERE ${geometryCol} IS NOT NULL\n ) AS \"__sqlrooms_dashboard_map_extent\"\n WHERE extent IS NOT NULL\n `;\n }\n\n if (fitToData.longitudeColumn && fitToData.latitudeColumn) {\n const longitudeColumn = escapeId(fitToData.longitudeColumn);\n const latitudeColumn = escapeId(fitToData.latitudeColumn);\n return `\n SELECT\n ST_XMin(extent) AS min_longitude,\n ST_YMin(extent) AS min_latitude,\n ST_XMax(extent) AS max_longitude,\n ST_YMax(extent) AS max_latitude\n FROM (\n SELECT ST_Extent_Agg(ST_Point(${longitudeColumn}, ${latitudeColumn})) AS extent\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_points\"\n WHERE ${longitudeColumn} IS NOT NULL AND ${latitudeColumn} IS NOT NULL\n ) AS \"__sqlrooms_dashboard_map_extent\"\n WHERE extent IS NOT NULL\n `;\n }\n\n // Fallback: use DuckDB unquoted identifiers for case-insensitive resolution\n // of common coordinate column names (longitude, lat, etc.)\n return `\n SELECT\n ST_XMin(extent) AS min_longitude,\n ST_YMin(extent) AS min_latitude,\n ST_XMax(extent) AS max_longitude,\n ST_YMax(extent) AS max_latitude\n FROM (\n SELECT ST_Extent_Agg(ST_Point(Longitude, Latitude)) AS extent\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_points\"\n WHERE Longitude IS NOT NULL AND Latitude IS NOT NULL\n ) AS \"__sqlrooms_dashboard_map_extent\"\n WHERE extent IS NOT NULL\n `;\n}\n\nfunction readBoundsFromExtentResult(result: ArrowTable) {\n const minLongitude = getColValAsNumber(result, 'min_longitude');\n const minLatitude = getColValAsNumber(result, 'min_latitude');\n const maxLongitude = getColValAsNumber(result, 'max_longitude');\n const maxLatitude = getColValAsNumber(result, 'max_latitude');\n if (\n !Number.isFinite(minLongitude) ||\n !Number.isFinite(minLatitude) ||\n !Number.isFinite(maxLongitude) ||\n !Number.isFinite(maxLatitude)\n ) {\n return null;\n }\n\n return [\n [\n minLongitude === maxLongitude ? minLongitude - 0.01 : minLongitude,\n minLatitude === maxLatitude ? minLatitude - 0.01 : minLatitude,\n ],\n [\n minLongitude === maxLongitude ? maxLongitude + 0.01 : maxLongitude,\n minLatitude === maxLatitude ? maxLatitude + 0.01 : maxLatitude,\n ],\n ] as const;\n}\n\nfunction fitViewStateToBounds(options: {\n bounds: readonly [readonly [number, number], readonly [number, number]];\n width: number;\n height: number;\n padding?: number;\n maxZoom?: number;\n}) {\n const {bounds, width, height, padding = 40, maxZoom = 18} = options;\n const viewport = new WebMercatorViewport({\n width: Math.max(width, 1),\n height: Math.max(height, 1),\n });\n const fitted = viewport.fitBounds(\n [\n [bounds[0][0], bounds[0][1]],\n [bounds[1][0], bounds[1][1]],\n ],\n {padding},\n ) as WebMercatorViewport & {\n longitude: number;\n latitude: number;\n zoom: number;\n };\n\n return {\n longitude: fitted.longitude,\n latitude: fitted.latitude,\n zoom: Math.min(fitted.zoom, maxZoom),\n };\n}\n\nconst deckMapDashboardFitRequestTarget = new EventTarget();\n\nexport function emitDeckMapDashboardFitRequest(panelId: string) {\n deckMapDashboardFitRequestTarget.dispatchEvent(\n new CustomEvent('fit-view', {detail: {panelId}}),\n );\n}\n\n/**\n * Manages the \"fit to bounds\" state machine for a deck map panel.\n * Handles auto-fit on mount, manual fit requests, and bounds query execution.\n */\nexport function useDeckMapFitToBounds(options: {\n panelId: string;\n dashboard: MosaicDashboardEntryType;\n panel: MosaicDashboardPanelConfigType;\n containerSize: {width: number; height: number};\n deckMapRef: RefObject<DeckJsonMapHandle | null>;\n}) {\n const {panelId, dashboard, panel, containerSize, deckMapRef} = options;\n const mapConfig = asDeckJsonMapConfig(panel.config);\n const executeSql = useStoreWithDuckDb((state) => state.db.executeSql);\n\n const fitToDataRaw = mapConfig?.fitToData ?? null;\n const fitToData: DeckMapDashboardFitToDataConfig | null = useMemo(\n () =>\n resolveFitToData(\n fitToDataRaw,\n mapConfig?.datasets,\n mapConfig?.interaction,\n mapConfig?.spec,\n ),\n [\n fitToDataRaw,\n mapConfig?.datasets,\n mapConfig?.interaction,\n mapConfig?.spec,\n ],\n );\n\n const fitToDataSource = useMemo(\n () =>\n fitToData\n ? resolveDeckMapDashboardDatasetSource({\n dashboard,\n panel,\n dataset: mapConfig?.datasets[fitToData.dataset],\n })\n : undefined,\n [dashboard, fitToData, mapConfig?.datasets, panel],\n );\n\n const fitToDataKey = useMemo(\n () =>\n fitToData && fitToDataSource\n ? JSON.stringify({\n source: fitToDataSource,\n fitToData,\n selectedTable: dashboard.selectedTable,\n })\n : null,\n [dashboard.selectedTable, fitToData, fitToDataSource],\n );\n\n const fitStateKey = useMemo(\n () => JSON.stringify({panelId, fitToDataKey}),\n [fitToDataKey, panelId],\n );\n\n const [fitState, setFitState] = useState<DeckMapDashboardFitState>(() =>\n createInitialFitState(fitStateKey),\n );\n\n const activeFitState =\n fitState.key === fitStateKey\n ? fitState\n : createInitialFitState(fitStateKey);\n const {didAutoFit, fitRequestVersion, handledFitRequestVersion} =\n activeFitState;\n\n // Listen for manual fit requests\n useEffect(() => {\n const handleFitRequest = (event: Event) => {\n const detail = (event as CustomEvent<{panelId?: string}>).detail;\n if (detail?.panelId === panelId) {\n setFitState((current) => {\n const scoped =\n current.key === fitStateKey\n ? current\n : createInitialFitState(fitStateKey);\n return {\n ...scoped,\n fitRequestVersion: scoped.fitRequestVersion + 1,\n };\n });\n }\n };\n\n deckMapDashboardFitRequestTarget.addEventListener(\n 'fit-view',\n handleFitRequest,\n );\n return () => {\n deckMapDashboardFitRequestTarget.removeEventListener(\n 'fit-view',\n handleFitRequest,\n );\n };\n }, [fitStateKey, panelId]);\n\n // Execute fit-to-bounds query\n useEffect(() => {\n const hasManualFitRequest = fitRequestVersion > handledFitRequestVersion;\n if (\n !isDeckMapFitToDataValid(fitToData) ||\n !fitToDataSource ||\n containerSize.width <= 0 ||\n containerSize.height <= 0 ||\n (!hasManualFitRequest && didAutoFit)\n ) {\n return;\n }\n\n let isCancelled = false;\n\n const fitToDataBounds = async () => {\n try {\n if (fitToData.h3Column) {\n try {\n await executeSql('INSTALL h3 FROM community');\n } catch {\n // May already be installed\n }\n try {\n await executeSql('LOAD h3');\n } catch {\n // May already be loaded\n }\n }\n const boundsQuery = createDeckMapBoundsQuery({\n source: fitToDataSource,\n fitToData,\n });\n if (!boundsQuery) return;\n\n const handle = await executeSql(boundsQuery);\n const result = handle ? await handle : null;\n if (isCancelled || !result) return;\n\n const bounds = readBoundsFromExtentResult(result);\n if (!bounds) {\n setFitState((current) => {\n const scoped =\n current.key === fitStateKey\n ? current\n : createInitialFitState(fitStateKey);\n return {\n ...scoped,\n didAutoFit: true,\n handledFitRequestVersion: fitRequestVersion,\n };\n });\n return;\n }\n\n const nextViewState = fitViewStateToBounds({\n bounds,\n width: containerSize.width,\n height: containerSize.height,\n padding: fitToData.padding,\n maxZoom: fitToData.maxZoom,\n });\n if (!isCancelled && deckMapRef.current) {\n deckMapRef.current.jumpTo(nextViewState);\n }\n setFitState((current) => {\n const scoped =\n current.key === fitStateKey\n ? current\n : createInitialFitState(fitStateKey);\n return {\n ...scoped,\n didAutoFit: true,\n handledFitRequestVersion: fitRequestVersion,\n };\n });\n } catch {\n if (!isCancelled) {\n setFitState((current) => {\n const scoped =\n current.key === fitStateKey\n ? current\n : createInitialFitState(fitStateKey);\n return {\n ...scoped,\n didAutoFit: true,\n handledFitRequestVersion: fitRequestVersion,\n };\n });\n }\n }\n };\n\n void fitToDataBounds();\n\n return () => {\n isCancelled = true;\n };\n }, [\n containerSize.height,\n containerSize.width,\n deckMapRef,\n didAutoFit,\n executeSql,\n fitRequestVersion,\n fitStateKey,\n fitToData,\n fitToDataSource,\n handledFitRequestVersion,\n ]);\n\n return {fitToData};\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/deck",
|
|
3
|
-
"version": "0.29.0-rc.
|
|
3
|
+
"version": "0.29.0-rc.9",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/sqlrooms/sqlrooms.git"
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@deck.gl/core": "^9.3.1",
|
|
31
|
+
"@deck.gl/geo-layers": "^9.3.1",
|
|
31
32
|
"@deck.gl/json": "^9.3.1",
|
|
32
33
|
"@deck.gl/layers": "^9.3.1",
|
|
33
34
|
"@deck.gl/mapbox": "^9.3.1",
|
|
@@ -37,10 +38,10 @@
|
|
|
37
38
|
"@loaders.gl/gis": "^4.4.1",
|
|
38
39
|
"@loaders.gl/wkt": "^4.4.1",
|
|
39
40
|
"@paralleldrive/cuid2": "^3.0.0",
|
|
40
|
-
"@sqlrooms/color-scales": "0.29.0-rc.
|
|
41
|
-
"@sqlrooms/duckdb": "0.29.0-rc.
|
|
42
|
-
"@sqlrooms/mosaic": "0.29.0-rc.
|
|
43
|
-
"@sqlrooms/ui": "0.29.0-rc.
|
|
41
|
+
"@sqlrooms/color-scales": "0.29.0-rc.9",
|
|
42
|
+
"@sqlrooms/duckdb": "0.29.0-rc.9",
|
|
43
|
+
"@sqlrooms/mosaic": "0.29.0-rc.9",
|
|
44
|
+
"@sqlrooms/ui": "0.29.0-rc.9",
|
|
44
45
|
"@uwdata/mosaic-core": "^0.21.0",
|
|
45
46
|
"@uwdata/mosaic-sql": "^0.21.0",
|
|
46
47
|
"ai": "^6.0.159",
|
|
@@ -65,5 +66,5 @@
|
|
|
65
66
|
"publishConfig": {
|
|
66
67
|
"access": "public"
|
|
67
68
|
},
|
|
68
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "e9e4ed45ebb0ceb3e1e647674a7039b8d9325583"
|
|
69
70
|
}
|