@geolibre/core 3.7.0 → 3.8.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 +304 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1558 -7
- package/dist/index.mjs.map +1 -1
- package/dist/{project-diff-B6AfZk8o.d.mts → project-diff-BksHN-qY.d.mts} +101 -4
- package/dist/project-diff-BksHN-qY.d.mts.map +1 -0
- package/dist/{project-diff-DPGO-zCS.mjs → project-diff-DJkOd8ff.mjs} +211 -11
- package/dist/project-diff-DJkOd8ff.mjs.map +1 -0
- package/dist/worker-safe.d.mts +1 -1
- package/dist/worker-safe.mjs +1 -1
- package/package.json +3 -3
- package/dist/project-diff-B6AfZk8o.d.mts.map +0 -1
- package/dist/project-diff-DPGO-zCS.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as GEOCODE_STATUS_KEY, $a as LAYER_TYPES, $i as unescapePolyline, $n as buildLayerTree, $r as isVectorColorExpression, $t as isHeaderReferenceOnly, A as normalizeSecondaryMapViews, Aa as CHINA_BASEMAPS, Ai as matchFeaturesByExpression, An as STYLE_LIBRARY_BUNDLE_VERSION, Ar as STAC_VERSION, At as resolveGeocoderConfig, B as storyMapHasContent, Ba as BLEND_MODES, Bi as getCesiumIonToken, Bn as createDefaultPrintLayout, Br as MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, Bt as normalizeStoryChapterDurationSeconds, C as normalizeModelGraph, Ca as ARCGIS_PROJECTION_PRESETS, Ci as vectorStrokeWidthValue, Cn as featureCollectionToGeoArrow, Cr as collectTransitiveJoinSourceIds, Ct as nominatimReverseResultToDisplay, D as normalizeProcessingHistory, Da as basemapToCesiumImagery, Di as formatExpressionPreviewValue, Dn as BUILT_IN_STYLE_PRESETS, Dr as applyLayerVirtualFields, Dt as normalizeGeocodingProviderId, E as normalizePrimaryRenderer, Ea as availableCesiumBasemap, Ei as evaluateMapExpression, En as withRasterSourceDefaults, Er as stripJoinFields, Et as normalizeGeocodingLocator, F as projectFromStore, Fa as getRegionalBasemapById, Fi as applySelectionMode, Fn as normalizeStyleLibraryEntries, Fr as metadataDateToRfc3339, Ft as shouldThrottle, G as DEFAULT_GEOCODING_PROVIDER_ID, Ga as DEFAULT_LEGEND_CONFIG, Gi as getRuntimeEnvironment, Gn as scrubPrintLayoutForRemovedLayers, Gr as extrusionFloorHeightValue, Gt as isCredentialUrlParam, H as CARTOCIUDAD_MIN_INTERVAL_MS, Ha as DEFAULT_BLEND_MODE, Hi as getMapboxAccessToken, Hn as normalizePrintLayoutConfig, Hr as effectiveVectorRules, Ht as PROJECT_CREDENTIAL_FIELDS, I as resolveMapGrid, Ia as getRegionalBasemapByStyleUrl, Ii as featureSelectionId, In as parseStyleLibrary, Ir as normalizeLayerDescriptiveMetadata, It as unmatchedGeocodeFeature, J as GEOCODE_DISPLAY_NAME_KEY, Ja as DEFAULT_PROJECT_PREFERENCES, Ji as decodePolyline, Jn as scrubLegendForRemovedLayers, Jr as generatorSizeRange, Jt as redactProjectCredentials, K as DEFAULT_REVERSE_GEOCODE_ENDPOINT, Ka as DEFAULT_MAP_GRID_LAYOUT, Ki as getSpatialExtensionPath, Kn as removedLayerIdSet, Kr as extrusionHeightValue, Kt as redactConfigurationCredentials, L as serializeProject, La as isRegionalBasemapSentinel, Li as invertSelection, Ln as sanitizeLayerStylePatch, Lr as parseMetadataKeywords, Lt as DEFAULT_STORY_CHAPTER_DURATION_SECONDS, M as normalizeWidgets, Ma as REGIONAL_BASEMAP_GROUPS, Mi as substituteExpressionVariables, Mn as createStyleLibraryEntryId, Mr as isValidMetadataDate, Mt as selectedGeocodingLocator, N as parseProject, Na as REGIONAL_BASEMAP_SENTINEL_PREFIX, Ni as validateMapExpression, Nn as extractStyleLibraryStyle, Nr as isValidMetadataEmail, Nt as setGeocodingFetch, O as normalizeProjectComments, Oa as normalizeCesiumBasemap, Oi as inferFieldTypes, On as RAMP_STYLE_KEYS, Or as stripVirtualFieldColumns, Ot as parseArcGISLocatorInfo, P as portableWmsTileUrl, Pa as gateRegionalBasemap, Pi as SELECTION_MODES, Pn as isStyleLibraryTargetLayer, Pr as isValidMetadataUrl, Pt as setGeocodingTokenProvider, Q as GEOCODE_SCORE_KEY, Qa as LARGE_VECTOR_FEATURE_THRESHOLD, Qi as polylineStrToGeoJSON, Qn as buildLayerPanelUnits, Qr as isStrokeDashPattern, Qt as hasHeaderReferences, R as serializeProjectWithLayerCache, Ra as ARCGIS_IMAGE_SERVICE_SOURCE_KIND, Ri as getArcgisApiKey, Rn as serializeStyleLibrary, Rr as stacAssetMediaType, Rt as MAX_STORY_CHAPTER_DURATION_SECONDS, S as normalizeMapViewState, Sa as parseMapCrsEpsgCode, Si as vectorOutlineColorValue, Sn as computeGeoArrowBBox, Sr as cascadeLayerJoinRefresh, St as nominatimResultToFeature, T as normalizeObiaWorkbench, Ta as CESIUM_BASEMAPS, Ti as compileFeatureExpression, Tn as isGeoArrowBatch, Tr as reapplyLayerJoins, Tt as normalizeGeocodeBbox, U as CARTOCIUDAD_PUBLIC_HOST, Ua as DEFAULT_DASHBOARD_COLUMNS, Ui as getProtomapsApiKey, Un as printLayoutConfigsEqual, Ur as extrusionBaseValue, Ut as PUBLISHABLE_PLUGIN_SETTINGS, V as withI3sSceneLayerKind, Va as DEFAULT_BASEMAP, Vi as getGoogleMapsApiKey, Vn as isDefaultPrintLayout, Vr as circleRadiusValue, Vt as MAX_REDACT_DEPTH, W as DEFAULT_FORWARD_GEOCODE_ENDPOINT, Wa as DEFAULT_LAYER_STYLE, Wi as getProtomapsStyleUrl, Wn as scrubPrintLayoutForLayers, Wr as extrusionColorValue, Wt as isCredentialFieldName, X as GEOCODE_LON_KEY, Xa as DUCKDB_VECTOR_FEATURE_WARN_COUNT, Xi as encodePolyline, Xn as DEFAULT_LAYER_GROUP_OPACITY, Xr as heatmapRampColors, Xt as setRegistryPublishableSettings, Y as GEOCODE_LAT_KEY, Ya as DEFAULT_STORY_MAP, Yi as decodePolylineDetailed, Yn as scrubWidgetsForRemovedLayers, Yr as hasSimpleStyleProperties, Yt as redactUrlCredentials, Z as GEOCODE_PROVIDER_KEY, Za as DUCKDB_VECTOR_ROUTE_BYTES, Zi as geoJSONToPolylineStr, Zn as applyGroupEffects, Zr as isHexColor, Zt as allowsCredentialHeaders, _ as normalizeBookmarkGroups, _a as adaptBboxTemplateToCrs, _i as vectorCircleColorValue, _n as CZML_SOURCE_KIND, _r as normalizeLayerCapabilities, _t as geocoderSupportsReverse, a as DEFAULT_PROJECT_NAME, aa as interpolateColors, ai as proportionalRadiusExpression, an as CESIUM_OSM_BUILDINGS_ASSET_ID, ao as OPENFREEMAP_BASEMAPS, ar as layerPanelGroupHeaders, at as PUBLIC_GEOCODE_ROW_CAP, b as normalizeLegendConfig, ba as mapCrsPreferencePatch, bi as vectorFillOpacityValue, bn as isCzmlLayer, br as applyJoinsToLayer, bt as getGeocodingProvider, c as createDefaultMapView, ca as normalizeHexColor, ci as readsSimpleStyleProperties, cn as createCesiumIonLayer, co as PROTOMAPS_BASEMAPS, cr as sortLayerGroupInPanel, ct as buildReverseGeocodeUrl, d as createProjectTemplate, da as rgbToHex, di as simpleStyleNumberValue, dn as parseCesiumIonAssetId, do as detectNonGeographicCoordinates, dr as appPrivilegeReason, dt as geocodeForward, ea as VECTOR_COLOR_RAMPS, ei as lineWidthValue, en as resolveHeaderReferences, eo as MAX_DASHBOARD_COLUMNS, er as effectiveLayerRenderState, et as GEOCODING_PROVIDERS, f as detachProjectCopy, fa as geojsonHasZCoordinates, fi as strokeBlurValue, fn as CESIUM_KML_SOURCE_KIND, fo as isDuckDBQueryLayer, fr as createDefaultAppCapabilities, ft as geocodeMatchToFeature, g as normalizeBlankBackgroundColor, ga as MAP_CRS_PRESETS, gi as strokeOpacityValue, gn as CZML_QUICK_PICKS, gr as normalizeAppPrivileges, gt as geocoderNeedsApiKey, h as isSessionOnlyLayer, ha as MAP_CRS_CHANGE_EVENT, hi as strokeOpacityPaintValue, hn as isCesiumKmlLayer, ho as styleValue, hr as intersectPrivileges, ht as geocoderMinIntervalMs, i as previewValue, ia as getVectorColorRamp, ii as parseJsonExpression, in as CESIUM_ION_SOURCE_KIND, io as NETCDF_IMAGE_SOURCE_KIND, ir as layerGroupSortability, it as NOMINATIM_PUBLIC_HOST, j as normalizeStoryMap, ja as REGIONAL_BASEMAPS, ji as removeTrailingJsonCommas, jn as SYMBOL_STYLE_KEYS, jr as buildLayerStacItem, jt as rowCap, k as normalizeProjectInteraction, ka as sameCesiumImagery, ki as isStyleSpecColor, kn as STYLE_LIBRARY_BUNDLE_TYPE, kr as STAC_PROCESSING_EXTENSION, kt as parseArcGISSuggestions, l as createEmptyProject, la as parseHexColor, li as ruleBasedColorExpression, ln as isCesiumIonLayer, lo as SQL_QUERY_SOURCE_KIND, lr as ALL_APP_PRIVILEGES, lt as csvRowsToGeocodeRequests, m as isAllowedPluginManifestUrl, ma as transformGeojsonElevation, mi as strokeDashValue, mn as createCesiumKmlLayer, mo as shouldUseTiledRendering, mr as inferLayerCapabilities, mt as geocodeSuggest, n as diffProjects, na as createGraduatedClassBreaks, ni as mercatorMetersPerPixelAtZoom0, nn as CESIUM_GOOGLE_PHOTOREALISTIC_ASSET_ID, no as MAX_PROCESSING_HISTORY, nr as layerGroupDepth, nt as GeocoderUnsupportedError, o as MAX_PROJECT_BOOKMARKS, oa as interpolateHexColor, oi as proportionalSizeRange, on as cesiumIonAssetId, oo as PLANETARY_COMPUTER_SOURCE_KIND, or as normalizeGroupContiguity, ot as arcgisLocatorInputField, p as hydrateProjectLayer, pa as horizontalBbox, pi as strokeDashPresetName, pn as cesiumKmlSource, po as isPlanetaryComputerLayer, pr as hasAppPrivilege, pt as geocodeReverse, q as GEOCODE_ATTRIBUTE_PREFIX, qa as DEFAULT_PRIMARY_RENDERER, qi as batchDecodePolylines, qn as scrubCommentsForRemovedLayers, qr as generatorCircleRadiusValue, qt as redactCredentials, r as jsonValuesEqual, ra as createQuantileBreaks, ri as metersWidthExpression, rn as CESIUM_ION_QUICK_PICKS, ro as MIN_DASHBOARD_COLUMNS, rr as layerGroupMoveability, rt as NOMINATIM_MIN_INTERVAL_MS, s as applyProjectToStore, sa as interpolateRampColors, si as readSimpleStyleNumberLookup, sn as cesiumIonAssetKind, so as PROJECT_VERSION, sr as reorderLayerGroupInPanel, st as buildForwardGeocodeUrl, t as diffFeatureCollections, ta as createEqualIntervalBreaks, ti as mapZoomStepOutputs, tn as CESIUM_BING_AERIAL_ASSET_ID, to as MAX_MAP_GRID_DIM, tr as groupMemberIndices, tt as GeocoderServiceError, u as createProjectLayerSerializationCache, ua as parseHexColorList, ui as ruleBasedVisibilityFilter, un as isCesiumOnlyLayer, uo as STROKE_DASH_PRESETS, ur as ROLE_PRIVILEGES, ut as fetchArcGISLocatorInfo, v as normalizeBookmarks, va as deriveTileMatrixSet, vi as vectorColorExpression, vn as createCzmlLayer, vr as resolveLayerCapabilities, vt as geocoderSupportsSuggest, w as normalizeModels, wa as normalizeArcgisWkid, wi as EXPRESSION_FUNCTION_CATEGORIES, wn as geoArrowToFeatureCollection, wr as layerJoinKey, wt as normalizeArcGISLocatorUrl, x as normalizeMapLayout, xa as normalizeMapCrs, xi as vectorLineColorValue, xn as parseCzml, xr as applyLayerJoins, xt as nextDelayMs, y as normalizeDashboardColumns, ya as getMapCrsPreset, yi as vectorFillColorValue, yn as czmlSource, yr as resolveRolePrivileges, yt as getGeocoderConfig, z as serializeProjectWithLayerCacheAsync, za as BLANK_BASEMAP, zi as getBuildEnvironment, zn as DEFAULT_PRINT_LAYOUT, zr as validateLayerDescriptiveMetadata, zt as MIN_STORY_CHAPTER_DURATION_SECONDS } from "./project-diff-DJkOd8ff.mjs";
|
|
2
2
|
import { _ as getPlanetaryBasemapByStyleUrl, a as PLANETARY_BASEMAP_GROUPS, c as bodyLengthToEarth, d as getActiveBodyRadiusRatio, f as getActiveEllipsoid, g as getPlanetaryBasemapById, h as getEllipsoid, i as PLANETARY_BASEMAPS, l as earthAreaToBody, m as getActiveSemiMajorAxisMeters, n as EARTH_MEAN_RADIUS_METERS, o as PLANETARY_BASEMAP_SENTINEL_PREFIX, p as getActiveMeanRadiusMeters, r as ELLIPSOIDS, s as PLANET_SWITCHER_OPTIONS, t as DEFAULT_ELLIPSOID_ID, u as earthLengthToBody, v as meanRadiusMeters, y as setActiveEllipsoidId } from "./ellipsoids-DlTLddDu.mjs";
|
|
3
3
|
import { centralAngle, geodesicDistance, geodesicLength, geodesicRadiusBounds, geodesicRingArea, isLonLat, latLonBoxArea, withinGeodesicDistance } from "./geodesic.mjs";
|
|
4
4
|
import { Color, featureFilter, latest, normalizePropertyExpression } from "@maplibre/maplibre-gl-style-spec";
|
|
@@ -1209,6 +1209,1342 @@ function rendererAppliesOpacity(layer, renderer) {
|
|
|
1209
1209
|
function isAdoptedVectorAwaitingFeatures(layer) {
|
|
1210
1210
|
return layer.metadata.sourceKind === "maplibre-gl-vector-adopted" && !layer.geojson;
|
|
1211
1211
|
}
|
|
1212
|
+
// src/qgis-expression.ts
|
|
1213
|
+
/** Longest expression the parser accepts; form expressions are one-liners. */
|
|
1214
|
+
const MAX_EXPRESSION_LENGTH = 4e3;
|
|
1215
|
+
/** Nesting bound so a crafted project cannot overflow the stack. */
|
|
1216
|
+
const MAX_DEPTH = 64;
|
|
1217
|
+
var QgisExpressionError = class extends Error {};
|
|
1218
|
+
/** Backslash escapes QGIS string literals define. */
|
|
1219
|
+
const STRING_ESCAPES = /* @__PURE__ */ new Map([
|
|
1220
|
+
["n", "\n"],
|
|
1221
|
+
["t", " "],
|
|
1222
|
+
["r", "\r"],
|
|
1223
|
+
["\\", "\\"],
|
|
1224
|
+
["'", "'"]
|
|
1225
|
+
]);
|
|
1226
|
+
function tokenize(source) {
|
|
1227
|
+
const tokens = [];
|
|
1228
|
+
let i = 0;
|
|
1229
|
+
const readQuoted = (quote) => {
|
|
1230
|
+
let value = "";
|
|
1231
|
+
i += 1;
|
|
1232
|
+
for (;;) {
|
|
1233
|
+
if (i >= source.length) throw new QgisExpressionError("Unterminated quoted text");
|
|
1234
|
+
const char = source[i];
|
|
1235
|
+
if (char === quote) {
|
|
1236
|
+
if (source[i + 1] === quote) {
|
|
1237
|
+
value += quote;
|
|
1238
|
+
i += 2;
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
i += 1;
|
|
1242
|
+
return value;
|
|
1243
|
+
}
|
|
1244
|
+
if (char === "\\" && quote === "'" && i + 1 < source.length) {
|
|
1245
|
+
const next = source[i + 1];
|
|
1246
|
+
const escaped = STRING_ESCAPES.get(next);
|
|
1247
|
+
if (escaped !== void 0) {
|
|
1248
|
+
value += escaped;
|
|
1249
|
+
i += 2;
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
value += char;
|
|
1254
|
+
i += 1;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
while (i < source.length) {
|
|
1258
|
+
const char = source[i];
|
|
1259
|
+
if (/\s/.test(char)) {
|
|
1260
|
+
i += 1;
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if (char === "-" && source[i + 1] === "-") {
|
|
1264
|
+
while (i < source.length && source[i] !== "\n") i += 1;
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (char === "/" && source[i + 1] === "*") {
|
|
1268
|
+
const end = source.indexOf("*/", i + 2);
|
|
1269
|
+
if (end < 0) throw new QgisExpressionError("Unterminated comment");
|
|
1270
|
+
i = end + 2;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
if (char === "'") {
|
|
1274
|
+
tokens.push({
|
|
1275
|
+
kind: "string",
|
|
1276
|
+
value: readQuoted("'")
|
|
1277
|
+
});
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (char === "\"") {
|
|
1281
|
+
tokens.push({
|
|
1282
|
+
kind: "identifier",
|
|
1283
|
+
value: readQuoted("\""),
|
|
1284
|
+
quoted: true
|
|
1285
|
+
});
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
const number = /^(?:\d+\.?\d*(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?)/.exec(source.slice(i));
|
|
1289
|
+
if (number) {
|
|
1290
|
+
tokens.push({
|
|
1291
|
+
kind: "number",
|
|
1292
|
+
value: Number(number[0])
|
|
1293
|
+
});
|
|
1294
|
+
i += number[0].length;
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (char === "@" || char === "$") {
|
|
1298
|
+
const name = /^[A-Za-z_][A-Za-z0-9_]*/.exec(source.slice(i + 1));
|
|
1299
|
+
if (!name) throw new QgisExpressionError(`Unexpected "${char}"`);
|
|
1300
|
+
tokens.push({
|
|
1301
|
+
kind: char === "@" ? "variable" : "special",
|
|
1302
|
+
value: name[0]
|
|
1303
|
+
});
|
|
1304
|
+
i += 1 + name[0].length;
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
const word = /^[A-Za-z_À-][A-Za-z0-9_À-]*/.exec(source.slice(i));
|
|
1308
|
+
if (word) {
|
|
1309
|
+
tokens.push({
|
|
1310
|
+
kind: "identifier",
|
|
1311
|
+
value: word[0],
|
|
1312
|
+
quoted: false
|
|
1313
|
+
});
|
|
1314
|
+
i += word[0].length;
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
const two = source.slice(i, i + 2);
|
|
1318
|
+
if (two === "||" || two === "<>" || two === "!=" || two === "<=" || two === ">=") {
|
|
1319
|
+
tokens.push({
|
|
1320
|
+
kind: "operator",
|
|
1321
|
+
value: two === "!=" ? "<>" : two
|
|
1322
|
+
});
|
|
1323
|
+
i += 2;
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
if ("+-*/%^=<>(),".includes(char)) {
|
|
1327
|
+
tokens.push({
|
|
1328
|
+
kind: "operator",
|
|
1329
|
+
value: char
|
|
1330
|
+
});
|
|
1331
|
+
i += 1;
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
throw new QgisExpressionError(`Unexpected "${char}"`);
|
|
1335
|
+
}
|
|
1336
|
+
return tokens;
|
|
1337
|
+
}
|
|
1338
|
+
const KEYWORDS = /* @__PURE__ */ new Set([
|
|
1339
|
+
"and",
|
|
1340
|
+
"or",
|
|
1341
|
+
"not",
|
|
1342
|
+
"is",
|
|
1343
|
+
"null",
|
|
1344
|
+
"in",
|
|
1345
|
+
"like",
|
|
1346
|
+
"ilike",
|
|
1347
|
+
"true",
|
|
1348
|
+
"false",
|
|
1349
|
+
"case",
|
|
1350
|
+
"when",
|
|
1351
|
+
"then",
|
|
1352
|
+
"else",
|
|
1353
|
+
"end"
|
|
1354
|
+
]);
|
|
1355
|
+
var Parser = class {
|
|
1356
|
+
tokens;
|
|
1357
|
+
index = 0;
|
|
1358
|
+
depth = 0;
|
|
1359
|
+
constructor(tokens) {
|
|
1360
|
+
this.tokens = tokens;
|
|
1361
|
+
}
|
|
1362
|
+
parse() {
|
|
1363
|
+
const node = this.parseOr();
|
|
1364
|
+
if (this.index < this.tokens.length) throw new QgisExpressionError(`Unexpected "${this.describe(this.tokens[this.index])}"`);
|
|
1365
|
+
return node;
|
|
1366
|
+
}
|
|
1367
|
+
describe(token) {
|
|
1368
|
+
return String(token.value);
|
|
1369
|
+
}
|
|
1370
|
+
peekKeyword() {
|
|
1371
|
+
const token = this.tokens[this.index];
|
|
1372
|
+
if (token?.kind !== "identifier" || token.quoted) return null;
|
|
1373
|
+
const lower = token.value.toLowerCase();
|
|
1374
|
+
return KEYWORDS.has(lower) ? lower : null;
|
|
1375
|
+
}
|
|
1376
|
+
acceptKeyword(keyword) {
|
|
1377
|
+
if (this.peekKeyword() !== keyword) return false;
|
|
1378
|
+
this.index += 1;
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
expectKeyword(keyword) {
|
|
1382
|
+
if (!this.acceptKeyword(keyword)) throw new QgisExpressionError(`Expected ${keyword.toUpperCase()}`);
|
|
1383
|
+
}
|
|
1384
|
+
acceptOperator(...operators) {
|
|
1385
|
+
const token = this.tokens[this.index];
|
|
1386
|
+
if (token?.kind !== "operator" || !operators.includes(token.value)) return null;
|
|
1387
|
+
this.index += 1;
|
|
1388
|
+
return token.value;
|
|
1389
|
+
}
|
|
1390
|
+
expectOperator(operator) {
|
|
1391
|
+
if (!this.acceptOperator(operator)) throw new QgisExpressionError(`Expected "${operator}"`);
|
|
1392
|
+
}
|
|
1393
|
+
nested(parse) {
|
|
1394
|
+
this.depth += 1;
|
|
1395
|
+
if (this.depth > MAX_DEPTH) throw new QgisExpressionError("Expression is nested too deeply");
|
|
1396
|
+
try {
|
|
1397
|
+
return parse();
|
|
1398
|
+
} finally {
|
|
1399
|
+
this.depth -= 1;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
parseOr() {
|
|
1403
|
+
return this.nested(() => {
|
|
1404
|
+
let left = this.parseAnd();
|
|
1405
|
+
while (this.acceptKeyword("or")) left = {
|
|
1406
|
+
type: "binary",
|
|
1407
|
+
op: "or",
|
|
1408
|
+
left,
|
|
1409
|
+
right: this.parseAnd()
|
|
1410
|
+
};
|
|
1411
|
+
return left;
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
parseAnd() {
|
|
1415
|
+
let left = this.parseNot();
|
|
1416
|
+
while (this.acceptKeyword("and")) left = {
|
|
1417
|
+
type: "binary",
|
|
1418
|
+
op: "and",
|
|
1419
|
+
left,
|
|
1420
|
+
right: this.parseNot()
|
|
1421
|
+
};
|
|
1422
|
+
return left;
|
|
1423
|
+
}
|
|
1424
|
+
parseNot() {
|
|
1425
|
+
if (this.acceptKeyword("not")) return this.nested(() => ({
|
|
1426
|
+
type: "unary",
|
|
1427
|
+
op: "not",
|
|
1428
|
+
arg: this.parseNot()
|
|
1429
|
+
}));
|
|
1430
|
+
return this.parseComparison();
|
|
1431
|
+
}
|
|
1432
|
+
parseComparison() {
|
|
1433
|
+
let left = this.parseAdditive();
|
|
1434
|
+
for (;;) {
|
|
1435
|
+
const operator = this.acceptOperator("=", "<>", "<", "<=", ">", ">=");
|
|
1436
|
+
if (operator) {
|
|
1437
|
+
left = {
|
|
1438
|
+
type: "binary",
|
|
1439
|
+
op: operator,
|
|
1440
|
+
left,
|
|
1441
|
+
right: this.parseAdditive()
|
|
1442
|
+
};
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (this.acceptKeyword("is")) {
|
|
1446
|
+
const negated = this.acceptKeyword("not");
|
|
1447
|
+
this.expectKeyword("null");
|
|
1448
|
+
left = {
|
|
1449
|
+
type: "isNull",
|
|
1450
|
+
arg: left,
|
|
1451
|
+
negated
|
|
1452
|
+
};
|
|
1453
|
+
continue;
|
|
1454
|
+
}
|
|
1455
|
+
const start = this.index;
|
|
1456
|
+
const negated = this.acceptKeyword("not");
|
|
1457
|
+
if (this.acceptKeyword("in")) {
|
|
1458
|
+
this.expectOperator("(");
|
|
1459
|
+
const list = [];
|
|
1460
|
+
if (!this.acceptOperator(")")) {
|
|
1461
|
+
do
|
|
1462
|
+
list.push(this.parseOr());
|
|
1463
|
+
while (this.acceptOperator(","));
|
|
1464
|
+
this.expectOperator(")");
|
|
1465
|
+
}
|
|
1466
|
+
left = {
|
|
1467
|
+
type: "in",
|
|
1468
|
+
arg: left,
|
|
1469
|
+
list,
|
|
1470
|
+
negated
|
|
1471
|
+
};
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
const like = this.acceptKeyword("like") ? "like" : this.acceptKeyword("ilike") ? "ilike" : "";
|
|
1475
|
+
if (like) {
|
|
1476
|
+
const node = {
|
|
1477
|
+
type: "binary",
|
|
1478
|
+
op: like,
|
|
1479
|
+
left,
|
|
1480
|
+
right: this.parseAdditive()
|
|
1481
|
+
};
|
|
1482
|
+
left = negated ? {
|
|
1483
|
+
type: "unary",
|
|
1484
|
+
op: "not",
|
|
1485
|
+
arg: node
|
|
1486
|
+
} : node;
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
this.index = start;
|
|
1490
|
+
return left;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
parseAdditive() {
|
|
1494
|
+
let left = this.parseMultiplicative();
|
|
1495
|
+
for (;;) {
|
|
1496
|
+
const operator = this.acceptOperator("+", "-");
|
|
1497
|
+
if (!operator) return left;
|
|
1498
|
+
left = {
|
|
1499
|
+
type: "binary",
|
|
1500
|
+
op: operator,
|
|
1501
|
+
left,
|
|
1502
|
+
right: this.parseMultiplicative()
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
parseMultiplicative() {
|
|
1507
|
+
let left = this.parsePower();
|
|
1508
|
+
for (;;) {
|
|
1509
|
+
const operator = this.acceptOperator("*", "/", "%");
|
|
1510
|
+
if (!operator) return left;
|
|
1511
|
+
left = {
|
|
1512
|
+
type: "binary",
|
|
1513
|
+
op: operator,
|
|
1514
|
+
left,
|
|
1515
|
+
right: this.parsePower()
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
parsePower() {
|
|
1520
|
+
const left = this.parseConcat();
|
|
1521
|
+
if (this.acceptOperator("^")) return this.nested(() => ({
|
|
1522
|
+
type: "binary",
|
|
1523
|
+
op: "^",
|
|
1524
|
+
left,
|
|
1525
|
+
right: this.parsePower()
|
|
1526
|
+
}));
|
|
1527
|
+
return left;
|
|
1528
|
+
}
|
|
1529
|
+
parseConcat() {
|
|
1530
|
+
let left = this.parseUnary();
|
|
1531
|
+
while (this.acceptOperator("||")) left = {
|
|
1532
|
+
type: "binary",
|
|
1533
|
+
op: "||",
|
|
1534
|
+
left,
|
|
1535
|
+
right: this.parseUnary()
|
|
1536
|
+
};
|
|
1537
|
+
return left;
|
|
1538
|
+
}
|
|
1539
|
+
parseUnary() {
|
|
1540
|
+
if (this.acceptOperator("-")) return this.nested(() => ({
|
|
1541
|
+
type: "unary",
|
|
1542
|
+
op: "-",
|
|
1543
|
+
arg: this.parseUnary()
|
|
1544
|
+
}));
|
|
1545
|
+
if (this.acceptOperator("+")) return this.nested(() => this.parseUnary());
|
|
1546
|
+
return this.parsePrimary();
|
|
1547
|
+
}
|
|
1548
|
+
parsePrimary() {
|
|
1549
|
+
const token = this.tokens[this.index];
|
|
1550
|
+
if (!token) throw new QgisExpressionError("Unexpected end of expression");
|
|
1551
|
+
if (token.kind === "number" || token.kind === "string") {
|
|
1552
|
+
this.index += 1;
|
|
1553
|
+
return {
|
|
1554
|
+
type: "literal",
|
|
1555
|
+
value: token.value
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
if (token.kind === "variable") {
|
|
1559
|
+
this.index += 1;
|
|
1560
|
+
return {
|
|
1561
|
+
type: "variable",
|
|
1562
|
+
name: token.value
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
if (token.kind === "special") {
|
|
1566
|
+
this.index += 1;
|
|
1567
|
+
return {
|
|
1568
|
+
type: "special",
|
|
1569
|
+
name: token.value.toLowerCase()
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
if (token.kind === "operator") {
|
|
1573
|
+
if (token.value !== "(") throw new QgisExpressionError(`Unexpected "${token.value}"`);
|
|
1574
|
+
this.index += 1;
|
|
1575
|
+
const node = this.parseOr();
|
|
1576
|
+
this.expectOperator(")");
|
|
1577
|
+
return node;
|
|
1578
|
+
}
|
|
1579
|
+
if (token.quoted) {
|
|
1580
|
+
this.index += 1;
|
|
1581
|
+
return {
|
|
1582
|
+
type: "field",
|
|
1583
|
+
name: token.value
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
const keyword = this.peekKeyword();
|
|
1587
|
+
if (keyword === "true" || keyword === "false") {
|
|
1588
|
+
this.index += 1;
|
|
1589
|
+
return {
|
|
1590
|
+
type: "literal",
|
|
1591
|
+
value: keyword === "true"
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
if (keyword === "null") {
|
|
1595
|
+
this.index += 1;
|
|
1596
|
+
return {
|
|
1597
|
+
type: "literal",
|
|
1598
|
+
value: null
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
if (keyword === "case") {
|
|
1602
|
+
this.index += 1;
|
|
1603
|
+
const branches = [];
|
|
1604
|
+
while (this.acceptKeyword("when")) {
|
|
1605
|
+
const when = this.parseOr();
|
|
1606
|
+
this.expectKeyword("then");
|
|
1607
|
+
branches.push({
|
|
1608
|
+
when,
|
|
1609
|
+
then: this.parseOr()
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
if (branches.length === 0) throw new QgisExpressionError("Expected WHEN");
|
|
1613
|
+
const otherwise = this.acceptKeyword("else") ? this.parseOr() : null;
|
|
1614
|
+
this.expectKeyword("end");
|
|
1615
|
+
return {
|
|
1616
|
+
type: "case",
|
|
1617
|
+
branches,
|
|
1618
|
+
otherwise
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
if (keyword) throw new QgisExpressionError(`Unexpected "${token.value}"`);
|
|
1622
|
+
this.index += 1;
|
|
1623
|
+
if (this.acceptOperator("(")) {
|
|
1624
|
+
const args = [];
|
|
1625
|
+
if (!this.acceptOperator(")")) {
|
|
1626
|
+
do
|
|
1627
|
+
args.push(this.parseOr());
|
|
1628
|
+
while (this.acceptOperator(","));
|
|
1629
|
+
this.expectOperator(")");
|
|
1630
|
+
}
|
|
1631
|
+
return {
|
|
1632
|
+
type: "call",
|
|
1633
|
+
name: token.value.toLowerCase(),
|
|
1634
|
+
args
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
return {
|
|
1638
|
+
type: "field",
|
|
1639
|
+
name: token.value
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
/**
|
|
1644
|
+
* Parse a QGIS expression into an AST.
|
|
1645
|
+
*
|
|
1646
|
+
* @param source - The expression text.
|
|
1647
|
+
* @returns The AST, or the first syntax error.
|
|
1648
|
+
*/
|
|
1649
|
+
function parseQgisExpression(source) {
|
|
1650
|
+
if (source.length > MAX_EXPRESSION_LENGTH) return {
|
|
1651
|
+
ok: false,
|
|
1652
|
+
error: "Expression is too long"
|
|
1653
|
+
};
|
|
1654
|
+
try {
|
|
1655
|
+
const tokens = tokenize(source);
|
|
1656
|
+
if (tokens.length === 0) return {
|
|
1657
|
+
ok: false,
|
|
1658
|
+
error: "Expression is empty"
|
|
1659
|
+
};
|
|
1660
|
+
return {
|
|
1661
|
+
ok: true,
|
|
1662
|
+
node: new Parser(tokens).parse()
|
|
1663
|
+
};
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
return {
|
|
1666
|
+
ok: false,
|
|
1667
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
function isPoint(value) {
|
|
1672
|
+
return typeof value === "object" && value !== null && value.type === "Point" && Array.isArray(value.coordinates);
|
|
1673
|
+
}
|
|
1674
|
+
function isNull(value) {
|
|
1675
|
+
return value === null || value === void 0 || typeof value === "number" && Number.isNaN(value);
|
|
1676
|
+
}
|
|
1677
|
+
function pad(value, width) {
|
|
1678
|
+
return String(Math.trunc(Math.abs(value))).padStart(width, "0");
|
|
1679
|
+
}
|
|
1680
|
+
/** The local-time ISO form QGIS writes for a datetime (`2026-11-11T16:05:09`). */
|
|
1681
|
+
function formatQgisDateTime(date) {
|
|
1682
|
+
return `${formatQgisDate(date)}T${pad(date.getHours(), 2)}:${pad(date.getMinutes(), 2)}:${pad(date.getSeconds(), 2)}`;
|
|
1683
|
+
}
|
|
1684
|
+
/** The local-time ISO date (`2026-11-11`). */
|
|
1685
|
+
function formatQgisDate(date) {
|
|
1686
|
+
return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1, 2)}-${pad(date.getDate(), 2)}`;
|
|
1687
|
+
}
|
|
1688
|
+
function toText(value) {
|
|
1689
|
+
if (value instanceof Date) return formatQgisDateTime(value);
|
|
1690
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1691
|
+
if (isPoint(value)) return `POINT(${value.coordinates.join(" ")})`;
|
|
1692
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
1693
|
+
return String(value);
|
|
1694
|
+
}
|
|
1695
|
+
function toNumber(value) {
|
|
1696
|
+
if (typeof value === "number") return value;
|
|
1697
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
1698
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1699
|
+
const parsed = Number(value);
|
|
1700
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1701
|
+
}
|
|
1702
|
+
throw new QgisExpressionError(`Cannot convert "${toText(value)}" to a number`);
|
|
1703
|
+
}
|
|
1704
|
+
function toDate$1(value) {
|
|
1705
|
+
if (value instanceof Date) return value;
|
|
1706
|
+
if (typeof value === "string") {
|
|
1707
|
+
const local = /^\s*(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?)?\s*$/.exec(value);
|
|
1708
|
+
if (local) {
|
|
1709
|
+
const [, year, month, day, hour = "0", minute = "0", second = "0", ms = "0"] = local;
|
|
1710
|
+
const parsed = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second), Number(ms.padEnd(3, "0")));
|
|
1711
|
+
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
1715
|
+
const parsed = new Date(value);
|
|
1716
|
+
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
1717
|
+
}
|
|
1718
|
+
throw new QgisExpressionError(`Cannot convert "${toText(value)}" to a date`);
|
|
1719
|
+
}
|
|
1720
|
+
/** Three-valued truthiness: `null` stays unknown. */
|
|
1721
|
+
function toBoolean(value) {
|
|
1722
|
+
if (isNull(value)) return null;
|
|
1723
|
+
if (typeof value === "boolean") return value;
|
|
1724
|
+
if (typeof value === "number") return value !== 0;
|
|
1725
|
+
if (typeof value === "string") {
|
|
1726
|
+
const lower = value.trim().toLowerCase();
|
|
1727
|
+
if (lower === "" || lower === "false" || lower === "0") return false;
|
|
1728
|
+
return true;
|
|
1729
|
+
}
|
|
1730
|
+
return true;
|
|
1731
|
+
}
|
|
1732
|
+
const MONTHS = [
|
|
1733
|
+
"January",
|
|
1734
|
+
"February",
|
|
1735
|
+
"March",
|
|
1736
|
+
"April",
|
|
1737
|
+
"May",
|
|
1738
|
+
"June",
|
|
1739
|
+
"July",
|
|
1740
|
+
"August",
|
|
1741
|
+
"September",
|
|
1742
|
+
"October",
|
|
1743
|
+
"November",
|
|
1744
|
+
"December"
|
|
1745
|
+
];
|
|
1746
|
+
const DAYS = [
|
|
1747
|
+
"Sunday",
|
|
1748
|
+
"Monday",
|
|
1749
|
+
"Tuesday",
|
|
1750
|
+
"Wednesday",
|
|
1751
|
+
"Thursday",
|
|
1752
|
+
"Friday",
|
|
1753
|
+
"Saturday"
|
|
1754
|
+
];
|
|
1755
|
+
/** Render a date with a Qt format string (`yyyy-MM-dd HH:mm:ss`, …). */
|
|
1756
|
+
function formatQtDate(date, format) {
|
|
1757
|
+
const hours = date.getHours();
|
|
1758
|
+
const twelveHour = /(?:ap|a)/i.test(format.replace(/'[^']*'/g, ""));
|
|
1759
|
+
const replacements = {
|
|
1760
|
+
yyyy: () => pad(date.getFullYear(), 4),
|
|
1761
|
+
yy: () => pad(date.getFullYear() % 100, 2),
|
|
1762
|
+
MMMM: () => MONTHS[date.getMonth()],
|
|
1763
|
+
MMM: () => MONTHS[date.getMonth()].slice(0, 3),
|
|
1764
|
+
MM: () => pad(date.getMonth() + 1, 2),
|
|
1765
|
+
M: () => String(date.getMonth() + 1),
|
|
1766
|
+
dddd: () => DAYS[date.getDay()],
|
|
1767
|
+
ddd: () => DAYS[date.getDay()].slice(0, 3),
|
|
1768
|
+
dd: () => pad(date.getDate(), 2),
|
|
1769
|
+
d: () => String(date.getDate()),
|
|
1770
|
+
HH: () => pad(hours, 2),
|
|
1771
|
+
H: () => String(hours),
|
|
1772
|
+
hh: () => pad(twelveHour ? hours % 12 || 12 : hours, 2),
|
|
1773
|
+
h: () => String(twelveHour ? hours % 12 || 12 : hours),
|
|
1774
|
+
mm: () => pad(date.getMinutes(), 2),
|
|
1775
|
+
m: () => String(date.getMinutes()),
|
|
1776
|
+
ss: () => pad(date.getSeconds(), 2),
|
|
1777
|
+
s: () => String(date.getSeconds()),
|
|
1778
|
+
zzz: () => pad(date.getMilliseconds(), 3),
|
|
1779
|
+
z: () => String(date.getMilliseconds()),
|
|
1780
|
+
AP: () => hours < 12 ? "AM" : "PM",
|
|
1781
|
+
ap: () => hours < 12 ? "am" : "pm",
|
|
1782
|
+
A: () => hours < 12 ? "AM" : "PM",
|
|
1783
|
+
a: () => hours < 12 ? "am" : "pm"
|
|
1784
|
+
};
|
|
1785
|
+
return format.replace(/'([^']*)'|yyyy|yy|MMMM|MMM|MM|M|dddd|ddd|dd|d|HH|H|hh|h|mm|m|ss|s|zzz|z|AP|ap|A|a/g, (match, quoted) => quoted !== void 0 ? quoted || "'" : replacements[match]());
|
|
1786
|
+
}
|
|
1787
|
+
function pointOf(value, name) {
|
|
1788
|
+
if (isPoint(value)) return value;
|
|
1789
|
+
throw new QgisExpressionError(`${name}() needs a point`);
|
|
1790
|
+
}
|
|
1791
|
+
function defaultUuid() {
|
|
1792
|
+
const cryptoApi = globalThis.crypto;
|
|
1793
|
+
if (cryptoApi?.randomUUID) return cryptoApi.randomUUID();
|
|
1794
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
1795
|
+
if (cryptoApi?.getRandomValues) cryptoApi.getRandomValues(bytes);
|
|
1796
|
+
else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
1797
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
1798
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
1799
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1800
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
1801
|
+
}
|
|
1802
|
+
/** Longest text a function may build; a default value is a short string. */
|
|
1803
|
+
const MAX_TEXT_LENGTH = 1e4;
|
|
1804
|
+
/**
|
|
1805
|
+
* Every text a function or `||` produces passes through here, so nested
|
|
1806
|
+
* calls (`replace(replace(...))`, repeated concatenation) cannot multiply a
|
|
1807
|
+
* short expression into an enormous string.
|
|
1808
|
+
*/
|
|
1809
|
+
function boundedText(value) {
|
|
1810
|
+
if (typeof value === "string" && value.length > MAX_TEXT_LENGTH) throw new QgisExpressionError("The text is too long");
|
|
1811
|
+
return value;
|
|
1812
|
+
}
|
|
1813
|
+
function padWidth(value) {
|
|
1814
|
+
const width = Math.max(0, Math.trunc(toNumber(value)));
|
|
1815
|
+
if (width > MAX_TEXT_LENGTH) throw new QgisExpressionError("The padded text is too long");
|
|
1816
|
+
return width;
|
|
1817
|
+
}
|
|
1818
|
+
const text = (run, max = 1) => ({
|
|
1819
|
+
min: 1,
|
|
1820
|
+
max,
|
|
1821
|
+
run: (args) => run(toText(args[0]), args)
|
|
1822
|
+
});
|
|
1823
|
+
const numeric = (run) => ({
|
|
1824
|
+
min: 1,
|
|
1825
|
+
max: 1,
|
|
1826
|
+
run: (args) => run(toNumber(args[0]))
|
|
1827
|
+
});
|
|
1828
|
+
/** Every callable function; the whole surface an imported expression can reach. */
|
|
1829
|
+
const FUNCTIONS = {
|
|
1830
|
+
now: {
|
|
1831
|
+
min: 0,
|
|
1832
|
+
max: 0,
|
|
1833
|
+
run: (_args, context) => context.now?.() ?? /* @__PURE__ */ new Date()
|
|
1834
|
+
},
|
|
1835
|
+
uuid: {
|
|
1836
|
+
min: 0,
|
|
1837
|
+
max: 1,
|
|
1838
|
+
run: (args, context) => {
|
|
1839
|
+
const id = context.uuid?.() ?? defaultUuid();
|
|
1840
|
+
const format = args.length > 0 ? toText(args[0]).toLowerCase() : "withbraces";
|
|
1841
|
+
if (format === "withoutbraces") return id;
|
|
1842
|
+
if (format === "id128") return id.replaceAll("-", "");
|
|
1843
|
+
return `{${id}}`;
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
coalesce: {
|
|
1847
|
+
min: 0,
|
|
1848
|
+
max: Infinity,
|
|
1849
|
+
handlesNull: true,
|
|
1850
|
+
run: (args) => args.find((arg) => !isNull(arg)) ?? null
|
|
1851
|
+
},
|
|
1852
|
+
concat: {
|
|
1853
|
+
min: 0,
|
|
1854
|
+
max: Infinity,
|
|
1855
|
+
handlesNull: true,
|
|
1856
|
+
run: (args) => args.map((arg) => isNull(arg) ? "" : toText(arg)).join("")
|
|
1857
|
+
},
|
|
1858
|
+
if: {
|
|
1859
|
+
min: 3,
|
|
1860
|
+
max: 3,
|
|
1861
|
+
handlesNull: true,
|
|
1862
|
+
run: (args) => toBoolean(args[0]) === true ? args[1] : args[2]
|
|
1863
|
+
},
|
|
1864
|
+
nullif: {
|
|
1865
|
+
min: 2,
|
|
1866
|
+
max: 2,
|
|
1867
|
+
handlesNull: true,
|
|
1868
|
+
run: (args) => compare(args[0], args[1]) === 0 ? null : args[0]
|
|
1869
|
+
},
|
|
1870
|
+
to_string: {
|
|
1871
|
+
min: 1,
|
|
1872
|
+
max: 1,
|
|
1873
|
+
run: (args) => toText(args[0])
|
|
1874
|
+
},
|
|
1875
|
+
to_int: {
|
|
1876
|
+
min: 1,
|
|
1877
|
+
max: 1,
|
|
1878
|
+
run: (args) => Math.trunc(toNumber(args[0]))
|
|
1879
|
+
},
|
|
1880
|
+
to_real: {
|
|
1881
|
+
min: 1,
|
|
1882
|
+
max: 1,
|
|
1883
|
+
run: (args) => toNumber(args[0])
|
|
1884
|
+
},
|
|
1885
|
+
to_date: {
|
|
1886
|
+
min: 1,
|
|
1887
|
+
max: 1,
|
|
1888
|
+
run: (args) => formatQgisDate(toDate$1(args[0]))
|
|
1889
|
+
},
|
|
1890
|
+
to_datetime: {
|
|
1891
|
+
min: 1,
|
|
1892
|
+
max: 1,
|
|
1893
|
+
run: (args) => toDate$1(args[0])
|
|
1894
|
+
},
|
|
1895
|
+
upper: text((value) => value.toUpperCase()),
|
|
1896
|
+
lower: text((value) => value.toLowerCase()),
|
|
1897
|
+
trim: text((value) => value.trim()),
|
|
1898
|
+
title: text((value) => value.replace(/\S+/g, (word) => word[0].toUpperCase() + word.slice(1).toLowerCase())),
|
|
1899
|
+
length: text((value) => value.length),
|
|
1900
|
+
char_length: text((value) => value.length),
|
|
1901
|
+
left: text((value, args) => value.slice(0, Math.max(0, toNumber(args[1]))), 2),
|
|
1902
|
+
right: text((value, args) => {
|
|
1903
|
+
const count = Math.max(0, toNumber(args[1]));
|
|
1904
|
+
return count === 0 ? "" : value.slice(-count);
|
|
1905
|
+
}, 2),
|
|
1906
|
+
substr: {
|
|
1907
|
+
min: 2,
|
|
1908
|
+
max: 3,
|
|
1909
|
+
run: (args) => {
|
|
1910
|
+
const value = toText(args[0]);
|
|
1911
|
+
const position = Math.trunc(toNumber(args[1]));
|
|
1912
|
+
const start = position > 0 ? position - 1 : Math.max(0, value.length + position);
|
|
1913
|
+
if (args.length < 3) return value.slice(start);
|
|
1914
|
+
return value.slice(start, start + Math.max(0, Math.trunc(toNumber(args[2]))));
|
|
1915
|
+
}
|
|
1916
|
+
},
|
|
1917
|
+
replace: {
|
|
1918
|
+
min: 3,
|
|
1919
|
+
max: 3,
|
|
1920
|
+
run: (args) => {
|
|
1921
|
+
const search = toText(args[1]);
|
|
1922
|
+
return search ? toText(args[0]).split(search).join(toText(args[2])) : toText(args[0]);
|
|
1923
|
+
}
|
|
1924
|
+
},
|
|
1925
|
+
lpad: {
|
|
1926
|
+
min: 3,
|
|
1927
|
+
max: 3,
|
|
1928
|
+
run: (args) => {
|
|
1929
|
+
const width = padWidth(args[1]);
|
|
1930
|
+
return toText(args[0]).slice(0, width).padStart(width, toText(args[2]) || " ");
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
rpad: {
|
|
1934
|
+
min: 3,
|
|
1935
|
+
max: 3,
|
|
1936
|
+
run: (args) => {
|
|
1937
|
+
const width = padWidth(args[1]);
|
|
1938
|
+
return toText(args[0]).slice(0, width).padEnd(width, toText(args[2]) || " ");
|
|
1939
|
+
}
|
|
1940
|
+
},
|
|
1941
|
+
abs: numeric(Math.abs),
|
|
1942
|
+
floor: numeric(Math.floor),
|
|
1943
|
+
ceil: numeric(Math.ceil),
|
|
1944
|
+
sqrt: numeric(Math.sqrt),
|
|
1945
|
+
round: {
|
|
1946
|
+
min: 1,
|
|
1947
|
+
max: 2,
|
|
1948
|
+
run: (args) => {
|
|
1949
|
+
const factor = 10 ** (args.length > 1 ? Math.trunc(toNumber(args[1])) : 0);
|
|
1950
|
+
const scaled = toNumber(args[0]) * factor;
|
|
1951
|
+
return Math.sign(scaled) * Math.round(Math.abs(scaled)) / factor;
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
min: {
|
|
1955
|
+
min: 1,
|
|
1956
|
+
max: Infinity,
|
|
1957
|
+
run: (args) => Math.min(...args.map(toNumber))
|
|
1958
|
+
},
|
|
1959
|
+
max: {
|
|
1960
|
+
min: 1,
|
|
1961
|
+
max: Infinity,
|
|
1962
|
+
run: (args) => Math.max(...args.map(toNumber))
|
|
1963
|
+
},
|
|
1964
|
+
format_number: {
|
|
1965
|
+
min: 1,
|
|
1966
|
+
max: 2,
|
|
1967
|
+
run: (args) => toNumber(args[0]).toFixed(args.length > 1 ? Math.trunc(toNumber(args[1])) : 0)
|
|
1968
|
+
},
|
|
1969
|
+
format_date: {
|
|
1970
|
+
min: 2,
|
|
1971
|
+
max: 2,
|
|
1972
|
+
run: (args) => formatQtDate(toDate$1(args[0]), toText(args[1]))
|
|
1973
|
+
},
|
|
1974
|
+
year: {
|
|
1975
|
+
min: 1,
|
|
1976
|
+
max: 1,
|
|
1977
|
+
run: (args) => toDate$1(args[0]).getFullYear()
|
|
1978
|
+
},
|
|
1979
|
+
month: {
|
|
1980
|
+
min: 1,
|
|
1981
|
+
max: 1,
|
|
1982
|
+
run: (args) => toDate$1(args[0]).getMonth() + 1
|
|
1983
|
+
},
|
|
1984
|
+
day: {
|
|
1985
|
+
min: 1,
|
|
1986
|
+
max: 1,
|
|
1987
|
+
run: (args) => toDate$1(args[0]).getDate()
|
|
1988
|
+
},
|
|
1989
|
+
hour: {
|
|
1990
|
+
min: 1,
|
|
1991
|
+
max: 1,
|
|
1992
|
+
run: (args) => toDate$1(args[0]).getHours()
|
|
1993
|
+
},
|
|
1994
|
+
minute: {
|
|
1995
|
+
min: 1,
|
|
1996
|
+
max: 1,
|
|
1997
|
+
run: (args) => toDate$1(args[0]).getMinutes()
|
|
1998
|
+
},
|
|
1999
|
+
second: {
|
|
2000
|
+
min: 1,
|
|
2001
|
+
max: 1,
|
|
2002
|
+
run: (args) => toDate$1(args[0]).getSeconds()
|
|
2003
|
+
},
|
|
2004
|
+
epoch: {
|
|
2005
|
+
min: 1,
|
|
2006
|
+
max: 1,
|
|
2007
|
+
run: (args) => toDate$1(args[0]).getTime()
|
|
2008
|
+
},
|
|
2009
|
+
x: {
|
|
2010
|
+
min: 1,
|
|
2011
|
+
max: 1,
|
|
2012
|
+
run: (args) => pointOf(args[0], "x").coordinates[0] ?? null
|
|
2013
|
+
},
|
|
2014
|
+
y: {
|
|
2015
|
+
min: 1,
|
|
2016
|
+
max: 1,
|
|
2017
|
+
run: (args) => pointOf(args[0], "y").coordinates[1] ?? null
|
|
2018
|
+
},
|
|
2019
|
+
z: {
|
|
2020
|
+
min: 1,
|
|
2021
|
+
max: 1,
|
|
2022
|
+
run: (args) => pointOf(args[0], "z").coordinates[2] ?? null
|
|
2023
|
+
},
|
|
2024
|
+
make_point: {
|
|
2025
|
+
min: 2,
|
|
2026
|
+
max: 3,
|
|
2027
|
+
run: (args) => ({
|
|
2028
|
+
type: "Point",
|
|
2029
|
+
coordinates: args.map(toNumber)
|
|
2030
|
+
})
|
|
2031
|
+
},
|
|
2032
|
+
transform: {
|
|
2033
|
+
min: 3,
|
|
2034
|
+
max: 3,
|
|
2035
|
+
run: (args, context) => {
|
|
2036
|
+
const [x, y, ...rest] = pointOf(args[0], "transform").coordinates;
|
|
2037
|
+
const from = toText(args[1]);
|
|
2038
|
+
const to = toText(args[2]);
|
|
2039
|
+
const projected = from.toUpperCase() === to.toUpperCase() ? [x, y] : context.transform?.([x, y], from, to) ?? null;
|
|
2040
|
+
if (!projected || !projected.every(Number.isFinite)) throw new QgisExpressionError(`Cannot transform from ${from} to ${to}`);
|
|
2041
|
+
return {
|
|
2042
|
+
type: "Point",
|
|
2043
|
+
coordinates: [...projected, ...rest]
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
},
|
|
2047
|
+
attribute: {
|
|
2048
|
+
min: 1,
|
|
2049
|
+
max: 1,
|
|
2050
|
+
run: (args, context) => {
|
|
2051
|
+
const name = toText(args[0]);
|
|
2052
|
+
return context.properties && Object.prototype.hasOwnProperty.call(context.properties, name) ? context.properties[name] ?? null : null;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
/** Alternate spellings QGIS accepts for the same function. */
|
|
2057
|
+
const FUNCTION_ALIASES = {
|
|
2058
|
+
tostring: "to_string",
|
|
2059
|
+
toint: "to_int",
|
|
2060
|
+
toreal: "to_real",
|
|
2061
|
+
todate: "to_date",
|
|
2062
|
+
todatetime: "to_datetime"
|
|
2063
|
+
};
|
|
2064
|
+
function resolveFunction(name) {
|
|
2065
|
+
const canonical = FUNCTION_ALIASES[name] ?? name;
|
|
2066
|
+
return Object.prototype.hasOwnProperty.call(FUNCTIONS, canonical) ? FUNCTIONS[canonical] : void 0;
|
|
2067
|
+
}
|
|
2068
|
+
/** -1/0/1 ordering; numbers numerically when both sides are numeric. */
|
|
2069
|
+
function compare(left, right) {
|
|
2070
|
+
const a = left instanceof Date ? left.getTime() : left;
|
|
2071
|
+
const b = right instanceof Date ? right.getTime() : right;
|
|
2072
|
+
if (typeof a === "boolean" || typeof b === "boolean") {
|
|
2073
|
+
const x = toBoolean(a) ? 1 : 0;
|
|
2074
|
+
const y = toBoolean(b) ? 1 : 0;
|
|
2075
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
2076
|
+
}
|
|
2077
|
+
if ((typeof a === "number" || typeof a === "string" && a.trim() !== "" && Number.isFinite(Number(a))) && (typeof b === "number" || typeof b === "string" && b.trim() !== "" && Number.isFinite(Number(b))) && (typeof a === "number" || typeof b === "number")) {
|
|
2078
|
+
const x = Number(a);
|
|
2079
|
+
const y = Number(b);
|
|
2080
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
2081
|
+
}
|
|
2082
|
+
const x = toText(a);
|
|
2083
|
+
const y = toText(b);
|
|
2084
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* SQL LIKE: `%` matches any run of characters, `_` exactly one, and a
|
|
2088
|
+
* backslash makes the next character literal. Matched with the two-pointer
|
|
2089
|
+
* wildcard walk rather than a generated `RegExp`, so a pattern from an
|
|
2090
|
+
* imported project cannot trigger catastrophic backtracking.
|
|
2091
|
+
*/
|
|
2092
|
+
function matchesLike(text, pattern, caseInsensitive) {
|
|
2093
|
+
const tokens = [];
|
|
2094
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
2095
|
+
const char = pattern[i];
|
|
2096
|
+
if (char === "\\" && i + 1 < pattern.length) {
|
|
2097
|
+
i += 1;
|
|
2098
|
+
tokens.push({
|
|
2099
|
+
kind: "char",
|
|
2100
|
+
char: pattern[i]
|
|
2101
|
+
});
|
|
2102
|
+
} else if (char === "%") {
|
|
2103
|
+
if (tokens[tokens.length - 1]?.kind !== "any") tokens.push({ kind: "any" });
|
|
2104
|
+
} else if (char === "_") tokens.push({ kind: "one" });
|
|
2105
|
+
else tokens.push({
|
|
2106
|
+
kind: "char",
|
|
2107
|
+
char
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
const subject = caseInsensitive ? text.toLowerCase() : text;
|
|
2111
|
+
const same = (token, char) => (caseInsensitive ? token.char.toLowerCase() : token.char) === char;
|
|
2112
|
+
let t = 0;
|
|
2113
|
+
let p = 0;
|
|
2114
|
+
let star = -1;
|
|
2115
|
+
let mark = 0;
|
|
2116
|
+
while (t < subject.length) {
|
|
2117
|
+
const token = tokens[p];
|
|
2118
|
+
if (token && (token.kind === "one" || token.kind === "char" && same(token, subject[t]))) {
|
|
2119
|
+
t += 1;
|
|
2120
|
+
p += 1;
|
|
2121
|
+
} else if (token?.kind === "any") {
|
|
2122
|
+
star = p;
|
|
2123
|
+
mark = t;
|
|
2124
|
+
p += 1;
|
|
2125
|
+
} else if (star >= 0) {
|
|
2126
|
+
p = star + 1;
|
|
2127
|
+
mark += 1;
|
|
2128
|
+
t = mark;
|
|
2129
|
+
} else return false;
|
|
2130
|
+
}
|
|
2131
|
+
while (tokens[p]?.kind === "any") p += 1;
|
|
2132
|
+
return p === tokens.length;
|
|
2133
|
+
}
|
|
2134
|
+
function evaluateNode(node, context) {
|
|
2135
|
+
switch (node.type) {
|
|
2136
|
+
case "literal": return node.value;
|
|
2137
|
+
case "field": {
|
|
2138
|
+
const properties = context.properties;
|
|
2139
|
+
if (properties && Object.prototype.hasOwnProperty.call(properties, node.name)) return properties[node.name] ?? null;
|
|
2140
|
+
return null;
|
|
2141
|
+
}
|
|
2142
|
+
case "variable": {
|
|
2143
|
+
const variables = context.variables;
|
|
2144
|
+
if (variables && Object.prototype.hasOwnProperty.call(variables, node.name)) return variables[node.name] ?? null;
|
|
2145
|
+
return null;
|
|
2146
|
+
}
|
|
2147
|
+
case "special": {
|
|
2148
|
+
const geometry = context.geometry ?? null;
|
|
2149
|
+
if (node.name === "geometry") return geometry;
|
|
2150
|
+
if (node.name === "x" || node.name === "y") {
|
|
2151
|
+
if (!isPoint(geometry)) return null;
|
|
2152
|
+
return geometry.coordinates[node.name === "x" ? 0 : 1] ?? null;
|
|
2153
|
+
}
|
|
2154
|
+
if (node.name === "now") return context.now?.() ?? /* @__PURE__ */ new Date();
|
|
2155
|
+
throw new QgisExpressionError(`$${node.name} is not supported`);
|
|
2156
|
+
}
|
|
2157
|
+
case "call": {
|
|
2158
|
+
const spec = resolveFunction(node.name);
|
|
2159
|
+
if (!spec) throw new QgisExpressionError(`The function ${node.name}() is not supported`);
|
|
2160
|
+
if (node.args.length < spec.min || node.args.length > spec.max) throw new QgisExpressionError(`Wrong number of arguments for ${node.name}()`);
|
|
2161
|
+
if (node.name === "if") {
|
|
2162
|
+
const chosen = toBoolean(evaluateNode(node.args[0], context)) === true ? 1 : 2;
|
|
2163
|
+
return evaluateNode(node.args[chosen], context);
|
|
2164
|
+
}
|
|
2165
|
+
const args = node.args.map((arg) => evaluateNode(arg, context));
|
|
2166
|
+
if (!spec.handlesNull && args.some(isNull)) return null;
|
|
2167
|
+
return boundedText(spec.run(args, context));
|
|
2168
|
+
}
|
|
2169
|
+
case "unary": {
|
|
2170
|
+
const value = evaluateNode(node.arg, context);
|
|
2171
|
+
if (node.op === "not") {
|
|
2172
|
+
const truth = toBoolean(value);
|
|
2173
|
+
return truth === null ? null : !truth;
|
|
2174
|
+
}
|
|
2175
|
+
return isNull(value) ? null : -toNumber(value);
|
|
2176
|
+
}
|
|
2177
|
+
case "isNull": return isNull(evaluateNode(node.arg, context)) !== node.negated;
|
|
2178
|
+
case "in": {
|
|
2179
|
+
const value = evaluateNode(node.arg, context);
|
|
2180
|
+
if (isNull(value)) return null;
|
|
2181
|
+
return node.list.some((entry) => {
|
|
2182
|
+
const candidate = evaluateNode(entry, context);
|
|
2183
|
+
return !isNull(candidate) && compare(value, candidate) === 0;
|
|
2184
|
+
}) !== node.negated;
|
|
2185
|
+
}
|
|
2186
|
+
case "case":
|
|
2187
|
+
for (const branch of node.branches) if (toBoolean(evaluateNode(branch.when, context)) === true) return evaluateNode(branch.then, context);
|
|
2188
|
+
return node.otherwise ? evaluateNode(node.otherwise, context) : null;
|
|
2189
|
+
case "binary": return evaluateBinary(node, context);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
function evaluateBinary(node, context) {
|
|
2193
|
+
if (node.op === "and" || node.op === "or") {
|
|
2194
|
+
const left = toBoolean(evaluateNode(node.left, context));
|
|
2195
|
+
if (node.op === "and" && left === false) return false;
|
|
2196
|
+
if (node.op === "or" && left === true) return true;
|
|
2197
|
+
const right = toBoolean(evaluateNode(node.right, context));
|
|
2198
|
+
if (node.op === "and") return right === false ? false : left === null ? null : right;
|
|
2199
|
+
return right === true ? true : left === null ? null : right;
|
|
2200
|
+
}
|
|
2201
|
+
const left = evaluateNode(node.left, context);
|
|
2202
|
+
const right = evaluateNode(node.right, context);
|
|
2203
|
+
if (isNull(left) || isNull(right)) return null;
|
|
2204
|
+
switch (node.op) {
|
|
2205
|
+
case "||": return boundedText(toText(left) + toText(right));
|
|
2206
|
+
case "=": return compare(left, right) === 0;
|
|
2207
|
+
case "<>": return compare(left, right) !== 0;
|
|
2208
|
+
case "<": return compare(left, right) < 0;
|
|
2209
|
+
case "<=": return compare(left, right) <= 0;
|
|
2210
|
+
case ">": return compare(left, right) > 0;
|
|
2211
|
+
case ">=": return compare(left, right) >= 0;
|
|
2212
|
+
case "like":
|
|
2213
|
+
case "ilike": return matchesLike(toText(left), toText(right), node.op === "ilike");
|
|
2214
|
+
case "+":
|
|
2215
|
+
if (typeof left === "string" && !Number.isFinite(Number(left)) || typeof right === "string" && !Number.isFinite(Number(right))) return toText(left) + toText(right);
|
|
2216
|
+
return toNumber(left) + toNumber(right);
|
|
2217
|
+
case "-": return toNumber(left) - toNumber(right);
|
|
2218
|
+
case "*": return toNumber(left) * toNumber(right);
|
|
2219
|
+
case "/": {
|
|
2220
|
+
const divisor = toNumber(right);
|
|
2221
|
+
return divisor === 0 ? null : toNumber(left) / divisor;
|
|
2222
|
+
}
|
|
2223
|
+
case "%": {
|
|
2224
|
+
const divisor = toNumber(right);
|
|
2225
|
+
return divisor === 0 ? null : toNumber(left) % divisor;
|
|
2226
|
+
}
|
|
2227
|
+
case "^": return toNumber(left) ** toNumber(right);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Evaluate a QGIS expression.
|
|
2232
|
+
*
|
|
2233
|
+
* @param source - Expression text, or an AST from {@link parseQgisExpression}.
|
|
2234
|
+
* @param context - Field values, variables, geometry and injectable clock.
|
|
2235
|
+
* @returns The value (possibly `null`), or why it could not be computed.
|
|
2236
|
+
*/
|
|
2237
|
+
function evaluateQgisExpression(source, context = {}) {
|
|
2238
|
+
let node;
|
|
2239
|
+
if (typeof source === "string") {
|
|
2240
|
+
const parsed = parseQgisExpression(source);
|
|
2241
|
+
if (!parsed.ok) return parsed;
|
|
2242
|
+
node = parsed.node;
|
|
2243
|
+
} else node = source;
|
|
2244
|
+
try {
|
|
2245
|
+
const value = evaluateNode(node, context);
|
|
2246
|
+
return {
|
|
2247
|
+
ok: true,
|
|
2248
|
+
value: value === void 0 ? null : value
|
|
2249
|
+
};
|
|
2250
|
+
} catch (error) {
|
|
2251
|
+
return {
|
|
2252
|
+
ok: false,
|
|
2253
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
/** MapLibre operators that map one-to-one onto a QGIS binary operator. */
|
|
2258
|
+
const MAPLIBRE_BINARY = {
|
|
2259
|
+
"=": "==",
|
|
2260
|
+
"<>": "!=",
|
|
2261
|
+
"<": "<",
|
|
2262
|
+
"<=": "<=",
|
|
2263
|
+
">": ">",
|
|
2264
|
+
">=": ">=",
|
|
2265
|
+
"+": "+",
|
|
2266
|
+
"-": "-",
|
|
2267
|
+
"*": "*",
|
|
2268
|
+
"/": "/",
|
|
2269
|
+
"%": "%",
|
|
2270
|
+
"^": "^",
|
|
2271
|
+
and: "all",
|
|
2272
|
+
or: "any"
|
|
2273
|
+
};
|
|
2274
|
+
function operandKind(node) {
|
|
2275
|
+
if (node.type === "literal") {
|
|
2276
|
+
if (typeof node.value === "number") return "number";
|
|
2277
|
+
if (typeof node.value === "string") return "string";
|
|
2278
|
+
if (typeof node.value === "boolean") return "boolean";
|
|
2279
|
+
return "unknown";
|
|
2280
|
+
}
|
|
2281
|
+
if (node.type === "unary") return node.op === "-" ? "number" : "boolean";
|
|
2282
|
+
if (node.type === "binary") {
|
|
2283
|
+
if ([
|
|
2284
|
+
"+",
|
|
2285
|
+
"-",
|
|
2286
|
+
"*",
|
|
2287
|
+
"/",
|
|
2288
|
+
"%",
|
|
2289
|
+
"^"
|
|
2290
|
+
].includes(node.op)) return "number";
|
|
2291
|
+
if (node.op === "||") return "string";
|
|
2292
|
+
return "boolean";
|
|
2293
|
+
}
|
|
2294
|
+
if (node.type === "call") {
|
|
2295
|
+
if ([
|
|
2296
|
+
"length",
|
|
2297
|
+
"char_length",
|
|
2298
|
+
"abs",
|
|
2299
|
+
"floor",
|
|
2300
|
+
"ceil",
|
|
2301
|
+
"round",
|
|
2302
|
+
"sqrt"
|
|
2303
|
+
].includes(node.name)) return "number";
|
|
2304
|
+
if ([
|
|
2305
|
+
"upper",
|
|
2306
|
+
"lower",
|
|
2307
|
+
"trim",
|
|
2308
|
+
"concat",
|
|
2309
|
+
"to_string",
|
|
2310
|
+
"tostring"
|
|
2311
|
+
].includes(node.name)) return "string";
|
|
2312
|
+
}
|
|
2313
|
+
return "unknown";
|
|
2314
|
+
}
|
|
2315
|
+
function translate(node) {
|
|
2316
|
+
switch (node.type) {
|
|
2317
|
+
case "literal": return node.value;
|
|
2318
|
+
case "field": return ["get", node.name];
|
|
2319
|
+
case "unary": return node.op === "not" ? ["!", translate(node.arg)] : ["-", translate(node.arg)];
|
|
2320
|
+
case "isNull": {
|
|
2321
|
+
const test = [
|
|
2322
|
+
"==",
|
|
2323
|
+
translate(node.arg),
|
|
2324
|
+
null
|
|
2325
|
+
];
|
|
2326
|
+
return node.negated ? ["!", test] : test;
|
|
2327
|
+
}
|
|
2328
|
+
case "in": {
|
|
2329
|
+
if (!node.list.every((entry) => entry.type === "literal" && entry.value !== null)) throw new QgisExpressionError("IN needs literal values");
|
|
2330
|
+
const test = [
|
|
2331
|
+
"in",
|
|
2332
|
+
translate(node.arg),
|
|
2333
|
+
["literal", node.list.map((entry) => entry.value)]
|
|
2334
|
+
];
|
|
2335
|
+
return node.negated ? ["!", test] : test;
|
|
2336
|
+
}
|
|
2337
|
+
case "binary": {
|
|
2338
|
+
if (node.op === "||") return [
|
|
2339
|
+
"concat",
|
|
2340
|
+
translate(node.left),
|
|
2341
|
+
translate(node.right)
|
|
2342
|
+
];
|
|
2343
|
+
const operator = MAPLIBRE_BINARY[node.op];
|
|
2344
|
+
if (!operator) throw new QgisExpressionError(`${node.op.toUpperCase()} is not supported`);
|
|
2345
|
+
if (operator === "all" || operator === "any") return [
|
|
2346
|
+
operator,
|
|
2347
|
+
translate(node.left),
|
|
2348
|
+
translate(node.right)
|
|
2349
|
+
];
|
|
2350
|
+
const leftKind = operandKind(node.left);
|
|
2351
|
+
const rightKind = operandKind(node.right);
|
|
2352
|
+
const target = [
|
|
2353
|
+
"+",
|
|
2354
|
+
"-",
|
|
2355
|
+
"*",
|
|
2356
|
+
"/",
|
|
2357
|
+
"%",
|
|
2358
|
+
"^"
|
|
2359
|
+
].includes(operator) ? "number" : leftKind !== "unknown" ? leftKind : rightKind;
|
|
2360
|
+
const coerce = (side, kind) => {
|
|
2361
|
+
const translated = translate(side);
|
|
2362
|
+
if (kind !== "unknown" || target === "unknown" || target === "boolean") return translated;
|
|
2363
|
+
return [target === "number" ? "to-number" : "to-string", translated];
|
|
2364
|
+
};
|
|
2365
|
+
return [
|
|
2366
|
+
operator,
|
|
2367
|
+
coerce(node.left, leftKind),
|
|
2368
|
+
coerce(node.right, rightKind)
|
|
2369
|
+
];
|
|
2370
|
+
}
|
|
2371
|
+
case "call": {
|
|
2372
|
+
const args = node.args.map(translate);
|
|
2373
|
+
const arity = (count) => {
|
|
2374
|
+
if (args.length !== count) throw new QgisExpressionError(`Wrong number of arguments for ${node.name}()`);
|
|
2375
|
+
};
|
|
2376
|
+
switch (FUNCTION_ALIASES[node.name] ?? node.name) {
|
|
2377
|
+
case "length":
|
|
2378
|
+
case "char_length":
|
|
2379
|
+
arity(1);
|
|
2380
|
+
return ["length", ["to-string", [
|
|
2381
|
+
"coalesce",
|
|
2382
|
+
args[0],
|
|
2383
|
+
""
|
|
2384
|
+
]]];
|
|
2385
|
+
case "upper":
|
|
2386
|
+
arity(1);
|
|
2387
|
+
return ["upcase", ["to-string", args[0]]];
|
|
2388
|
+
case "lower":
|
|
2389
|
+
arity(1);
|
|
2390
|
+
return ["downcase", ["to-string", args[0]]];
|
|
2391
|
+
case "to_string":
|
|
2392
|
+
arity(1);
|
|
2393
|
+
return ["to-string", args[0]];
|
|
2394
|
+
case "to_real":
|
|
2395
|
+
arity(1);
|
|
2396
|
+
return ["to-number", args[0]];
|
|
2397
|
+
case "abs":
|
|
2398
|
+
case "floor":
|
|
2399
|
+
case "ceil":
|
|
2400
|
+
case "sqrt":
|
|
2401
|
+
arity(1);
|
|
2402
|
+
return [node.name, ["to-number", args[0]]];
|
|
2403
|
+
case "round":
|
|
2404
|
+
arity(1);
|
|
2405
|
+
return ["round", ["to-number", args[0]]];
|
|
2406
|
+
case "coalesce": return ["coalesce", ...args];
|
|
2407
|
+
case "concat": return ["concat", ...args];
|
|
2408
|
+
default: throw new QgisExpressionError(`The function ${node.name}() is not supported`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
default: throw new QgisExpressionError("This expression cannot be converted");
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Convert a boolean QGIS expression (a form constraint) into the MapLibre
|
|
2416
|
+
* expression source an {@link AttributeFormFieldConfig.constraintExpression}
|
|
2417
|
+
* stores. Only field references, literals, comparison, boolean logic,
|
|
2418
|
+
* arithmetic and a few string/number functions convert; anything else
|
|
2419
|
+
* (variables, dates, geometry) returns `null` so the importer can report the
|
|
2420
|
+
* constraint as not carried over instead of installing a wrong rule.
|
|
2421
|
+
*
|
|
2422
|
+
* @param source - QGIS constraint expression.
|
|
2423
|
+
* @returns JSON source of an equivalent MapLibre expression, or `null`.
|
|
2424
|
+
*/
|
|
2425
|
+
function qgisExpressionToMapLibre(source) {
|
|
2426
|
+
const parsed = parseQgisExpression(source);
|
|
2427
|
+
if (!parsed.ok) return null;
|
|
2428
|
+
try {
|
|
2429
|
+
const expression = translate(parsed.node);
|
|
2430
|
+
return Array.isArray(expression) ? JSON.stringify(expression) : null;
|
|
2431
|
+
} catch {
|
|
2432
|
+
return null;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
function collectUnsupported(node, found) {
|
|
2436
|
+
switch (node.type) {
|
|
2437
|
+
case "literal":
|
|
2438
|
+
case "field":
|
|
2439
|
+
case "variable": return;
|
|
2440
|
+
case "special":
|
|
2441
|
+
if (![
|
|
2442
|
+
"geometry",
|
|
2443
|
+
"x",
|
|
2444
|
+
"y",
|
|
2445
|
+
"now"
|
|
2446
|
+
].includes(node.name)) found.add(`$${node.name}`);
|
|
2447
|
+
return;
|
|
2448
|
+
case "call":
|
|
2449
|
+
if (!resolveFunction(node.name)) found.add(`${node.name}()`);
|
|
2450
|
+
else if (node.args.length < resolveFunction(node.name).min || node.args.length > resolveFunction(node.name).max) found.add(`${node.name}()`);
|
|
2451
|
+
node.args.forEach((arg) => collectUnsupported(arg, found));
|
|
2452
|
+
return;
|
|
2453
|
+
case "unary":
|
|
2454
|
+
case "isNull":
|
|
2455
|
+
collectUnsupported(node.arg, found);
|
|
2456
|
+
return;
|
|
2457
|
+
case "in":
|
|
2458
|
+
collectUnsupported(node.arg, found);
|
|
2459
|
+
node.list.forEach((entry) => collectUnsupported(entry, found));
|
|
2460
|
+
return;
|
|
2461
|
+
case "case":
|
|
2462
|
+
for (const branch of node.branches) {
|
|
2463
|
+
collectUnsupported(branch.when, found);
|
|
2464
|
+
collectUnsupported(branch.then, found);
|
|
2465
|
+
}
|
|
2466
|
+
if (node.otherwise) collectUnsupported(node.otherwise, found);
|
|
2467
|
+
return;
|
|
2468
|
+
case "binary":
|
|
2469
|
+
collectUnsupported(node.left, found);
|
|
2470
|
+
collectUnsupported(node.right, found);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Check, without evaluating, whether an expression stays inside the supported
|
|
2475
|
+
* subset. An importer uses this to decide up front whether a default value
|
|
2476
|
+
* can be carried over, rather than installing one that can never produce a
|
|
2477
|
+
* value.
|
|
2478
|
+
*
|
|
2479
|
+
* @param source - QGIS expression text.
|
|
2480
|
+
* @returns `null` when the expression is usable; otherwise the syntax error,
|
|
2481
|
+
* or the functions and `$` variables it uses that are not supported.
|
|
2482
|
+
*/
|
|
2483
|
+
function qgisExpressionProblem(source) {
|
|
2484
|
+
const parsed = parseQgisExpression(source);
|
|
2485
|
+
if (!parsed.ok) return parsed.error;
|
|
2486
|
+
const found = /* @__PURE__ */ new Set();
|
|
2487
|
+
collectUnsupported(parsed.node, found);
|
|
2488
|
+
return found.size > 0 ? [...found].join(", ") : null;
|
|
2489
|
+
}
|
|
2490
|
+
/** Bit flags {@link collectReferences} returns. */
|
|
2491
|
+
const USES_NOW = 1;
|
|
2492
|
+
const USES_UUID = 2;
|
|
2493
|
+
function collectReferences(node, fields) {
|
|
2494
|
+
switch (node.type) {
|
|
2495
|
+
case "literal":
|
|
2496
|
+
case "variable": return 0;
|
|
2497
|
+
case "special": return node.name === "now" ? USES_NOW : 0;
|
|
2498
|
+
case "field":
|
|
2499
|
+
fields.add(node.name);
|
|
2500
|
+
return 0;
|
|
2501
|
+
case "call": {
|
|
2502
|
+
let volatile = node.name === "now" ? USES_NOW : node.name === "uuid" ? USES_UUID : 0;
|
|
2503
|
+
if (node.name === "attribute") {
|
|
2504
|
+
const [name] = node.args;
|
|
2505
|
+
if (name?.type === "literal" && typeof name.value === "string") fields.add(name.value);
|
|
2506
|
+
else fields.add("*");
|
|
2507
|
+
}
|
|
2508
|
+
for (const arg of node.args) volatile |= collectReferences(arg, fields);
|
|
2509
|
+
return volatile;
|
|
2510
|
+
}
|
|
2511
|
+
case "unary":
|
|
2512
|
+
case "isNull": return collectReferences(node.arg, fields);
|
|
2513
|
+
case "in": {
|
|
2514
|
+
let volatile = collectReferences(node.arg, fields);
|
|
2515
|
+
for (const entry of node.list) volatile |= collectReferences(entry, fields);
|
|
2516
|
+
return volatile;
|
|
2517
|
+
}
|
|
2518
|
+
case "case": {
|
|
2519
|
+
let volatile = 0;
|
|
2520
|
+
for (const branch of node.branches) {
|
|
2521
|
+
volatile |= collectReferences(branch.when, fields);
|
|
2522
|
+
volatile |= collectReferences(branch.then, fields);
|
|
2523
|
+
}
|
|
2524
|
+
if (node.otherwise) volatile |= collectReferences(node.otherwise, fields);
|
|
2525
|
+
return volatile;
|
|
2526
|
+
}
|
|
2527
|
+
case "binary": return collectReferences(node.left, fields) | collectReferences(node.right, fields);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* List the fields an expression reads and whether its result changes between
|
|
2532
|
+
* runs with the same inputs.
|
|
2533
|
+
*
|
|
2534
|
+
* @param source - QGIS expression text.
|
|
2535
|
+
* @returns The references, or `null` when the expression does not parse.
|
|
2536
|
+
*/
|
|
2537
|
+
function qgisExpressionReferences(source) {
|
|
2538
|
+
const parsed = parseQgisExpression(source);
|
|
2539
|
+
if (!parsed.ok) return null;
|
|
2540
|
+
const fields = /* @__PURE__ */ new Set();
|
|
2541
|
+
const flags = collectReferences(parsed.node, fields);
|
|
2542
|
+
return {
|
|
2543
|
+
fields: [...fields],
|
|
2544
|
+
usesNow: (flags & USES_NOW) !== 0,
|
|
2545
|
+
usesUuid: (flags & USES_UUID) !== 0
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
1212
2548
|
// src/attribute-form.ts
|
|
1213
2549
|
/** Look up the configuration for a field, if any. */
|
|
1214
2550
|
function getAttributeFormField(form, field) {
|
|
@@ -1296,6 +2632,56 @@ function isAttributeFormFieldVisible(config, properties, options = {}) {
|
|
|
1296
2632
|
return result.kind === "value" ? result.value === true : true;
|
|
1297
2633
|
}
|
|
1298
2634
|
/**
|
|
2635
|
+
* Render a computed default as the raw string a form input holds, matching
|
|
2636
|
+
* what {@link coerceAttributeFormValue} parses back for the field's widget.
|
|
2637
|
+
*/
|
|
2638
|
+
function defaultValueText(config, value) {
|
|
2639
|
+
if (value == null) return null;
|
|
2640
|
+
if (config.widget === "checkbox") {
|
|
2641
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
2642
|
+
if (typeof value === "number") return value !== 0 ? "true" : "false";
|
|
2643
|
+
const lower = String(value).trim().toLowerCase();
|
|
2644
|
+
return lower === "true" || lower === "1" ? "true" : lower === "false" || lower === "0" ? "false" : null;
|
|
2645
|
+
}
|
|
2646
|
+
if (value instanceof Date) return config.widget === "date" ? formatQgisDate(value) : formatQgisDateTime(value);
|
|
2647
|
+
if (typeof value === "number") return Number.isFinite(value) ? String(value) : null;
|
|
2648
|
+
if (typeof value === "object") return null;
|
|
2649
|
+
const text = String(value);
|
|
2650
|
+
if (config.widget === "date") return /^\d{4}-\d{2}-\d{2}/.exec(text)?.[0] ?? null;
|
|
2651
|
+
return text;
|
|
2652
|
+
}
|
|
2653
|
+
/**
|
|
2654
|
+
* Compute the default values a form pre-fills for a new feature. Fields are
|
|
2655
|
+
* evaluated in form order and each result is visible to later expressions, so
|
|
2656
|
+
* a default can build on an earlier one (a photo name built from an ID that
|
|
2657
|
+
* is itself defaulted).
|
|
2658
|
+
*
|
|
2659
|
+
* @param form - The layer's form config.
|
|
2660
|
+
* @param context - Variables, geometry and clock for the expressions; its
|
|
2661
|
+
* `properties` seed the record (values the user already entered).
|
|
2662
|
+
* @param options.skip - Fields to leave alone, e.g. ones the user has edited.
|
|
2663
|
+
* @returns Field name to raw form string, only for fields whose default
|
|
2664
|
+
* evaluated to a non-null value.
|
|
2665
|
+
*/
|
|
2666
|
+
function computeAttributeFormDefaults(form, context = {}, options = {}) {
|
|
2667
|
+
const defaults = {};
|
|
2668
|
+
const properties = { ...context.properties };
|
|
2669
|
+
for (const config of form?.fields ?? []) {
|
|
2670
|
+
const source = config.defaultValue?.trim();
|
|
2671
|
+
if (!source || options.skip?.has(config.field)) continue;
|
|
2672
|
+
const result = evaluateQgisExpression(source, {
|
|
2673
|
+
...context,
|
|
2674
|
+
properties
|
|
2675
|
+
});
|
|
2676
|
+
if (!result.ok) continue;
|
|
2677
|
+
const text = defaultValueText(config, result.value);
|
|
2678
|
+
if (text === null) continue;
|
|
2679
|
+
defaults[config.field] = text;
|
|
2680
|
+
properties[config.field] = coerceAttributeFormValue(config, text);
|
|
2681
|
+
}
|
|
2682
|
+
return defaults;
|
|
2683
|
+
}
|
|
2684
|
+
/**
|
|
1299
2685
|
* Validate one configured field against the candidate properties record (the
|
|
1300
2686
|
* field's own candidate value lives at `properties[config.field]`). Returns
|
|
1301
2687
|
* `null` when the value passes. Checks run in order: required, widget typing,
|
|
@@ -1443,6 +2829,101 @@ function featureForEvaluation(properties, feature) {
|
|
|
1443
2829
|
properties
|
|
1444
2830
|
};
|
|
1445
2831
|
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Turn a referenced layer's rows into dropdown entries.
|
|
2834
|
+
*
|
|
2835
|
+
* @param rows - The referenced rows, as plain property records.
|
|
2836
|
+
* @param relation - Which properties hold the stored value and the label.
|
|
2837
|
+
* @param options.maxEntries - Stop once more than this many distinct entries
|
|
2838
|
+
* are found; the result is then longer than the limit and unsorted.
|
|
2839
|
+
* @returns Every distinct entry, sorted by label. The caller decides what to
|
|
2840
|
+
* do with a very long list.
|
|
2841
|
+
*/
|
|
2842
|
+
function buildRelationValueMap(rows, relation, options = {}) {
|
|
2843
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2844
|
+
const entries = [];
|
|
2845
|
+
const source = relation.labelExpression?.trim();
|
|
2846
|
+
const parsed = source ? parseQgisExpression(source) : null;
|
|
2847
|
+
const expression = parsed?.ok ? parsed.node : null;
|
|
2848
|
+
let numeric = true;
|
|
2849
|
+
for (const row of rows) {
|
|
2850
|
+
const raw = row?.[relation.keyField];
|
|
2851
|
+
if (!row || raw == null || raw === "") continue;
|
|
2852
|
+
const value = String(raw);
|
|
2853
|
+
if (seen.has(value)) continue;
|
|
2854
|
+
seen.add(value);
|
|
2855
|
+
if (typeof raw !== "number" && typeof raw !== "bigint") numeric = false;
|
|
2856
|
+
let label = relation.labelField ? row[relation.labelField] : void 0;
|
|
2857
|
+
if (label == null && expression) {
|
|
2858
|
+
const result = evaluateQgisExpression(expression, { properties: row });
|
|
2859
|
+
if (result.ok) label = result.value;
|
|
2860
|
+
}
|
|
2861
|
+
const text = label == null ? "" : String(label).trim();
|
|
2862
|
+
entries.push(text && text !== value ? {
|
|
2863
|
+
value,
|
|
2864
|
+
label: text
|
|
2865
|
+
} : { value });
|
|
2866
|
+
if (options.maxEntries !== void 0 && entries.length > options.maxEntries) return {
|
|
2867
|
+
entries,
|
|
2868
|
+
valueType: numeric ? "number" : "string"
|
|
2869
|
+
};
|
|
2870
|
+
}
|
|
2871
|
+
entries.sort((a, b) => (a.label ?? a.value).localeCompare(b.label ?? b.value, void 0, { numeric: true }));
|
|
2872
|
+
return {
|
|
2873
|
+
entries,
|
|
2874
|
+
valueType: numeric ? "number" : "string"
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
/** Most entries a live relation dropdown shows; a longer list stays as it was. */
|
|
2878
|
+
const MAX_RELATION_ENTRIES = 1e3;
|
|
2879
|
+
/**
|
|
2880
|
+
* Fill relation dropdowns from the layers they reference.
|
|
2881
|
+
*
|
|
2882
|
+
* @param form - The layer's form config.
|
|
2883
|
+
* @param rowsOf - The current feature properties of a layer by id, or
|
|
2884
|
+
* `undefined` when the layer is absent or its features have not loaded.
|
|
2885
|
+
* @returns A form whose relation fields carry entries read from the
|
|
2886
|
+
* referenced layer. A field whose layer is unavailable, or too long for a
|
|
2887
|
+
* list, keeps the entries it already had; one whose layer is loaded but
|
|
2888
|
+
* empty has none. Returns `form` itself when
|
|
2889
|
+
* nothing was resolved.
|
|
2890
|
+
*/
|
|
2891
|
+
function resolveAttributeFormRelations(form, rowsOf) {
|
|
2892
|
+
if (!form?.fields.some((field) => field.valueRelation)) return form;
|
|
2893
|
+
let changed = false;
|
|
2894
|
+
const fields = form.fields.map((field) => {
|
|
2895
|
+
const relation = field.valueRelation;
|
|
2896
|
+
const rows = relation ? rowsOf(relation.layerId) : void 0;
|
|
2897
|
+
if (!relation || !rows) return field;
|
|
2898
|
+
const { entries, valueType } = buildRelationValueMap(rows, relation, { maxEntries: MAX_RELATION_ENTRIES });
|
|
2899
|
+
if (entries.length > 1e3) return field;
|
|
2900
|
+
changed = true;
|
|
2901
|
+
if (entries.length === 0) return {
|
|
2902
|
+
...field,
|
|
2903
|
+
valueMap: []
|
|
2904
|
+
};
|
|
2905
|
+
return {
|
|
2906
|
+
...field,
|
|
2907
|
+
widget: "valueMap",
|
|
2908
|
+
valueMap: entries,
|
|
2909
|
+
valueType: field.valueType ?? valueType
|
|
2910
|
+
};
|
|
2911
|
+
});
|
|
2912
|
+
return changed ? {
|
|
2913
|
+
...form,
|
|
2914
|
+
fields
|
|
2915
|
+
} : form;
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* `metadata` flag on a layer that is a table: its features have attributes
|
|
2919
|
+
* but no geometry (a lookup list, or child records linked to another layer's
|
|
2920
|
+
* features). It draws nothing; its rows show in the attribute table.
|
|
2921
|
+
*/
|
|
2922
|
+
const ATTRIBUTE_TABLE_FLAG = "attributeTable";
|
|
2923
|
+
/** Whether a layer is a geometry-less table (see {@link ATTRIBUTE_TABLE_FLAG}). */
|
|
2924
|
+
function isAttributeTableLayer(layer) {
|
|
2925
|
+
return layer.metadata?.[ATTRIBUTE_TABLE_FLAG] === true;
|
|
2926
|
+
}
|
|
1446
2927
|
// src/map-click-claims.ts
|
|
1447
2928
|
/**
|
|
1448
2929
|
* Map tools that own the map's clicks while they draw (the Measure control
|
|
@@ -3553,7 +5034,7 @@ function initialLayerStyle(options = {}) {
|
|
|
3553
5034
|
...DEFAULT_LAYER_STYLE,
|
|
3554
5035
|
fillColor,
|
|
3555
5036
|
strokeColor: darkenHex(fillColor, STROKE_DARKEN),
|
|
3556
|
-
...GEOMETRY_DEFAULTS[dominantGeometry(geojson)],
|
|
5037
|
+
...GEOMETRY_DEFAULTS[options.geometry ?? dominantGeometry(geojson)],
|
|
3557
5038
|
...overrides
|
|
3558
5039
|
};
|
|
3559
5040
|
}
|
|
@@ -6629,6 +8110,75 @@ function formatCameraAltitude(meters, unit) {
|
|
|
6629
8110
|
maximumFractionDigits: digits
|
|
6630
8111
|
})} ${label}`;
|
|
6631
8112
|
}
|
|
8113
|
+
// src/i3s-building-state.ts
|
|
8114
|
+
/** The drawable leaves under the given nodes whose whole branch is visible. */
|
|
8115
|
+
function visibleLeaves(nodes, out) {
|
|
8116
|
+
for (const node of nodes) {
|
|
8117
|
+
if (!node.visible) continue;
|
|
8118
|
+
if (node.children?.length) visibleLeaves(node.children, out);
|
|
8119
|
+
else if (node.layerType === "3DObject") out.push(node.id);
|
|
8120
|
+
}
|
|
8121
|
+
return out;
|
|
8122
|
+
}
|
|
8123
|
+
/**
|
|
8124
|
+
* The sublayer ids to draw for a Building Scene Layer.
|
|
8125
|
+
*
|
|
8126
|
+
* @param state - The layer's `metadata.i3sBuilding`.
|
|
8127
|
+
* @returns The Overview id in Overview mode; otherwise every visible 3DObject leaf.
|
|
8128
|
+
*/
|
|
8129
|
+
function visibleI3sBuildingSublayers(state) {
|
|
8130
|
+
if (state.mode === "overview" && state.overviewId !== null) return [state.overviewId];
|
|
8131
|
+
return visibleLeaves(state.disciplines, []);
|
|
8132
|
+
}
|
|
8133
|
+
/**
|
|
8134
|
+
* How far to lower an I3S scene layer onto a map or globe without terrain,
|
|
8135
|
+
* in metres (issue #3374). Shared by the 2D overlay and the globe so the rule
|
|
8136
|
+
* cannot drift between them.
|
|
8137
|
+
*
|
|
8138
|
+
* Scene layers store absolute elevations. Without terrain the ground is at
|
|
8139
|
+
* 0 m, so a layer whose reported lowest elevation (`fullExtent.zmin`) is above
|
|
8140
|
+
* 0 m is lowered by it; one that already reaches 0 m or below (a coastal city)
|
|
8141
|
+
* is left alone. With terrain the absolute heights are kept.
|
|
8142
|
+
*
|
|
8143
|
+
* @param baseElevation - The layer's `fullExtent.zmin`, if known.
|
|
8144
|
+
* @param terrain - Whether the map or globe draws 3D terrain.
|
|
8145
|
+
* @returns The offset (0 or negative).
|
|
8146
|
+
*/
|
|
8147
|
+
function i3sGroundOffset(baseElevation, terrain) {
|
|
8148
|
+
return !terrain && typeof baseElevation === "number" && Number.isFinite(baseElevation) && baseElevation > 0 ? -baseElevation : 0;
|
|
8149
|
+
}
|
|
8150
|
+
/**
|
|
8151
|
+
* Carry a saved Building selection onto a freshly parsed sublayer tree.
|
|
8152
|
+
*
|
|
8153
|
+
* Re-probing a layer refreshes its tree from the service. The saved mode and
|
|
8154
|
+
* each node's visibility are kept where the node still exists (matched by id);
|
|
8155
|
+
* a node the service added takes its parsed default, and one it removed is
|
|
8156
|
+
* dropped, so the renderer never asks for a sublayer that is gone.
|
|
8157
|
+
*
|
|
8158
|
+
* @param fresh - The tree parsed from the service now.
|
|
8159
|
+
* @param saved - The selection saved on the layer.
|
|
8160
|
+
* @returns The fresh tree with the saved choices applied.
|
|
8161
|
+
*/
|
|
8162
|
+
function mergeI3sBuildingSelection(fresh, saved) {
|
|
8163
|
+
const visibility = /* @__PURE__ */ new Map();
|
|
8164
|
+
const collect = (nodes) => {
|
|
8165
|
+
for (const node of nodes) {
|
|
8166
|
+
visibility.set(node.id, node.visible);
|
|
8167
|
+
if (node.children) collect(node.children);
|
|
8168
|
+
}
|
|
8169
|
+
};
|
|
8170
|
+
collect(saved.disciplines);
|
|
8171
|
+
const apply = (nodes) => nodes.map((node) => ({
|
|
8172
|
+
...node,
|
|
8173
|
+
visible: visibility.get(node.id) ?? node.visible,
|
|
8174
|
+
...node.children ? { children: apply(node.children) } : {}
|
|
8175
|
+
}));
|
|
8176
|
+
return {
|
|
8177
|
+
...fresh,
|
|
8178
|
+
mode: saved.mode === "overview" && fresh.overviewId === null ? "full" : saved.mode,
|
|
8179
|
+
disciplines: apply(fresh.disciplines)
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
6632
8182
|
// src/layers-file.ts
|
|
6633
8183
|
/** `type` tag identifying a layers file. */
|
|
6634
8184
|
const LAYERS_FILE_TYPE = "geolibre-layers";
|
|
@@ -7394,10 +8944,11 @@ function storyLayerOpacityFactor(opacities, layerId) {
|
|
|
7394
8944
|
* A layer whose opacity reflects the story presentation's fade.
|
|
7395
8945
|
*
|
|
7396
8946
|
* A chapter's opacity *replaces* the layer's own opacity rather than scaling
|
|
7397
|
-
* it, matching `MapController.setStoryLayerOpacity`, which
|
|
7398
|
-
*
|
|
7399
|
-
*
|
|
7400
|
-
*
|
|
8947
|
+
* it, matching `MapController.setStoryLayerOpacity`, which redraws the paint
|
|
8948
|
+
* with the chapter value in place of the layer opacity (the style's own fill
|
|
8949
|
+
* opacity still applies); otherwise a layer set to 0.7 in the Layers panel and
|
|
8950
|
+
* faded to 0.4 by a chapter would draw its diagrams at 0.28 while its MapLibre
|
|
8951
|
+
* layers draw at 0.4.
|
|
7401
8952
|
*
|
|
7402
8953
|
* Returns the same object when the presentation has not touched the layer (or
|
|
7403
8954
|
* recorded the opacity it already has), so identity-keyed caches downstream
|
|
@@ -8672,6 +10223,6 @@ function withJpegTablesPatch(module) {
|
|
|
8672
10223
|
openCog: (async (input) => patchJpegCogSource(await module.openCog(input)))
|
|
8673
10224
|
};
|
|
8674
10225
|
}
|
|
8675
|
-
export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, ARCGIS_PROJECTION_PRESETS, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CESIUM_BASEMAPS, CESIUM_BING_AERIAL_ASSET_ID, CESIUM_GOOGLE_PHOTOREALISTIC_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_CHAPTER_DURATION_SECONDS, 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_ATTRIBUTE_PREFIX, 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, GeocoderServiceError, GeocoderUnsupportedError, IDENTIFY_ALL_LAYERS_ID, LABEL_NUMBER_LOCALES, LABEL_NUMBER_SAMPLE, LARGE_VECTOR_FEATURE_THRESHOLD, LAYERS_FILE_TYPE, LAYERS_FILE_VERSION, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_STYLES_FILE_TYPE, LAYER_STYLES_FILE_VERSION, LAYER_TYPES, MAP_CRS_CHANGE_EVENT, MAP_CRS_PRESETS, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYERS_FILE_BYTES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_PROJECT_BOOKMARKS, MAX_REDACT_DEPTH, MAX_STORY_CHAPTER_DURATION_SECONDS, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, MIN_STORY_CHAPTER_DURATION_SECONDS, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, NO_LAYERS, 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, POPUP_IMAGE_HEIGHT_RANGE, POPUP_MAX_WIDTH_RANGE, 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, S3CorsError, S3_MAX_PRESIGN_SECONDS, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STAC_PROCESSING_EXTENSION, STAC_VERSION, STROKE_DASH_PRESETS, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeLayerFilterExpression, activeQuickFilters, adaptBboxTemplateToCrs, addLayersToProject, allowsCredentialHeaders, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerFilters, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, applyStoryLayerOpacity, arcgisLocatorInputField, asTileScheme, attributeFormFieldLabel, attributeLinkUrl, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, blobRangeReader, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerStacItem, buildLayerTree, buildMatrixRequest, buildReverseGeocodeUrl, buildRouteRequest, bytesRangeReader, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, centralAngle, cesiumIonAssetId, cesiumIonAssetKind, cesiumKmlSource, changedPreferenceCredentials, circleRadiusValue, clampLabelDecimals, clampLatitude, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectLevels, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileLayerFilters, compileLevelFilter, compileQuickFilter, compileQuickFilters, computeGeoArrowBBox, controlRendersLayer, convertTiffYCbCrToRgb, copyableLayerStyleKind, createCesiumIonLayer, createCesiumKmlLayer, createCzmlLayer, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectLayerSerializationCache, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLayerEvaluator, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, currentLevelChoice, czmlSource, darkenHex, datumShiftFor, decodePolyline, decodePolylineDetailed, deriveTileMatrixSet, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, diffFeatureCollections, diffProjects, documentLocale, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, encodeS3Key, ensureEditorTrackingFields, environmentVariableAccount, evaluateMapExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, explainS3ReadError, extractCopiedLayerStyle, extractLayerStyleEntries, extractLayersFileContent, extractStyleLibraryStyle, extrusionBaseValue, extrusionColorValue, extrusionFloorHeightValue, extrusionHeightValue, featureCollectionToGeoArrow, featureLevel, featureSelectionId, fetchArcGISLocatorInfo, fetchElevations, findLayerStyleEntryIndex, formatCameraAltitude, formatExpressionPreviewValue, formatLabelNumber, formatLabelNumberSample, formatPixelValue, formatPopupValue, formatRoundNum, formatS3Uri, gateRegionalBasemap, generatorCircleRadiusValue, generatorSizeRange, geoArrowToFeatureCollection, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocodeSuggest, geocoderMinIntervalMs, geocoderNeedsApiKey, geocoderSupportsReverse, geocoderSupportsSuggest, geocodingApiKeyAccount, geodesicDistance, geodesicLength, geodesicRadiusBounds, geodesicRingArea, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getArcgisApiKey, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapCrsPreset, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getS3UrlSigner, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, guessLevelField, hasActiveLayerFilter, hasActiveQuickFilter, hasAppPrivilege, hasHeaderReferences, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, heatmapRampColors, horizontalBbox, hydrateProjectLayer, identifyAllIncludes, identifyStateWithoutLayers, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, installCogTilerDatumShift, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbbreviatedJpegCompression, isAbsoluteFilesystemPath, isAdoptedVectorAwaitingFeatures, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isCesiumIonLayer, isCesiumKmlLayer, isCesiumOnlyLayer, isCredentialFieldName, isCredentialUrlParam, isCredentialedS3Url, isCzmlLayer, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGeoArrowBatch, isGooglePhotorealisticTilesetUrl, isHeaderReferenceOnly, isHexColor, isImdfArchiveEntries, isImdfZipBlob, isImdfZipRange, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isLevelFilterActive, isLonLat, isMacOsZipMetadata, isMaintainedEditorTrackingField, isMapClickClaimed, isPopupClickEnabled, isPopupHoverEnabled, isRasterMarkerSource, isReferencedLayer, isRegionalBasemapSentinel, isS3Uri, isSafePopupUrl, isSecretEnvironmentVariable, isSessionOnlyLayer, isStoryHiddenLayer, isStrokeDashPattern, isStyleLibraryTargetLayer, isStyleSpecColor, isSvgMarkerResponse, isTileAddressError, isValidMetadataDate, isValidMetadataEmail, isValidMetadataUrl, isVectorColorExpression, isochroneResponseToFeatures, jsonValuesEqual, labelFieldTextField, latLonBoxArea, layerGroupDepth, layerGroupMoveability, layerGroupSortability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerLocalPath, layerPanelGroupHeaders, levelValueMatches, lineWidthValue, listZipEntryPaths, localFileName, lookupProjectCredential, mapCrsPreferencePatch, mapZoomStepOutputs, matchFeaturesByExpression, matchLayerStyleEntry, matchingLevelValues, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, mergeAttributeFormConfigs, mergeLevels, metadataDateToRfc3339, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeArcGISLocatorUrl, normalizeArcgisWkid, normalizeBlankBackgroundColor, normalizeBookmarkGroups, normalizeBookmarks, normalizeCesiumBasemap, normalizeDashboardColumns, normalizeGeocodingLocator, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerDescriptiveMetadata, normalizeLayerLibraryEntries, normalizeLayerStyleEntries, normalizeLayersFileContent, normalizeLegendConfig, normalizeLongitude, normalizeMapCrs, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizeObiaWorkbench, normalizePrimaryRenderer, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeProjectInteraction, normalizeSecondaryMapViews, normalizeStoryChapterDurationSeconds, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, overlayStoredEnvironmentVariables, overlayStoredGeocodingApiKeys, overlayStoredPreferenceCredentials, parseArcGISLocatorInfo, parseArcGISSuggestions, parseCesiumIonAssetId, parseContours, parseCzml, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseLayerStylesFile, parseLayersFile, parseLevelValue, parseMapCrsEpsgCode, parseMetadataKeywords, parseProject, parseS3BucketList, parseS3Error, parseS3ListObjects, parseS3Url, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, patchJpegCogSource, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, portableWmsTileUrl, presignS3Url, previewValue, printLayoutConfigsEqual, probeImdfZipUrl, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, readSimpleStyleNumberLookup, readStoredAuthorName, readsSimpleStyleProperties, reapplyLayerJoins, redactConfigurationCredentials, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerMapClickClaim, registerProjectRestoreHistory, registerS3UrlSigner, removeTrailingJsonCommas, rendererAppliesOpacity, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestNavigationRoute, requestRoute, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveHeaderReferences, resolveIdentifyTarget, resolveLabelNumberLocale, resolveLayerCapabilities, resolveLayerTileAddress, resolveLevelLinks, resolveMapGrid, resolvePopupBody, resolvePopupImageHeight, resolvePopupMaxWidth, resolvePopupRows, resolvePopupTitle, resolveProjectHeaderReferences, resolveReadableUrl, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, resolveTileAddress, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, s3ObjectHttpsUrl, sameCesiumImagery, sameLevelLink, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, selectLayerById, selectLayerIds, selectLayerSummaries, selectLayersWhen, selectLevel, selectedGeocodingLocator, serializeLayerLibrary, serializeLayerStylesFile, serializeLayersFile, serializeProject, serializeProjectWithLayerCache, serializeProjectWithLayerCacheAsync, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setGeocodingTokenProvider, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setProjectCredentialLookup, setRegistryPublishableSettings, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, shouldZoomToNewLayers, simpleStyleNumberValue, sortLayerGroupInPanel, splitProjectCredentials, stacAssetMediaType, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyLayerOpacityFactor, storyLocationView, storyMapHasContent, storyVisibleLayers, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, strokeBlurValue, strokeDashPresetName, strokeDashValue, strokeOpacityPaintValue, strokeOpacityValue, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, uniqueImportedLayerName, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, unsignedSourceUrl, useAppCapability, useAppStore, useLayer, useLayerIds, useLayerSummaries, useLayersWhen, validateAttributeFormField, validateAttributeFormValues, validateLayerDescriptiveMetadata, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visiblePopupFields, visiblePopupProperties, withDatumShift, withGeoKeysDatumShift, withJpegTablesPatch, withWktDatumShift, withinGeodesicDistance, withoutHistory };
|
|
10226
|
+
export { ALL_APP_PRIVILEGES, ALL_DEPLOYMENT_CAPABILITIES, ARCGIS_IMAGE_SERVICE_SOURCE_KIND, ARCGIS_PROJECTION_PRESETS, ATTRIBUTE_TABLE_FLAG, BLANK_BASEMAP, BLEND_MODES, BUILT_IN_STYLE_PRESETS, CARTOCIUDAD_MIN_INTERVAL_MS, CARTOCIUDAD_PUBLIC_HOST, CESIUM_BASEMAPS, CESIUM_BING_AERIAL_ASSET_ID, CESIUM_GOOGLE_PHOTOREALISTIC_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_CHAPTER_DURATION_SECONDS, 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_ATTRIBUTE_PREFIX, 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, GeocoderServiceError, GeocoderUnsupportedError, IDENTIFY_ALL_LAYERS_ID, LABEL_NUMBER_LOCALES, LABEL_NUMBER_SAMPLE, LARGE_VECTOR_FEATURE_THRESHOLD, LAYERS_FILE_TYPE, LAYERS_FILE_VERSION, LAYER_LIBRARY_BUNDLE_TYPE, LAYER_LIBRARY_BUNDLE_VERSION, LAYER_PALETTE, LAYER_STYLES_FILE_TYPE, LAYER_STYLES_FILE_VERSION, LAYER_TYPES, MAP_CRS_CHANGE_EVENT, MAP_CRS_PRESETS, MAX_DASHBOARD_COLUMNS, MAX_DIAGRAM_FEATURES, MAX_DIAGRAM_SCAN_FEATURES, MAX_LAYERS_FILE_BYTES, MAX_LAYER_LIBRARY_ENTRIES, MAX_LAYER_LIBRARY_ENTRY_BYTES, MAX_MAP_GRID_DIM, MAX_POINTS_PER_REQUEST, MAX_PROCESSING_HISTORY, MAX_PROJECT_BOOKMARKS, MAX_REDACT_DEPTH, MAX_RELATION_ENTRIES, MAX_STORY_CHAPTER_DURATION_SECONDS, MERCATOR_METERS_PER_PIXEL_AT_ZOOM_0, METERS_PER_FOOT, METERS_PER_MILE, METERS_PER_NAUTICAL_MILE, MIN_DASHBOARD_COLUMNS, MIN_DIAGRAM_SIZE, MIN_STORY_CHAPTER_DURATION_SECONDS, NETCDF_IMAGE_SOURCE_KIND, NOMINATIM_MIN_INTERVAL_MS, NOMINATIM_PUBLIC_HOST, NO_DEPLOYMENT_CAPABILITIES, NO_LAYERS, OPENFREEMAP_BASEMAPS, PHOTO_FULL_PROPERTY, PHOTO_PROPERTY, PLANETARY_BASEMAPS, PLANETARY_BASEMAP_GROUPS, PLANETARY_BASEMAP_SENTINEL_PREFIX, PLANETARY_COMPUTER_SOURCE_KIND, PLANET_SWITCHER_OPTIONS, POINTER_ELEVATION_CACHE_LIMIT, POINTER_ELEVATION_DEBOUNCE_MS, POPUP_IMAGE_HEIGHT_RANGE, POPUP_MAX_WIDTH_RANGE, 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, S3CorsError, S3_MAX_PRESIGN_SECONDS, SELECTION_MODES, SQL_QUERY_SOURCE_KIND, STAC_PROCESSING_EXTENSION, STAC_VERSION, STROKE_DASH_PRESETS, STYLE_LIBRARY_BUNDLE_TYPE, STYLE_LIBRARY_BUNDLE_VERSION, SYMBOL_STYLE_KEYS, VECTOR_COLOR_RAMPS, activeLayerFilterExpression, activeQuickFilters, adaptBboxTemplateToCrs, addLayersToProject, allowsCredentialHeaders, appPrivilegeReason, applyCopiedLayerStyle, applyGroupEffects, applyJoinsToLayer, applyLayerFilters, applyLayerJoins, applyLayerVirtualFields, applyMatchedSelection, applyProjectToStore, applySelectionMode, applyStoryLayerOpacity, arcgisLocatorInputField, asTileScheme, attributeFormFieldLabel, attributeLinkUrl, availableCesiumBasemap, basemapToCesiumImagery, batchDecodePolylines, blobRangeReader, bodyLengthToEarth, buildForwardGeocodeUrl, buildIsochroneRequest, buildLayerPanelUnits, buildLayerStacItem, buildLayerTree, buildMatrixRequest, buildRelationValueMap, buildReverseGeocodeUrl, buildRouteRequest, bytesRangeReader, canRedoProjectRestore, canSaveLayerToLibrary, canUndoProjectRestore, captureLayerLibraryEntry, cascadeLayerJoinRefresh, centralAngle, cesiumIonAssetId, cesiumIonAssetKind, cesiumKmlSource, changedPreferenceCredentials, circleRadiusValue, clampLabelDecimals, clampLatitude, clearExternalNativePaintBridge, clearHistory, clearQuickFilterValues, coerceAttributeFormValue, collectDiagramData, collectLevels, collectTransitiveJoinSourceIds, compareSequenceValues, compileFeatureExpression, compileLayerFilters, compileLevelFilter, compileQuickFilter, compileQuickFilters, computeAttributeFormDefaults, computeGeoArrowBBox, controlRendersLayer, convertTiffYCbCrToRgb, copyableLayerStyleKind, createCesiumIonLayer, createCesiumKmlLayer, createCzmlLayer, createDefaultAppCapabilities, createDefaultMapView, createDefaultPrintLayout, createEmptyProject, createEqualIntervalBreaks, createGraduatedClassBreaks, createLayerLibraryEntryId, createPointerElevationResolver, createProjectLayerSerializationCache, createProjectTemplate, createQuantileBreaks, createSampleStoryMap, createStyleLayerEvaluator, createStyleLibraryEntryId, csvRowsToGeocodeRequests, currentEditorIdentity, currentLevelChoice, czmlSource, darkenHex, datumShiftFor, decodePolyline, decodePolylineDetailed, deriveTileMatrixSet, detachProjectCopy, detectNonGeographicCoordinates, diagramAnchor, diagramPixelSize, diagramsSuppressedByPointRenderer, diffFeatureCollections, diffProjects, documentLocale, dominantGeometry, drawMarkerPath, earthAreaToBody, earthLengthToBody, editorTrackingFieldNames, effectiveLayerRenderState, effectiveVectorRules, encodePolyline, encodeS3Key, ensureEditorTrackingFields, environmentVariableAccount, evaluateMapExpression, evaluateQgisExpression, excludeHiddenFieldsFromGeojson, excludeHiddenFieldsFromProject, explainS3ReadError, extractCopiedLayerStyle, extractLayerStyleEntries, extractLayersFileContent, extractStyleLibraryStyle, extrusionBaseValue, extrusionColorValue, extrusionFloorHeightValue, extrusionHeightValue, featureCollectionToGeoArrow, featureLevel, featureSelectionId, fetchArcGISLocatorInfo, fetchElevations, findLayerStyleEntryIndex, formatCameraAltitude, formatExpressionPreviewValue, formatLabelNumber, formatLabelNumberSample, formatPixelValue, formatPopupValue, formatQgisDate, formatQgisDateTime, formatRoundNum, formatS3Uri, gateRegionalBasemap, generatorCircleRadiusValue, generatorSizeRange, geoArrowToFeatureCollection, geoJSONToPolylineStr, geocodeForward, geocodeMatchToFeature, geocodeReverse, geocodeSuggest, geocoderMinIntervalMs, geocoderNeedsApiKey, geocoderSupportsReverse, geocoderSupportsSuggest, geocodingApiKeyAccount, geodesicDistance, geodesicLength, geodesicRadiusBounds, geodesicRingArea, geojsonHasZCoordinates, getActiveBodyRadiusRatio, getActiveEllipsoid, getActiveMeanRadiusMeters, getActiveSemiMajorAxisMeters, getArcgisApiKey, getAttributeFormField, getBuildEnvironment, getCesiumIonToken, getEllipsoid, getExternalNativePaintBridge, getGeocoderConfig, getGeocodingProvider, getGoogleMapsApiKey, getHistoryCoalesceMs, getMapCrsPreset, getMapboxAccessToken, getMaxHistoryFeatureCount, getPlanetaryBasemapById, getPlanetaryBasemapByStyleUrl, getProtomapsApiKey, getProtomapsStyleUrl, getRegionalBasemapById, getRegionalBasemapByStyleUrl, getRoundNum, getRoutingConfig, getRuntimeEnvironment, getS3UrlSigner, getSpatialExtensionPath, getVectorColorRamp, googleMapsApiKeyHeaderValue, groupMemberIndices, guessLevelField, hasActiveLayerFilter, hasActiveQuickFilter, hasAppPrivilege, hasHeaderReferences, hasPathTraversal, hasRestorableLayerSource, hasSimpleStyleProperties, heatmapRampColors, horizontalBbox, hydrateProjectLayer, i3sGroundOffset, identifyAllIncludes, identifyStateWithoutLayers, inferFieldTypes, inferLayerCapabilities, initialLayerStyle, installCogTilerDatumShift, interpolateColors, interpolateHexColor, interpolateRampColors, intersectPrivileges, invertSelection, isAbbreviatedJpegCompression, isAbsoluteFilesystemPath, isAdoptedVectorAwaitingFeatures, isAllowedPluginManifestUrl, isAttributeFormFieldVisible, isAttributeTableLayer, isCesiumIonLayer, isCesiumKmlLayer, isCesiumOnlyLayer, isCredentialFieldName, isCredentialUrlParam, isCredentialedS3Url, isCzmlLayer, isDefaultPrintLayout, isDeploymentCapability, isDiagramStyleEnabled, isDuckDBQueryLayer, isExternalNativeLayerRecord, isGeoArrowBatch, isGooglePhotorealisticTilesetUrl, isHeaderReferenceOnly, isHexColor, isImdfArchiveEntries, isImdfZipBlob, isImdfZipRange, isInitialLayerStyle, isInlineImageValue, isInternalPopupField, isIpadDesktopUserAgent, isLevelFilterActive, isLonLat, isMacOsZipMetadata, isMaintainedEditorTrackingField, isMapClickClaimed, isPlanetaryComputerLayer, isPopupClickEnabled, isPopupHoverEnabled, isRasterMarkerSource, isReferencedLayer, isRegionalBasemapSentinel, isS3Uri, isSafePopupUrl, isSecretEnvironmentVariable, isSessionOnlyLayer, isStoryHiddenLayer, isStrokeDashPattern, isStyleLibraryTargetLayer, isStyleSpecColor, isSvgMarkerResponse, isTileAddressError, isValidMetadataDate, isValidMetadataEmail, isValidMetadataUrl, isVectorColorExpression, isochroneResponseToFeatures, jsonValuesEqual, labelFieldTextField, latLonBoxArea, layerGroupDepth, layerGroupMoveability, layerGroupSortability, layerJoinKey, layerLibraryConfigPatch, layerLibraryEntryNeedsLocalFile, layerLocalPath, layerPanelGroupHeaders, levelValueMatches, lineWidthValue, listZipEntryPaths, localFileName, lookupProjectCredential, mapCrsPreferencePatch, mapZoomStepOutputs, matchFeaturesByExpression, matchLayerStyleEntry, matchingLevelValues, matrixResponseToFeatures, meanRadiusMeters, mercatorMetersPerPixelAtZoom0, mergeAttributeFormConfigs, mergeI3sBuildingSelection, mergeLevels, metadataDateToRfc3339, metersWidthExpression, nextDelayMs, nextLayerPaletteColor, nominatimResultToFeature, nominatimReverseResultToDisplay, nonEmptyRecord, normalizeAppPrivileges, normalizeArcGISLocatorUrl, normalizeArcgisWkid, normalizeBlankBackgroundColor, normalizeBookmarkGroups, normalizeBookmarks, normalizeCesiumBasemap, normalizeDashboardColumns, normalizeGeocodeBbox, normalizeGeocodingLocator, normalizeGeocodingProviderId, normalizeGroupContiguity, normalizeHexColor, normalizeLayerCapabilities, normalizeLayerDescriptiveMetadata, normalizeLayerLibraryEntries, normalizeLayerStyleEntries, normalizeLayersFileContent, normalizeLegendConfig, normalizeLongitude, normalizeMapCrs, normalizeMapLayout, normalizeMapViewState, normalizeModelGraph, normalizeModels, normalizeObiaWorkbench, normalizePrimaryRenderer, normalizePrintLayoutConfig, normalizeProcessingHistory, normalizeProjectComments, normalizeProjectInteraction, normalizeSecondaryMapViews, normalizeStoryChapterDurationSeconds, normalizeStoryMap, normalizeStyleLibraryEntries, normalizeWidgets, overlayStoredEnvironmentVariables, overlayStoredGeocodingApiKeys, overlayStoredPreferenceCredentials, parseArcGISLocatorInfo, parseArcGISSuggestions, parseCesiumIonAssetId, parseContours, parseCzml, parseDeploymentCapabilities, parseHexColor, parseHexColorList, parseJsonExpression, parseLayerLibrary, parseLayerStylesFile, parseLayersFile, parseLevelValue, parseMapCrsEpsgCode, parseMetadataKeywords, parseProject, parseQgisExpression, parseS3BucketList, parseS3Error, parseS3ListObjects, parseS3Url, parseStoryMapCsv, parseStoryMapJson, parseStyleLibrary, parseValueMapText, patchJpegCogSource, persistedThreeDTilesRequestHeaders, pickEditorIdentity, planLayerLibraryAdd, pluginOwnsPaint, polylineStrToGeoJSON, popupFieldLabel, portableWmsTileUrl, presignS3Url, previewValue, printLayoutConfigsEqual, probeImdfZipUrl, projectFromStore, projectPathLabel, proportionalRadiusExpression, proportionalSizeRange, qgisExpressionProblem, qgisExpressionReferences, qgisExpressionToMapLibre, readSimpleStyleNumberLookup, readStoredAuthorName, readsSimpleStyleProperties, reapplyLayerJoins, redactConfigurationCredentials, redactCredentials, redactProjectCredentials, redactUrlCredentials, redo, registerMapClickClaim, registerProjectRestoreHistory, registerS3UrlSigner, removeTrailingJsonCommas, rendererAppliesOpacity, reorderLayerGroupInPanel, requestIsochrone, requestMatrix, requestNavigationRoute, requestRoute, resolveAttributeFormRelations, resolveConfiguredPopupTitle, resolveEditorTrackingConfig, resolveGeocoderConfig, resolveHeaderReferences, resolveIdentifyTarget, resolveLabelNumberLocale, resolveLayerCapabilities, resolveLayerTileAddress, resolveLevelLinks, resolveMapGrid, resolvePopupBody, resolvePopupImageHeight, resolvePopupMaxWidth, resolvePopupRows, resolvePopupTitle, resolveProjectHeaderReferences, resolveReadableUrl, resolveRolePrivileges, resolveSvgSource, resolveThreeDTilesRequestHeaders, resolveTileAddress, rgbToHex, routeResponseToFeatures, rowCap, ruleBasedColorExpression, ruleBasedVisibilityFilter, s3ObjectHttpsUrl, sameCesiumImagery, sameLevelLink, sampleMapTerrain, sampleMapTerrainPoint, sampleRemoteElevations, sanitizeLayerStylePatch, scaleAltitudeToActiveBody, scaleDenomination, scaleSpan, scrubCommentsForRemovedLayers, scrubLegendForRemovedLayers, scrubPrintLayoutForLayers, scrubPrintLayoutForRemovedLayers, scrubWidgetsForRemovedLayers, selectLayerById, selectLayerIds, selectLayerSummaries, selectLayersWhen, selectLevel, selectedGeocodingLocator, serializeLayerLibrary, serializeLayerStylesFile, serializeLayersFile, serializeProject, serializeProjectWithLayerCache, serializeProjectWithLayerCacheAsync, serializeStoryMapCsv, serializeStoryMapJson, serializeStyleLibrary, setActiveEllipsoidId, setExternalNativePaintBridge, setGeocodingFetch, setGeocodingTokenProvider, setHistoryCoalesceMs, setMaxHistoryFeatureCount, setProjectCredentialLookup, setRegistryPublishableSettings, setStoredAuthorName, shouldThrottle, shouldUseTiledRendering, shouldZoomToNewLayers, simpleStyleNumberValue, sortLayerGroupInPanel, splitProjectCredentials, stacAssetMediaType, stampFeatureCollectionEditorTracking, stampFeatureEditorTracking, stampFeaturePropertiesEditorTracking, storyLayerOpacityFactor, storyLocationView, storyMapHasContent, storyVisibleLayers, stringifyPopupValue, stripGoogleMapsApiKeyHeader, stripJoinFields, stripVirtualFieldColumns, strokeBlurValue, strokeDashPresetName, strokeDashValue, strokeOpacityPaintValue, strokeOpacityValue, styleValue, subscribeProjectRestoreHistory, substituteExpressionVariables, supportsBridgedOpacity, transformGeojsonElevation, trimHistoryBySize, undo, unescapePolyline, uniqueImportedLayerName, unmatchedGeocodeFeature, unresolvedLayerLibraryJoins, unsignedSourceUrl, useAppCapability, useAppStore, useLayer, useLayerIds, useLayerSummaries, useLayersWhen, validateAttributeFormField, validateAttributeFormValues, validateLayerDescriptiveMetadata, validateMapExpression, valueMapLabelFor, valueMapToText, vectorCircleColorValue, vectorColorExpression, vectorFillColorValue, vectorFillOpacityValue, vectorLineColorValue, vectorOutlineColorValue, vectorStrokeWidthValue, visibleI3sBuildingSublayers, visiblePopupFields, visiblePopupProperties, withDatumShift, withGeoKeysDatumShift, withI3sSceneLayerKind, withJpegTablesPatch, withWktDatumShift, withinGeodesicDistance, withoutHistory };
|
|
8676
10227
|
|
|
8677
10228
|
//# sourceMappingURL=index.mjs.map
|