@sqlrooms/deck 0.29.0-rc.10 → 0.29.0-rc.11
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 +42 -6
- package/dist/DeckJsonMap.d.ts.map +1 -1
- package/dist/DeckJsonMap.js +2 -6
- package/dist/DeckJsonMap.js.map +1 -1
- package/dist/DeckJsonMapSpec.d.ts +875 -53
- package/dist/DeckJsonMapSpec.d.ts.map +1 -1
- package/dist/DeckJsonMapSpec.js +7 -0
- package/dist/DeckJsonMapSpec.js.map +1 -1
- package/dist/DeckMapDefaultStylesProvider.d.ts +3 -4
- package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -1
- package/dist/DeckMapDefaultStylesProvider.js +13 -7
- package/dist/DeckMapDefaultStylesProvider.js.map +1 -1
- package/dist/MapSettings.d.ts.map +1 -1
- package/dist/MapSettings.js +25 -11
- package/dist/MapSettings.js.map +1 -1
- package/dist/ai.d.ts +19 -5
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +60 -112
- package/dist/ai.js.map +1 -1
- package/dist/aiNormalize.d.ts +31 -0
- package/dist/aiNormalize.d.ts.map +1 -0
- package/dist/aiNormalize.js +629 -0
- package/dist/aiNormalize.js.map +1 -0
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/dashboard.js +72 -8
- package/dist/dashboard.js.map +1 -1
- package/dist/dashboardConfig.d.ts +10 -1
- package/dist/dashboardConfig.d.ts.map +1 -1
- package/dist/dashboardConfig.js +55 -22
- package/dist/dashboardConfig.js.map +1 -1
- package/dist/datasets/PreparedDatasetStore.d.ts.map +1 -1
- package/dist/datasets/PreparedDatasetStore.js +31 -2
- package/dist/datasets/PreparedDatasetStore.js.map +1 -1
- package/dist/datasets/wrapGeometryAsWkb.d.ts +20 -0
- package/dist/datasets/wrapGeometryAsWkb.d.ts.map +1 -0
- package/dist/datasets/wrapGeometryAsWkb.js +65 -0
- package/dist/datasets/wrapGeometryAsWkb.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json/compileGeoArrowAccessor.d.ts +5 -0
- package/dist/json/compileGeoArrowAccessor.d.ts.map +1 -1
- package/dist/json/compileGeoArrowAccessor.js +17 -3
- package/dist/json/compileGeoArrowAccessor.js.map +1 -1
- package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -1
- package/dist/json/createDeckJsonConfiguration.js +85 -18
- package/dist/json/createDeckJsonConfiguration.js.map +1 -1
- package/dist/json/extractColorScaleLegends.d.ts.map +1 -1
- package/dist/json/extractColorScaleLegends.js +21 -7
- package/dist/json/extractColorScaleLegends.js.map +1 -1
- package/dist/json/layers/DeckH3HexagonLayer.d.ts.map +1 -1
- package/dist/json/layers/DeckH3HexagonLayer.js +9 -14
- package/dist/json/layers/DeckH3HexagonLayer.js.map +1 -1
- package/dist/json/layers/h3IndexFormat.d.ts +6 -0
- package/dist/json/layers/h3IndexFormat.d.ts.map +1 -0
- package/dist/json/layers/h3IndexFormat.js +17 -0
- package/dist/json/layers/h3IndexFormat.js.map +1 -0
- package/dist/json/layers/index.d.ts +1 -0
- package/dist/json/layers/index.d.ts.map +1 -1
- package/dist/json/layers/index.js +1 -0
- package/dist/json/layers/index.js.map +1 -1
- package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -1
- package/dist/json/rewriteGeoArrowAccessors.js +9 -0
- package/dist/json/rewriteGeoArrowAccessors.js.map +1 -1
- package/dist/json/scaleFunction.d.ts +30 -0
- package/dist/json/scaleFunction.d.ts.map +1 -0
- package/dist/json/scaleFunction.js +126 -0
- package/dist/json/scaleFunction.js.map +1 -0
- package/dist/mapAiConfig.d.ts +2 -0
- package/dist/mapAiConfig.d.ts.map +1 -1
- package/dist/mapAiConfig.js +2 -1
- package/dist/mapAiConfig.js.map +1 -1
- package/dist/mapAiSharedInstructions.d.ts +3 -0
- package/dist/mapAiSharedInstructions.d.ts.map +1 -0
- package/dist/mapAiSharedInstructions.js +16 -0
- package/dist/mapAiSharedInstructions.js.map +1 -0
- package/dist/mapConfig.d.ts +6 -0
- package/dist/mapConfig.d.ts.map +1 -1
- package/dist/mapConfig.js.map +1 -1
- package/dist/mapConfigUtils.d.ts.map +1 -1
- package/dist/mapConfigUtils.js +2 -0
- package/dist/mapConfigUtils.js.map +1 -1
- package/dist/mapFit.d.ts +16 -0
- package/dist/mapFit.d.ts.map +1 -1
- package/dist/mapFit.js +208 -30
- package/dist/mapFit.js.map +1 -1
- package/dist/mapLayerConfigUtils.d.ts +9 -1
- package/dist/mapLayerConfigUtils.d.ts.map +1 -1
- package/dist/mapLayerConfigUtils.js +62 -12
- package/dist/mapLayerConfigUtils.js.map +1 -1
- package/dist/mapResourceAuthoring.d.ts +2 -0
- package/dist/mapResourceAuthoring.d.ts.map +1 -1
- package/dist/mapResourceAuthoring.js +402 -16
- package/dist/mapResourceAuthoring.js.map +1 -1
- package/dist/mosaic.d.ts +1 -1
- package/dist/mosaic.d.ts.map +1 -1
- package/dist/mosaic.js.map +1 -1
- package/dist/prepare/geoarrow.js.map +1 -1
- package/dist/prepare/wkbDecoder.d.ts +10 -0
- package/dist/prepare/wkbDecoder.d.ts.map +1 -1
- package/dist/prepare/wkbDecoder.js +93 -18
- package/dist/prepare/wkbDecoder.js.map +1 -1
- package/dist/prepare/wkbParser.d.ts +7 -0
- package/dist/prepare/wkbParser.d.ts.map +1 -1
- package/dist/prepare/wkbParser.js +21 -0
- package/dist/prepare/wkbParser.js.map +1 -1
- package/dist/selectStarAsWkbCollision.d.ts +3 -0
- package/dist/selectStarAsWkbCollision.d.ts.map +1 -0
- package/dist/selectStarAsWkbCollision.js +78 -0
- package/dist/selectStarAsWkbCollision.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/useDeckMapFitToBounds.d.ts.map +1 -1
- package/dist/useDeckMapFitToBounds.js +1 -1
- package/dist/useDeckMapFitToBounds.js.map +1 -1
- package/package.json +15 -15
package/dist/mapFit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapFit.d.ts","sourceRoot":"","sources":["../src/mapFit.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"mapFit.d.ts","sourceRoot":"","sources":["../src/mapFit.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAK3E,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,SAAS,CAAC;AA4BjB;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACvC,sBAAsB,GAAG,IAAI,CA4F/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,gBAAgB,GAAG,SAAS,GACpC,oBAAoB,GAAG,IAAI,CAU7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,sBAAsB,CAAC;CACnC,iBAgHA;AAqED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG;IACF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAaA;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAChD,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,QAmLA"}
|
package/dist/mapFit.js
CHANGED
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import { WebMercatorViewport } from '@deck.gl/core';
|
|
2
2
|
import { escapeId, getColValAsNumber, useStoreWithDuckDb, } from '@sqlrooms/duckdb';
|
|
3
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { DeckTableDatasetInvalidTableNameError, createDeckTableDatasetSql, } from './datasets/tableDatasetSql';
|
|
5
5
|
import { isDeckMapSqlDatasetSource, } from './mapConfig';
|
|
6
6
|
import { isSqlDatasetInput, isTableDatasetInput, } from './types';
|
|
7
|
+
/** True when `columnName` is an `ST_AsWKB(...) AS` alias (per-column fit decode). */
|
|
8
|
+
function columnAliasProducedByStAsWkb(sql, columnName) {
|
|
9
|
+
if (!sql || !columnName)
|
|
10
|
+
return false;
|
|
11
|
+
const lowerCol = columnName.toLowerCase();
|
|
12
|
+
const parts = sql.split(/\bST_AsWKB\s*\(/i);
|
|
13
|
+
for (let i = 1; i < parts.length; i++) {
|
|
14
|
+
const rest = parts[i];
|
|
15
|
+
let depth = 1;
|
|
16
|
+
let j = 0;
|
|
17
|
+
for (; j < rest.length && depth > 0; j++) {
|
|
18
|
+
const ch = rest[j];
|
|
19
|
+
if (ch === '(')
|
|
20
|
+
depth++;
|
|
21
|
+
else if (ch === ')')
|
|
22
|
+
depth--;
|
|
23
|
+
}
|
|
24
|
+
const after = rest.slice(j);
|
|
25
|
+
const match = after.match(/^\s*AS\s+("([^"]+)"|([A-Za-z_][\w$]*))/i);
|
|
26
|
+
if (!match)
|
|
27
|
+
continue;
|
|
28
|
+
const alias = (match[2] ?? match[3] ?? '').toLowerCase();
|
|
29
|
+
if (alias === lowerCol)
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
7
34
|
/**
|
|
8
35
|
* Resolves the effective fit-to-data configuration for a Deck map.
|
|
9
36
|
*
|
|
@@ -22,29 +49,79 @@ export function resolveDeckMapFitToData(config) {
|
|
|
22
49
|
return null;
|
|
23
50
|
if (fitToData.longitudeColumn && fitToData.latitudeColumn)
|
|
24
51
|
return fitToData;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
...fitToData,
|
|
33
|
-
longitudeColumn: config.interaction.longitudeColumn,
|
|
34
|
-
latitudeColumn: config.interaction.latitudeColumn,
|
|
35
|
-
};
|
|
52
|
+
if (fitToData.geometryColumns && fitToData.geometryColumns.length > 0) {
|
|
53
|
+
return fitToData;
|
|
54
|
+
}
|
|
55
|
+
// Explicit geometryColumn wins over inferred arc/H3.
|
|
56
|
+
if (fitToData.geometryColumn) {
|
|
57
|
+
return fitToData;
|
|
36
58
|
}
|
|
37
59
|
const spec = typeof config.spec === 'string'
|
|
38
60
|
? undefined
|
|
39
61
|
: config.spec;
|
|
40
62
|
const layers = Array.isArray(spec?.layers) ? spec.layers : [];
|
|
63
|
+
// Prefer both arc endpoints when fitToData did not name a single column.
|
|
41
64
|
for (const layer of layers) {
|
|
42
65
|
if (!layer || typeof layer !== 'object')
|
|
43
66
|
continue;
|
|
44
|
-
const
|
|
45
|
-
|
|
67
|
+
const layerRecord = layer;
|
|
68
|
+
// Hidden layers must not drive fit (e.g. stale H3 over visible lon/lat).
|
|
69
|
+
if (layerRecord.visible === false)
|
|
70
|
+
continue;
|
|
71
|
+
const binding = layerRecord._sqlroomsBinding;
|
|
72
|
+
if (binding?.dataset !== fitToData.dataset)
|
|
73
|
+
continue;
|
|
74
|
+
if (layerRecord['@@type'] === 'GeoArrowArcLayer' &&
|
|
75
|
+
typeof binding.sourceGeometryColumn === 'string' &&
|
|
76
|
+
binding.sourceGeometryColumn.trim() &&
|
|
77
|
+
typeof binding.targetGeometryColumn === 'string' &&
|
|
78
|
+
binding.targetGeometryColumn.trim()) {
|
|
79
|
+
return {
|
|
80
|
+
...fitToData,
|
|
81
|
+
geometryColumns: [
|
|
82
|
+
String(binding.sourceGeometryColumn),
|
|
83
|
+
String(binding.targetGeometryColumn),
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Dataset default geometry wins over inferred H3.
|
|
89
|
+
const dataset = config.datasets[fitToData.dataset];
|
|
90
|
+
const geometryColumn = dataset?.geometryColumn;
|
|
91
|
+
if (geometryColumn)
|
|
92
|
+
return { ...fitToData, geometryColumn };
|
|
93
|
+
for (const layer of layers) {
|
|
94
|
+
if (!layer || typeof layer !== 'object')
|
|
95
|
+
continue;
|
|
96
|
+
const layerRecord = layer;
|
|
97
|
+
// Hidden layers must not drive fit (e.g. stale H3 over visible lon/lat).
|
|
98
|
+
if (layerRecord.visible === false)
|
|
99
|
+
continue;
|
|
100
|
+
const binding = layerRecord._sqlroomsBinding;
|
|
101
|
+
if (binding?.dataset !== fitToData.dataset)
|
|
102
|
+
continue;
|
|
103
|
+
if (binding.hexagonColumn) {
|
|
46
104
|
return { ...fitToData, h3Column: String(binding.hexagonColumn) };
|
|
47
105
|
}
|
|
106
|
+
// Fallback: getHexagon "@@=column" → h3Column.
|
|
107
|
+
if (layerRecord['@@type'] === 'GeoArrowH3HexagonLayer') {
|
|
108
|
+
const getHexagon = layerRecord.getHexagon;
|
|
109
|
+
if (typeof getHexagon === 'string') {
|
|
110
|
+
const match = getHexagon.trim().match(/^@@=([A-Za-z_][\w]*)$/);
|
|
111
|
+
const column = match?.[1];
|
|
112
|
+
if (column) {
|
|
113
|
+
return { ...fitToData, h3Column: column };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (config.interaction?.longitudeColumn &&
|
|
119
|
+
config.interaction.latitudeColumn) {
|
|
120
|
+
return {
|
|
121
|
+
...fitToData,
|
|
122
|
+
longitudeColumn: config.interaction.longitudeColumn,
|
|
123
|
+
latitudeColumn: config.interaction.latitudeColumn,
|
|
124
|
+
};
|
|
48
125
|
}
|
|
49
126
|
return fitToData;
|
|
50
127
|
}
|
|
@@ -90,6 +167,7 @@ export function createDeckMapBoundsQuery(options) {
|
|
|
90
167
|
: createDeckMapBoundsTableSourceSql(source);
|
|
91
168
|
if (fitToData.h3Column) {
|
|
92
169
|
const column = escapeId(fitToData.h3Column);
|
|
170
|
+
// Do not CAST to VARCHAR — numeric H3 IDs become invalid decimal text.
|
|
93
171
|
return `
|
|
94
172
|
SELECT
|
|
95
173
|
MIN(h3_cell_to_lng(${column})) AS min_longitude,
|
|
@@ -100,26 +178,59 @@ export function createDeckMapBoundsQuery(options) {
|
|
|
100
178
|
WHERE ${column} IS NOT NULL
|
|
101
179
|
`;
|
|
102
180
|
}
|
|
103
|
-
|
|
104
|
-
|
|
181
|
+
const geometryColumns = fitToData.geometryColumns && fitToData.geometryColumns.length > 0
|
|
182
|
+
? fitToData.geometryColumns
|
|
183
|
+
: fitToData.geometryColumn
|
|
184
|
+
? [fitToData.geometryColumn]
|
|
185
|
+
: null;
|
|
186
|
+
if (geometryColumns) {
|
|
105
187
|
const sourceSql = isDeckMapSqlDatasetSource(source)
|
|
106
188
|
? source.sqlQuery
|
|
107
189
|
: (source.transformSql ?? '');
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
190
|
+
const asGeometry = (columnName) => {
|
|
191
|
+
// Per-column decode — a global ST_AsWKB check would break mixed arc columns.
|
|
192
|
+
const quoted = escapeId(columnName);
|
|
193
|
+
return columnAliasProducedByStAsWkb(sourceSql, columnName)
|
|
194
|
+
? `ST_GeomFromWKB(${quoted})`
|
|
195
|
+
: `${quoted}::GEOMETRY`;
|
|
196
|
+
};
|
|
197
|
+
if (geometryColumns.length === 1) {
|
|
198
|
+
const columnName = geometryColumns[0];
|
|
199
|
+
const column = escapeId(columnName);
|
|
200
|
+
return `
|
|
112
201
|
SELECT
|
|
113
202
|
ST_XMin(extent) AS min_longitude,
|
|
114
203
|
ST_YMin(extent) AS min_latitude,
|
|
115
204
|
ST_XMax(extent) AS max_longitude,
|
|
116
205
|
ST_YMax(extent) AS max_latitude
|
|
117
206
|
FROM (
|
|
118
|
-
SELECT ST_Extent_Agg(${
|
|
207
|
+
SELECT ST_Extent_Agg(${asGeometry(columnName)}) AS extent
|
|
119
208
|
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_geom"
|
|
120
209
|
WHERE ${column} IS NOT NULL
|
|
121
210
|
) AS "__sqlrooms_dashboard_map_extent"
|
|
122
211
|
WHERE extent IS NOT NULL
|
|
212
|
+
`;
|
|
213
|
+
}
|
|
214
|
+
// Multiple geom columns: unnest once so the source is scanned once.
|
|
215
|
+
const geomExprs = geometryColumns.map((col) => {
|
|
216
|
+
const column = escapeId(col);
|
|
217
|
+
return `CASE WHEN ${column} IS NOT NULL THEN ${asGeometry(col)} END`;
|
|
218
|
+
});
|
|
219
|
+
return `
|
|
220
|
+
SELECT
|
|
221
|
+
ST_XMin(extent) AS min_longitude,
|
|
222
|
+
ST_YMin(extent) AS min_latitude,
|
|
223
|
+
ST_XMax(extent) AS max_longitude,
|
|
224
|
+
ST_YMax(extent) AS max_latitude
|
|
225
|
+
FROM (
|
|
226
|
+
SELECT ST_Extent_Agg(geom) AS extent
|
|
227
|
+
FROM (
|
|
228
|
+
SELECT UNNEST([${geomExprs.join(', ')}]) AS geom
|
|
229
|
+
FROM (${baseSourceSql}) AS "__sqlrooms_dashboard_map_geom"
|
|
230
|
+
) AS "__sqlrooms_dashboard_map_geoms"
|
|
231
|
+
WHERE geom IS NOT NULL
|
|
232
|
+
) AS "__sqlrooms_dashboard_map_extent"
|
|
233
|
+
WHERE extent IS NOT NULL
|
|
123
234
|
`;
|
|
124
235
|
}
|
|
125
236
|
if (fitToData.longitudeColumn && fitToData.latitudeColumn) {
|
|
@@ -201,6 +312,21 @@ function fitDeckMapView(options) {
|
|
|
201
312
|
zoom: Math.min(fitted.zoom, maxZoom),
|
|
202
313
|
};
|
|
203
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* MapLibre `jumpTo` options. Omits pitch/bearing unless set so fit preserves
|
|
317
|
+
* a pitched `initialViewState` (needed for extruded columns).
|
|
318
|
+
*/
|
|
319
|
+
export function buildDeckMapJumpToOptions(opts) {
|
|
320
|
+
const jumpOpts = {
|
|
321
|
+
center: [opts.longitude, opts.latitude],
|
|
322
|
+
zoom: opts.zoom,
|
|
323
|
+
};
|
|
324
|
+
if (opts.bearing != null)
|
|
325
|
+
jumpOpts.bearing = opts.bearing;
|
|
326
|
+
if (opts.pitch != null)
|
|
327
|
+
jumpOpts.pitch = opts.pitch;
|
|
328
|
+
return jumpOpts;
|
|
329
|
+
}
|
|
204
330
|
function createInitialFitState(key, requestVersion) {
|
|
205
331
|
return {
|
|
206
332
|
key,
|
|
@@ -208,6 +334,9 @@ function createInitialFitState(key, requestVersion) {
|
|
|
208
334
|
handledRequestVersion: requestVersion,
|
|
209
335
|
};
|
|
210
336
|
}
|
|
337
|
+
/** Retries for H3 extension load races. */
|
|
338
|
+
const FIT_MAX_ATTEMPTS = 5;
|
|
339
|
+
const FIT_RETRY_DELAY_MS = 750;
|
|
211
340
|
/**
|
|
212
341
|
* Executes host-neutral fit-to-data requests for a Deck map surface.
|
|
213
342
|
*
|
|
@@ -223,6 +352,7 @@ export function useDeckMapFitController(options) {
|
|
|
223
352
|
const { scopeId, fitToData, source, container, deckMapRef, requestVersion, autoFit = false, onSuccess, onError, } = options;
|
|
224
353
|
const executeSql = useStoreWithDuckDb((state) => state.db.executeSql);
|
|
225
354
|
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
|
|
355
|
+
const [retryNonce, setRetryNonce] = useState(0);
|
|
226
356
|
const fitKey = useMemo(() => JSON.stringify({ scopeId, fitToData, source }), [fitToData, scopeId, source]);
|
|
227
357
|
const [fitState, setFitState] = useState(() => createInitialFitState(fitKey, requestVersion));
|
|
228
358
|
// A source change resets auto-fit state, but must not mark a newer manual
|
|
@@ -230,6 +360,7 @@ export function useDeckMapFitController(options) {
|
|
|
230
360
|
const activeFitState = fitState.key === fitKey
|
|
231
361
|
? fitState
|
|
232
362
|
: createInitialFitState(fitKey, fitState.handledRequestVersion);
|
|
363
|
+
const fitAttemptsRef = useRef({ key: '', count: 0 });
|
|
233
364
|
useEffect(() => {
|
|
234
365
|
if (!container)
|
|
235
366
|
return;
|
|
@@ -245,6 +376,11 @@ export function useDeckMapFitController(options) {
|
|
|
245
376
|
return () => observer.disconnect();
|
|
246
377
|
}, [container]);
|
|
247
378
|
useEffect(() => {
|
|
379
|
+
// Reset retries on source change or Fit click — else exhausted autofit no-ops Fit.
|
|
380
|
+
const attemptsKey = `${fitKey}:${requestVersion}`;
|
|
381
|
+
if (fitAttemptsRef.current.key !== attemptsKey) {
|
|
382
|
+
fitAttemptsRef.current = { key: attemptsKey, count: 0 };
|
|
383
|
+
}
|
|
248
384
|
const hasManualRequest = requestVersion > activeFitState.handledRequestVersion;
|
|
249
385
|
if (!fitToData ||
|
|
250
386
|
!source ||
|
|
@@ -254,11 +390,25 @@ export function useDeckMapFitController(options) {
|
|
|
254
390
|
return;
|
|
255
391
|
}
|
|
256
392
|
let cancelled = false;
|
|
393
|
+
let retryTimer;
|
|
257
394
|
const markHandled = () => setFitState({
|
|
258
395
|
key: fitKey,
|
|
259
396
|
didAutoFit: true,
|
|
260
397
|
handledRequestVersion: requestVersion,
|
|
261
398
|
});
|
|
399
|
+
const scheduleRetry = (error) => {
|
|
400
|
+
fitAttemptsRef.current.count += 1;
|
|
401
|
+
if (fitAttemptsRef.current.count >= FIT_MAX_ATTEMPTS) {
|
|
402
|
+
if (error)
|
|
403
|
+
onError?.(error);
|
|
404
|
+
markHandled();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
retryTimer = setTimeout(() => {
|
|
408
|
+
if (!cancelled)
|
|
409
|
+
setRetryNonce((value) => value + 1);
|
|
410
|
+
}, FIT_RETRY_DELAY_MS);
|
|
411
|
+
};
|
|
262
412
|
void (async () => {
|
|
263
413
|
try {
|
|
264
414
|
if (fitToData.h3Column) {
|
|
@@ -278,18 +428,32 @@ export function useDeckMapFitController(options) {
|
|
|
278
428
|
}
|
|
279
429
|
const handle = await executeSql(query);
|
|
280
430
|
const result = handle ? await handle : null;
|
|
281
|
-
if (cancelled
|
|
431
|
+
if (cancelled)
|
|
432
|
+
return;
|
|
433
|
+
if (!result) {
|
|
434
|
+
// Empty handle ≈ cancelled query, not a transient race.
|
|
435
|
+
onError?.(new Error('Unable to fit map view to data: empty query result.'));
|
|
436
|
+
markHandled();
|
|
282
437
|
return;
|
|
438
|
+
}
|
|
283
439
|
const bounds = readDeckMapBounds(result);
|
|
284
|
-
if (bounds) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
440
|
+
if (!bounds) {
|
|
441
|
+
// Empty geometry is permanent; only retry H3 cold-start.
|
|
442
|
+
if (fitToData.h3Column) {
|
|
443
|
+
scheduleRetry(new Error('Unable to fit map view to H3 data. Check that the hexagon column contains valid H3 indexes.'));
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
onError?.(new Error('Unable to determine map bounds from data.'));
|
|
447
|
+
markHandled();
|
|
448
|
+
return;
|
|
292
449
|
}
|
|
450
|
+
deckMapRef.current?.jumpTo(fitDeckMapView({
|
|
451
|
+
bounds,
|
|
452
|
+
width: containerSize.width,
|
|
453
|
+
height: containerSize.height,
|
|
454
|
+
padding: fitToData.padding,
|
|
455
|
+
maxZoom: fitToData.maxZoom,
|
|
456
|
+
}));
|
|
293
457
|
markHandled();
|
|
294
458
|
onSuccess?.();
|
|
295
459
|
}
|
|
@@ -299,12 +463,25 @@ export function useDeckMapFitController(options) {
|
|
|
299
463
|
const fitError = error instanceof Error
|
|
300
464
|
? error
|
|
301
465
|
: new Error('Unable to fit map view to data.');
|
|
466
|
+
// Retry only likely-transient H3 extension races.
|
|
467
|
+
const message = fitError.message.toLowerCase();
|
|
468
|
+
const maybeTransientH3 = Boolean(fitToData.h3Column) &&
|
|
469
|
+
(message.includes('h3') ||
|
|
470
|
+
message.includes('extension') ||
|
|
471
|
+
message.includes('not loaded') ||
|
|
472
|
+
message.includes('catalog'));
|
|
473
|
+
if (maybeTransientH3) {
|
|
474
|
+
scheduleRetry(fitError);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
302
477
|
onError?.(fitError);
|
|
303
478
|
markHandled();
|
|
304
479
|
}
|
|
305
480
|
})();
|
|
306
481
|
return () => {
|
|
307
482
|
cancelled = true;
|
|
483
|
+
if (retryTimer)
|
|
484
|
+
clearTimeout(retryTimer);
|
|
308
485
|
};
|
|
309
486
|
}, [
|
|
310
487
|
activeFitState.didAutoFit,
|
|
@@ -319,6 +496,7 @@ export function useDeckMapFitController(options) {
|
|
|
319
496
|
onError,
|
|
320
497
|
onSuccess,
|
|
321
498
|
requestVersion,
|
|
499
|
+
retryNonce,
|
|
322
500
|
source,
|
|
323
501
|
]);
|
|
324
502
|
}
|
package/dist/mapFit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapFit.js","sourceRoot":"","sources":["../src/mapFit.ts"],"names":[],"mappings":"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","sourcesContent":["import {WebMercatorViewport} from '@deck.gl/core';\nimport {\n escapeId,\n getColValAsNumber,\n useStoreWithDuckDb,\n} from '@sqlrooms/duckdb';\nimport type {Table as ArrowTable} from 'apache-arrow';\nimport {useEffect, useMemo, useState, type RefObject} from 'react';\nimport {\n DeckTableDatasetInvalidTableNameError,\n createDeckTableDatasetSql,\n} from './datasets/tableDatasetSql';\nimport {\n isDeckMapSqlDatasetSource,\n type DeckMapConfig,\n type DeckMapDatasetSource,\n type DeckMapFitToDataConfig,\n} from './mapConfig';\nimport {\n isSqlDatasetInput,\n isTableDatasetInput,\n type DeckDatasetInput,\n type DeckJsonMapHandle,\n} from './types';\n\n/**\n * Resolves the effective fit-to-data configuration for a Deck map.\n *\n * Explicit coordinate or geometry fields take precedence. Missing geometry and\n * H3 fields are inferred from dataset metadata, interaction configuration, and\n * layer bindings without consulting host-specific state.\n *\n * @param config - Durable Deck map configuration to inspect.\n * @returns A normalized fit configuration, or `null` when fitting is disabled.\n */\nexport function resolveDeckMapFitToData(\n config: DeckMapConfig | null | undefined,\n): DeckMapFitToDataConfig | null {\n if (!config) return null;\n const fitToData = config.fitToData;\n if (!fitToData?.dataset) return null;\n if (fitToData.longitudeColumn && fitToData.latitudeColumn) return fitToData;\n\n const dataset = config.datasets[fitToData.dataset];\n const geometryColumn = fitToData.geometryColumn ?? dataset?.geometryColumn;\n if (geometryColumn) return {...fitToData, geometryColumn};\n\n if (\n config.interaction?.longitudeColumn &&\n config.interaction.latitudeColumn\n ) {\n return {\n ...fitToData,\n longitudeColumn: config.interaction.longitudeColumn,\n latitudeColumn: config.interaction.latitudeColumn,\n };\n }\n\n const spec =\n typeof config.spec === 'string'\n ? undefined\n : (config.spec as Record<string, unknown>);\n const layers = Array.isArray(spec?.layers) ? spec.layers : [];\n for (const layer of layers) {\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 === fitToData.dataset && binding.hexagonColumn) {\n return {...fitToData, h3Column: String(binding.hexagonColumn)};\n }\n }\n\n return fitToData;\n}\n\n/**\n * Converts a host-resolved Deck dataset input into a queryable map source.\n *\n * Prepared Arrow datasets intentionally return `null` because fitting them\n * requires host-provided bounds rather than a DuckDB source query.\n *\n * @param dataset - Dataset after the host data adapter has resolved its source.\n * @returns A SQL or table source suitable for bounds queries, or `null`.\n */\nexport function getDeckMapDatasetSource(\n dataset: DeckDatasetInput | undefined,\n): DeckMapDatasetSource | null {\n if (!dataset) return null;\n if (isSqlDatasetInput(dataset)) return {sqlQuery: dataset.sqlQuery};\n if (isTableDatasetInput(dataset)) {\n return {\n tableName: dataset.tableName,\n transformSql: dataset.transformSql,\n };\n }\n return null;\n}\n\n/**\n * Builds the DuckDB query used to calculate a map dataset's geographic extent.\n *\n * The query strategy follows the normalized fit configuration: H3 cells,\n * geometry values, explicit longitude/latitude columns, or conventional\n * `Longitude`/`Latitude` column names as a final fallback.\n *\n * @param options - Resolved dataset source and normalized fit configuration.\n * @returns SQL that yields min/max longitude and latitude, or `null` when the\n * configuration cannot identify a target dataset.\n */\nexport function createDeckMapBoundsQuery(options: {\n source: DeckMapDatasetSource;\n fitToData: DeckMapFitToDataConfig;\n}) {\n const {source, fitToData} = options;\n if (!fitToData.dataset) return null;\n const baseSourceSql = isDeckMapSqlDatasetSource(source)\n ? `SELECT * FROM (${source.sqlQuery.trim().replace(/(?:\\s*;+\\s*)+$/, '')}) AS \"__sqlrooms_dashboard_map_source\"`\n : createDeckMapBoundsTableSourceSql(source);\n\n if (fitToData.h3Column) {\n const column = escapeId(fitToData.h3Column);\n return `\n SELECT\n MIN(h3_cell_to_lng(${column})) AS min_longitude,\n MIN(h3_cell_to_lat(${column})) AS min_latitude,\n MAX(h3_cell_to_lng(${column})) AS max_longitude,\n MAX(h3_cell_to_lat(${column})) AS max_latitude\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_h3\"\n WHERE ${column} IS NOT NULL\n `;\n }\n\n if (fitToData.geometryColumn) {\n const column = escapeId(fitToData.geometryColumn);\n const sourceSql = isDeckMapSqlDatasetSource(source)\n ? source.sqlQuery\n : (source.transformSql ?? '');\n const geometry = sourceSql.toLowerCase().includes('st_aswkb')\n ? `ST_GeomFromWKB(${column})`\n : `${column}::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(${geometry}) AS extent\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_geom\"\n WHERE ${column} 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 longitude = escapeId(fitToData.longitudeColumn);\n const latitude = 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(${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\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 createDeckMapBoundsTableSourceSql(\n source: Exclude<DeckMapDatasetSource, {sqlQuery: string}>,\n) {\n try {\n return createDeckTableDatasetSql(source);\n } catch (error) {\n if (error instanceof DeckTableDatasetInvalidTableNameError) {\n throw new Error('Deck map fit-to-data requires a valid table source.');\n }\n throw error;\n }\n}\n\nfunction readDeckMapBounds(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 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 fitDeckMapView(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 fitted = new WebMercatorViewport({\n width: Math.max(width, 1),\n height: Math.max(height, 1),\n }).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 return {\n longitude: fitted.longitude,\n latitude: fitted.latitude,\n zoom: Math.min(fitted.zoom, maxZoom),\n };\n}\n\ntype DeckMapFitState = {\n key: string;\n didAutoFit: boolean;\n handledRequestVersion: number;\n};\n\nfunction createInitialFitState(key: string, requestVersion: number) {\n return {\n key,\n didAutoFit: false,\n handledRequestVersion: requestVersion,\n };\n}\n\n/**\n * Executes host-neutral fit-to-data requests for a Deck map surface.\n *\n * The controller observes the map container, de-duplicates manual requests,\n * optionally auto-fits when the source changes, loads H3 support when needed,\n * queries dataset bounds, and updates the imperative Deck map view. Hosts are\n * responsible only for resolving their dataset source and request version.\n *\n * @param options - Map identity, resolved fit inputs, view refs, request state,\n * and optional success and error callbacks.\n */\nexport function useDeckMapFitController(options: {\n scopeId: string;\n fitToData: DeckMapFitToDataConfig | null;\n source: DeckMapDatasetSource | null;\n container: HTMLElement | null;\n deckMapRef: RefObject<DeckJsonMapHandle | null>;\n requestVersion: number;\n autoFit?: boolean;\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n}) {\n const {\n scopeId,\n fitToData,\n source,\n container,\n deckMapRef,\n requestVersion,\n autoFit = false,\n onSuccess,\n onError,\n } = options;\n const executeSql = useStoreWithDuckDb((state) => state.db.executeSql);\n const [containerSize, setContainerSize] = useState({width: 0, height: 0});\n const fitKey = useMemo(\n () => JSON.stringify({scopeId, fitToData, source}),\n [fitToData, scopeId, source],\n );\n const [fitState, setFitState] = useState<DeckMapFitState>(() =>\n createInitialFitState(fitKey, requestVersion),\n );\n // A source change resets auto-fit state, but must not mark a newer manual\n // request as handled before the effect observes it.\n const activeFitState =\n fitState.key === fitKey\n ? fitState\n : createInitialFitState(fitKey, fitState.handledRequestVersion);\n\n useEffect(() => {\n if (!container) return;\n const updateSize = () =>\n setContainerSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n updateSize();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(updateSize);\n observer.observe(container);\n return () => observer.disconnect();\n }, [container]);\n\n useEffect(() => {\n const hasManualRequest =\n requestVersion > activeFitState.handledRequestVersion;\n if (\n !fitToData ||\n !source ||\n containerSize.width <= 0 ||\n containerSize.height <= 0 ||\n (!hasManualRequest && (!autoFit || activeFitState.didAutoFit))\n ) {\n return;\n }\n\n let cancelled = false;\n const markHandled = () =>\n setFitState({\n key: fitKey,\n didAutoFit: true,\n handledRequestVersion: requestVersion,\n });\n\n void (async () => {\n try {\n if (fitToData.h3Column) {\n try {\n await executeSql('INSTALL h3 FROM community');\n } catch {\n // The extension may already be installed.\n }\n await executeSql('LOAD h3');\n }\n const query = createDeckMapBoundsQuery({source, fitToData});\n if (!query) {\n markHandled();\n onSuccess?.();\n return;\n }\n const handle = await executeSql(query);\n const result = handle ? await handle : null;\n if (cancelled || !result) return;\n const bounds = readDeckMapBounds(result);\n if (bounds) {\n deckMapRef.current?.jumpTo(\n fitDeckMapView({\n bounds,\n width: containerSize.width,\n height: containerSize.height,\n padding: fitToData.padding,\n maxZoom: fitToData.maxZoom,\n }),\n );\n }\n markHandled();\n onSuccess?.();\n } catch (error) {\n if (cancelled) return;\n const fitError =\n error instanceof Error\n ? error\n : new Error('Unable to fit map view to data.');\n onError?.(fitError);\n markHandled();\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [\n activeFitState.didAutoFit,\n activeFitState.handledRequestVersion,\n autoFit,\n containerSize.height,\n containerSize.width,\n deckMapRef,\n executeSql,\n fitKey,\n fitToData,\n onError,\n onSuccess,\n requestVersion,\n source,\n ]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mapFit.js","sourceRoot":"","sources":["../src/mapFit.ts"],"names":[],"mappings":"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","sourcesContent":["import {WebMercatorViewport} from '@deck.gl/core';\nimport {\n escapeId,\n getColValAsNumber,\n useStoreWithDuckDb,\n} from '@sqlrooms/duckdb';\nimport type {Table as ArrowTable} from 'apache-arrow';\nimport {useEffect, useMemo, useRef, useState, type RefObject} from 'react';\nimport {\n DeckTableDatasetInvalidTableNameError,\n createDeckTableDatasetSql,\n} from './datasets/tableDatasetSql';\nimport {\n isDeckMapSqlDatasetSource,\n type DeckMapConfig,\n type DeckMapDatasetSource,\n type DeckMapFitToDataConfig,\n} from './mapConfig';\nimport {\n isSqlDatasetInput,\n isTableDatasetInput,\n type DeckDatasetInput,\n type DeckJsonMapHandle,\n} from './types';\n\n/** True when `columnName` is an `ST_AsWKB(...) AS` alias (per-column fit decode). */\nfunction columnAliasProducedByStAsWkb(\n sql: string,\n columnName: string,\n): boolean {\n if (!sql || !columnName) return false;\n const lowerCol = columnName.toLowerCase();\n const parts = sql.split(/\\bST_AsWKB\\s*\\(/i);\n for (let i = 1; i < parts.length; i++) {\n const rest = parts[i]!;\n let depth = 1;\n let j = 0;\n for (; j < rest.length && depth > 0; j++) {\n const ch = rest[j];\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n }\n const after = rest.slice(j);\n const match = after.match(/^\\s*AS\\s+(\"([^\"]+)\"|([A-Za-z_][\\w$]*))/i);\n if (!match) continue;\n const alias = (match[2] ?? match[3] ?? '').toLowerCase();\n if (alias === lowerCol) return true;\n }\n return false;\n}\n\n/**\n * Resolves the effective fit-to-data configuration for a Deck map.\n *\n * Explicit coordinate or geometry fields take precedence. Missing geometry and\n * H3 fields are inferred from dataset metadata, interaction configuration, and\n * layer bindings without consulting host-specific state.\n *\n * @param config - Durable Deck map configuration to inspect.\n * @returns A normalized fit configuration, or `null` when fitting is disabled.\n */\nexport function resolveDeckMapFitToData(\n config: DeckMapConfig | null | undefined,\n): DeckMapFitToDataConfig | null {\n if (!config) return null;\n const fitToData = config.fitToData;\n if (!fitToData?.dataset) return null;\n if (fitToData.longitudeColumn && fitToData.latitudeColumn) return fitToData;\n if (fitToData.geometryColumns && fitToData.geometryColumns.length > 0) {\n return fitToData;\n }\n\n // Explicit geometryColumn wins over inferred arc/H3.\n if (fitToData.geometryColumn) {\n return fitToData;\n }\n\n const spec =\n typeof config.spec === 'string'\n ? undefined\n : (config.spec as Record<string, unknown>);\n const layers = Array.isArray(spec?.layers) ? spec.layers : [];\n\n // Prefer both arc endpoints when fitToData did not name a single column.\n for (const layer of layers) {\n if (!layer || typeof layer !== 'object') continue;\n const layerRecord = layer as Record<string, unknown>;\n // Hidden layers must not drive fit (e.g. stale H3 over visible lon/lat).\n if (layerRecord.visible === false) continue;\n const binding = layerRecord._sqlroomsBinding as\n | Record<string, unknown>\n | undefined;\n if (binding?.dataset !== fitToData.dataset) continue;\n\n if (\n layerRecord['@@type'] === 'GeoArrowArcLayer' &&\n typeof binding.sourceGeometryColumn === 'string' &&\n binding.sourceGeometryColumn.trim() &&\n typeof binding.targetGeometryColumn === 'string' &&\n binding.targetGeometryColumn.trim()\n ) {\n return {\n ...fitToData,\n geometryColumns: [\n String(binding.sourceGeometryColumn),\n String(binding.targetGeometryColumn),\n ],\n };\n }\n }\n\n // Dataset default geometry wins over inferred H3.\n const dataset = config.datasets[fitToData.dataset];\n const geometryColumn = dataset?.geometryColumn;\n if (geometryColumn) return {...fitToData, geometryColumn};\n\n for (const layer of layers) {\n if (!layer || typeof layer !== 'object') continue;\n const layerRecord = layer as Record<string, unknown>;\n // Hidden layers must not drive fit (e.g. stale H3 over visible lon/lat).\n if (layerRecord.visible === false) continue;\n const binding = layerRecord._sqlroomsBinding as\n | Record<string, unknown>\n | undefined;\n if (binding?.dataset !== fitToData.dataset) continue;\n\n if (binding.hexagonColumn) {\n return {...fitToData, h3Column: String(binding.hexagonColumn)};\n }\n\n // Fallback: getHexagon \"@@=column\" → h3Column.\n if (layerRecord['@@type'] === 'GeoArrowH3HexagonLayer') {\n const getHexagon = layerRecord.getHexagon;\n if (typeof getHexagon === 'string') {\n const match = getHexagon.trim().match(/^@@=([A-Za-z_][\\w]*)$/);\n const column = match?.[1];\n if (column) {\n return {...fitToData, h3Column: column};\n }\n }\n }\n }\n\n if (\n config.interaction?.longitudeColumn &&\n config.interaction.latitudeColumn\n ) {\n return {\n ...fitToData,\n longitudeColumn: config.interaction.longitudeColumn,\n latitudeColumn: config.interaction.latitudeColumn,\n };\n }\n\n return fitToData;\n}\n\n/**\n * Converts a host-resolved Deck dataset input into a queryable map source.\n *\n * Prepared Arrow datasets intentionally return `null` because fitting them\n * requires host-provided bounds rather than a DuckDB source query.\n *\n * @param dataset - Dataset after the host data adapter has resolved its source.\n * @returns A SQL or table source suitable for bounds queries, or `null`.\n */\nexport function getDeckMapDatasetSource(\n dataset: DeckDatasetInput | undefined,\n): DeckMapDatasetSource | null {\n if (!dataset) return null;\n if (isSqlDatasetInput(dataset)) return {sqlQuery: dataset.sqlQuery};\n if (isTableDatasetInput(dataset)) {\n return {\n tableName: dataset.tableName,\n transformSql: dataset.transformSql,\n };\n }\n return null;\n}\n\n/**\n * Builds the DuckDB query used to calculate a map dataset's geographic extent.\n *\n * The query strategy follows the normalized fit configuration: H3 cells,\n * geometry values, explicit longitude/latitude columns, or conventional\n * `Longitude`/`Latitude` column names as a final fallback.\n *\n * @param options - Resolved dataset source and normalized fit configuration.\n * @returns SQL that yields min/max longitude and latitude, or `null` when the\n * configuration cannot identify a target dataset.\n */\nexport function createDeckMapBoundsQuery(options: {\n source: DeckMapDatasetSource;\n fitToData: DeckMapFitToDataConfig;\n}) {\n const {source, fitToData} = options;\n if (!fitToData.dataset) return null;\n const baseSourceSql = isDeckMapSqlDatasetSource(source)\n ? `SELECT * FROM (${source.sqlQuery.trim().replace(/(?:\\s*;+\\s*)+$/, '')}) AS \"__sqlrooms_dashboard_map_source\"`\n : createDeckMapBoundsTableSourceSql(source);\n\n if (fitToData.h3Column) {\n const column = escapeId(fitToData.h3Column);\n // Do not CAST to VARCHAR — numeric H3 IDs become invalid decimal text.\n return `\n SELECT\n MIN(h3_cell_to_lng(${column})) AS min_longitude,\n MIN(h3_cell_to_lat(${column})) AS min_latitude,\n MAX(h3_cell_to_lng(${column})) AS max_longitude,\n MAX(h3_cell_to_lat(${column})) AS max_latitude\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_h3\"\n WHERE ${column} IS NOT NULL\n `;\n }\n\n const geometryColumns =\n fitToData.geometryColumns && fitToData.geometryColumns.length > 0\n ? fitToData.geometryColumns\n : fitToData.geometryColumn\n ? [fitToData.geometryColumn]\n : null;\n\n if (geometryColumns) {\n const sourceSql = isDeckMapSqlDatasetSource(source)\n ? source.sqlQuery\n : (source.transformSql ?? '');\n const asGeometry = (columnName: string) => {\n // Per-column decode — a global ST_AsWKB check would break mixed arc columns.\n const quoted = escapeId(columnName);\n return columnAliasProducedByStAsWkb(sourceSql, columnName)\n ? `ST_GeomFromWKB(${quoted})`\n : `${quoted}::GEOMETRY`;\n };\n\n if (geometryColumns.length === 1) {\n const columnName = geometryColumns[0]!;\n const column = escapeId(columnName);\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(${asGeometry(columnName)}) AS extent\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_geom\"\n WHERE ${column} IS NOT NULL\n ) AS \"__sqlrooms_dashboard_map_extent\"\n WHERE extent IS NOT NULL\n `;\n }\n\n // Multiple geom columns: unnest once so the source is scanned once.\n const geomExprs = geometryColumns.map((col) => {\n const column = escapeId(col);\n return `CASE WHEN ${column} IS NOT NULL THEN ${asGeometry(col)} END`;\n });\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(geom) AS extent\n FROM (\n SELECT UNNEST([${geomExprs.join(', ')}]) AS geom\n FROM (${baseSourceSql}) AS \"__sqlrooms_dashboard_map_geom\"\n ) AS \"__sqlrooms_dashboard_map_geoms\"\n WHERE geom 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 longitude = escapeId(fitToData.longitudeColumn);\n const latitude = 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(${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\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 createDeckMapBoundsTableSourceSql(\n source: Exclude<DeckMapDatasetSource, {sqlQuery: string}>,\n) {\n try {\n return createDeckTableDatasetSql(source);\n } catch (error) {\n if (error instanceof DeckTableDatasetInvalidTableNameError) {\n throw new Error('Deck map fit-to-data requires a valid table source.');\n }\n throw error;\n }\n}\n\nfunction readDeckMapBounds(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 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 fitDeckMapView(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 fitted = new WebMercatorViewport({\n width: Math.max(width, 1),\n height: Math.max(height, 1),\n }).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 return {\n longitude: fitted.longitude,\n latitude: fitted.latitude,\n zoom: Math.min(fitted.zoom, maxZoom),\n };\n}\n\n/**\n * MapLibre `jumpTo` options. Omits pitch/bearing unless set so fit preserves\n * a pitched `initialViewState` (needed for extruded columns).\n */\nexport function buildDeckMapJumpToOptions(opts: {\n longitude: number;\n latitude: number;\n zoom: number;\n bearing?: number;\n pitch?: number;\n}): {\n center: [number, number];\n zoom: number;\n bearing?: number;\n pitch?: number;\n} {\n const jumpOpts: {\n center: [number, number];\n zoom: number;\n bearing?: number;\n pitch?: number;\n } = {\n center: [opts.longitude, opts.latitude],\n zoom: opts.zoom,\n };\n if (opts.bearing != null) jumpOpts.bearing = opts.bearing;\n if (opts.pitch != null) jumpOpts.pitch = opts.pitch;\n return jumpOpts;\n}\n\ntype DeckMapFitState = {\n key: string;\n didAutoFit: boolean;\n handledRequestVersion: number;\n};\n\nfunction createInitialFitState(key: string, requestVersion: number) {\n return {\n key,\n didAutoFit: false,\n handledRequestVersion: requestVersion,\n };\n}\n\n/** Retries for H3 extension load races. */\nconst FIT_MAX_ATTEMPTS = 5;\nconst FIT_RETRY_DELAY_MS = 750;\n\n/**\n * Executes host-neutral fit-to-data requests for a Deck map surface.\n *\n * The controller observes the map container, de-duplicates manual requests,\n * optionally auto-fits when the source changes, loads H3 support when needed,\n * queries dataset bounds, and updates the imperative Deck map view. Hosts are\n * responsible only for resolving their dataset source and request version.\n *\n * @param options - Map identity, resolved fit inputs, view refs, request state,\n * and optional success and error callbacks.\n */\nexport function useDeckMapFitController(options: {\n scopeId: string;\n fitToData: DeckMapFitToDataConfig | null;\n source: DeckMapDatasetSource | null;\n container: HTMLElement | null;\n deckMapRef: RefObject<DeckJsonMapHandle | null>;\n requestVersion: number;\n autoFit?: boolean;\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n}) {\n const {\n scopeId,\n fitToData,\n source,\n container,\n deckMapRef,\n requestVersion,\n autoFit = false,\n onSuccess,\n onError,\n } = options;\n const executeSql = useStoreWithDuckDb((state) => state.db.executeSql);\n const [containerSize, setContainerSize] = useState({width: 0, height: 0});\n const [retryNonce, setRetryNonce] = useState(0);\n const fitKey = useMemo(\n () => JSON.stringify({scopeId, fitToData, source}),\n [fitToData, scopeId, source],\n );\n const [fitState, setFitState] = useState<DeckMapFitState>(() =>\n createInitialFitState(fitKey, requestVersion),\n );\n // A source change resets auto-fit state, but must not mark a newer manual\n // request as handled before the effect observes it.\n const activeFitState =\n fitState.key === fitKey\n ? fitState\n : createInitialFitState(fitKey, fitState.handledRequestVersion);\n const fitAttemptsRef = useRef({key: '', count: 0});\n\n useEffect(() => {\n if (!container) return;\n const updateSize = () =>\n setContainerSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n updateSize();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(updateSize);\n observer.observe(container);\n return () => observer.disconnect();\n }, [container]);\n\n useEffect(() => {\n // Reset retries on source change or Fit click — else exhausted autofit no-ops Fit.\n const attemptsKey = `${fitKey}:${requestVersion}`;\n if (fitAttemptsRef.current.key !== attemptsKey) {\n fitAttemptsRef.current = {key: attemptsKey, count: 0};\n }\n\n const hasManualRequest =\n requestVersion > activeFitState.handledRequestVersion;\n if (\n !fitToData ||\n !source ||\n containerSize.width <= 0 ||\n containerSize.height <= 0 ||\n (!hasManualRequest && (!autoFit || activeFitState.didAutoFit))\n ) {\n return;\n }\n\n let cancelled = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n const markHandled = () =>\n setFitState({\n key: fitKey,\n didAutoFit: true,\n handledRequestVersion: requestVersion,\n });\n const scheduleRetry = (error?: Error) => {\n fitAttemptsRef.current.count += 1;\n if (fitAttemptsRef.current.count >= FIT_MAX_ATTEMPTS) {\n if (error) onError?.(error);\n markHandled();\n return;\n }\n retryTimer = setTimeout(() => {\n if (!cancelled) setRetryNonce((value) => value + 1);\n }, FIT_RETRY_DELAY_MS);\n };\n\n void (async () => {\n try {\n if (fitToData.h3Column) {\n try {\n await executeSql('INSTALL h3 FROM community');\n } catch {\n // The extension may already be installed.\n }\n await executeSql('LOAD h3');\n }\n const query = createDeckMapBoundsQuery({source, fitToData});\n if (!query) {\n markHandled();\n onSuccess?.();\n return;\n }\n const handle = await executeSql(query);\n const result = handle ? await handle : null;\n if (cancelled) return;\n if (!result) {\n // Empty handle ≈ cancelled query, not a transient race.\n onError?.(\n new Error('Unable to fit map view to data: empty query result.'),\n );\n markHandled();\n return;\n }\n const bounds = readDeckMapBounds(result);\n if (!bounds) {\n // Empty geometry is permanent; only retry H3 cold-start.\n if (fitToData.h3Column) {\n scheduleRetry(\n new Error(\n 'Unable to fit map view to H3 data. Check that the hexagon column contains valid H3 indexes.',\n ),\n );\n return;\n }\n onError?.(new Error('Unable to determine map bounds from data.'));\n markHandled();\n return;\n }\n deckMapRef.current?.jumpTo(\n fitDeckMapView({\n bounds,\n width: containerSize.width,\n height: containerSize.height,\n padding: fitToData.padding,\n maxZoom: fitToData.maxZoom,\n }),\n );\n markHandled();\n onSuccess?.();\n } catch (error) {\n if (cancelled) return;\n const fitError =\n error instanceof Error\n ? error\n : new Error('Unable to fit map view to data.');\n // Retry only likely-transient H3 extension races.\n const message = fitError.message.toLowerCase();\n const maybeTransientH3 =\n Boolean(fitToData.h3Column) &&\n (message.includes('h3') ||\n message.includes('extension') ||\n message.includes('not loaded') ||\n message.includes('catalog'));\n if (maybeTransientH3) {\n scheduleRetry(fitError);\n return;\n }\n onError?.(fitError);\n markHandled();\n }\n })();\n\n return () => {\n cancelled = true;\n if (retryTimer) clearTimeout(retryTimer);\n };\n }, [\n activeFitState.didAutoFit,\n activeFitState.handledRequestVersion,\n autoFit,\n containerSize.height,\n containerSize.width,\n deckMapRef,\n executeSql,\n fitKey,\n fitToData,\n onError,\n onSuccess,\n requestVersion,\n retryNonce,\n source,\n ]);\n}\n"]}
|
|
@@ -7,7 +7,7 @@ export type DeckMapLayerColorScaleFunction = ColorScaleConfig & {
|
|
|
7
7
|
};
|
|
8
8
|
export type DeckMapLayerColorAccessor = 'getFillColor' | 'getLineColor' | 'getColor' | 'getSourceColor' | 'getTargetColor';
|
|
9
9
|
export declare const DECK_MAP_LAYER_TYPE_OPTIONS: ReadonlyArray<{
|
|
10
|
-
value: DeckAutoLayerType
|
|
10
|
+
value: DeckAutoLayerType;
|
|
11
11
|
label: string;
|
|
12
12
|
}>;
|
|
13
13
|
export declare const DECK_MAP_COLOR_ACCESSOR_OPTIONS: ReadonlyArray<{
|
|
@@ -31,6 +31,14 @@ export declare function usesExtrusionSettings(layerType: unknown): boolean;
|
|
|
31
31
|
export declare function getDeckMapColorAccessorOptions(layerType: unknown): typeof DECK_MAP_COLOR_ACCESSOR_OPTIONS;
|
|
32
32
|
export declare function updateDeckMapLayer(config: DeckMapConfig, layerIndex: number, updater: (layer: DeckMapLayerRecord) => DeckMapLayerRecord): DeckMapConfig;
|
|
33
33
|
export declare function setDeckMapLayerType(config: DeckMapConfig, layerIndex: number, layerType: string): DeckMapConfig;
|
|
34
|
+
/** Force meters and strip point/heatmap radius leftovers (does not set `radius`). */
|
|
35
|
+
export declare function stripDeckMapColumnLayerRadiusConflicts(layer: DeckMapLayerRecord): DeckMapLayerRecord;
|
|
36
|
+
/** True when ColumnLayer still carries point/heatmap radius props or pixel units. */
|
|
37
|
+
export declare function deckMapColumnLayerHasRadiusConflicts(layer: DeckMapLayerRecord): boolean;
|
|
38
|
+
/** Apply a column radius in meters and strip conflicting point/heatmap radius props. */
|
|
39
|
+
export declare function applyDeckMapColumnRadiusMeters(layer: DeckMapLayerRecord, radiusMeters: number): DeckMapLayerRecord;
|
|
40
|
+
/** Set GeoArrowColumnLayer disk radius in meters. */
|
|
41
|
+
export declare function setDeckMapLayerColumnRadius(config: DeckMapConfig, layerIndex: number, radiusMeters: number): DeckMapConfig;
|
|
34
42
|
export declare function setDeckMapLayerGeometryColumn(config: DeckMapConfig, layerIndex: number, geometryColumn: string): DeckMapConfig;
|
|
35
43
|
export declare function setDeckMapLayerHexagonColumn(config: DeckMapConfig, layerIndex: number, hexagonColumn: string): DeckMapConfig;
|
|
36
44
|
export declare function setDeckMapLayerArcColumns(config: DeckMapConfig, layerIndex: number, columns: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapLayerConfigUtils.d.ts","sourceRoot":"","sources":["../src/mapLayerConfigUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAC/E,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAG/C,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,GAAG;IAC9D,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,yBAAyB,GACjC,cAAc,GACd,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,gBAAgB,CAAC;AAErB,eAAO,MAAM,2BAA2B,EAAE,aAAa,CAAC;IACtD,KAAK,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"mapLayerConfigUtils.d.ts","sourceRoot":"","sources":["../src/mapLayerConfigUtils.ts"],"names":[],"mappings":"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"}
|