@geolibre/core 2.9.0 → 3.0.0
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/dist/index.d.mts +1246 -668
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +995 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -169,6 +169,9 @@ const DEFAULT_LAYER_STYLE = {
|
|
|
169
169
|
rotation: 0,
|
|
170
170
|
maxWidth: 10,
|
|
171
171
|
transform: "none",
|
|
172
|
+
numberFormatEnabled: false,
|
|
173
|
+
numberDecimals: 0,
|
|
174
|
+
numberLocale: "",
|
|
172
175
|
dedupe: "off",
|
|
173
176
|
sizeExpression: "",
|
|
174
177
|
colorExpression: "",
|
|
@@ -227,6 +230,8 @@ const DEFAULT_LAYER_STYLE = {
|
|
|
227
230
|
pointRenderer: "single",
|
|
228
231
|
heatmapRadius: 30,
|
|
229
232
|
heatmapIntensity: 1,
|
|
233
|
+
heatmapColorRamp: "turbo",
|
|
234
|
+
heatmapWeightProperty: "",
|
|
230
235
|
clusterRadius: 50,
|
|
231
236
|
clusterMaxZoom: 14,
|
|
232
237
|
invertedFillEnabled: false,
|
|
@@ -306,6 +311,12 @@ const NETCDF_IMAGE_SOURCE_KIND = "netcdf-image";
|
|
|
306
311
|
function isDuckDBQueryLayer(layer) {
|
|
307
312
|
return layer?.type === "duckdb-query" && layer.metadata.sourceKind === "duckdb-query" && layer.metadata.externalDeckLayer === true;
|
|
308
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* The engine that draws the primary map area when a project says nothing. The
|
|
316
|
+
* 2D map: it is the renderer every tool, plugin, and panel is wired to, so an
|
|
317
|
+
* existing project (and a new one) opens exactly as it always did.
|
|
318
|
+
*/
|
|
319
|
+
const DEFAULT_PRIMARY_RENDERER = "maplibre";
|
|
309
320
|
/** The default single-map grid (one pane, sync enabled so it turns on cleanly). */
|
|
310
321
|
const DEFAULT_MAP_GRID_LAYOUT = {
|
|
311
322
|
rows: 1,
|
|
@@ -1424,6 +1435,162 @@ function isRegionalBasemapSentinel(styleUrl) {
|
|
|
1424
1435
|
* {@link CesiumBasemapImagery}): tiles to draw, nothing to draw, or "no raster
|
|
1425
1436
|
* equivalent exists — pick your own default".
|
|
1426
1437
|
*/
|
|
1438
|
+
/** Stable project IDs and the corresponding Cesium bundled thumbnails / ion assets. */
|
|
1439
|
+
const CESIUM_BASEMAPS = [
|
|
1440
|
+
{
|
|
1441
|
+
id: "project",
|
|
1442
|
+
name: "Project basemap",
|
|
1443
|
+
icon: "naturalEarthII.png"
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
id: "natural-earth",
|
|
1447
|
+
name: "Natural Earth II",
|
|
1448
|
+
icon: "naturalEarthII.png"
|
|
1449
|
+
},
|
|
1450
|
+
{
|
|
1451
|
+
id: "bing-aerial",
|
|
1452
|
+
name: "Bing Maps Aerial",
|
|
1453
|
+
icon: "bingAerial.png",
|
|
1454
|
+
assetId: 2
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
id: "bing-labels",
|
|
1458
|
+
name: "Bing Maps Aerial with Labels",
|
|
1459
|
+
icon: "bingAerialLabels.png",
|
|
1460
|
+
assetId: 3
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
id: "bing-roads",
|
|
1464
|
+
name: "Bing Maps Roads",
|
|
1465
|
+
icon: "bingRoads.png",
|
|
1466
|
+
assetId: 4
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
id: "sentinel-2",
|
|
1470
|
+
name: "Sentinel-2",
|
|
1471
|
+
icon: "sentinel-2.png",
|
|
1472
|
+
assetId: 3954
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
id: "blue-marble",
|
|
1476
|
+
name: "Blue Marble",
|
|
1477
|
+
icon: "blueMarble.png",
|
|
1478
|
+
assetId: 3845
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
id: "earth-at-night",
|
|
1482
|
+
name: "Earth at night",
|
|
1483
|
+
icon: "earthAtNight.png",
|
|
1484
|
+
assetId: 3812
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
id: "google-satellite",
|
|
1488
|
+
name: "Google Maps Satellite",
|
|
1489
|
+
icon: "googleSatellite.png",
|
|
1490
|
+
assetId: 3830182
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
id: "google-labels",
|
|
1494
|
+
name: "Google Maps Satellite with Labels",
|
|
1495
|
+
icon: "googleSatelliteLabels.png",
|
|
1496
|
+
assetId: 3830183
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
id: "google-roads",
|
|
1500
|
+
name: "Google Maps Roadmap",
|
|
1501
|
+
icon: "googleRoadmap.png",
|
|
1502
|
+
assetId: 3830184
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
id: "google-contour",
|
|
1506
|
+
name: "Google Maps Contour",
|
|
1507
|
+
icon: "googleContour.png",
|
|
1508
|
+
assetId: 3830186
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
id: "azure-aerial",
|
|
1512
|
+
name: "Azure Maps Aerial",
|
|
1513
|
+
icon: "azureAerial.png",
|
|
1514
|
+
assetId: 3891168
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
id: "azure-roads",
|
|
1518
|
+
name: "Azure Maps Roads",
|
|
1519
|
+
icon: "azureRoads.png",
|
|
1520
|
+
assetId: 3891169
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
id: "esri-imagery",
|
|
1524
|
+
name: "ArcGIS World Imagery",
|
|
1525
|
+
icon: "ArcGisMapServiceWorldImagery.png",
|
|
1526
|
+
category: "Other",
|
|
1527
|
+
service: "World_Imagery"
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
id: "esri-hillshade",
|
|
1531
|
+
name: "ArcGIS World Hillshade",
|
|
1532
|
+
icon: "ArcGisMapServiceWorldHillshade.png",
|
|
1533
|
+
category: "Other",
|
|
1534
|
+
service: "Elevation/World_Hillshade"
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
id: "esri-ocean",
|
|
1538
|
+
name: "Esri World Ocean",
|
|
1539
|
+
icon: "ArcGisMapServiceWorldOcean.png",
|
|
1540
|
+
category: "Other",
|
|
1541
|
+
service: "Ocean/World_Ocean_Base"
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
id: "osm",
|
|
1545
|
+
name: "OpenStreetMap",
|
|
1546
|
+
icon: "openStreetMap.png",
|
|
1547
|
+
category: "Other"
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
id: "stadia-watercolor",
|
|
1551
|
+
name: "Stadia x Stamen Watercolor",
|
|
1552
|
+
icon: "stamenWatercolor.png",
|
|
1553
|
+
category: "Other",
|
|
1554
|
+
stadiaStyle: "stamen_watercolor",
|
|
1555
|
+
maximumLevel: 16,
|
|
1556
|
+
extension: "jpg"
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
id: "stadia-toner",
|
|
1560
|
+
name: "Stadia x Stamen Toner",
|
|
1561
|
+
icon: "stamenToner.png",
|
|
1562
|
+
category: "Other",
|
|
1563
|
+
stadiaStyle: "stamen_toner",
|
|
1564
|
+
maximumLevel: 20,
|
|
1565
|
+
extension: "png"
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
id: "stadia-smooth",
|
|
1569
|
+
name: "Stadia Alidade Smooth",
|
|
1570
|
+
icon: "stadiaAlidadeSmooth.png",
|
|
1571
|
+
category: "Other",
|
|
1572
|
+
stadiaStyle: "alidade_smooth",
|
|
1573
|
+
maximumLevel: 20,
|
|
1574
|
+
extension: "png"
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
id: "stadia-dark",
|
|
1578
|
+
name: "Stadia Alidade Smooth Dark",
|
|
1579
|
+
icon: "stadiaAlidadeSmoothDark.png",
|
|
1580
|
+
category: "Other",
|
|
1581
|
+
stadiaStyle: "alidade_smooth_dark",
|
|
1582
|
+
maximumLevel: 20,
|
|
1583
|
+
extension: "png"
|
|
1584
|
+
}
|
|
1585
|
+
];
|
|
1586
|
+
function normalizeCesiumBasemap(value) {
|
|
1587
|
+
return CESIUM_BASEMAPS.find((entry) => entry.id === value)?.id ?? "project";
|
|
1588
|
+
}
|
|
1589
|
+
/** Missing credentials fall back to the project background without changing the saved choice. */
|
|
1590
|
+
function availableCesiumBasemap(value, hasIonToken) {
|
|
1591
|
+
const entry = CESIUM_BASEMAPS.find((entry) => entry.id === value);
|
|
1592
|
+
return entry && (!("assetId" in entry) || hasIonToken) ? entry.id : "project";
|
|
1593
|
+
}
|
|
1427
1594
|
const OSM_CREDIT = "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors";
|
|
1428
1595
|
/** EOX Maps serves these layers keyless under CC BY-SA 4.0. */
|
|
1429
1596
|
const EOX_CREDIT = "<a href=\"https://maps.eox.at\">EOX Maps</a> (CC BY-SA 4.0)";
|
|
@@ -1488,8 +1655,10 @@ function toImagery(analogue) {
|
|
|
1488
1655
|
*/
|
|
1489
1656
|
function sameCesiumImagery(a, b) {
|
|
1490
1657
|
if (a.kind !== b.kind) return false;
|
|
1658
|
+
if (a.kind === "arcgis" && b.kind === "arcgis") return a.url === b.url;
|
|
1659
|
+
if (a.kind === "ion" && b.kind === "ion") return a.assetId === b.assetId;
|
|
1491
1660
|
if (a.kind !== "xyz" || b.kind !== "xyz") return true;
|
|
1492
|
-
return a.template === b.template && a.attribution === b.attribution && a.maximumLevel === b.maximumLevel && a.scheme === b.scheme && a.overlayTemplate === b.overlayTemplate;
|
|
1661
|
+
return a.template === b.template && a.apiKeyProvider === b.apiKeyProvider && a.attribution === b.attribution && a.maximumLevel === b.maximumLevel && a.scheme === b.scheme && a.overlayTemplate === b.overlayTemplate;
|
|
1493
1662
|
}
|
|
1494
1663
|
/**
|
|
1495
1664
|
* `decodeURIComponent` that reports failure instead of throwing. A URL keeps a
|
|
@@ -1546,7 +1715,25 @@ function vectorStyleAnalogue(styleUrl) {
|
|
|
1546
1715
|
* @param styleUrl - The project's `basemapStyleUrl`.
|
|
1547
1716
|
* @returns What the globe should draw beneath the data layers.
|
|
1548
1717
|
*/
|
|
1549
|
-
function basemapToCesiumImagery(styleUrl) {
|
|
1718
|
+
function basemapToCesiumImagery(styleUrl, cesiumBasemap = "project") {
|
|
1719
|
+
const entry = CESIUM_BASEMAPS.find((entry) => entry.id === cesiumBasemap);
|
|
1720
|
+
if (entry && "service" in entry) return {
|
|
1721
|
+
kind: "arcgis",
|
|
1722
|
+
url: `https://services.arcgisonline.com/ArcGIS/rest/services/${entry.service}/MapServer`
|
|
1723
|
+
};
|
|
1724
|
+
if (entry && "stadiaStyle" in entry) return {
|
|
1725
|
+
kind: "xyz",
|
|
1726
|
+
template: `https://tiles.stadiamaps.com/tiles/${entry.stadiaStyle}/{z}/{x}/{y}.${entry.extension}`,
|
|
1727
|
+
maximumLevel: entry.maximumLevel,
|
|
1728
|
+
apiKeyProvider: "stadia",
|
|
1729
|
+
attribution: "<a href=\"https://stadiamaps.com/\">© Stadia Maps</a> " + (entry.stadiaStyle.startsWith("stamen_") ? "<a href=\"https://stamen.com/\">© Stamen Design</a> " : "") + "<a href=\"https://openmaptiles.org/\">© OpenMapTiles</a> <a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a>"
|
|
1730
|
+
};
|
|
1731
|
+
if (entry && "assetId" in entry) return {
|
|
1732
|
+
kind: "ion",
|
|
1733
|
+
assetId: entry.assetId
|
|
1734
|
+
};
|
|
1735
|
+
if (cesiumBasemap === "natural-earth") return { kind: "natural-earth" };
|
|
1736
|
+
if (cesiumBasemap === "osm") return toImagery(STREETS);
|
|
1550
1737
|
if (styleUrl === "") return { kind: "none" };
|
|
1551
1738
|
const url = styleUrl ?? "https://tiles.openfreemap.org/styles/liberty";
|
|
1552
1739
|
const planetary = getPlanetaryBasemapByStyleUrl(url);
|
|
@@ -1576,8 +1763,42 @@ function basemapToCesiumImagery(styleUrl) {
|
|
|
1576
1763
|
* GPX tracks with `<ele>` readings or LineStringZ/PointZ geometries. MapLibre's
|
|
1577
1764
|
* 2D style layers ignore the third coordinate, so layers that want to render
|
|
1578
1765
|
* their Z values use these helpers to detect and rescale elevations before
|
|
1579
|
-
* handing the data to a deck.gl overlay.
|
|
1580
|
-
|
|
1766
|
+
* handing the data to a deck.gl overlay. The same data can carry its elevation
|
|
1767
|
+
* in a `bbox` member too, which `horizontalBbox` trims back to two dimensions.
|
|
1768
|
+
*/
|
|
1769
|
+
/**
|
|
1770
|
+
* Reduces a bounding box to its horizontal extent, `[west, south, east, north]`.
|
|
1771
|
+
*
|
|
1772
|
+
* RFC 7946 §5 lets a `bbox` member carry elevation, in which case it holds six
|
|
1773
|
+
* values — `[west, south, minAltitude, east, north, maxAltitude]` — and
|
|
1774
|
+
* `@turf/bbox` returns a collection's own member verbatim rather than
|
|
1775
|
+
* recomputing it. A consumer reading such a box as four values takes the
|
|
1776
|
+
* altitude for a longitude and the east edge for a latitude; that is how a 3D
|
|
1777
|
+
* feed reached MapLibre as an out-of-range latitude (#2358).
|
|
1778
|
+
*
|
|
1779
|
+
* Returns null when there is no usable horizontal extent: a box of any other
|
|
1780
|
+
* length, or one carrying a non-finite value anywhere — an empty collection, or
|
|
1781
|
+
* one whose features all have a null geometry, bboxes to ±Infinity, and a box
|
|
1782
|
+
* whose altitudes are not finite is not one to trust the rest of.
|
|
1783
|
+
*
|
|
1784
|
+
* @param box - A GeoJSON or Turf bounding box, of four or six values.
|
|
1785
|
+
*/
|
|
1786
|
+
function horizontalBbox(box) {
|
|
1787
|
+
if (!box) return null;
|
|
1788
|
+
if (box.length !== 4 && box.length !== 6) return null;
|
|
1789
|
+
if (!box.every((value) => Number.isFinite(value))) return null;
|
|
1790
|
+
return box.length === 6 ? [
|
|
1791
|
+
box[0],
|
|
1792
|
+
box[1],
|
|
1793
|
+
box[3],
|
|
1794
|
+
box[4]
|
|
1795
|
+
] : [
|
|
1796
|
+
box[0],
|
|
1797
|
+
box[1],
|
|
1798
|
+
box[2],
|
|
1799
|
+
box[3]
|
|
1800
|
+
];
|
|
1801
|
+
}
|
|
1581
1802
|
const hasZCache = /* @__PURE__ */ new WeakMap();
|
|
1582
1803
|
/**
|
|
1583
1804
|
* Returns true when any coordinate in the GeoJSON carries a finite, non-zero
|
|
@@ -3017,7 +3238,8 @@ const EXPRESSION_FUNCTION_CATEGORIES = [
|
|
|
3017
3238
|
fn("concat", "[\"concat\", [\"get\", \"field\"], \" suffix\"]", "concat"),
|
|
3018
3239
|
fn("upcase", "[\"upcase\", [\"get\", \"field\"]]", "upcase"),
|
|
3019
3240
|
fn("downcase", "[\"downcase\", [\"get\", \"field\"]]", "downcase"),
|
|
3020
|
-
fn("slice", "[\"slice\", [\"get\", \"field\"], 0, 3]", "slice")
|
|
3241
|
+
fn("slice", "[\"slice\", [\"get\", \"field\"], 0, 3]", "slice"),
|
|
3242
|
+
fn("number-format", "[\"number-format\", [\"get\", \"field\"], {\"locale\": \"en-US\", \"max-fraction-digits\": 2}]", "numberFormat")
|
|
3021
3243
|
]
|
|
3022
3244
|
},
|
|
3023
3245
|
{
|
|
@@ -3235,11 +3457,11 @@ function compileFeatureExpression(source, options = {}) {
|
|
|
3235
3457
|
ok: false,
|
|
3236
3458
|
errors: validation.errors
|
|
3237
3459
|
};
|
|
3238
|
-
const
|
|
3460
|
+
const compiledZoom = options.zoom ?? 0;
|
|
3239
3461
|
return {
|
|
3240
3462
|
ok: true,
|
|
3241
3463
|
errors: [],
|
|
3242
|
-
evaluate: (feature) => expression.evaluateWithoutErrorHandling({ zoom }, {
|
|
3464
|
+
evaluate: (feature, zoom = compiledZoom) => expression.evaluateWithoutErrorHandling({ zoom }, {
|
|
3243
3465
|
type: feature.geometry?.type ?? "Unknown",
|
|
3244
3466
|
properties: feature.properties ?? {},
|
|
3245
3467
|
...feature.id !== void 0 ? { id: feature.id } : {},
|
|
@@ -3873,6 +4095,23 @@ function vectorFillOpacityValue(style, fallback) {
|
|
|
3873
4095
|
return ruleOverrideValue(style, "fillOpacity", fallback);
|
|
3874
4096
|
}
|
|
3875
4097
|
/**
|
|
4098
|
+
* The color-ramp colors a density heatmap paints with.
|
|
4099
|
+
*
|
|
4100
|
+
* {@link getVectorColorRamp} falls back to the *first* ramp ("viridis") for an
|
|
4101
|
+
* unknown name, which is not the heatmap default ("turbo"). Both the map paint
|
|
4102
|
+
* (`heatmapPaint` in `@geolibre/map`) and the auto-legend gradient resolve the
|
|
4103
|
+
* ramp here so a stale or hand-edited `heatmapColorRamp` cannot make the legend
|
|
4104
|
+
* and the rendered heatmap disagree about which ramp is in use.
|
|
4105
|
+
*
|
|
4106
|
+
* @param style - The layer style.
|
|
4107
|
+
* @returns The ramp's colors, or the default ramp's colors for an unknown name.
|
|
4108
|
+
*/
|
|
4109
|
+
function heatmapRampColors(style) {
|
|
4110
|
+
const value = styleValue(style, "heatmapColorRamp");
|
|
4111
|
+
const ramp = getVectorColorRamp(value);
|
|
4112
|
+
return (ramp.value === value ? ramp : getVectorColorRamp(DEFAULT_LAYER_STYLE.heatmapColorRamp)).colors;
|
|
4113
|
+
}
|
|
4114
|
+
/**
|
|
3876
4115
|
* The proportional-size configuration, or `null` when proportional sizing
|
|
3877
4116
|
* does not apply: disabled, no field chosen, non-finite or degenerate
|
|
3878
4117
|
* (`maxValue <= minValue`) value range, or non-finite radii. This is the
|
|
@@ -4112,6 +4351,236 @@ function extrusionColorValue(style) {
|
|
|
4112
4351
|
return parseJsonExpression(styleValue(style, "extrusionAdvancedStyleEnabled") ? styleValue(style, "extrusionColorExpression") : "") ?? flat;
|
|
4113
4352
|
}
|
|
4114
4353
|
//#endregion
|
|
4354
|
+
//#region src/document-locale.ts
|
|
4355
|
+
/**
|
|
4356
|
+
* The document language, so formatted numbers and dates follow the UI locale.
|
|
4357
|
+
*
|
|
4358
|
+
* Shared by the Identify popup's value formatting and by label number
|
|
4359
|
+
* formatting, which both fall back to it when the author has not pinned an
|
|
4360
|
+
* explicit locale. Returns `undefined` outside a browser (tests, the headless
|
|
4361
|
+
* project authoring paths), which leaves `Intl` on its own default.
|
|
4362
|
+
*/
|
|
4363
|
+
function documentLocale() {
|
|
4364
|
+
return (typeof document !== "undefined" ? document.documentElement.lang.trim() : "") || void 0;
|
|
4365
|
+
}
|
|
4366
|
+
//#endregion
|
|
4367
|
+
//#region src/label-number-format.ts
|
|
4368
|
+
/**
|
|
4369
|
+
* Locale-aware number formatting for map labels (issue #2336).
|
|
4370
|
+
*
|
|
4371
|
+
* A numeric attribute rendered straight into a label reads as `1234567.5`,
|
|
4372
|
+
* which is hard to compare at a glance. QGIS solves this with
|
|
4373
|
+
* `format_number(field, places, language)`; the equivalent here is three
|
|
4374
|
+
* {@link LabelStyle} fields — {@link LabelStyle.numberFormatEnabled},
|
|
4375
|
+
* {@link LabelStyle.numberDecimals} and {@link LabelStyle.numberLocale} —
|
|
4376
|
+
* whose effect this module renders two ways:
|
|
4377
|
+
*
|
|
4378
|
+
* - {@link labelFieldTextField} builds the MapLibre `text-field` expression the
|
|
4379
|
+
* 2D map and the Mapbox/MapLibre style export both use, so the map and the
|
|
4380
|
+
* exported style agree.
|
|
4381
|
+
* - {@link formatLabelNumber} does the same in JavaScript for the paths that
|
|
4382
|
+
* resolve label text themselves: the Cesium globe and the duplicate-label
|
|
4383
|
+
* collapsing modes.
|
|
4384
|
+
*
|
|
4385
|
+
* Formatting applies to {@link LabelStyle.field} only. A label
|
|
4386
|
+
* {@link LabelStyle.expression} formats its own output (with `number-format`,
|
|
4387
|
+
* offered in the Expression Builder), so wrapping it here would fight the
|
|
4388
|
+
* author.
|
|
4389
|
+
*/
|
|
4390
|
+
/**
|
|
4391
|
+
* Locales offered for {@link LabelStyle.numberLocale}, alongside the default
|
|
4392
|
+
* empty value that follows the app's own language.
|
|
4393
|
+
*
|
|
4394
|
+
* Deliberately short, and every entry groups with a character the map's glyph
|
|
4395
|
+
* stack can draw: ASCII `,`/`.` or U+00A0. Locales whose CLDR grouping uses
|
|
4396
|
+
* the narrow no-break space U+202F (`fr-FR`) or non-Latin digits (`ar-EG`)
|
|
4397
|
+
* are left out because a missing glyph renders as a blank box on the map
|
|
4398
|
+
* rather than as a separator. An author who needs one of those can still
|
|
4399
|
+
* write `["number-format", …]` in the label expression.
|
|
4400
|
+
*/
|
|
4401
|
+
const LABEL_NUMBER_LOCALES = [
|
|
4402
|
+
"en-US",
|
|
4403
|
+
"de-DE",
|
|
4404
|
+
"ru-RU",
|
|
4405
|
+
"hi-IN"
|
|
4406
|
+
];
|
|
4407
|
+
/**
|
|
4408
|
+
* Magnitude at which the text-field stops treating a value as a finite number.
|
|
4409
|
+
*
|
|
4410
|
+
* JSON carries no `Infinity`, so the expression cannot test against it
|
|
4411
|
+
* directly; this is the largest power of ten a double holds, which doubles as
|
|
4412
|
+
* the `to-number` fallback so an unconvertible value fails the same test. A
|
|
4413
|
+
* real attribute at or above it labels as plain text instead of a formatted
|
|
4414
|
+
* number -- a trade worth making for values already past 1e308.
|
|
4415
|
+
*/
|
|
4416
|
+
const NON_FINITE_SENTINEL = 1e308;
|
|
4417
|
+
/** Sample value used to preview a locale's separators in the Style panel. */
|
|
4418
|
+
const LABEL_NUMBER_SAMPLE = 1234567.5;
|
|
4419
|
+
/**
|
|
4420
|
+
* Cached `Intl.NumberFormat`s, keyed by locale + decimals. `formatLabelNumber`
|
|
4421
|
+
* runs once per labeled feature (per Cesium entity, per deduplicated point), so
|
|
4422
|
+
* constructing a formatter on every call is the wrong order of magnitude.
|
|
4423
|
+
*
|
|
4424
|
+
* The key space is small: {@link LABEL_NUMBER_LOCALES} plus the app's own
|
|
4425
|
+
* language, times eleven decimal settings, times whatever a hand-edited project
|
|
4426
|
+
* pins.
|
|
4427
|
+
*/
|
|
4428
|
+
const formatterCache = /* @__PURE__ */ new Map();
|
|
4429
|
+
/** Locale tags already accepted or rejected, so each is tested once. */
|
|
4430
|
+
const localeSupport = /* @__PURE__ */ new Map();
|
|
4431
|
+
/**
|
|
4432
|
+
* A formatter for this locale/decimals pair. Grouping is always on: it is the
|
|
4433
|
+
* whole point of the setting. The locale must already have been vetted by
|
|
4434
|
+
* {@link resolveLabelNumberLocale}, so this never throws.
|
|
4435
|
+
*/
|
|
4436
|
+
function labelNumberFormatter(locale, decimals) {
|
|
4437
|
+
const digits = clampLabelDecimals(decimals);
|
|
4438
|
+
const key = `${locale ?? ""}\u0000${digits}`;
|
|
4439
|
+
const cached = formatterCache.get(key);
|
|
4440
|
+
if (cached) return cached;
|
|
4441
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
4442
|
+
useGrouping: true,
|
|
4443
|
+
minimumFractionDigits: digits,
|
|
4444
|
+
maximumFractionDigits: digits
|
|
4445
|
+
});
|
|
4446
|
+
formatterCache.set(key, formatter);
|
|
4447
|
+
return formatter;
|
|
4448
|
+
}
|
|
4449
|
+
/**
|
|
4450
|
+
* Whether every character a locale's grouping produces is one the map's glyph
|
|
4451
|
+
* stack can draw: ASCII, or the no-break space U+00A0.
|
|
4452
|
+
*
|
|
4453
|
+
* {@link LABEL_NUMBER_LOCALES} is curated on exactly this basis, but the picker
|
|
4454
|
+
* is not the only way a locale reaches the renderer. A hand-edited project can
|
|
4455
|
+
* pin any tag, and the default (`numberLocale: ""`) follows the app's own
|
|
4456
|
+
* language, so a French or Arabic UI would otherwise put U+202F or Arabic-Indic
|
|
4457
|
+
* digits into the `text-field` and label the map with blank boxes. Testing the
|
|
4458
|
+
* rendered sample rather than the tag covers both routes and needs no list to
|
|
4459
|
+
* keep in sync.
|
|
4460
|
+
*/
|
|
4461
|
+
function glyphSafeLocale(tag) {
|
|
4462
|
+
try {
|
|
4463
|
+
const format = new Intl.NumberFormat(tag, {
|
|
4464
|
+
useGrouping: true,
|
|
4465
|
+
minimumFractionDigits: 2,
|
|
4466
|
+
maximumFractionDigits: 2
|
|
4467
|
+
});
|
|
4468
|
+
for (const sample of [format.format(LABEL_NUMBER_SAMPLE), format.format(-1234567.5)]) for (const char of sample) {
|
|
4469
|
+
const code = char.codePointAt(0) ?? 0;
|
|
4470
|
+
if (code >= 128 && code !== 160) return false;
|
|
4471
|
+
}
|
|
4472
|
+
return true;
|
|
4473
|
+
} catch {
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
/** Clamp a decimal-places setting to the range the UI and `Intl` both accept. */
|
|
4478
|
+
function clampLabelDecimals(decimals) {
|
|
4479
|
+
if (!Number.isFinite(decimals)) return 0;
|
|
4480
|
+
return Math.max(0, Math.min(10, Math.trunc(decimals)));
|
|
4481
|
+
}
|
|
4482
|
+
/**
|
|
4483
|
+
* The first locale tag that both `Intl` accepts and the map can draw, or
|
|
4484
|
+
* `undefined` to leave `Intl` and MapLibre on the runtime default.
|
|
4485
|
+
*
|
|
4486
|
+
* Every path resolves its locale through here -- the JavaScript formatter, the
|
|
4487
|
+
* MapLibre expression, and the dedup cache key -- so all three agree on which
|
|
4488
|
+
* locale is actually in effect.
|
|
4489
|
+
*/
|
|
4490
|
+
function resolveLabelNumberLocale(...tags) {
|
|
4491
|
+
for (const tag of tags) {
|
|
4492
|
+
if (!tag) continue;
|
|
4493
|
+
let usable = localeSupport.get(tag);
|
|
4494
|
+
if (usable === void 0) {
|
|
4495
|
+
usable = glyphSafeLocale(tag);
|
|
4496
|
+
localeSupport.set(tag, usable);
|
|
4497
|
+
}
|
|
4498
|
+
if (usable) return tag;
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
/**
|
|
4502
|
+
* Format one label value as a number, or `null` when the setting is off or the
|
|
4503
|
+
* value is not a finite number (a text attribute keeps its own rendering).
|
|
4504
|
+
*
|
|
4505
|
+
* @param value - The raw attribute value.
|
|
4506
|
+
* @param labels - The layer's label configuration.
|
|
4507
|
+
* @param fallbackLocale - Locale used when {@link LabelStyle.numberLocale} is
|
|
4508
|
+
* empty; the caller passes the app's language. Omitted means the runtime
|
|
4509
|
+
* default, matching how popups format their numbers.
|
|
4510
|
+
*/
|
|
4511
|
+
function formatLabelNumber(value, labels, fallbackLocale) {
|
|
4512
|
+
if (!labels.numberFormatEnabled) return null;
|
|
4513
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
4514
|
+
return labelNumberFormatter(resolveLabelNumberLocale(labels.numberLocale, fallbackLocale), labels.numberDecimals).format(value);
|
|
4515
|
+
}
|
|
4516
|
+
/**
|
|
4517
|
+
* The MapLibre expression that renders {@link LabelStyle.field} as text, with
|
|
4518
|
+
* number formatting applied when it is on. Returns `""` when no field is set,
|
|
4519
|
+
* which callers treat as "no label text".
|
|
4520
|
+
*
|
|
4521
|
+
* Number formatting is guarded by a `typeof` test rather than applied blindly
|
|
4522
|
+
* so a mixed or text column still labels normally; only real numbers take the
|
|
4523
|
+
* `number-format` branch.
|
|
4524
|
+
*/
|
|
4525
|
+
function labelFieldTextField(labels, fallbackLocale) {
|
|
4526
|
+
const field = labels.field;
|
|
4527
|
+
if (!field) return "";
|
|
4528
|
+
const asText = ["to-string", [
|
|
4529
|
+
"coalesce",
|
|
4530
|
+
["get", field],
|
|
4531
|
+
""
|
|
4532
|
+
]];
|
|
4533
|
+
if (!labels.numberFormatEnabled) return asText;
|
|
4534
|
+
const locale = resolveLabelNumberLocale(labels.numberLocale, fallbackLocale);
|
|
4535
|
+
const digits = clampLabelDecimals(labels.numberDecimals);
|
|
4536
|
+
const options = {};
|
|
4537
|
+
if (locale) options.locale = locale;
|
|
4538
|
+
const numeric = [
|
|
4539
|
+
"to-number",
|
|
4540
|
+
["get", field],
|
|
4541
|
+
NON_FINITE_SENTINEL
|
|
4542
|
+
];
|
|
4543
|
+
const number = digits > 0 ? numeric : ["round", numeric];
|
|
4544
|
+
if (digits > 0) {
|
|
4545
|
+
options["min-fraction-digits"] = digits;
|
|
4546
|
+
options["max-fraction-digits"] = digits;
|
|
4547
|
+
}
|
|
4548
|
+
return [
|
|
4549
|
+
"case",
|
|
4550
|
+
[
|
|
4551
|
+
"all",
|
|
4552
|
+
[
|
|
4553
|
+
"==",
|
|
4554
|
+
["typeof", ["get", field]],
|
|
4555
|
+
"number"
|
|
4556
|
+
],
|
|
4557
|
+
[
|
|
4558
|
+
"<",
|
|
4559
|
+
["abs", numeric],
|
|
4560
|
+
NON_FINITE_SENTINEL
|
|
4561
|
+
]
|
|
4562
|
+
],
|
|
4563
|
+
[
|
|
4564
|
+
"number-format",
|
|
4565
|
+
number,
|
|
4566
|
+
options
|
|
4567
|
+
],
|
|
4568
|
+
asText
|
|
4569
|
+
];
|
|
4570
|
+
}
|
|
4571
|
+
/**
|
|
4572
|
+
* Render {@link LABEL_NUMBER_SAMPLE} the way a locale/decimals pair would, so
|
|
4573
|
+
* the Style panel can name each choice by the separators it actually produces
|
|
4574
|
+
* ("1,234,567.50") instead of by an opaque language tag.
|
|
4575
|
+
*
|
|
4576
|
+
* A tag `Intl` rejects (or one the map cannot draw) falls back to the runtime
|
|
4577
|
+
* locale's formatting rather than to an unformatted number, matching what
|
|
4578
|
+
* {@link formatLabelNumber} and the emitted expression do with the same tag.
|
|
4579
|
+
*/
|
|
4580
|
+
function formatLabelNumberSample(locale, decimals) {
|
|
4581
|
+
return labelNumberFormatter(resolveLabelNumberLocale(locale), decimals).format(LABEL_NUMBER_SAMPLE);
|
|
4582
|
+
}
|
|
4583
|
+
//#endregion
|
|
4115
4584
|
//#region src/external-native-paint.ts
|
|
4116
4585
|
/**
|
|
4117
4586
|
* Whether a layer is drawn by a **custom render layer** its control created — a
|
|
@@ -4805,6 +5274,10 @@ function valueToString(value) {
|
|
|
4805
5274
|
function layerJoinKey(value) {
|
|
4806
5275
|
return value === null || value === void 0 || typeof value === "number" && Number.isNaN(value) || valueToString(value) === "" ? null : valueToString(value);
|
|
4807
5276
|
}
|
|
5277
|
+
/** GeoJSON columns are own properties; inherited object members are not data. */
|
|
5278
|
+
function fieldValue(properties, field) {
|
|
5279
|
+
return properties && Object.hasOwn(properties, field) ? properties[field] : void 0;
|
|
5280
|
+
}
|
|
4808
5281
|
/**
|
|
4809
5282
|
* Remove every column previously added by `joins` (per their `addedFields`
|
|
4810
5283
|
* bookkeeping) from a copy of `features`, restoring the pre-join properties.
|
|
@@ -4897,7 +5370,7 @@ function applyLayerJoins(features, joins, resolveSource) {
|
|
|
4897
5370
|
}
|
|
4898
5371
|
const lookup = /* @__PURE__ */ new Map();
|
|
4899
5372
|
for (const jf of joinFeatures) {
|
|
4900
|
-
const key = layerJoinKey(jf.properties
|
|
5373
|
+
const key = layerJoinKey(fieldValue(jf.properties, join.joinField));
|
|
4901
5374
|
if (key === null || lookup.has(key)) continue;
|
|
4902
5375
|
lookup.set(key, jf.properties ?? {});
|
|
4903
5376
|
}
|
|
@@ -4905,17 +5378,17 @@ function applyLayerJoins(features, joins, resolveSource) {
|
|
|
4905
5378
|
const targetKeys = /* @__PURE__ */ new Set();
|
|
4906
5379
|
for (const feature of out) {
|
|
4907
5380
|
const props = feature.properties;
|
|
4908
|
-
const key = layerJoinKey(props
|
|
5381
|
+
const key = layerJoinKey(fieldValue(props, join.targetField));
|
|
4909
5382
|
if (key !== null) targetKeys.add(key);
|
|
4910
5383
|
const row = key === null ? void 0 : lookup.get(key);
|
|
4911
5384
|
if (row) {
|
|
4912
5385
|
matched += 1;
|
|
4913
|
-
for (const [field, outputName] of fieldPairs) props[outputName] = row
|
|
5386
|
+
for (const [field, outputName] of fieldPairs) props[outputName] = fieldValue(row, field) ?? null;
|
|
4914
5387
|
} else for (const [, outputName] of fieldPairs) props[outputName] = null;
|
|
4915
5388
|
}
|
|
4916
5389
|
let unmatchedJoin = 0;
|
|
4917
5390
|
for (const jf of joinFeatures) {
|
|
4918
|
-
const key = layerJoinKey(jf.properties
|
|
5391
|
+
const key = layerJoinKey(fieldValue(jf.properties, join.joinField));
|
|
4919
5392
|
if (key !== null && !targetKeys.has(key)) unmatchedJoin += 1;
|
|
4920
5393
|
}
|
|
4921
5394
|
return {
|
|
@@ -5325,6 +5798,42 @@ function clearQuickFilterValues(filters) {
|
|
|
5325
5798
|
}));
|
|
5326
5799
|
}
|
|
5327
5800
|
//#endregion
|
|
5801
|
+
//#region src/layer-filters.ts
|
|
5802
|
+
/**
|
|
5803
|
+
* Return a layer's persisted expression filter when it has a usable array
|
|
5804
|
+
* shape, otherwise return null.
|
|
5805
|
+
*
|
|
5806
|
+
* @param layer - The layer whose expression filter should be read.
|
|
5807
|
+
* @returns The active expression filter, or null when none is configured.
|
|
5808
|
+
*/
|
|
5809
|
+
function activeLayerFilterExpression(layer) {
|
|
5810
|
+
const expression = layer.filterExpression;
|
|
5811
|
+
return Array.isArray(expression) && expression.length > 0 ? expression : null;
|
|
5812
|
+
}
|
|
5813
|
+
/**
|
|
5814
|
+
* Compile all project-authored filters for a layer into one MapLibre boolean
|
|
5815
|
+
* expression. An explicit expression and Quick Filter controls narrow the
|
|
5816
|
+
* layer together.
|
|
5817
|
+
*
|
|
5818
|
+
* @param layer - The layer whose authored filters should be composed.
|
|
5819
|
+
* @returns A MapLibre filter expression, or null when no authored filter is active.
|
|
5820
|
+
*/
|
|
5821
|
+
function compileLayerFilters(layer) {
|
|
5822
|
+
const filters = [activeLayerFilterExpression(layer), compileQuickFilters(layer.quickFilters)].filter((filter) => filter !== null);
|
|
5823
|
+
if (filters.length === 0) return null;
|
|
5824
|
+
return filters.length === 1 ? filters[0] : ["all", ...filters];
|
|
5825
|
+
}
|
|
5826
|
+
/**
|
|
5827
|
+
* Whether a project-authored expression or Quick Filter currently narrows a
|
|
5828
|
+
* layer. This drives the filtered indicator and clear action in the layer UI.
|
|
5829
|
+
*
|
|
5830
|
+
* @param layer - The layer to inspect.
|
|
5831
|
+
* @returns True when at least one persisted filter is active.
|
|
5832
|
+
*/
|
|
5833
|
+
function hasActiveLayerFilter(layer) {
|
|
5834
|
+
return compileLayerFilters(layer) !== null;
|
|
5835
|
+
}
|
|
5836
|
+
//#endregion
|
|
5328
5837
|
//#region src/capabilities.ts
|
|
5329
5838
|
/**
|
|
5330
5839
|
* Default inferred capabilities for a layer based on its type and metadata.
|
|
@@ -5569,6 +6078,45 @@ function parseDeploymentCapabilities(raw) {
|
|
|
5569
6078
|
return granted;
|
|
5570
6079
|
}
|
|
5571
6080
|
//#endregion
|
|
6081
|
+
//#region src/file-name.ts
|
|
6082
|
+
/**
|
|
6083
|
+
* A bare Android SAF document ID, as the browser `File.name` carries after a
|
|
6084
|
+
* native import: a known volume prefix (`primary:`, `home:`, `raw:`, `msf:`, or a
|
|
6085
|
+
* removable `XXXX-XXXX:` volume), or any provider prefix followed by a folder
|
|
6086
|
+
* separator. Ordinary encoded filenames such as `Report%3AQ1.geojson` do not match.
|
|
6087
|
+
*/
|
|
6088
|
+
const SAF_DOCUMENT_ID = /^(?:(?:primary|home|raw|msf|[0-9a-f]{4}-[0-9a-f]{4})%3a|[^%/\\:]+%3a.*%2f)/i;
|
|
6089
|
+
/** Extract a display filename without changing the source used for file access. */
|
|
6090
|
+
function localFileName(path) {
|
|
6091
|
+
const isUri = /^(?:content|file):\/\//i.test(path);
|
|
6092
|
+
const value = isUri ? path.split(/[?#]/)[0] : path;
|
|
6093
|
+
const segment = value.split(/[/\\]/).pop() ?? value;
|
|
6094
|
+
if (!isUri && (!SAF_DOCUMENT_ID.test(segment) || /[/\\]/.test(path))) return segment;
|
|
6095
|
+
let decoded = segment;
|
|
6096
|
+
try {
|
|
6097
|
+
decoded = decodeURIComponent(segment);
|
|
6098
|
+
} catch {
|
|
6099
|
+
return segment;
|
|
6100
|
+
}
|
|
6101
|
+
return decoded.replace(/^[^/\\:]+:/, "").split(/[/\\]/).pop() ?? decoded;
|
|
6102
|
+
}
|
|
6103
|
+
/**
|
|
6104
|
+
* Whether a layer name was derived from its local source filename (with or
|
|
6105
|
+
* without the extension) rather than supplied explicitly by a caller.
|
|
6106
|
+
*/
|
|
6107
|
+
function isSourceDerivedLayerName(name, sourcePath) {
|
|
6108
|
+
const fileName = localFileName(sourcePath);
|
|
6109
|
+
return name === fileName || name === fileName.replace(/\.[^.]+$/, "");
|
|
6110
|
+
}
|
|
6111
|
+
/** Keep imported layers distinguishable, including within a multi-file import. */
|
|
6112
|
+
function uniqueImportedLayerName(name, names) {
|
|
6113
|
+
const existing = new Set(names);
|
|
6114
|
+
let candidate = name;
|
|
6115
|
+
let suffix = 2;
|
|
6116
|
+
while (existing.has(candidate)) candidate = `${name}_${suffix++}`;
|
|
6117
|
+
return candidate;
|
|
6118
|
+
}
|
|
6119
|
+
//#endregion
|
|
5572
6120
|
//#region src/history.ts
|
|
5573
6121
|
/**
|
|
5574
6122
|
* Runtime-settable window (ms) used to coalesce rapid history-producing changes
|
|
@@ -7345,6 +7893,7 @@ function parseProject(json) {
|
|
|
7345
7893
|
secondaryMapViews,
|
|
7346
7894
|
...normalizeString(data.primaryMapLabel) ? { primaryMapLabel: normalizeString(data.primaryMapLabel) } : {}
|
|
7347
7895
|
} : {},
|
|
7896
|
+
...normalizePrimaryRenderer(data.primaryRenderer) ? { primaryRenderer: "cesium" } : {},
|
|
7348
7897
|
...styleLibrary.length > 0 ? { styleLibrary } : {},
|
|
7349
7898
|
...parsedComments.length > 0 ? { comments: parsedComments } : {},
|
|
7350
7899
|
metadata: data.metadata ?? {}
|
|
@@ -7810,6 +8359,18 @@ function normalizeMapLayout(value) {
|
|
|
7810
8359
|
};
|
|
7811
8360
|
}
|
|
7812
8361
|
/**
|
|
8362
|
+
* Coerce an untrusted `primaryRenderer` into a known engine id (issue #2217).
|
|
8363
|
+
*
|
|
8364
|
+
* Returns null for the default 2D map — absent, unknown, or an explicit
|
|
8365
|
+
* `"maplibre"` — because the field is only written when it is not the default,
|
|
8366
|
+
* so a MapLibre project serializes byte-identically to before this existed.
|
|
8367
|
+
* The return type is narrowed to `"cesium" | null` rather than the full
|
|
8368
|
+
* {@link MapRendererKind} for that reason: `"maplibre"` is never a result.
|
|
8369
|
+
*/
|
|
8370
|
+
function normalizePrimaryRenderer(value) {
|
|
8371
|
+
return value === "cesium" ? "cesium" : null;
|
|
8372
|
+
}
|
|
8373
|
+
/**
|
|
7813
8374
|
* Coerce an untrusted `secondaryMapViews` array into valid
|
|
7814
8375
|
* {@link SecondaryMapView} records, dropping entries without a usable id and
|
|
7815
8376
|
* de-duplicating by id. Returns null when none are valid.
|
|
@@ -8011,6 +8572,7 @@ function normalizeProjectPreferences(preferences) {
|
|
|
8011
8572
|
ellipsoidId: getEllipsoid(map.ellipsoidId).id,
|
|
8012
8573
|
scaleUnit: normalizeScaleUnit(map.scaleUnit),
|
|
8013
8574
|
showPointerElevation: normalizeBoolean(map.showPointerElevation, DEFAULT_PROJECT_PREFERENCES.map.showPointerElevation),
|
|
8575
|
+
cesiumBasemap: normalizeCesiumBasemap(map.cesiumBasemap),
|
|
8014
8576
|
terrainEnabled: normalizeBoolean(map.terrainEnabled, DEFAULT_PROJECT_PREFERENCES.map.terrainEnabled),
|
|
8015
8577
|
coordinateFormat: typeof map.coordinateFormat === "string" ? map.coordinateFormat : DEFAULT_PROJECT_PREFERENCES.map.coordinateFormat
|
|
8016
8578
|
},
|
|
@@ -8148,8 +8710,9 @@ function isPlainObject$2(value) {
|
|
|
8148
8710
|
return prototype === Object.prototype || prototype === null;
|
|
8149
8711
|
}
|
|
8150
8712
|
function normalizeLayer(layer) {
|
|
8151
|
-
const { capabilities: rawCapabilities, ...rest } = layer;
|
|
8713
|
+
const { capabilities: rawCapabilities, filterExpression: rawFilterExpression, ...rest } = layer;
|
|
8152
8714
|
const capabilities = normalizeLayerCapabilities(rawCapabilities);
|
|
8715
|
+
const filterExpression = Array.isArray(rawFilterExpression) && rawFilterExpression.length > 0 && validateMapExpression(JSON.stringify(rawFilterExpression), { expectedType: "boolean" }).ok ? rawFilterExpression : void 0;
|
|
8153
8716
|
return {
|
|
8154
8717
|
...rest,
|
|
8155
8718
|
style: {
|
|
@@ -8160,7 +8723,8 @@ function normalizeLayer(layer) {
|
|
|
8160
8723
|
opacity: layer.opacity ?? 1,
|
|
8161
8724
|
metadata: layer.metadata ?? {},
|
|
8162
8725
|
source: layer.source ?? {},
|
|
8163
|
-
...capabilities ? { capabilities } : {}
|
|
8726
|
+
...capabilities ? { capabilities } : {},
|
|
8727
|
+
...filterExpression ? { filterExpression } : {}
|
|
8164
8728
|
};
|
|
8165
8729
|
}
|
|
8166
8730
|
function normalizeProjectComments(rawComments) {
|
|
@@ -8263,6 +8827,7 @@ function projectFromStore(state) {
|
|
|
8263
8827
|
secondaryMapViews,
|
|
8264
8828
|
...normalizeString(state.primaryMapLabel) ? { primaryMapLabel: normalizeString(state.primaryMapLabel) } : {}
|
|
8265
8829
|
} : {},
|
|
8830
|
+
...normalizePrimaryRenderer(state.primaryRenderer) ? { primaryRenderer: "cesium" } : {},
|
|
8266
8831
|
...styleLibrary.length > 0 ? { styleLibrary } : {},
|
|
8267
8832
|
...comments.length > 0 ? { comments } : {},
|
|
8268
8833
|
metadata: state.metadata
|
|
@@ -8274,6 +8839,13 @@ function hasRestorableSourceUrl(layer) {
|
|
|
8274
8839
|
return typeof sourceUrl === "string" && sourceUrl.trim() !== "" || typeof originalUrl === "string" && originalUrl.trim() !== "";
|
|
8275
8840
|
}
|
|
8276
8841
|
function prepareLayerForSave(layer) {
|
|
8842
|
+
if (layer.metadata.geometryEdited !== void 0) {
|
|
8843
|
+
const { geometryEdited: _geometryEdited, ...metadata } = layer.metadata;
|
|
8844
|
+
layer = {
|
|
8845
|
+
...layer,
|
|
8846
|
+
metadata
|
|
8847
|
+
};
|
|
8848
|
+
}
|
|
8277
8849
|
if (layer.timeFilter !== void 0) {
|
|
8278
8850
|
const { timeFilter: _timeFilter, ...rest } = layer;
|
|
8279
8851
|
layer = rest;
|
|
@@ -8394,6 +8966,7 @@ function applyProjectToStore(project) {
|
|
|
8394
8966
|
mapLayout,
|
|
8395
8967
|
secondaryMapViews,
|
|
8396
8968
|
primaryMapLabel: normalizeString(project.primaryMapLabel),
|
|
8969
|
+
primaryRenderer: normalizePrimaryRenderer(project.primaryRenderer) ?? "maplibre",
|
|
8397
8970
|
projectStyleLibrary: normalizeStyleLibraryEntries(project.styleLibrary),
|
|
8398
8971
|
comments: scrubbedComments,
|
|
8399
8972
|
metadata: project.metadata
|
|
@@ -8738,6 +9311,313 @@ function rescaleEntryCount(state) {
|
|
|
8738
9311
|
return 1;
|
|
8739
9312
|
}
|
|
8740
9313
|
//#endregion
|
|
9314
|
+
//#region src/czml.ts
|
|
9315
|
+
/**
|
|
9316
|
+
* CZML (Cesium Language) domain models and layer authoring (issue #2290).
|
|
9317
|
+
*
|
|
9318
|
+
* CZML is a JSON-based format for describing dynamic, time-varying 3D geospatial
|
|
9319
|
+
* scenes on the Cesium globe (trajectories, satellites, vehicles, sensor
|
|
9320
|
+
* networks, moving entities with orientations and paths). The globe loads CZML
|
|
9321
|
+
* natively through `CzmlDataSource`; the 2D map has no equivalent, so CZML
|
|
9322
|
+
* layers are badged "3D only" there.
|
|
9323
|
+
*/
|
|
9324
|
+
/** `metadata.sourceKind` of a layer that carries or references CZML data. */
|
|
9325
|
+
const CZML_SOURCE_KIND = "czml";
|
|
9326
|
+
/** One-click CZML samples offered in the Add Data dialog. */
|
|
9327
|
+
const CZML_QUICK_PICKS = [{
|
|
9328
|
+
name: "CZML Point",
|
|
9329
|
+
data: [{
|
|
9330
|
+
id: "document",
|
|
9331
|
+
name: "CZML Point Sample",
|
|
9332
|
+
version: "1.0"
|
|
9333
|
+
}, {
|
|
9334
|
+
id: "point 1",
|
|
9335
|
+
name: "Extruded Point",
|
|
9336
|
+
position: { cartographicDegrees: [
|
|
9337
|
+
-75.59777,
|
|
9338
|
+
40.03883,
|
|
9339
|
+
1e3
|
|
9340
|
+
] },
|
|
9341
|
+
point: {
|
|
9342
|
+
color: { rgba: [
|
|
9343
|
+
255,
|
|
9344
|
+
128,
|
|
9345
|
+
0,
|
|
9346
|
+
255
|
|
9347
|
+
] },
|
|
9348
|
+
pixelSize: 14,
|
|
9349
|
+
outlineColor: { rgba: [
|
|
9350
|
+
255,
|
|
9351
|
+
255,
|
|
9352
|
+
255,
|
|
9353
|
+
255
|
|
9354
|
+
] },
|
|
9355
|
+
outlineWidth: 2
|
|
9356
|
+
}
|
|
9357
|
+
}]
|
|
9358
|
+
}, {
|
|
9359
|
+
name: "CZML Dynamic Trajectory",
|
|
9360
|
+
data: [{
|
|
9361
|
+
id: "document",
|
|
9362
|
+
name: "CZML Trajectory Sample",
|
|
9363
|
+
version: "1.0",
|
|
9364
|
+
clock: {
|
|
9365
|
+
interval: "2026-09-09T00:00:00Z/2026-09-09T02:00:00Z",
|
|
9366
|
+
currentTime: "2026-09-09T00:00:00Z",
|
|
9367
|
+
multiplier: 60,
|
|
9368
|
+
range: "LOOP_STOP"
|
|
9369
|
+
}
|
|
9370
|
+
}, {
|
|
9371
|
+
id: "satellite",
|
|
9372
|
+
name: "Satellite Track",
|
|
9373
|
+
availability: "2026-09-09T00:00:00Z/2026-09-09T02:00:00Z",
|
|
9374
|
+
path: {
|
|
9375
|
+
material: { solidColor: { color: { rgba: [
|
|
9376
|
+
0,
|
|
9377
|
+
200,
|
|
9378
|
+
255,
|
|
9379
|
+
255
|
|
9380
|
+
] } } },
|
|
9381
|
+
width: 2,
|
|
9382
|
+
leadTime: 1800,
|
|
9383
|
+
trailTime: 1800
|
|
9384
|
+
},
|
|
9385
|
+
position: {
|
|
9386
|
+
epoch: "2026-09-09T00:00:00Z",
|
|
9387
|
+
cartographicDegrees: [
|
|
9388
|
+
0,
|
|
9389
|
+
-75,
|
|
9390
|
+
40,
|
|
9391
|
+
25e4,
|
|
9392
|
+
1800,
|
|
9393
|
+
-30,
|
|
9394
|
+
20,
|
|
9395
|
+
25e4,
|
|
9396
|
+
3600,
|
|
9397
|
+
20,
|
|
9398
|
+
0,
|
|
9399
|
+
25e4,
|
|
9400
|
+
5400,
|
|
9401
|
+
70,
|
|
9402
|
+
-20,
|
|
9403
|
+
25e4,
|
|
9404
|
+
7200,
|
|
9405
|
+
120,
|
|
9406
|
+
-40,
|
|
9407
|
+
25e4
|
|
9408
|
+
]
|
|
9409
|
+
},
|
|
9410
|
+
point: {
|
|
9411
|
+
color: { rgba: [
|
|
9412
|
+
255,
|
|
9413
|
+
255,
|
|
9414
|
+
255,
|
|
9415
|
+
255
|
|
9416
|
+
] },
|
|
9417
|
+
pixelSize: 10
|
|
9418
|
+
}
|
|
9419
|
+
}]
|
|
9420
|
+
}];
|
|
9421
|
+
/**
|
|
9422
|
+
* Parse and validate a CZML payload from a JSON string or raw object.
|
|
9423
|
+
* Returns an array of CZML packets or null when the input is malformed.
|
|
9424
|
+
*/
|
|
9425
|
+
function parseCzml(value) {
|
|
9426
|
+
if (!value) return null;
|
|
9427
|
+
let parsed = value;
|
|
9428
|
+
if (typeof value === "string") {
|
|
9429
|
+
const trimmed = value.trim();
|
|
9430
|
+
if (!trimmed.startsWith("[") && !trimmed.startsWith("{")) return null;
|
|
9431
|
+
try {
|
|
9432
|
+
parsed = JSON.parse(trimmed);
|
|
9433
|
+
} catch {
|
|
9434
|
+
return null;
|
|
9435
|
+
}
|
|
9436
|
+
}
|
|
9437
|
+
if (Array.isArray(parsed)) return parsed.length > 0 && parsed.every((item) => item && typeof item === "object") ? parsed : null;
|
|
9438
|
+
if (parsed && typeof parsed === "object") return [parsed];
|
|
9439
|
+
return null;
|
|
9440
|
+
}
|
|
9441
|
+
/**
|
|
9442
|
+
* Whether `layer` represents a CZML dynamic 3D scene.
|
|
9443
|
+
*/
|
|
9444
|
+
function isCzmlLayer(layer) {
|
|
9445
|
+
return layer.metadata?.sourceKind === CZML_SOURCE_KIND;
|
|
9446
|
+
}
|
|
9447
|
+
/**
|
|
9448
|
+
* Extract the CZML content and/or URL from a layer, or null if not a CZML layer.
|
|
9449
|
+
*/
|
|
9450
|
+
function czmlSource(layer) {
|
|
9451
|
+
if (!isCzmlLayer(layer)) return null;
|
|
9452
|
+
const raw = layer.source?.czmlData;
|
|
9453
|
+
const data = Array.isArray(raw) ? raw.length > 0 ? raw : void 0 : raw && typeof raw === "object" ? [raw] : raw || void 0;
|
|
9454
|
+
const rawUrl = layer.source?.url;
|
|
9455
|
+
const url = typeof rawUrl === "string" && rawUrl.trim() ? rawUrl.trim() : void 0;
|
|
9456
|
+
if (!data && !url) return null;
|
|
9457
|
+
return {
|
|
9458
|
+
url,
|
|
9459
|
+
data
|
|
9460
|
+
};
|
|
9461
|
+
}
|
|
9462
|
+
/** Generate a unique identifier for a newly created layer. */
|
|
9463
|
+
function newLayerId$1() {
|
|
9464
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
9465
|
+
}
|
|
9466
|
+
/**
|
|
9467
|
+
* Build a typed GeoLibreLayer representing a CZML dynamic scene on the Cesium globe.
|
|
9468
|
+
*/
|
|
9469
|
+
function createCzmlLayer(options) {
|
|
9470
|
+
const id = options.id ?? newLayerId$1();
|
|
9471
|
+
const data = options.data;
|
|
9472
|
+
const url = options.url?.trim() || void 0;
|
|
9473
|
+
const sourcePath = options.sourcePath?.trim() || void 0;
|
|
9474
|
+
return {
|
|
9475
|
+
id,
|
|
9476
|
+
name: options.name,
|
|
9477
|
+
type: "3d-tiles",
|
|
9478
|
+
...sourcePath ? { sourcePath } : {},
|
|
9479
|
+
source: {
|
|
9480
|
+
type: "3d-tiles",
|
|
9481
|
+
sourceId: id,
|
|
9482
|
+
...data !== void 0 ? { czmlData: data } : {},
|
|
9483
|
+
...url ? { url } : {},
|
|
9484
|
+
...sourcePath ? { sourcePath } : {}
|
|
9485
|
+
},
|
|
9486
|
+
visible: true,
|
|
9487
|
+
opacity: 1,
|
|
9488
|
+
style: { ...DEFAULT_LAYER_STYLE },
|
|
9489
|
+
metadata: {
|
|
9490
|
+
sourceKind: CZML_SOURCE_KIND,
|
|
9491
|
+
externalNativeLayer: true,
|
|
9492
|
+
identifiable: false,
|
|
9493
|
+
sourceId: id,
|
|
9494
|
+
nativeLayerIds: [id]
|
|
9495
|
+
}
|
|
9496
|
+
};
|
|
9497
|
+
}
|
|
9498
|
+
//#endregion
|
|
9499
|
+
//#region src/cesium-kml.ts
|
|
9500
|
+
/** Native KML/KMZ documents retain their own styling on the globe. */
|
|
9501
|
+
const CESIUM_KML_SOURCE_KIND = "cesium-kml";
|
|
9502
|
+
function isCesiumKmlLayer(layer) {
|
|
9503
|
+
return layer.metadata?.sourceKind === CESIUM_KML_SOURCE_KIND;
|
|
9504
|
+
}
|
|
9505
|
+
function cesiumKmlSource(layer) {
|
|
9506
|
+
if (!isCesiumKmlLayer(layer)) return null;
|
|
9507
|
+
for (const value of [layer.source.kmlData, layer.source.url]) if (typeof value === "string" && value.trim()) return value.trim();
|
|
9508
|
+
return null;
|
|
9509
|
+
}
|
|
9510
|
+
function createCesiumKmlLayer(options) {
|
|
9511
|
+
const data = options.data?.trim();
|
|
9512
|
+
const url = options.url?.trim();
|
|
9513
|
+
if (!data && !url) throw new Error("Provide a KML/KMZ document or URL.");
|
|
9514
|
+
const id = options.id ?? crypto.randomUUID();
|
|
9515
|
+
return {
|
|
9516
|
+
id,
|
|
9517
|
+
name: options.name,
|
|
9518
|
+
type: "3d-tiles",
|
|
9519
|
+
...options.sourcePath ? { sourcePath: options.sourcePath } : {},
|
|
9520
|
+
source: {
|
|
9521
|
+
type: "3d-tiles",
|
|
9522
|
+
sourceId: id,
|
|
9523
|
+
...data ? { kmlData: data } : { url }
|
|
9524
|
+
},
|
|
9525
|
+
visible: true,
|
|
9526
|
+
opacity: 1,
|
|
9527
|
+
style: { ...DEFAULT_LAYER_STYLE },
|
|
9528
|
+
metadata: {
|
|
9529
|
+
sourceKind: CESIUM_KML_SOURCE_KIND,
|
|
9530
|
+
externalNativeLayer: true,
|
|
9531
|
+
identifiable: false
|
|
9532
|
+
}
|
|
9533
|
+
};
|
|
9534
|
+
}
|
|
9535
|
+
//#endregion
|
|
9536
|
+
//#region src/cesium-ion.ts
|
|
9537
|
+
/** `metadata.sourceKind` of a layer that references a Cesium Ion asset. */
|
|
9538
|
+
const CESIUM_ION_SOURCE_KIND = "cesium-ion";
|
|
9539
|
+
/** Cesium OSM Buildings, the one-click 3D Tiles asset every Ion account can use. */
|
|
9540
|
+
const CESIUM_OSM_BUILDINGS_ASSET_ID = 96188;
|
|
9541
|
+
/** Bing Maps Aerial through Ion, the imagery asset every Ion account can use. */
|
|
9542
|
+
const CESIUM_BING_AERIAL_ASSET_ID = 2;
|
|
9543
|
+
/** Ion assets offered as one-click picks in the Add Data dialog. */
|
|
9544
|
+
const CESIUM_ION_QUICK_PICKS = [{
|
|
9545
|
+
assetId: CESIUM_OSM_BUILDINGS_ASSET_ID,
|
|
9546
|
+
kind: "3d-tiles",
|
|
9547
|
+
name: "Cesium OSM Buildings"
|
|
9548
|
+
}, {
|
|
9549
|
+
assetId: 2,
|
|
9550
|
+
kind: "imagery",
|
|
9551
|
+
name: "Bing Maps Aerial (Ion)"
|
|
9552
|
+
}];
|
|
9553
|
+
/** Parse an Ion asset id the way the Add Data form and the project file carry it. */
|
|
9554
|
+
function parseCesiumIonAssetId(value) {
|
|
9555
|
+
const id = typeof value === "string" ? Number(value.trim()) : value;
|
|
9556
|
+
return typeof id === "number" && Number.isInteger(id) && id > 0 ? id : null;
|
|
9557
|
+
}
|
|
9558
|
+
/**
|
|
9559
|
+
* The Ion asset id a layer references, or null when the layer is not an Ion
|
|
9560
|
+
* asset layer (or carries no usable id).
|
|
9561
|
+
*/
|
|
9562
|
+
function cesiumIonAssetId(layer) {
|
|
9563
|
+
if (layer.metadata?.sourceKind !== "cesium-ion") return null;
|
|
9564
|
+
return parseCesiumIonAssetId(layer.source?.ionAssetId);
|
|
9565
|
+
}
|
|
9566
|
+
/** Whether `layer` references a Cesium Ion asset. */
|
|
9567
|
+
function isCesiumIonLayer(layer) {
|
|
9568
|
+
return cesiumIonAssetId(layer) !== null;
|
|
9569
|
+
}
|
|
9570
|
+
/** What the Ion asset draws as: a tileset primitive or an imagery layer. */
|
|
9571
|
+
function cesiumIonAssetKind(layer) {
|
|
9572
|
+
return layer.type === "3d-tiles" ? "3d-tiles" : "imagery";
|
|
9573
|
+
}
|
|
9574
|
+
/**
|
|
9575
|
+
* Whether only the 3D globe can render `layer`: the mirror of the globe's
|
|
9576
|
+
* `isCesiumSupportedLayerType`, for the Layers panel to badge on the 2D map.
|
|
9577
|
+
*/
|
|
9578
|
+
function isCesiumOnlyLayer(layer) {
|
|
9579
|
+
return isCesiumIonLayer(layer) || isCzmlLayer(layer) || isCesiumKmlLayer(layer);
|
|
9580
|
+
}
|
|
9581
|
+
function newLayerId() {
|
|
9582
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
9583
|
+
}
|
|
9584
|
+
/**
|
|
9585
|
+
* Build the store layer for a Cesium Ion asset. A tileset is a `3d-tiles`
|
|
9586
|
+
* layer whose `sourceKind` keeps it out of the 2D deck.gl control; imagery is
|
|
9587
|
+
* a `raster` layer with no tiles, marked external so the 2D sync leaves it
|
|
9588
|
+
* alone. Both persist only the asset id: the token is runtime configuration.
|
|
9589
|
+
*/
|
|
9590
|
+
function createCesiumIonLayer(options) {
|
|
9591
|
+
const id = options.id ?? newLayerId();
|
|
9592
|
+
const isTileset = options.kind === "3d-tiles";
|
|
9593
|
+
const altitudeOffset = Number.isFinite(options.altitudeOffset) ? options.altitudeOffset : 0;
|
|
9594
|
+
return {
|
|
9595
|
+
id,
|
|
9596
|
+
name: options.name,
|
|
9597
|
+
type: isTileset ? "3d-tiles" : "raster",
|
|
9598
|
+
source: {
|
|
9599
|
+
type: isTileset ? "3d-tiles" : "raster",
|
|
9600
|
+
ionAssetId: options.assetId,
|
|
9601
|
+
sourceId: id,
|
|
9602
|
+
...isTileset ? { altitudeOffset } : {}
|
|
9603
|
+
},
|
|
9604
|
+
visible: true,
|
|
9605
|
+
opacity: 1,
|
|
9606
|
+
style: { ...DEFAULT_LAYER_STYLE },
|
|
9607
|
+
metadata: {
|
|
9608
|
+
sourceKind: CESIUM_ION_SOURCE_KIND,
|
|
9609
|
+
externalNativeLayer: true,
|
|
9610
|
+
identifiable: false,
|
|
9611
|
+
sourceId: id,
|
|
9612
|
+
nativeLayerIds: [id],
|
|
9613
|
+
...isTileset ? {
|
|
9614
|
+
customLayerType: "3d-tiles",
|
|
9615
|
+
altitudeOffset
|
|
9616
|
+
} : {}
|
|
9617
|
+
}
|
|
9618
|
+
};
|
|
9619
|
+
}
|
|
9620
|
+
//#endregion
|
|
8741
9621
|
//#region src/credentials.ts
|
|
8742
9622
|
/**
|
|
8743
9623
|
* Project fields whose values are credentials. Keep this registry as the
|
|
@@ -8788,7 +9668,8 @@ const PROJECT_CREDENTIAL_FIELDS = {
|
|
|
8788
9668
|
*/
|
|
8789
9669
|
const PUBLISHABLE_PLUGIN_SETTINGS = {
|
|
8790
9670
|
"maplibre-gl-swipe": null,
|
|
8791
|
-
"maplibre-gl-components": ["legend", "colorbar"]
|
|
9671
|
+
"maplibre-gl-components": ["legend", "colorbar"],
|
|
9672
|
+
"maplibre-gl-time-slider": null
|
|
8792
9673
|
};
|
|
8793
9674
|
/**
|
|
8794
9675
|
* Hash a credential value to a short, stable 64-bit token: two independent
|
|
@@ -9083,8 +9964,9 @@ const MAX_LAYER_LIBRARY_ENTRIES = 500;
|
|
|
9083
9964
|
* Metadata keys dropped when capturing an entry. `resolvedUrl` is the dev-server
|
|
9084
9965
|
* proxy rewrite of an XYZ template — a per-session artifact that must not be
|
|
9085
9966
|
* baked into a saved source (the same reason `prepareLayerForSave` strips it).
|
|
9967
|
+
* `geometryEdited` tracks changes to the live layer, not a library source.
|
|
9086
9968
|
*/
|
|
9087
|
-
const TRANSIENT_METADATA_KEYS = ["resolvedUrl"];
|
|
9969
|
+
const TRANSIENT_METADATA_KEYS = ["resolvedUrl", "geometryEdited"];
|
|
9088
9970
|
function nonEmptyString(value) {
|
|
9089
9971
|
return typeof value === "string" && value.trim() !== "";
|
|
9090
9972
|
}
|
|
@@ -9103,6 +9985,7 @@ function hasRestorableLayerSource(layer) {
|
|
|
9103
9985
|
if (nonEmptyString(source.url)) return true;
|
|
9104
9986
|
if (nonEmptyString(source.data)) return true;
|
|
9105
9987
|
if (Array.isArray(source.tiles) && source.tiles.some(nonEmptyString)) return true;
|
|
9988
|
+
if (cesiumIonAssetId(layer) !== null) return true;
|
|
9106
9989
|
return nonEmptyString((layer.metadata ?? {}).originalUrl);
|
|
9107
9990
|
}
|
|
9108
9991
|
/**
|
|
@@ -9966,6 +10849,7 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
9966
10849
|
mapLayout: { ...DEFAULT_MAP_GRID_LAYOUT },
|
|
9967
10850
|
secondaryMapViews: [],
|
|
9968
10851
|
primaryMapLabel: "",
|
|
10852
|
+
primaryRenderer: DEFAULT_PRIMARY_RENDERER,
|
|
9969
10853
|
copiedLayerStyle: null,
|
|
9970
10854
|
selectedLayerId: null,
|
|
9971
10855
|
selectedFeatureId: null,
|
|
@@ -10006,6 +10890,7 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
10006
10890
|
storymapPanelOpen: false,
|
|
10007
10891
|
storymapPresenting: false,
|
|
10008
10892
|
storymapReturnToEditor: false,
|
|
10893
|
+
storymapLayerOpacity: {},
|
|
10009
10894
|
storymapComposingId: null,
|
|
10010
10895
|
batchToolsOpen: false,
|
|
10011
10896
|
modelBuilderOpen: false,
|
|
@@ -10178,6 +11063,10 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
10178
11063
|
primaryMapLabel: label,
|
|
10179
11064
|
isDirty: true
|
|
10180
11065
|
}),
|
|
11066
|
+
setPrimaryRenderer: (renderer) => set((s) => s.primaryRenderer === renderer ? s : {
|
|
11067
|
+
primaryRenderer: renderer,
|
|
11068
|
+
isDirty: true
|
|
11069
|
+
}),
|
|
10181
11070
|
setSecondaryMapLabel: (id, label) => set((s) => {
|
|
10182
11071
|
let changed = false;
|
|
10183
11072
|
const secondaryMapViews = s.secondaryMapViews.map((pane) => {
|
|
@@ -10379,8 +11268,23 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
10379
11268
|
setStorymapPresenting: (presenting, returnToEditor = false) => set((s) => ({ ui: {
|
|
10380
11269
|
...s.ui,
|
|
10381
11270
|
storymapPresenting: presenting,
|
|
10382
|
-
storymapReturnToEditor: presenting ? returnToEditor : false
|
|
11271
|
+
storymapReturnToEditor: presenting ? returnToEditor : false,
|
|
11272
|
+
storymapLayerOpacity: {}
|
|
10383
11273
|
} })),
|
|
11274
|
+
setStorymapLayerOpacity: (changes) => set((s) => {
|
|
11275
|
+
const next = { ...s.ui.storymapLayerOpacity };
|
|
11276
|
+
let changed = false;
|
|
11277
|
+
for (const [layerId, opacity] of Object.entries(changes)) {
|
|
11278
|
+
const clamped = Math.min(1, Math.max(0, opacity));
|
|
11279
|
+
if (next[layerId] === clamped) continue;
|
|
11280
|
+
next[layerId] = clamped;
|
|
11281
|
+
changed = true;
|
|
11282
|
+
}
|
|
11283
|
+
return changed ? { ui: {
|
|
11284
|
+
...s.ui,
|
|
11285
|
+
storymapLayerOpacity: next
|
|
11286
|
+
} } : s;
|
|
11287
|
+
}),
|
|
10384
11288
|
setStorymapComposing: (chapterId) => set((s) => ({ ui: {
|
|
10385
11289
|
...s.ui,
|
|
10386
11290
|
storymapComposingId: chapterId
|
|
@@ -10623,6 +11527,11 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
10623
11527
|
markSaved: () => set({ isDirty: false }),
|
|
10624
11528
|
addLayer: (layer, beforeLayerId = null) => set((s) => {
|
|
10625
11529
|
const layers = [...s.layers];
|
|
11530
|
+
const { sourcePath } = layer;
|
|
11531
|
+
if (sourcePath && (!/^[a-z][a-z0-9+.-]*:\/\//i.test(sourcePath) || /^(content|file):\/\//i.test(sourcePath)) && isSourceDerivedLayerName(layer.name, sourcePath)) layer = {
|
|
11532
|
+
...layer,
|
|
11533
|
+
name: uniqueImportedLayerName(layer.name, layers.map((item) => item.name))
|
|
11534
|
+
};
|
|
10626
11535
|
const beforeIndex = beforeLayerId ? layers.findIndex((l) => l.id === beforeLayerId) : -1;
|
|
10627
11536
|
const layerWithBeforeId = beforeLayerId && beforeIndex < 0 ? {
|
|
10628
11537
|
...layer,
|
|
@@ -10693,6 +11602,7 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
10693
11602
|
};
|
|
10694
11603
|
}),
|
|
10695
11604
|
setLayerQuickFilters: (id, filters) => get().updateLayer(id, { quickFilters: filters.length > 0 ? filters : void 0 }),
|
|
11605
|
+
setLayerFilterExpression: (id, expression) => get().updateLayer(id, { filterExpression: expression ?? void 0 }),
|
|
10696
11606
|
setLayerVisibility: (id, visible) => get().updateLayer(id, { visible }),
|
|
10697
11607
|
setLayerOpacity: (id, opacity) => get().updateLayer(id, { opacity }),
|
|
10698
11608
|
setLayerStyle: (id, style) => set((s) => ({
|
|
@@ -11025,6 +11935,7 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
11025
11935
|
...s.ui,
|
|
11026
11936
|
storymapPresenting: false,
|
|
11027
11937
|
storymapReturnToEditor: false,
|
|
11938
|
+
storymapLayerOpacity: {},
|
|
11028
11939
|
storymapPanelOpen: false,
|
|
11029
11940
|
storymapComposingId: null,
|
|
11030
11941
|
selectByExpressionOpen: false,
|
|
@@ -11060,6 +11971,7 @@ const useAppStore = create()(temporal((set, get) => ({
|
|
|
11060
11971
|
...s.ui,
|
|
11061
11972
|
storymapPresenting: presentStory,
|
|
11062
11973
|
storymapReturnToEditor: false,
|
|
11974
|
+
storymapLayerOpacity: {},
|
|
11063
11975
|
storymapPanelOpen: false,
|
|
11064
11976
|
storymapComposingId: null,
|
|
11065
11977
|
selectByExpressionOpen: false,
|
|
@@ -11734,6 +12646,72 @@ function createSampleStoryMap() {
|
|
|
11734
12646
|
};
|
|
11735
12647
|
}
|
|
11736
12648
|
//#endregion
|
|
12649
|
+
//#region src/storymap-playback.ts
|
|
12650
|
+
/** The clamped opacity recorded for a layer, or undefined when untouched. */
|
|
12651
|
+
function recordedStoryOpacity(opacities, layerId) {
|
|
12652
|
+
const value = opacities?.[layerId];
|
|
12653
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
12654
|
+
return Math.min(1, Math.max(0, value));
|
|
12655
|
+
}
|
|
12656
|
+
/**
|
|
12657
|
+
* The opacity a story presentation has applied to a layer.
|
|
12658
|
+
*
|
|
12659
|
+
* @param opacities The store's `ui.storymapLayerOpacity` record.
|
|
12660
|
+
* @param layerId Store layer id.
|
|
12661
|
+
* @returns The recorded opacity in `[0, 1]`, or `1` when the presentation has
|
|
12662
|
+
* not touched the layer (or nothing is presenting).
|
|
12663
|
+
*/
|
|
12664
|
+
function storyLayerOpacityFactor(opacities, layerId) {
|
|
12665
|
+
return recordedStoryOpacity(opacities, layerId) ?? 1;
|
|
12666
|
+
}
|
|
12667
|
+
/**
|
|
12668
|
+
* A layer whose opacity reflects the story presentation's fade.
|
|
12669
|
+
*
|
|
12670
|
+
* A chapter's opacity *replaces* the layer's own opacity rather than scaling
|
|
12671
|
+
* it, matching `MapController.setStoryLayerOpacity`, which writes the chapter
|
|
12672
|
+
* value as the absolute paint opacity; otherwise a layer set to 0.7 in the
|
|
12673
|
+
* Layers panel and faded to 0.4 by a chapter would draw its diagrams at 0.28
|
|
12674
|
+
* while its MapLibre layers draw at 0.4.
|
|
12675
|
+
*
|
|
12676
|
+
* Returns the same object when the presentation has not touched the layer (or
|
|
12677
|
+
* recorded the opacity it already has), so identity-keyed caches downstream
|
|
12678
|
+
* keep hitting.
|
|
12679
|
+
*
|
|
12680
|
+
* @param layer Store layer.
|
|
12681
|
+
* @param opacities The store's `ui.storymapLayerOpacity` record.
|
|
12682
|
+
*/
|
|
12683
|
+
function applyStoryLayerOpacity(layer, opacities) {
|
|
12684
|
+
const opacity = recordedStoryOpacity(opacities, layer.id);
|
|
12685
|
+
if (opacity === void 0 || opacity === layer.opacity) return layer;
|
|
12686
|
+
return {
|
|
12687
|
+
...layer,
|
|
12688
|
+
opacity
|
|
12689
|
+
};
|
|
12690
|
+
}
|
|
12691
|
+
/**
|
|
12692
|
+
* Whether a story presentation has faded a layer fully out.
|
|
12693
|
+
*
|
|
12694
|
+
* @param opacities The store's `ui.storymapLayerOpacity` record.
|
|
12695
|
+
* @param layerId Store layer id.
|
|
12696
|
+
*/
|
|
12697
|
+
function isStoryHiddenLayer(opacities, layerId) {
|
|
12698
|
+
return storyLayerOpacityFactor(opacities, layerId) === 0;
|
|
12699
|
+
}
|
|
12700
|
+
/**
|
|
12701
|
+
* The layers a reader can currently see during a story presentation: the
|
|
12702
|
+
* input minus those the active chapter has faded fully out. Outside a
|
|
12703
|
+
* presentation the input is returned as-is (same array identity).
|
|
12704
|
+
*
|
|
12705
|
+
* @param layers Store layers in panel order.
|
|
12706
|
+
* @param presenting The store's `ui.storymapPresenting` flag.
|
|
12707
|
+
* @param opacities The store's `ui.storymapLayerOpacity` record.
|
|
12708
|
+
*/
|
|
12709
|
+
function storyVisibleLayers(layers, presenting, opacities) {
|
|
12710
|
+
if (!presenting) return layers;
|
|
12711
|
+
if (!layers.some((layer) => isStoryHiddenLayer(opacities, layer.id))) return layers;
|
|
12712
|
+
return layers.filter((layer) => !isStoryHiddenLayer(opacities, layer.id));
|
|
12713
|
+
}
|
|
12714
|
+
//#endregion
|
|
11737
12715
|
//#region src/storymap-io.ts
|
|
11738
12716
|
/**
|
|
11739
12717
|
* Serialize a story map to pretty-printed JSON for export.
|
|
@@ -13091,6 +14069,6 @@ function currentEditorIdentity() {
|
|
|
13091
14069
|
return pickEditorIdentity(isActive ? selfName : void 0, readStoredAuthorName());
|
|
13092
14070
|
}
|
|
13093
14071
|
//#endregion
|
|
13094
|
-
export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CHINA_BASEMAPS, DEFAULT_BASEMAP, DEFAULT_BLEND_MODE, DEFAULT_COLLABORATION_STATE, DEFAULT_DASHBOARD_COLUMNS, DEFAULT_EDITOR_IDENTITY, DEFAULT_EDITOR_TRACKING_CONFIG, DEFAULT_ELLIPSOID_ID, DEFAULT_FORWARD_GEOCODE_ENDPOINT, DEFAULT_GEOCODING_PROVIDER_ID, DEFAULT_LAYER_GROUP_OPACITY, DEFAULT_LAYER_STYLE, DEFAULT_LEGEND_CONFIG, DEFAULT_MAP_GRID_LAYOUT, DEFAULT_PRINT_LAYOUT, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_PREFERENCES, DEFAULT_REVERSE_GEOCODE_ENDPOINT, DEFAULT_ROUTING_ENDPOINT, DEFAULT_STORY_MAP, DEPLOYMENT_CAPABILITIES, DUCKDB_VECTOR_FEATURE_WARN_COUNT, DUCKDB_VECTOR_ROUTE_BYTES, EARTH_MEAN_RADIUS_METERS, EDITOR_IDENTITY_STORAGE_KEY, ELEVATION_REQUEST_TIMEOUT_MS, ELLIPSOIDS, EXPRESSION_FUNCTION_CATEGORIES, ElevationFetchError, FEET_PER_METER, FEET_PER_MILE, GEOCODE_DISPLAY_NAME_KEY, GEOCODE_LAT_KEY, GEOCODE_LON_KEY, GEOCODE_PROVIDER_KEY, GEOCODE_SCORE_KEY, GEOCODE_STATUS_KEY, GEOCODING_PROVIDERS, GOOGLE_MAPS_API_KEY_HEADER, IDENTIFY_ALL_LAYERS_ID, LARGE_VECTOR_FEATURE_THRESHOLD, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_TYPES, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_REDACT_DEPTH, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, OPENFREEMAP_BASEMAPS, PHOTO_FULL_PROPERTY, PHOTO_PROPERTY, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANET_SWITCHER_OPTIONS, POINTER_ELEVATION_CACHE_LIMIT, POINTER_ELEVATION_DEBOUNCE_MS, PROJECT_CREDENTIAL_FIELDS, PROJECT_VERSION, PROTOMAPS_BASEMAPS, PUBLIC_GEOCODE_ROW_CAP, PUBLISHABLE_PLUGIN_SETTINGS, RAMP_STYLE_KEYS, RASTER_APPEARANCE_STATE_KEYS, REGIONAL_BASEMAPS, REGIONAL_BASEMAP_GROUPS, REGIONAL_BASEMAP_SENTINEL_PREFIX, ROLE_PRIVILEGES, RoutingRequestError, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeQuickFilters, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, attributeFormFieldLabel, attributeLinkUrl, basemapToCesiumImagery, batchDecodePolylines, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerTree, buildMatrixRequest, buildReverseGeocodeUrl, buildRouteRequest, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, circleRadiusValue, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileQuickFilter, compileQuickFilters, controlRendersLayer, copyableLayerStyleKind, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, darkenHex, decodePolyline, decodePolylineDetailed, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, ensureEditorTrackingFields, evaluateMapExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, extractCopiedLayerStyle, extractStyleLibraryStyle, extrusionColorValue, extrusionHeightValue, featureSelectionId, fetchElevations, formatCameraAltitude, formatExpressionPreviewValue, formatPixelValue, formatPopupValue, formatRoundNum, generatorCircleRadiusValue, generatorSizeRange, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocoderMinIntervalMs, geocoderNeedsApiKey, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, hasActiveQuickFilter, hasAppPrivilege, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbsoluteFilesystemPath, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isCredentialFieldName, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGooglePhotorealisticTilesetUrl, isHexColor, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isMaintainedEditorTrackingField, isPopupClickEnabled, isPopupHoverEnabled, isRegionalBasemapSentinel, isSafePopupUrl, isStyleLibraryTargetLayer, isStyleSpecColor, isVectorColorExpression, isochroneResponseToFeatures, layerGroupDepth, layerGroupMoveability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerPanelGroupHeaders, lineWidthValue, mapZoomStepOutputs, matchFeaturesByExpression, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeBlankBackgroundColor, normalizeDashboardColumns, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerLibraryEntries, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeSecondaryMapViews, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, parseContours, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseProject, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, printLayoutConfigsEqual, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, readStoredAuthorName, reapplyLayerJoins, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerProjectRestoreHistory, removeTrailingJsonCommas, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestRoute, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveLayerCapabilities, resolveMapGrid, resolvePopupBody, resolvePopupRows, resolvePopupTitle, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, sameCesiumImagery, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, serializeLayerLibrary, serializeProject, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, simpleStyleNumberValue, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyMapHasContent, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, useAppCapability, useAppStore, validateAttributeFormField, validateAttributeFormValues, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visiblePopupFields, visiblePopupProperties };
|
|
14072
|
+
export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CESIUM_BASEMAPS, CESIUM_BING_AERIAL_ASSET_ID, CESIUM_ION_QUICK_PICKS, CESIUM_ION_SOURCE_KIND, CESIUM_KML_SOURCE_KIND, CESIUM_OSM_BUILDINGS_ASSET_ID, CHINA_BASEMAPS, CZML_QUICK_PICKS, CZML_SOURCE_KIND, DEFAULT_BASEMAP, DEFAULT_BLEND_MODE, DEFAULT_COLLABORATION_STATE, DEFAULT_DASHBOARD_COLUMNS, DEFAULT_EDITOR_IDENTITY, DEFAULT_EDITOR_TRACKING_CONFIG, DEFAULT_ELLIPSOID_ID, DEFAULT_FORWARD_GEOCODE_ENDPOINT, DEFAULT_GEOCODING_PROVIDER_ID, DEFAULT_LAYER_GROUP_OPACITY, DEFAULT_LAYER_STYLE, DEFAULT_LEGEND_CONFIG, DEFAULT_MAP_GRID_LAYOUT, DEFAULT_PRIMARY_RENDERER, DEFAULT_PRINT_LAYOUT, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_PREFERENCES, DEFAULT_REVERSE_GEOCODE_ENDPOINT, DEFAULT_ROUTING_ENDPOINT, DEFAULT_STORY_MAP, DEPLOYMENT_CAPABILITIES, DUCKDB_VECTOR_FEATURE_WARN_COUNT, DUCKDB_VECTOR_ROUTE_BYTES, EARTH_MEAN_RADIUS_METERS, EDITOR_IDENTITY_STORAGE_KEY, ELEVATION_REQUEST_TIMEOUT_MS, ELLIPSOIDS, EXPRESSION_FUNCTION_CATEGORIES, ElevationFetchError, FEET_PER_METER, FEET_PER_MILE, GEOCODE_DISPLAY_NAME_KEY, GEOCODE_LAT_KEY, GEOCODE_LON_KEY, GEOCODE_PROVIDER_KEY, GEOCODE_SCORE_KEY, GEOCODE_STATUS_KEY, GEOCODING_PROVIDERS, GOOGLE_MAPS_API_KEY_HEADER, IDENTIFY_ALL_LAYERS_ID, LABEL_NUMBER_LOCALES, LABEL_NUMBER_SAMPLE, LARGE_VECTOR_FEATURE_THRESHOLD, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_TYPES, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_REDACT_DEPTH, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, OPENFREEMAP_BASEMAPS, PHOTO_FULL_PROPERTY, PHOTO_PROPERTY, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANET_SWITCHER_OPTIONS, POINTER_ELEVATION_CACHE_LIMIT, POINTER_ELEVATION_DEBOUNCE_MS, PROJECT_CREDENTIAL_FIELDS, PROJECT_VERSION, PROTOMAPS_BASEMAPS, PUBLIC_GEOCODE_ROW_CAP, PUBLISHABLE_PLUGIN_SETTINGS, RAMP_STYLE_KEYS, RASTER_APPEARANCE_STATE_KEYS, REGIONAL_BASEMAPS, REGIONAL_BASEMAP_GROUPS, REGIONAL_BASEMAP_SENTINEL_PREFIX, ROLE_PRIVILEGES, RoutingRequestError, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeLayerFilterExpression, activeQuickFilters, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, applyStoryLayerOpacity, attributeFormFieldLabel, attributeLinkUrl, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerTree, buildMatrixRequest, buildReverseGeocodeUrl, buildRouteRequest, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, cesiumIonAssetId, cesiumIonAssetKind, cesiumKmlSource, circleRadiusValue, clampLabelDecimals, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileLayerFilters, compileQuickFilter, compileQuickFilters, controlRendersLayer, copyableLayerStyleKind, createCesiumIonLayer, createCesiumKmlLayer, createCzmlLayer, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, czmlSource, darkenHex, decodePolyline, decodePolylineDetailed, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, documentLocale, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, ensureEditorTrackingFields, evaluateMapExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, extractCopiedLayerStyle, extractStyleLibraryStyle, extrusionColorValue, extrusionHeightValue, featureSelectionId, fetchElevations, formatCameraAltitude, formatExpressionPreviewValue, formatLabelNumber, formatLabelNumberSample, formatPixelValue, formatPopupValue, formatRoundNum, generatorCircleRadiusValue, generatorSizeRange, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocoderMinIntervalMs, geocoderNeedsApiKey, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, hasActiveLayerFilter, hasActiveQuickFilter, hasAppPrivilege, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, heatmapRampColors, horizontalBbox, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbsoluteFilesystemPath, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isCesiumIonLayer, isCesiumKmlLayer, isCesiumOnlyLayer, isCredentialFieldName, isCzmlLayer, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGooglePhotorealisticTilesetUrl, isHexColor, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isMaintainedEditorTrackingField, isPopupClickEnabled, isPopupHoverEnabled, isRegionalBasemapSentinel, isSafePopupUrl, isStoryHiddenLayer, isStyleLibraryTargetLayer, isStyleSpecColor, isVectorColorExpression, isochroneResponseToFeatures, labelFieldTextField, layerGroupDepth, layerGroupMoveability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerPanelGroupHeaders, lineWidthValue, localFileName, mapZoomStepOutputs, matchFeaturesByExpression, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeBlankBackgroundColor, normalizeCesiumBasemap, normalizeDashboardColumns, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerLibraryEntries, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizePrimaryRenderer, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeSecondaryMapViews, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, parseCesiumIonAssetId, parseContours, parseCzml, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseProject, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, printLayoutConfigsEqual, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, readStoredAuthorName, reapplyLayerJoins, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerProjectRestoreHistory, removeTrailingJsonCommas, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestRoute, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveLabelNumberLocale, resolveLayerCapabilities, resolveMapGrid, resolvePopupBody, resolvePopupRows, resolvePopupTitle, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, sameCesiumImagery, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, serializeLayerLibrary, serializeProject, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, simpleStyleNumberValue, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyLayerOpacityFactor, storyMapHasContent, storyVisibleLayers, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, uniqueImportedLayerName, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, useAppCapability, useAppStore, validateAttributeFormField, validateAttributeFormValues, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visiblePopupFields, visiblePopupProperties };
|
|
13095
14073
|
|
|
13096
14074
|
//# sourceMappingURL=index.mjs.map
|