@eodash/eodash 5.7.0 → 5.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/core/client/components/EodashOverlay.vue +27 -4
- package/core/client/composables/DefineEodash.js +0 -1
- package/core/client/composables/DefineWidgets.js +3 -1
- package/core/client/eodashSTAC/EodashCollection.js +87 -24
- package/core/client/eodashSTAC/cql.js +64 -0
- package/core/client/eodashSTAC/createLayers.js +353 -187
- package/core/client/eodashSTAC/helpers.js +694 -110
- package/core/client/eodashSTAC/mosaic.js +422 -0
- package/core/client/plugins/axios.js +31 -1
- package/core/client/store/actions.js +1 -1
- package/core/client/store/stac.js +54 -4
- package/core/client/store/states.js +20 -2
- package/core/client/types.ts +152 -28
- package/core/client/utils/bands-editor/exampleSchema.json +50 -50
- package/core/client/utils/states.js +10 -0
- package/dist/client/{DashboardLayout-kRsmsNW5.js → DashboardLayout-DEs4f_Vy.js} +2 -2
- package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-PvnV88BA.js} +1 -1
- package/dist/client/EodashChart-D-3AGKZY.js +260 -0
- package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-Dm_goZzs.js} +3 -3
- package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-CJy3zYzD.js} +2 -1
- package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-rL8UnpkA.js} +55 -33
- package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
- package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-CG7u2Aq8.js} +227 -112
- package/dist/client/EodashStacInfo-CJhhxthD.js +169 -0
- package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-CWyK8QJT.js} +4 -4
- package/dist/client/{ExportState-DbOm8hBI.js → ExportState-BXhy5TOU.js} +20 -174
- package/dist/client/{Footer-BKg-VVkj.js → Footer-DZPvnhcT.js} +1 -1
- package/dist/client/{Header-DCrwCvmY.js → Header-CiGsDE_a.js} +2 -2
- package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BJHW7x5V.js} +2 -2
- package/dist/client/{PopUp-9uimfK49.js → PopUp-7_XnhvCc.js} +4 -168
- package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-LL_UCbvc.js} +5 -5
- package/dist/client/VDivider-DWYM0WUr.js +168 -0
- package/dist/client/{VImg-CrmB76ZY.js → VImg-B75dgw2K.js} +2 -2
- package/dist/client/{VMain-OKZNpcEG.js → VMain-DvMmOL4f.js} +1 -1
- package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-ClWHUmWL.js} +2 -2
- package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-BqaWs4ch.js} +1 -1
- package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-IZanwL-A.js} +677 -379
- package/dist/client/{async-DSKj6g1f.js → async-CiNIOBVz.js} +31 -29
- package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-3S3y_kZg.js} +17 -17
- package/dist/client/dialog-transition-c4ZSnzac.js +170 -0
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-B-c1_6yo.js} +8 -87
- package/dist/client/{handling-BrcDDlgS.js → handling-NvY8Csry.js} +3 -4
- package/dist/client/{helpers-DjZVl_WF.js → helpers-BFuh19CP.js} +1094 -194
- package/dist/client/{index-1JKE68n3.js → index-1wMAyuNL.js} +219 -67
- package/dist/client/{index-CSG_JoOA.js → index-B8jGJSTA.js} +62 -56
- package/dist/client/{index-Cu3-13Xg.js → index-BKQQJkhy.js} +167 -61
- package/dist/client/index-D0jUyol8.js +3167 -0
- package/dist/client/{index-CUjl-ZsI.js → index-KGxz5Egv.js} +2 -2
- package/dist/client/mosaic-vHvSuih6.js +459 -0
- package/dist/client/templates.js +251 -121
- package/dist/client/{transition-OHcz5Qq1.js → transition-BkyUsZh_.js} +1 -1
- package/dist/node/cli.js +5 -5
- package/dist/node/types.d.ts +4 -0
- package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
- package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +57 -25
- package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
- package/dist/types/core/client/eodashSTAC/createLayers.d.ts +42 -23
- package/dist/types/core/client/eodashSTAC/helpers.d.ts +118 -19
- package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
- package/dist/types/core/client/store/stac.d.ts +38 -8
- package/dist/types/core/client/store/states.d.ts +24 -2
- package/dist/types/core/client/types.d.ts +128 -24
- package/dist/types/core/client/utils/states.d.ts +12 -0
- package/dist/types/templates/baseConfig.d.ts +1 -100
- package/dist/types/templates/compare.d.ts +32 -3
- package/dist/types/templates/expert.d.ts +18 -0
- package/dist/types/templates/explore.d.ts +10 -1
- package/dist/types/templates/lite.d.ts +11 -0
- package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
- package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
- package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
- package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
- package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
- package/dist/types/widgets/EodashLayerControl.vue.d.ts +35 -16
- package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +14 -10
- package/dist/types/widgets/EodashMap/index.vue.d.ts +86 -37
- package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
- package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
- package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
- package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
- package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
- package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
- package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
- package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
- package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
- package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
- package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
- package/package.json +34 -34
- package/templates/baseConfig.js +37 -6
- package/templates/compare.js +61 -7
- package/templates/expert.js +24 -2
- package/templates/explore.js +12 -5
- package/templates/lite.js +13 -0
- package/widgets/EodashChart.vue +162 -21
- package/widgets/EodashItemCatalog/index.vue +363 -35
- package/widgets/EodashItemCatalog/methods/filters.js +134 -70
- package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
- package/widgets/EodashItemCatalog/methods/map.js +141 -28
- package/widgets/EodashLayerControl.vue +51 -25
- package/widgets/EodashMap/EodashMapBtns.vue +109 -48
- package/widgets/EodashMap/index.vue +91 -105
- package/widgets/EodashMap/methods/btns.js +64 -31
- package/widgets/EodashMap/methods/create-layers-config.js +16 -16
- package/widgets/EodashMap/methods/index.js +71 -8
- package/widgets/EodashMap/methods/use-map-loading.js +58 -0
- package/widgets/EodashProcess/index.vue +136 -16
- package/widgets/EodashProcess/methods/composables.js +13 -0
- package/widgets/EodashProcess/methods/outputs.js +0 -1
- package/widgets/EodashProcess/methods/utils.js +29 -27
- package/widgets/EodashProcess/states.js +1 -1
- package/widgets/EodashStacInfo.vue +64 -2
- package/widgets/EodashTimeSlider/index.vue +58 -2
- package/widgets/EodashTimeSlider/methods/index.js +5 -48
- package/widgets/EodashTimeSlider/types.ts +7 -17
- package/widgets/ExportState.vue +13 -4
- package/dist/client/EodashChart-CLDqE93C.js +0 -144
- package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
- package/dist/client/index-5Jhsa8Xe.js +0 -570
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { toAbsolute } from 'stac-js/src/http.js';
|
|
2
2
|
import { setupCache } from 'axios-cache-interceptor';
|
|
3
3
|
import log from 'loglevel';
|
|
4
|
-
import { ref, shallowRef } from 'vue';
|
|
4
|
+
import { ref, shallowRef, reactive, shallowReactive } from 'vue';
|
|
5
|
+
import mustache from 'mustache';
|
|
6
|
+
import { updateVectorLayerStyle } from '@eox/layercontrol';
|
|
7
|
+
import { mdiWrench, mdiSnowflake, mdiVolleyball, mdiHiking, mdiLightningBolt, mdiLeaf, mdiAccountMultiple, mdiCurrencyEur, mdiWeatherWindy, mdiSetCenter, mdiHospitalBoxOutline, mdiImageFilterHdr, mdiWater, mdiBarley } from '@mdi/js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Create a bound version of a function with a specified `this` context
|
|
@@ -426,7 +429,9 @@ function merge(...objs) {
|
|
|
426
429
|
return;
|
|
427
430
|
}
|
|
428
431
|
|
|
429
|
-
|
|
432
|
+
// findKey lowercases the key, so caseless lookup only applies to strings —
|
|
433
|
+
// symbol keys are identity-matched.
|
|
434
|
+
const targetKey = (caseless && typeof key === 'string' && findKey(result, key)) || key;
|
|
430
435
|
// Read via own-prop only — a bare `result[targetKey]` walks the prototype
|
|
431
436
|
// chain, so a polluted Object.prototype value could surface here and get
|
|
432
437
|
// copied into the merged result.
|
|
@@ -443,7 +448,24 @@ function merge(...objs) {
|
|
|
443
448
|
};
|
|
444
449
|
|
|
445
450
|
for (let i = 0, l = objs.length; i < l; i++) {
|
|
446
|
-
|
|
451
|
+
const source = objs[i];
|
|
452
|
+
if (!source || isBuffer(source)) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
forEach(source, assignValue);
|
|
457
|
+
|
|
458
|
+
if (typeof source !== 'object' || isArray(source)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const symbols = Object.getOwnPropertySymbols(source);
|
|
463
|
+
for (let j = 0; j < symbols.length; j++) {
|
|
464
|
+
const symbol = symbols[j];
|
|
465
|
+
if (propertyIsEnumerable.call(source, symbol)) {
|
|
466
|
+
assignValue(source[symbol], symbol);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
447
469
|
}
|
|
448
470
|
return result;
|
|
449
471
|
}
|
|
@@ -672,6 +694,8 @@ const hasOwnProperty = (
|
|
|
672
694
|
hasOwnProperty.call(obj, prop)
|
|
673
695
|
)(Object.prototype);
|
|
674
696
|
|
|
697
|
+
const { propertyIsEnumerable } = Object.prototype;
|
|
698
|
+
|
|
675
699
|
/**
|
|
676
700
|
* Determine if a value is a RegExp object
|
|
677
701
|
*
|
|
@@ -777,11 +801,11 @@ function isSpecCompliantForm(thing) {
|
|
|
777
801
|
* @returns {Object} The JSON-compatible object.
|
|
778
802
|
*/
|
|
779
803
|
const toJSONObject = (obj) => {
|
|
780
|
-
const
|
|
804
|
+
const visited = new WeakSet();
|
|
781
805
|
|
|
782
|
-
const visit = (source
|
|
806
|
+
const visit = (source) => {
|
|
783
807
|
if (isObject(source)) {
|
|
784
|
-
if (
|
|
808
|
+
if (visited.has(source)) {
|
|
785
809
|
return;
|
|
786
810
|
}
|
|
787
811
|
|
|
@@ -791,15 +815,16 @@ const toJSONObject = (obj) => {
|
|
|
791
815
|
}
|
|
792
816
|
|
|
793
817
|
if (!('toJSON' in source)) {
|
|
794
|
-
|
|
818
|
+
// add-on descent / delete-on-ascent: preserves path semantics, so DAG nodes serialise at every occurrence (see #7230).
|
|
819
|
+
visited.add(source);
|
|
795
820
|
const target = isArray(source) ? [] : {};
|
|
796
821
|
|
|
797
822
|
forEach(source, (value, key) => {
|
|
798
|
-
const reducedValue = visit(value
|
|
823
|
+
const reducedValue = visit(value);
|
|
799
824
|
!isUndefined(reducedValue) && (target[key] = reducedValue);
|
|
800
825
|
});
|
|
801
826
|
|
|
802
|
-
|
|
827
|
+
visited.delete(source);
|
|
803
828
|
|
|
804
829
|
return target;
|
|
805
830
|
}
|
|
@@ -808,7 +833,7 @@ const toJSONObject = (obj) => {
|
|
|
808
833
|
return source;
|
|
809
834
|
};
|
|
810
835
|
|
|
811
|
-
return visit(obj
|
|
836
|
+
return visit(obj);
|
|
812
837
|
};
|
|
813
838
|
|
|
814
839
|
/**
|
|
@@ -1010,10 +1035,6 @@ const parseHeaders = (rawHeaders) => {
|
|
|
1010
1035
|
return parsed;
|
|
1011
1036
|
};
|
|
1012
1037
|
|
|
1013
|
-
const $internals = Symbol('internals');
|
|
1014
|
-
|
|
1015
|
-
const INVALID_HEADER_VALUE_CHARS_RE = /[^\x09\x20-\x7E\x80-\xFF]/g;
|
|
1016
|
-
|
|
1017
1038
|
function trimSPorHTAB(str) {
|
|
1018
1039
|
let start = 0;
|
|
1019
1040
|
let end = str.length;
|
|
@@ -1041,12 +1062,40 @@ function trimSPorHTAB(str) {
|
|
|
1041
1062
|
return start === 0 && end === str.length ? str : str.slice(start, end);
|
|
1042
1063
|
}
|
|
1043
1064
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1065
|
+
// The control-code ranges are intentional: header sanitization strips C0/DEL bytes.
|
|
1066
|
+
// eslint-disable-next-line no-control-regex
|
|
1067
|
+
const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp('[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+', 'g');
|
|
1068
|
+
// eslint-disable-next-line no-control-regex
|
|
1069
|
+
const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp('[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+', 'g');
|
|
1070
|
+
|
|
1071
|
+
function sanitizeValue(value, invalidChars) {
|
|
1072
|
+
if (utils$1.isArray(value)) {
|
|
1073
|
+
return value.map((item) => sanitizeValue(item, invalidChars));
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
return trimSPorHTAB(String(value).replace(invalidChars, ''));
|
|
1046
1077
|
}
|
|
1047
1078
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1079
|
+
const sanitizeHeaderValue = (value) =>
|
|
1080
|
+
sanitizeValue(value, INVALID_UNICODE_HEADER_VALUE_CHARS);
|
|
1081
|
+
|
|
1082
|
+
const sanitizeByteStringHeaderValue = (value) =>
|
|
1083
|
+
sanitizeValue(value, INVALID_BYTE_STRING_HEADER_VALUE_CHARS);
|
|
1084
|
+
|
|
1085
|
+
function toByteStringHeaderObject(headers) {
|
|
1086
|
+
const byteStringHeaders = Object.create(null);
|
|
1087
|
+
|
|
1088
|
+
utils$1.forEach(headers.toJSON(), (value, header) => {
|
|
1089
|
+
byteStringHeaders[header] = sanitizeByteStringHeaderValue(value);
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
return byteStringHeaders;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const $internals = Symbol('internals');
|
|
1096
|
+
|
|
1097
|
+
function normalizeHeader(header) {
|
|
1098
|
+
return header && String(header).trim().toLowerCase();
|
|
1050
1099
|
}
|
|
1051
1100
|
|
|
1052
1101
|
function normalizeValue(value) {
|
|
@@ -1128,7 +1177,7 @@ class AxiosHeaders {
|
|
|
1128
1177
|
const lHeader = normalizeHeader(_header);
|
|
1129
1178
|
|
|
1130
1179
|
if (!lHeader) {
|
|
1131
|
-
|
|
1180
|
+
return;
|
|
1132
1181
|
}
|
|
1133
1182
|
|
|
1134
1183
|
const key = utils$1.findKey(self, lHeader);
|
|
@@ -1156,7 +1205,7 @@ class AxiosHeaders {
|
|
|
1156
1205
|
key;
|
|
1157
1206
|
for (const entry of header) {
|
|
1158
1207
|
if (!utils$1.isArray(entry)) {
|
|
1159
|
-
throw TypeError('Object iterator must return a key-value pair');
|
|
1208
|
+
throw new TypeError('Object iterator must return a key-value pair');
|
|
1160
1209
|
}
|
|
1161
1210
|
|
|
1162
1211
|
obj[(key = entry[0])] = (dest = obj[key])
|
|
@@ -1771,7 +1820,7 @@ function toFormData(obj, formData, options) {
|
|
|
1771
1820
|
}
|
|
1772
1821
|
|
|
1773
1822
|
if (stack.indexOf(value) !== -1) {
|
|
1774
|
-
throw Error('Circular reference detected in ' + path.join('.'));
|
|
1823
|
+
throw new Error('Circular reference detected in ' + path.join('.'));
|
|
1775
1824
|
}
|
|
1776
1825
|
|
|
1777
1826
|
stack.push(value);
|
|
@@ -1988,6 +2037,7 @@ const transitionalDefaults = {
|
|
|
1988
2037
|
forcedJSONParsing: true,
|
|
1989
2038
|
clarifyTimeoutError: false,
|
|
1990
2039
|
legacyInterceptorReqResOrdering: true,
|
|
2040
|
+
advertiseZstdAcceptEncoding: false,
|
|
1991
2041
|
};
|
|
1992
2042
|
|
|
1993
2043
|
const URLSearchParams$1 = typeof URLSearchParams !== 'undefined' ? URLSearchParams : AxiosURLSearchParams;
|
|
@@ -2145,7 +2195,7 @@ function formDataToJSON(formData) {
|
|
|
2145
2195
|
return !isNumericKey;
|
|
2146
2196
|
}
|
|
2147
2197
|
|
|
2148
|
-
if (!target
|
|
2198
|
+
if (!utils$1.hasOwnProp(target, name) || !utils$1.isObject(target[name])) {
|
|
2149
2199
|
target[name] = [];
|
|
2150
2200
|
}
|
|
2151
2201
|
|
|
@@ -2510,6 +2560,9 @@ const progressEventReducer = (listener, isDownloadStream, freq = 3) => {
|
|
|
2510
2560
|
const _speedometer = speedometer(50, 250);
|
|
2511
2561
|
|
|
2512
2562
|
return throttle((e) => {
|
|
2563
|
+
if (!e || typeof e.loaded !== 'number') {
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2513
2566
|
const rawLoaded = e.loaded;
|
|
2514
2567
|
const total = e.lengthComputable ? e.total : undefined;
|
|
2515
2568
|
const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
|
|
@@ -2819,12 +2872,12 @@ function setFormDataHeaders(headers, formHeaders, policy) {
|
|
|
2819
2872
|
*
|
|
2820
2873
|
* @returns {string} UTF-8 bytes as a Latin-1 string
|
|
2821
2874
|
*/
|
|
2822
|
-
const encodeUTF8 = (str) =>
|
|
2875
|
+
const encodeUTF8$1 = (str) =>
|
|
2823
2876
|
encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
|
|
2824
2877
|
String.fromCharCode(parseInt(hex, 16))
|
|
2825
2878
|
);
|
|
2826
2879
|
|
|
2827
|
-
|
|
2880
|
+
function resolveConfig(config) {
|
|
2828
2881
|
const newConfig = mergeConfig({}, config);
|
|
2829
2882
|
|
|
2830
2883
|
// Read only own properties to prevent prototype pollution gadgets
|
|
@@ -2845,8 +2898,8 @@ const resolveConfig = (config) => {
|
|
|
2845
2898
|
|
|
2846
2899
|
newConfig.url = buildURL(
|
|
2847
2900
|
buildFullPath(baseURL, url, allowAbsoluteUrls),
|
|
2848
|
-
|
|
2849
|
-
|
|
2901
|
+
own('params'),
|
|
2902
|
+
own('paramsSerializer')
|
|
2850
2903
|
);
|
|
2851
2904
|
|
|
2852
2905
|
// HTTP basic authentication
|
|
@@ -2854,13 +2907,17 @@ const resolveConfig = (config) => {
|
|
|
2854
2907
|
headers.set(
|
|
2855
2908
|
'Authorization',
|
|
2856
2909
|
'Basic ' +
|
|
2857
|
-
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
|
|
2910
|
+
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8$1(auth.password) : ''))
|
|
2858
2911
|
);
|
|
2859
2912
|
}
|
|
2860
2913
|
|
|
2861
2914
|
if (utils$1.isFormData(data)) {
|
|
2862
|
-
if (
|
|
2863
|
-
|
|
2915
|
+
if (
|
|
2916
|
+
platform.hasStandardBrowserEnv ||
|
|
2917
|
+
platform.hasStandardBrowserWebWorkerEnv ||
|
|
2918
|
+
utils$1.isReactNative(data)
|
|
2919
|
+
) {
|
|
2920
|
+
headers.setContentType(undefined); // browser/web worker/RN handles it
|
|
2864
2921
|
} else if (utils$1.isFunction(data.getHeaders)) {
|
|
2865
2922
|
// Node.js FormData (like form-data package)
|
|
2866
2923
|
setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy'));
|
|
@@ -2892,7 +2949,7 @@ const resolveConfig = (config) => {
|
|
|
2892
2949
|
}
|
|
2893
2950
|
|
|
2894
2951
|
return newConfig;
|
|
2895
|
-
}
|
|
2952
|
+
}
|
|
2896
2953
|
|
|
2897
2954
|
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
|
2898
2955
|
|
|
@@ -3041,7 +3098,7 @@ const xhrAdapter = isXHRAdapterSupported &&
|
|
|
3041
3098
|
|
|
3042
3099
|
// Add headers to the request
|
|
3043
3100
|
if ('setRequestHeader' in request) {
|
|
3044
|
-
utils$1.forEach(requestHeaders
|
|
3101
|
+
utils$1.forEach(toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) {
|
|
3045
3102
|
request.setRequestHeader(key, val);
|
|
3046
3103
|
});
|
|
3047
3104
|
}
|
|
@@ -3111,54 +3168,55 @@ const xhrAdapter = isXHRAdapterSupported &&
|
|
|
3111
3168
|
};
|
|
3112
3169
|
|
|
3113
3170
|
const composeSignals = (signals, timeout) => {
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
if (timeout || length) {
|
|
3117
|
-
let controller = new AbortController();
|
|
3118
|
-
|
|
3119
|
-
let aborted;
|
|
3120
|
-
|
|
3121
|
-
const onabort = function (reason) {
|
|
3122
|
-
if (!aborted) {
|
|
3123
|
-
aborted = true;
|
|
3124
|
-
unsubscribe();
|
|
3125
|
-
const err = reason instanceof Error ? reason : this.reason;
|
|
3126
|
-
controller.abort(
|
|
3127
|
-
err instanceof AxiosError
|
|
3128
|
-
? err
|
|
3129
|
-
: new CanceledError(err instanceof Error ? err.message : err)
|
|
3130
|
-
);
|
|
3131
|
-
}
|
|
3132
|
-
};
|
|
3171
|
+
signals = signals ? signals.filter(Boolean) : [];
|
|
3133
3172
|
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
timer = null;
|
|
3138
|
-
onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT));
|
|
3139
|
-
}, timeout);
|
|
3140
|
-
|
|
3141
|
-
const unsubscribe = () => {
|
|
3142
|
-
if (signals) {
|
|
3143
|
-
timer && clearTimeout(timer);
|
|
3144
|
-
timer = null;
|
|
3145
|
-
signals.forEach((signal) => {
|
|
3146
|
-
signal.unsubscribe
|
|
3147
|
-
? signal.unsubscribe(onabort)
|
|
3148
|
-
: signal.removeEventListener('abort', onabort);
|
|
3149
|
-
});
|
|
3150
|
-
signals = null;
|
|
3151
|
-
}
|
|
3152
|
-
};
|
|
3173
|
+
if (!timeout && !signals.length) {
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3153
3176
|
|
|
3154
|
-
|
|
3177
|
+
const controller = new AbortController();
|
|
3155
3178
|
|
|
3156
|
-
|
|
3179
|
+
let aborted = false;
|
|
3157
3180
|
|
|
3158
|
-
|
|
3181
|
+
const onabort = function (reason) {
|
|
3182
|
+
if (!aborted) {
|
|
3183
|
+
aborted = true;
|
|
3184
|
+
unsubscribe();
|
|
3185
|
+
const err = reason instanceof Error ? reason : this.reason;
|
|
3186
|
+
controller.abort(
|
|
3187
|
+
err instanceof AxiosError
|
|
3188
|
+
? err
|
|
3189
|
+
: new CanceledError(err instanceof Error ? err.message : err)
|
|
3190
|
+
);
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3159
3193
|
|
|
3160
|
-
|
|
3161
|
-
|
|
3194
|
+
let timer =
|
|
3195
|
+
timeout &&
|
|
3196
|
+
setTimeout(() => {
|
|
3197
|
+
timer = null;
|
|
3198
|
+
onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT));
|
|
3199
|
+
}, timeout);
|
|
3200
|
+
|
|
3201
|
+
const unsubscribe = () => {
|
|
3202
|
+
if (!signals) { return; }
|
|
3203
|
+
timer && clearTimeout(timer);
|
|
3204
|
+
timer = null;
|
|
3205
|
+
signals.forEach((signal) => {
|
|
3206
|
+
signal.unsubscribe
|
|
3207
|
+
? signal.unsubscribe(onabort)
|
|
3208
|
+
: signal.removeEventListener('abort', onabort);
|
|
3209
|
+
});
|
|
3210
|
+
signals = null;
|
|
3211
|
+
};
|
|
3212
|
+
|
|
3213
|
+
signals.forEach((signal) => signal.addEventListener('abort', onabort));
|
|
3214
|
+
|
|
3215
|
+
const { signal } = controller;
|
|
3216
|
+
|
|
3217
|
+
signal.unsubscribe = () => utils$1.asap(unsubscribe);
|
|
3218
|
+
|
|
3219
|
+
return signal;
|
|
3162
3220
|
};
|
|
3163
3221
|
|
|
3164
3222
|
const streamChunk = function* (chunk, chunkSize) {
|
|
@@ -3352,12 +3410,41 @@ function estimateDataURLDecodedBytes(url) {
|
|
|
3352
3410
|
return bytes;
|
|
3353
3411
|
}
|
|
3354
3412
|
|
|
3355
|
-
const VERSION = "1.
|
|
3413
|
+
const VERSION = "1.17.0";
|
|
3356
3414
|
|
|
3357
3415
|
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
3358
3416
|
|
|
3359
3417
|
const { isFunction } = utils$1;
|
|
3360
3418
|
|
|
3419
|
+
/**
|
|
3420
|
+
* Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
|
|
3421
|
+
* This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
|
|
3422
|
+
*
|
|
3423
|
+
* @param {string} str The string to encode
|
|
3424
|
+
*
|
|
3425
|
+
* @returns {string} UTF-8 bytes as a Latin-1 string
|
|
3426
|
+
*/
|
|
3427
|
+
const encodeUTF8 = (str) =>
|
|
3428
|
+
encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
|
|
3429
|
+
String.fromCharCode(parseInt(hex, 16))
|
|
3430
|
+
);
|
|
3431
|
+
|
|
3432
|
+
// Node's WHATWG URL parser returns `username` and `password` percent-encoded.
|
|
3433
|
+
// Decode before composing the `auth` option so credentials such as
|
|
3434
|
+
// `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
|
|
3435
|
+
// original value for malformed input so a bad encoding never throws.
|
|
3436
|
+
const decodeURIComponentSafe = (value) => {
|
|
3437
|
+
if (!utils$1.isString(value)) {
|
|
3438
|
+
return value;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
try {
|
|
3442
|
+
return decodeURIComponent(value);
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
return value;
|
|
3445
|
+
}
|
|
3446
|
+
};
|
|
3447
|
+
|
|
3361
3448
|
const test = (fn, ...args) => {
|
|
3362
3449
|
try {
|
|
3363
3450
|
return !!fn(...args);
|
|
@@ -3366,8 +3453,20 @@ const test = (fn, ...args) => {
|
|
|
3366
3453
|
}
|
|
3367
3454
|
};
|
|
3368
3455
|
|
|
3456
|
+
const maybeWithAuthCredentials = (url) => {
|
|
3457
|
+
const protocolIndex = url.indexOf('://');
|
|
3458
|
+
let urlToCheck = url;
|
|
3459
|
+
if (protocolIndex !== -1) {
|
|
3460
|
+
urlToCheck = urlToCheck.slice(protocolIndex + 3);
|
|
3461
|
+
}
|
|
3462
|
+
return urlToCheck.includes('@') || urlToCheck.includes(':');
|
|
3463
|
+
};
|
|
3464
|
+
|
|
3369
3465
|
const factory = (env) => {
|
|
3370
|
-
const globalObject =
|
|
3466
|
+
const globalObject =
|
|
3467
|
+
utils$1.global !== undefined && utils$1.global !== null
|
|
3468
|
+
? utils$1.global
|
|
3469
|
+
: globalThis;
|
|
3371
3470
|
const { ReadableStream, TextEncoder } = globalObject;
|
|
3372
3471
|
|
|
3373
3472
|
env = utils$1.merge.call(
|
|
@@ -3510,6 +3609,7 @@ const factory = (env) => {
|
|
|
3510
3609
|
|
|
3511
3610
|
const hasMaxContentLength = utils$1.isNumber(maxContentLength) && maxContentLength > -1;
|
|
3512
3611
|
const hasMaxBodyLength = utils$1.isNumber(maxBodyLength) && maxBodyLength > -1;
|
|
3612
|
+
const own = (key) => (utils$1.hasOwnProp(config, key) ? config[key] : undefined);
|
|
3513
3613
|
|
|
3514
3614
|
let _fetch = envFetch || fetch;
|
|
3515
3615
|
|
|
@@ -3532,6 +3632,46 @@ const factory = (env) => {
|
|
|
3532
3632
|
let requestContentLength;
|
|
3533
3633
|
|
|
3534
3634
|
try {
|
|
3635
|
+
// HTTP basic authentication
|
|
3636
|
+
let auth = undefined;
|
|
3637
|
+
const configAuth = own('auth');
|
|
3638
|
+
|
|
3639
|
+
if (configAuth) {
|
|
3640
|
+
const username = configAuth.username || '';
|
|
3641
|
+
const password = configAuth.password || '';
|
|
3642
|
+
auth = {
|
|
3643
|
+
username,
|
|
3644
|
+
password
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
if (maybeWithAuthCredentials(url)) {
|
|
3649
|
+
const parsedURL = new URL(url, platform.origin);
|
|
3650
|
+
|
|
3651
|
+
if (!auth && (parsedURL.username || parsedURL.password)) {
|
|
3652
|
+
const urlUsername = decodeURIComponentSafe(parsedURL.username);
|
|
3653
|
+
const urlPassword = decodeURIComponentSafe(parsedURL.password);
|
|
3654
|
+
auth = {
|
|
3655
|
+
username: urlUsername,
|
|
3656
|
+
password: urlPassword
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
if (parsedURL.username || parsedURL.password) {
|
|
3661
|
+
parsedURL.username = '';
|
|
3662
|
+
parsedURL.password = '';
|
|
3663
|
+
url = parsedURL.href;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3667
|
+
if (auth) {
|
|
3668
|
+
headers.delete('authorization');
|
|
3669
|
+
headers.set(
|
|
3670
|
+
'Authorization',
|
|
3671
|
+
'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3535
3675
|
// Enforce maxContentLength for data: URLs up-front so we never materialize
|
|
3536
3676
|
// an oversized payload. The HTTP adapter applies the same check (see http.js
|
|
3537
3677
|
// "if (protocol === 'data:')" branch).
|
|
@@ -3624,7 +3764,7 @@ const factory = (env) => {
|
|
|
3624
3764
|
...fetchOptions,
|
|
3625
3765
|
signal: composedSignal,
|
|
3626
3766
|
method: method.toUpperCase(),
|
|
3627
|
-
headers: headers.normalize()
|
|
3767
|
+
headers: toByteStringHeaderObject(headers.normalize()),
|
|
3628
3768
|
body: data,
|
|
3629
3769
|
duplex: 'half',
|
|
3630
3770
|
credentials: isCredentialsSupported ? withCredentials : undefined,
|
|
@@ -4213,6 +4353,7 @@ class Axios {
|
|
|
4213
4353
|
forcedJSONParsing: validators.transitional(validators.boolean),
|
|
4214
4354
|
clarifyTimeoutError: validators.transitional(validators.boolean),
|
|
4215
4355
|
legacyInterceptorReqResOrdering: validators.transitional(validators.boolean),
|
|
4356
|
+
advertiseZstdAcceptEncoding: validators.transitional(validators.boolean),
|
|
4216
4357
|
},
|
|
4217
4358
|
false
|
|
4218
4359
|
);
|
|
@@ -4699,10 +4840,6 @@ axios$1.HttpStatusCode = HttpStatusCode;
|
|
|
4699
4840
|
|
|
4700
4841
|
axios$1.default = axios$1;
|
|
4701
4842
|
|
|
4702
|
-
const instance = axios$1.create();
|
|
4703
|
-
|
|
4704
|
-
const axios = setupCache(instance, { cacheTakeover: false });
|
|
4705
|
-
|
|
4706
4843
|
/** setting default log level globally to warning */
|
|
4707
4844
|
log.setLevel(log.levels.WARN, true);
|
|
4708
4845
|
|
|
@@ -4732,7 +4869,7 @@ const registeredProjections = ["EPSG:4326", "EPSG:3857"];
|
|
|
4732
4869
|
/** available projection to be rendered by `EodashMap` */
|
|
4733
4870
|
const availableMapProjection = ref("EPSG:3857");
|
|
4734
4871
|
|
|
4735
|
-
/** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
|
|
4872
|
+
/** @type {import("vue").Ref<import("@eox/map").EOxMap & { mapUpdateId?: number } | null>} */
|
|
4736
4873
|
const mapEl = shallowRef(null);
|
|
4737
4874
|
|
|
4738
4875
|
/** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
|
|
@@ -4776,6 +4913,24 @@ const chartSpec = ref(null);
|
|
|
4776
4913
|
*/
|
|
4777
4914
|
const compareChartSpec = ref(null);
|
|
4778
4915
|
|
|
4916
|
+
/**
|
|
4917
|
+
* Global loading state.
|
|
4918
|
+
* - `loading.activeLoads` — increment/decrement to track concurrent loads
|
|
4919
|
+
* - `loading.value` — derived boolean, true when `activeLoads > 0`
|
|
4920
|
+
*/
|
|
4921
|
+
const loading = reactive({
|
|
4922
|
+
activeLoads: 0,
|
|
4923
|
+
get value() {
|
|
4924
|
+
return this.activeLoads > 0;
|
|
4925
|
+
},
|
|
4926
|
+
});
|
|
4927
|
+
|
|
4928
|
+
/**
|
|
4929
|
+
* Adapter allows external widgets to hook into tooltip property transformation
|
|
4930
|
+
* @type {import("vue").Ref<((param: {key: string, value: any}, map?: string) => {key: string, value: any} | undefined) | null>}
|
|
4931
|
+
*/
|
|
4932
|
+
const tooltipAdapter = shallowRef(null);
|
|
4933
|
+
|
|
4779
4934
|
const states = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
4780
4935
|
__proto__: null,
|
|
4781
4936
|
activeTemplate,
|
|
@@ -4792,13 +4947,180 @@ const states = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
4792
4947
|
datetime,
|
|
4793
4948
|
indicator,
|
|
4794
4949
|
isGlobe,
|
|
4950
|
+
loading,
|
|
4795
4951
|
mapCompareEl,
|
|
4796
4952
|
mapEl,
|
|
4797
4953
|
mapPosition,
|
|
4798
4954
|
poi,
|
|
4799
|
-
registeredProjections
|
|
4955
|
+
registeredProjections,
|
|
4956
|
+
tooltipAdapter
|
|
4800
4957
|
}, Symbol.toStringTag, { value: 'Module' }));
|
|
4801
4958
|
|
|
4959
|
+
const instance = axios$1.create();
|
|
4960
|
+
const axios = setupCache(instance, { cacheTakeover: false });
|
|
4961
|
+
function installLoadingInterceptors() {
|
|
4962
|
+
const reqId = axios.interceptors.request.use((config) => {
|
|
4963
|
+
loading.activeLoads++;
|
|
4964
|
+
return config;
|
|
4965
|
+
});
|
|
4966
|
+
const resId = axios.interceptors.response.use(
|
|
4967
|
+
(response) => {
|
|
4968
|
+
loading.activeLoads = Math.max(0, loading.activeLoads - 1);
|
|
4969
|
+
return response;
|
|
4970
|
+
},
|
|
4971
|
+
(error) => {
|
|
4972
|
+
loading.activeLoads = Math.max(0, loading.activeLoads - 1);
|
|
4973
|
+
return Promise.reject(error);
|
|
4974
|
+
}
|
|
4975
|
+
);
|
|
4976
|
+
return () => {
|
|
4977
|
+
axios.interceptors.request.eject(reqId);
|
|
4978
|
+
axios.interceptors.response.eject(resId);
|
|
4979
|
+
};
|
|
4980
|
+
}
|
|
4981
|
+
installLoadingInterceptors();
|
|
4982
|
+
|
|
4983
|
+
/**
|
|
4984
|
+
* Array of eodash STAC Collections extracted from the current selected indicator.
|
|
4985
|
+
* Updated in {@link file://./../store/stac.js `loadSelectedSTAC`} widget
|
|
4986
|
+
*
|
|
4987
|
+
* @type {import('../eodashSTAC/EodashCollection').EodashCollection[]}
|
|
4988
|
+
* @private
|
|
4989
|
+
*/
|
|
4990
|
+
const eodashCollections = shallowReactive([]);
|
|
4991
|
+
|
|
4992
|
+
/**
|
|
4993
|
+
* Array of eodash STAC Collections extracted from the current selected COMPARE indicator.
|
|
4994
|
+
* Updated in {@link file://./../store/stac.js ` loadSelectedCompareSTAC`} widget
|
|
4995
|
+
*
|
|
4996
|
+
* @type {import('../eodashSTAC/EodashCollection').EodashCollection[]}
|
|
4997
|
+
* @private
|
|
4998
|
+
*/
|
|
4999
|
+
const eodashCompareCollections = shallowReactive([]);
|
|
5000
|
+
|
|
5001
|
+
/** whether it's the first load of the app, used to track if there are params in the url */
|
|
5002
|
+
const isFirstLoad = ref(false);
|
|
5003
|
+
|
|
5004
|
+
/**
|
|
5005
|
+
* Current value of the layer control JSON form for the latest layer the user interacted with.
|
|
5006
|
+
* @type {import("vue").Ref<Record<string, any> | undefined>}
|
|
5007
|
+
*/
|
|
5008
|
+
const layerControlFormValue = ref({});
|
|
5009
|
+
|
|
5010
|
+
/**
|
|
5011
|
+
* Current value of the layer control JSON form for the latest layer the user interacted with.
|
|
5012
|
+
* @type {import("vue").Ref<Record<string, any> | undefined>}
|
|
5013
|
+
*/
|
|
5014
|
+
const layerControlFormValueCompare = ref({});
|
|
5015
|
+
|
|
5016
|
+
/**
|
|
5017
|
+
* STAC indicators color palette, defaults to Bank-Wong palette
|
|
5018
|
+
* @type {string[]} */
|
|
5019
|
+
const collectionsPalette = reactive([
|
|
5020
|
+
"#009E73",
|
|
5021
|
+
"#0072B2",
|
|
5022
|
+
"#E69F00",
|
|
5023
|
+
"#CC79A7",
|
|
5024
|
+
"#56B4E9",
|
|
5025
|
+
"#D55E00",
|
|
5026
|
+
]);
|
|
5027
|
+
|
|
5028
|
+
/**
|
|
5029
|
+
* Object containing data themes and their associated brand properties (icon and color).
|
|
5030
|
+
*/
|
|
5031
|
+
const dataThemesBrands = {
|
|
5032
|
+
agriculture: {
|
|
5033
|
+
icon: mdiBarley,
|
|
5034
|
+
color: "#F2AF25",
|
|
5035
|
+
},
|
|
5036
|
+
water: {
|
|
5037
|
+
icon: mdiWater,
|
|
5038
|
+
color: "#73A6C7",
|
|
5039
|
+
},
|
|
5040
|
+
oceans: {
|
|
5041
|
+
icon: mdiWater,
|
|
5042
|
+
color: "#6DA2C5",
|
|
5043
|
+
},
|
|
5044
|
+
land: {
|
|
5045
|
+
icon: mdiImageFilterHdr,
|
|
5046
|
+
color: "#019E73",
|
|
5047
|
+
},
|
|
5048
|
+
health: {
|
|
5049
|
+
icon: mdiHospitalBoxOutline,
|
|
5050
|
+
color: "#32322C",
|
|
5051
|
+
},
|
|
5052
|
+
"covid-19": {
|
|
5053
|
+
icon: mdiHospitalBoxOutline,
|
|
5054
|
+
color: "#32322C",
|
|
5055
|
+
},
|
|
5056
|
+
combined: {
|
|
5057
|
+
icon: mdiSetCenter,
|
|
5058
|
+
color: "#56B4E9",
|
|
5059
|
+
},
|
|
5060
|
+
air: {
|
|
5061
|
+
icon: mdiWeatherWindy,
|
|
5062
|
+
color: "#475faf",
|
|
5063
|
+
},
|
|
5064
|
+
atmosphere: {
|
|
5065
|
+
icon: mdiWeatherWindy,
|
|
5066
|
+
color: "#475faf",
|
|
5067
|
+
},
|
|
5068
|
+
climate: {
|
|
5069
|
+
icon: mdiWeatherWindy,
|
|
5070
|
+
color: "#475faf",
|
|
5071
|
+
},
|
|
5072
|
+
economy: {
|
|
5073
|
+
icon: mdiCurrencyEur,
|
|
5074
|
+
color: "#8E81AF",
|
|
5075
|
+
},
|
|
5076
|
+
commerce: {
|
|
5077
|
+
icon: mdiCurrencyEur,
|
|
5078
|
+
color: "#8E81AF",
|
|
5079
|
+
},
|
|
5080
|
+
society: {
|
|
5081
|
+
icon: mdiAccountMultiple,
|
|
5082
|
+
color: "#8ac501",
|
|
5083
|
+
},
|
|
5084
|
+
biomass: {
|
|
5085
|
+
icon: mdiLeaf,
|
|
5086
|
+
color: "#009E73",
|
|
5087
|
+
},
|
|
5088
|
+
extremes: {
|
|
5089
|
+
icon: mdiLightningBolt,
|
|
5090
|
+
color: "#a1280a",
|
|
5091
|
+
},
|
|
5092
|
+
energy: {
|
|
5093
|
+
icon: mdiLightningBolt,
|
|
5094
|
+
color: "#475faf",
|
|
5095
|
+
},
|
|
5096
|
+
tourism: {
|
|
5097
|
+
icon: mdiHiking,
|
|
5098
|
+
color: "#80510aff",
|
|
5099
|
+
},
|
|
5100
|
+
sport: {
|
|
5101
|
+
icon: mdiVolleyball,
|
|
5102
|
+
color: "#e98e65ff",
|
|
5103
|
+
},
|
|
5104
|
+
cryosphere: {
|
|
5105
|
+
icon: mdiSnowflake,
|
|
5106
|
+
color: "#42C7B8",
|
|
5107
|
+
},
|
|
5108
|
+
industry: {
|
|
5109
|
+
icon: mdiWrench,
|
|
5110
|
+
color: "#8d845cff",
|
|
5111
|
+
},
|
|
5112
|
+
};
|
|
5113
|
+
|
|
5114
|
+
/** @type {Map<string, import("stac-ts").StacItem[]>} */
|
|
5115
|
+
const itemsCache = new Map();
|
|
5116
|
+
/**
|
|
5117
|
+
* Split items cache stores items with their time range metadata
|
|
5118
|
+
* @type {Map<string, {items: import("stac-ts").StacItem[], minTime: number, maxTime: number}>}
|
|
5119
|
+
*/
|
|
5120
|
+
const splitItemsCache = new Map();
|
|
5121
|
+
|
|
5122
|
+
ref(0);
|
|
5123
|
+
|
|
4802
5124
|
/**
|
|
4803
5125
|
* Returns the current layers of {@link mapEl}
|
|
4804
5126
|
* @returns {import("@eox/map").EoxLayer[]}
|
|
@@ -4807,7 +5129,7 @@ const getLayers = () => mapEl.value?.layers ?? [];
|
|
|
4807
5129
|
|
|
4808
5130
|
/**
|
|
4809
5131
|
* Returns the current layers of {@link mapCompareEl}
|
|
4810
|
-
*
|
|
5132
|
+
* @returns {import("@eox/map").EoxLayer[]}
|
|
4811
5133
|
*/
|
|
4812
5134
|
const getCompareLayers = () => mapCompareEl.value?.layers ?? [];
|
|
4813
5135
|
|
|
@@ -5043,6 +5365,26 @@ function generateFeatures(links, extraProperties = {}, rel = "item") {
|
|
|
5043
5365
|
return geojsonObject;
|
|
5044
5366
|
}
|
|
5045
5367
|
|
|
5368
|
+
/**
|
|
5369
|
+
* Fetches or extracts the raster form configuration for a STAC object.
|
|
5370
|
+
* Supports direct JSON objects, data URIs, and URL strings.
|
|
5371
|
+
*
|
|
5372
|
+
* @param {string|object|undefined} rasterform - The rasterform property from the STAC object.
|
|
5373
|
+
* @returns {Promise<import("@/types").EodashRasterJSONForm|undefined>}
|
|
5374
|
+
*/
|
|
5375
|
+
async function fetchRasterForm(rasterform) {
|
|
5376
|
+
if (!rasterform) {
|
|
5377
|
+
return undefined;
|
|
5378
|
+
}
|
|
5379
|
+
if (typeof rasterform === "object") {
|
|
5380
|
+
return /** @type {import("@/types").EodashRasterJSONForm} */ (rasterform);
|
|
5381
|
+
}
|
|
5382
|
+
if (typeof rasterform === "string") {
|
|
5383
|
+
return await axios.get(rasterform).then((resp) => resp.data);
|
|
5384
|
+
}
|
|
5385
|
+
return undefined;
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5046
5388
|
/**
|
|
5047
5389
|
* Spearates and extracts layerConfig (jsonform schema & legend) from a style json
|
|
5048
5390
|
*
|
|
@@ -5099,6 +5441,39 @@ function extractLayerConfig(
|
|
|
5099
5441
|
return { layerConfig, style };
|
|
5100
5442
|
}
|
|
5101
5443
|
|
|
5444
|
+
/**
|
|
5445
|
+
* Recursively extracts URL keys from a JSON Schema.
|
|
5446
|
+
* Maps schema property names to their defined `url_key`.
|
|
5447
|
+
* @param {Record<string, any> | null | undefined} schema
|
|
5448
|
+
* @returns {Record<string, string>}
|
|
5449
|
+
*/
|
|
5450
|
+
function extractUrlKeys(schema) {
|
|
5451
|
+
/** @type {Record<string, string>} */
|
|
5452
|
+
const keys = {};
|
|
5453
|
+
if (!schema || typeof schema !== "object") return keys;
|
|
5454
|
+
|
|
5455
|
+
if (schema.properties) {
|
|
5456
|
+
for (const [key, propDef] of Object.entries(schema.properties)) {
|
|
5457
|
+
if (propDef && typeof propDef === "object") {
|
|
5458
|
+
if (typeof propDef.url_key === "string") {
|
|
5459
|
+
keys[key] = propDef.url_key;
|
|
5460
|
+
}
|
|
5461
|
+
Object.assign(keys, extractUrlKeys(propDef));
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
|
|
5466
|
+
for (const combinator of ["oneOf", "allOf", "anyOf"]) {
|
|
5467
|
+
if (Array.isArray(schema[combinator])) {
|
|
5468
|
+
for (const sub of schema[combinator]) {
|
|
5469
|
+
Object.assign(keys, extractUrlKeys(sub));
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
|
|
5474
|
+
return keys;
|
|
5475
|
+
}
|
|
5476
|
+
|
|
5102
5477
|
/**
|
|
5103
5478
|
*
|
|
5104
5479
|
* @param {number[]} bbox
|
|
@@ -5173,7 +5548,7 @@ const extractRoles = (properties, linkOrAsset) => {
|
|
|
5173
5548
|
|
|
5174
5549
|
/**
|
|
5175
5550
|
* Extracts a single non-link style JSON from a STAC Item optionally for a selected key mapping
|
|
5176
|
-
* @param { import("stac-ts").StacItem | import("stac-ts").StacCollection } stacObject
|
|
5551
|
+
* @param { import("stac-ts").StacItem | import("stac-ts").StacCollection | null | undefined} stacObject
|
|
5177
5552
|
* @param {string | undefined} linkKey
|
|
5178
5553
|
* @param {string | undefined} assetKey
|
|
5179
5554
|
* @returns
|
|
@@ -5183,6 +5558,7 @@ const fetchStyle = async (
|
|
|
5183
5558
|
linkKey = undefined,
|
|
5184
5559
|
assetKey = undefined,
|
|
5185
5560
|
) => {
|
|
5561
|
+
if (!stacObject) return undefined;
|
|
5186
5562
|
let styleLink = null;
|
|
5187
5563
|
if (linkKey) {
|
|
5188
5564
|
styleLink = stacObject.links.find(
|
|
@@ -5214,6 +5590,20 @@ const fetchStyle = async (
|
|
|
5214
5590
|
}
|
|
5215
5591
|
};
|
|
5216
5592
|
|
|
5593
|
+
/**
|
|
5594
|
+
* Resolves a style by preferring the item's own `style` link and falling back
|
|
5595
|
+
* to the collection's. Takes the same key arguments as `fetchStyle`.
|
|
5596
|
+
*
|
|
5597
|
+
* @param {import("stac-ts").StacItem | import("stac-ts").StacCollection} item
|
|
5598
|
+
* @param {import("stac-ts").StacCollection | null | undefined} collection
|
|
5599
|
+
* @param {string} [linkKey]
|
|
5600
|
+
* @param {string} [assetKey]
|
|
5601
|
+
* @returns {Promise<import("@/types").EodashStyleJson | undefined>}
|
|
5602
|
+
*/
|
|
5603
|
+
const resolveStyle = async (item, collection, linkKey, assetKey) =>
|
|
5604
|
+
(await fetchStyle(item, linkKey, assetKey)) ??
|
|
5605
|
+
(await fetchStyle(collection, linkKey, assetKey));
|
|
5606
|
+
|
|
5217
5607
|
/**
|
|
5218
5608
|
* Fetches all style JSONs from a STAC Item and returns an array with style objects
|
|
5219
5609
|
* @param {import("stac-ts").StacItem | import("stac-ts").StacCollection} stacObject
|
|
@@ -5224,6 +5614,7 @@ const fetchAllStyles = async (stacObject) => {
|
|
|
5224
5614
|
link.rel.includes("style"),
|
|
5225
5615
|
);
|
|
5226
5616
|
const fetchPromises = styleLinks.map(async (link) => {
|
|
5617
|
+
/** @type {import("@/types").EodashStyleJson} */
|
|
5227
5618
|
const styleJson = await axios.get(link.href).then((resp) => resp.data);
|
|
5228
5619
|
log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
|
|
5229
5620
|
return styleJson;
|
|
@@ -5253,94 +5644,74 @@ const getProjectionCode = (projection) => {
|
|
|
5253
5644
|
};
|
|
5254
5645
|
|
|
5255
5646
|
/**
|
|
5256
|
-
*
|
|
5257
|
-
*
|
|
5258
|
-
* @param {
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
// check if items has a datetime value
|
|
5266
|
-
const dateProperty = getDatetimeProperty(items);
|
|
5267
|
-
|
|
5268
|
-
if (!dateProperty) {
|
|
5647
|
+
* Builds layercontrol LayerDatetime + timeControlValues from a list of dates.
|
|
5648
|
+
*
|
|
5649
|
+
* @param {Date[] | undefined} dates
|
|
5650
|
+
* @param {string | null} [currentStep] - target datetime; snapped to the closest available date
|
|
5651
|
+
* @returns {{ layerDatetime: Record<string, any> | undefined, timeControlValues: { date: string }[] | undefined }}
|
|
5652
|
+
*/
|
|
5653
|
+
const extractLayerTimeValues = (dates, currentStep) => {
|
|
5654
|
+
if (!currentStep || !dates?.length || dates.length <= 1) {
|
|
5269
5655
|
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
5270
5656
|
}
|
|
5271
|
-
/** @type {{date:string;itemId:string}[]} */
|
|
5272
|
-
const timeValues = [];
|
|
5273
|
-
try {
|
|
5274
|
-
/**
|
|
5275
|
-
* @param {typeof timeValues} vals
|
|
5276
|
-
* @param {import("stac-ts").StacLink} link
|
|
5277
|
-
*/
|
|
5278
|
-
const reduceLinks = (vals, link) => {
|
|
5279
|
-
if (link[dateProperty] && link.rel === "item") {
|
|
5280
|
-
vals.push({
|
|
5281
|
-
itemId: /** @type {string} */ (link.id),
|
|
5282
|
-
date: new Date(
|
|
5283
|
-
/** @type {string} */ (link[dateProperty]),
|
|
5284
|
-
).toISOString(),
|
|
5285
|
-
});
|
|
5286
|
-
}
|
|
5287
|
-
return vals;
|
|
5288
|
-
};
|
|
5289
5657
|
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
* @param {typeof timeValues} vals
|
|
5293
|
-
* @param {import("stac-ts").StacItem} item
|
|
5294
|
-
*/
|
|
5295
|
-
const reduceItems = (vals, item) => {
|
|
5296
|
-
const date = item.properties?.[dateProperty];
|
|
5297
|
-
if (date) {
|
|
5298
|
-
vals.push({
|
|
5299
|
-
itemId: /** @type {string} */ (item.id),
|
|
5300
|
-
date: new Date(/** @type {string} */ (date)).toISOString(),
|
|
5301
|
-
...item.properties,
|
|
5302
|
-
});
|
|
5303
|
-
}
|
|
5304
|
-
return vals;
|
|
5305
|
-
};
|
|
5306
|
-
currentStep = new Date(currentStep).toISOString();
|
|
5307
|
-
//@ts-expect-error TODO
|
|
5308
|
-
items.reduce(isSTACItem(items[0]) ? reduceItems : reduceLinks, timeValues);
|
|
5309
|
-
} catch (e) {
|
|
5310
|
-
console.warn("[eodash] not supported datetime format was provided", e);
|
|
5311
|
-
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
5312
|
-
}
|
|
5313
|
-
// not enough timeValues
|
|
5314
|
-
if (timeValues.length <= 1) {
|
|
5315
|
-
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
5316
|
-
}
|
|
5658
|
+
const controlValues = dates.map((d) => d.toISOString()).sort();
|
|
5659
|
+
const timeControlValues = controlValues.map((date) => ({ date }));
|
|
5317
5660
|
|
|
5318
|
-
|
|
5319
|
-
if (!
|
|
5320
|
-
const
|
|
5321
|
-
currentStep =
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5661
|
+
currentStep = new Date(currentStep).toISOString();
|
|
5662
|
+
if (!controlValues.includes(currentStep)) {
|
|
5663
|
+
const target = new Date(currentStep).getTime();
|
|
5664
|
+
currentStep = controlValues.reduce((best, d) =>
|
|
5665
|
+
Math.abs(new Date(d).getTime() - target) <
|
|
5666
|
+
Math.abs(new Date(best).getTime() - target)
|
|
5667
|
+
? d
|
|
5668
|
+
: best,
|
|
5669
|
+
);
|
|
5326
5670
|
}
|
|
5327
5671
|
|
|
5328
5672
|
const layerDatetime = {
|
|
5329
|
-
controlValues
|
|
5673
|
+
controlValues,
|
|
5330
5674
|
currentStep,
|
|
5331
5675
|
slider: true,
|
|
5332
5676
|
navigation: true,
|
|
5333
5677
|
play: false,
|
|
5334
5678
|
displayFormat: "DD.MM.YYYY HH:mm",
|
|
5335
5679
|
animateOnClickInterval: false,
|
|
5680
|
+
showUTC: true,
|
|
5336
5681
|
};
|
|
5337
5682
|
|
|
5338
|
-
return {
|
|
5339
|
-
layerDatetime,
|
|
5340
|
-
timeControlValues: timeValues,
|
|
5341
|
-
};
|
|
5683
|
+
return { layerDatetime, timeControlValues };
|
|
5342
5684
|
};
|
|
5343
5685
|
|
|
5686
|
+
/**
|
|
5687
|
+
* Fetches the daily pre-aggregation data for a STAC collection if available.
|
|
5688
|
+
* Returns the raw AggregationCollection,
|
|
5689
|
+
* or null if no daily pre-aggregation link exists, or the fetch fails.
|
|
5690
|
+
*
|
|
5691
|
+
* @param {import("stac-ts").StacCollection | undefined} stacCollection
|
|
5692
|
+
* @param {string} fallbackBaseUrl - base for relative href resolution when no `self` link is present
|
|
5693
|
+
* @returns {Promise<any | null>}
|
|
5694
|
+
*/
|
|
5695
|
+
async function fetchPreAggregations(stacCollection, fallbackBaseUrl) {
|
|
5696
|
+
if (!stacCollection) return null;
|
|
5697
|
+
const preAggregationLink = stacCollection.links?.find(
|
|
5698
|
+
(l) => l.rel === "pre-aggregation" && l["aggregation:interval"] === "daily",
|
|
5699
|
+
);
|
|
5700
|
+
if (!preAggregationLink) return null;
|
|
5701
|
+
|
|
5702
|
+
try {
|
|
5703
|
+
const selfLink = stacCollection.links?.find((l) => l.rel === "self")?.href;
|
|
5704
|
+
const url = toAbsolute(
|
|
5705
|
+
preAggregationLink.href,
|
|
5706
|
+
selfLink || fallbackBaseUrl,
|
|
5707
|
+
);
|
|
5708
|
+
return await axios.get(url).then((resp) => resp.data);
|
|
5709
|
+
} catch (e) {
|
|
5710
|
+
console.warn("[eodash] Failed to fetch pre-aggregation", e);
|
|
5711
|
+
return null;
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
|
|
5344
5715
|
/**
|
|
5345
5716
|
* Recursively find all layers whose ID up to the first ; is same as given layer
|
|
5346
5717
|
*
|
|
@@ -5349,6 +5720,9 @@ const extractLayerTimeValues = (items, currentStep) => {
|
|
|
5349
5720
|
* @returns {import("@eox/map").EoxLayer[]} Matching layer objects.
|
|
5350
5721
|
*/
|
|
5351
5722
|
const findLayersByLayerPrefix = (layers, referenceLayer) => {
|
|
5723
|
+
if (!layers || !referenceLayer) {
|
|
5724
|
+
return [];
|
|
5725
|
+
}
|
|
5352
5726
|
const refId = referenceLayer?.properties?.id;
|
|
5353
5727
|
|
|
5354
5728
|
if (typeof refId !== "string" || !refId.includes(";:;")) {
|
|
@@ -5535,20 +5909,147 @@ const createAssetID = (collectionId, itemId, index) => {
|
|
|
5535
5909
|
*
|
|
5536
5910
|
* @param {Record<string,any>[]} layers
|
|
5537
5911
|
*/
|
|
5538
|
-
const removeUnneededProperties = (layers) => {
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5912
|
+
const removeUnneededProperties = (layers, formValues = {}) => {
|
|
5913
|
+
/**
|
|
5914
|
+
* @param {Record<string,any>} layer
|
|
5915
|
+
* @returns {Record<string,any>[]}
|
|
5916
|
+
*/
|
|
5917
|
+
const processLayer = (layer) => {
|
|
5918
|
+
// If the layer (or group) is explicitly marked as not visible, skip it and all children
|
|
5919
|
+
if (layer.properties?.visible === false) {
|
|
5920
|
+
return [];
|
|
5921
|
+
}
|
|
5922
|
+
|
|
5923
|
+
// If it's a Group, we just want its children
|
|
5924
|
+
if (layer.type === "Group" && Array.isArray(layer.layers)) {
|
|
5925
|
+
return layer.layers.flatMap(processLayer);
|
|
5546
5926
|
}
|
|
5547
|
-
|
|
5548
|
-
|
|
5927
|
+
|
|
5928
|
+
// Break any Vue Proxies/OpenLayers getters by stringifying first
|
|
5929
|
+
let clonedLayer;
|
|
5930
|
+
try {
|
|
5931
|
+
clonedLayer = JSON.parse(JSON.stringify(layer));
|
|
5932
|
+
} catch (_e) {
|
|
5933
|
+
clonedLayer = structuredClone(layer);
|
|
5549
5934
|
}
|
|
5550
|
-
|
|
5551
|
-
|
|
5935
|
+
|
|
5936
|
+
// Flatten formValues to handle nested properties (e.g., vminmax: { vmin, vmax })
|
|
5937
|
+
/**
|
|
5938
|
+
* @param {Record<string,any>} obj
|
|
5939
|
+
* @returns {Record<string,any>}
|
|
5940
|
+
*/
|
|
5941
|
+
const flattenFormValues = (obj) => {
|
|
5942
|
+
/** @type {Record<string,any>} */
|
|
5943
|
+
let result = {};
|
|
5944
|
+
for (const key in obj) {
|
|
5945
|
+
if (
|
|
5946
|
+
obj[key] !== null &&
|
|
5947
|
+
typeof obj[key] === "object" &&
|
|
5948
|
+
!Array.isArray(obj[key])
|
|
5949
|
+
) {
|
|
5950
|
+
Object.assign(result, flattenFormValues(obj[key]));
|
|
5951
|
+
} else {
|
|
5952
|
+
result[key] = obj[key];
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
return result;
|
|
5956
|
+
};
|
|
5957
|
+
const flatFormValues = flattenFormValues(formValues);
|
|
5958
|
+
|
|
5959
|
+
// Burn in style variables using Mustache if formValues are provided
|
|
5960
|
+
if (Object.keys(flatFormValues).length > 0) {
|
|
5961
|
+
// Stringify, render mustache, then parse back
|
|
5962
|
+
try {
|
|
5963
|
+
const renderedString = mustache.render(
|
|
5964
|
+
JSON.stringify(clonedLayer),
|
|
5965
|
+
flatFormValues,
|
|
5966
|
+
);
|
|
5967
|
+
clonedLayer = JSON.parse(renderedString);
|
|
5968
|
+
} catch (e) {
|
|
5969
|
+
log.warn(
|
|
5970
|
+
"[eodash] Failed to apply mustache templating during export cleanup:",
|
|
5971
|
+
e,
|
|
5972
|
+
);
|
|
5973
|
+
}
|
|
5974
|
+
}
|
|
5975
|
+
|
|
5976
|
+
const {
|
|
5977
|
+
id,
|
|
5978
|
+
title,
|
|
5979
|
+
mapboxStyle,
|
|
5980
|
+
projection,
|
|
5981
|
+
applyOptions,
|
|
5982
|
+
layerConfig,
|
|
5983
|
+
visible,
|
|
5984
|
+
} = clonedLayer.properties || {};
|
|
5985
|
+
|
|
5986
|
+
// If style was not at root but in properties (layerConfig), move it to root early
|
|
5987
|
+
if (!clonedLayer.style && layerConfig?.style) {
|
|
5988
|
+
clonedLayer.style = layerConfig.style;
|
|
5989
|
+
}
|
|
5990
|
+
|
|
5991
|
+
// Burn in OpenLayers ["var", "name"] variables using flatFormValues overriding style.variables
|
|
5992
|
+
const styleVariables = {
|
|
5993
|
+
...(clonedLayer.style?.variables || {}),
|
|
5994
|
+
...flatFormValues,
|
|
5995
|
+
};
|
|
5996
|
+
|
|
5997
|
+
if (clonedLayer.style && Object.keys(styleVariables).length > 0) {
|
|
5998
|
+
clonedLayer.style = updateVectorLayerStyle({
|
|
5999
|
+
...clonedLayer.style,
|
|
6000
|
+
variables: styleVariables,
|
|
6001
|
+
});
|
|
6002
|
+
}
|
|
6003
|
+
|
|
6004
|
+
clonedLayer.properties = {
|
|
6005
|
+
id,
|
|
6006
|
+
title,
|
|
6007
|
+
...(mapboxStyle && { mapboxStyle }),
|
|
6008
|
+
...(projection && { projection }),
|
|
6009
|
+
...(applyOptions && { applyOptions }),
|
|
6010
|
+
...(visible !== undefined && { visible }),
|
|
6011
|
+
};
|
|
6012
|
+
|
|
6013
|
+
if (clonedLayer["interactions"]) {
|
|
6014
|
+
delete clonedLayer["interactions"];
|
|
6015
|
+
}
|
|
6016
|
+
|
|
6017
|
+
// Cleanup unnecessary properties
|
|
6018
|
+
/**
|
|
6019
|
+
* @param {any} obj
|
|
6020
|
+
*/
|
|
6021
|
+
const cleanupProperties = (obj) => {
|
|
6022
|
+
if (!obj || typeof obj !== "object") return;
|
|
6023
|
+
|
|
6024
|
+
for (const key in obj) {
|
|
6025
|
+
if (obj[key] === null) {
|
|
6026
|
+
delete obj[key];
|
|
6027
|
+
} else if (Array.isArray(obj[key])) {
|
|
6028
|
+
if (obj[key].length === 0) {
|
|
6029
|
+
// Remove empty arrays like empty interactions
|
|
6030
|
+
delete obj[key];
|
|
6031
|
+
} else {
|
|
6032
|
+
obj[key].forEach(cleanupProperties);
|
|
6033
|
+
}
|
|
6034
|
+
} else if (typeof obj[key] === "object") {
|
|
6035
|
+
cleanupProperties(obj[key]);
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
};
|
|
6039
|
+
cleanupProperties(clonedLayer);
|
|
6040
|
+
|
|
6041
|
+
return [clonedLayer];
|
|
6042
|
+
};
|
|
6043
|
+
|
|
6044
|
+
// ensure the top level array is also deeply un-proxied if needed
|
|
6045
|
+
let rawLayers = layers;
|
|
6046
|
+
try {
|
|
6047
|
+
rawLayers = JSON.parse(JSON.stringify(layers));
|
|
6048
|
+
} catch (_e) {
|
|
6049
|
+
rawLayers = structuredClone(layers);
|
|
6050
|
+
}
|
|
6051
|
+
|
|
6052
|
+
return rawLayers.flatMap(processLayer);
|
|
5552
6053
|
};
|
|
5553
6054
|
|
|
5554
6055
|
/**
|
|
@@ -5708,7 +6209,12 @@ function getDatetimeProperty(linksOrItems) {
|
|
|
5708
6209
|
}
|
|
5709
6210
|
|
|
5710
6211
|
// TODO: consider other properties for datetime ranges
|
|
5711
|
-
|
|
6212
|
+
|
|
6213
|
+
const datetimeProperties = /** @type {const} */ ([
|
|
6214
|
+
"datetime",
|
|
6215
|
+
"start_datetime",
|
|
6216
|
+
"end_datetime",
|
|
6217
|
+
]);
|
|
5712
6218
|
if (checkProperties) {
|
|
5713
6219
|
for (const prop of datetimeProperties) {
|
|
5714
6220
|
const propExists = linksOrItems.some(
|
|
@@ -5734,6 +6240,7 @@ function getDatetimeProperty(linksOrItems) {
|
|
|
5734
6240
|
return prop;
|
|
5735
6241
|
}
|
|
5736
6242
|
}
|
|
6243
|
+
|
|
5737
6244
|
/**
|
|
5738
6245
|
*
|
|
5739
6246
|
* @param {*} stacObject
|
|
@@ -5750,6 +6257,65 @@ function isSTACItem(stacObject) {
|
|
|
5750
6257
|
);
|
|
5751
6258
|
}
|
|
5752
6259
|
|
|
6260
|
+
/**
|
|
6261
|
+
* Fetches items using a split strategy (past/future) around a center date
|
|
6262
|
+
* @param {string} itemsUrl
|
|
6263
|
+
* @param {string} query
|
|
6264
|
+
* @param {string | Date} centerDatetime
|
|
6265
|
+
* @param {number} maxNumber
|
|
6266
|
+
* @returns {Promise<import("stac-ts").StacItem[]>}
|
|
6267
|
+
*/
|
|
6268
|
+
const fetchSplitItems = async (
|
|
6269
|
+
itemsUrl,
|
|
6270
|
+
query,
|
|
6271
|
+
centerDatetime,
|
|
6272
|
+
maxNumber,
|
|
6273
|
+
) => {
|
|
6274
|
+
const center = new Date(centerDatetime).toISOString();
|
|
6275
|
+
const limit = Math.ceil(maxNumber / 2);
|
|
6276
|
+
|
|
6277
|
+
/** @param {"past"|"future"} direction */
|
|
6278
|
+
const fetchSide = async (direction) => {
|
|
6279
|
+
const isPast = direction === "past";
|
|
6280
|
+
const datetimeRange = isPast ? `../${center}` : `${center}/..`;
|
|
6281
|
+
const sortOrder = isPast ? "-datetime" : "+datetime";
|
|
6282
|
+
const queryParams = new URLSearchParams(query);
|
|
6283
|
+
|
|
6284
|
+
queryParams.set("limit", limit.toString());
|
|
6285
|
+
queryParams.set("datetime", datetimeRange);
|
|
6286
|
+
queryParams.set("sortby", sortOrder);
|
|
6287
|
+
|
|
6288
|
+
const splitUrl = itemsUrl.split("/");
|
|
6289
|
+
const collectionId = /** @type {string} */ (splitUrl.at(-2));
|
|
6290
|
+
queryParams.set("collection", collectionId);
|
|
6291
|
+
|
|
6292
|
+
const searchEndpoint = `${splitUrl.slice(0, -3).join("/")}/search`;
|
|
6293
|
+
const items = await axios
|
|
6294
|
+
.get(searchEndpoint, { params: queryParams })
|
|
6295
|
+
.then((res) => res.data.features);
|
|
6296
|
+
return items;
|
|
6297
|
+
};
|
|
6298
|
+
|
|
6299
|
+
const [pastItems, futureItems] = await Promise.all([
|
|
6300
|
+
fetchSide("past"),
|
|
6301
|
+
fetchSide("future"),
|
|
6302
|
+
]);
|
|
6303
|
+
|
|
6304
|
+
const allItems = [...pastItems, ...futureItems];
|
|
6305
|
+
|
|
6306
|
+
// check for duplicates by ids
|
|
6307
|
+
const seen = new Set();
|
|
6308
|
+
const uniqueItems = [];
|
|
6309
|
+
for (const item of allItems) {
|
|
6310
|
+
if (!seen.has(item.id)) {
|
|
6311
|
+
seen.add(item.id);
|
|
6312
|
+
uniqueItems.push(item);
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
return uniqueItems;
|
|
6317
|
+
};
|
|
6318
|
+
|
|
5753
6319
|
/**
|
|
5754
6320
|
* Fetch all STAC items from a STAC API endpoint.
|
|
5755
6321
|
* @param {string} itemsUrl
|
|
@@ -5757,6 +6323,8 @@ function isSTACItem(stacObject) {
|
|
|
5757
6323
|
* @param {number} [limit=100] - The maximum number of items to fetch per request.
|
|
5758
6324
|
* @param {boolean} [returnFirst] - If true, only the first page of results will be returned.
|
|
5759
6325
|
* @param {number} [maxNumber=1000] - if the matched number of items exceed this, only the first page will be returned.
|
|
6326
|
+
* @param {string | Date} [centerDatetime] - Date to center the search around if items exceed maxNumber
|
|
6327
|
+
* @returns {Promise<import("stac-ts").StacItem[]>}
|
|
5760
6328
|
*/
|
|
5761
6329
|
async function fetchApiItems(
|
|
5762
6330
|
itemsUrl,
|
|
@@ -5764,23 +6332,42 @@ async function fetchApiItems(
|
|
|
5764
6332
|
limit = 100,
|
|
5765
6333
|
returnFirst = false,
|
|
5766
6334
|
maxNumber = 1000,
|
|
6335
|
+
centerDatetime,
|
|
5767
6336
|
) {
|
|
5768
|
-
|
|
5769
|
-
|
|
6337
|
+
// Exclude centerDatetime from cache key - it's only used for split search fallback
|
|
6338
|
+
const cacheKey = JSON.stringify({
|
|
6339
|
+
itemsUrl,
|
|
6340
|
+
query,
|
|
6341
|
+
limit,
|
|
6342
|
+
returnFirst,
|
|
6343
|
+
maxNumber,
|
|
6344
|
+
});
|
|
6345
|
+
|
|
6346
|
+
if (itemsCache.has(cacheKey)) {
|
|
6347
|
+
return itemsCache.get(cacheKey) ?? [];
|
|
6348
|
+
}
|
|
6349
|
+
|
|
6350
|
+
const urlQuery = new URLSearchParams(query);
|
|
6351
|
+
urlQuery.set("limit", limit.toString());
|
|
6352
|
+
|
|
6353
|
+
let finalItemsUrl = itemsUrl.includes("?")
|
|
6354
|
+
? itemsUrl.split("?")[0]
|
|
6355
|
+
: itemsUrl;
|
|
6356
|
+
finalItemsUrl += urlQuery.keys().toArray().length
|
|
6357
|
+
? `?${urlQuery.toString()}`
|
|
6358
|
+
: "";
|
|
5770
6359
|
|
|
5771
6360
|
const itemsFeatureCollection = await axios
|
|
5772
|
-
.get(
|
|
6361
|
+
.get(finalItemsUrl)
|
|
5773
6362
|
.then((resp) => resp.data);
|
|
6363
|
+
|
|
5774
6364
|
/** @type {import("stac-ts").StacItem[]} */
|
|
5775
6365
|
const items = itemsFeatureCollection.features;
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
(link) => link.rel === "next",
|
|
5779
|
-
);
|
|
5780
|
-
|
|
5781
|
-
if (!nextLink || returnFirst) {
|
|
6366
|
+
if (returnFirst) {
|
|
6367
|
+
itemsCache.set(cacheKey, items);
|
|
5782
6368
|
return items;
|
|
5783
6369
|
}
|
|
6370
|
+
|
|
5784
6371
|
/** @type {number} */
|
|
5785
6372
|
const matchedItems = itemsFeatureCollection.numberMatched;
|
|
5786
6373
|
// Avoid fetching too many items
|
|
@@ -5788,26 +6375,70 @@ async function fetchApiItems(
|
|
|
5788
6375
|
console.warn(
|
|
5789
6376
|
`[eodash] The number of items matched (${matchedItems}) exceeds the maximum allowed (${maxNumber})`,
|
|
5790
6377
|
);
|
|
5791
|
-
|
|
5792
|
-
|
|
6378
|
+
// we try to narrow down the search around the center datetime
|
|
6379
|
+
if (centerDatetime) {
|
|
6380
|
+
// Check if we have a cached split result that covers this centerDatetime
|
|
6381
|
+
const splitCacheKey = JSON.stringify({ itemsUrl, query, maxNumber });
|
|
6382
|
+
const cachedSplit = splitItemsCache.get(splitCacheKey);
|
|
6383
|
+
const centerTime = new Date(centerDatetime).getTime();
|
|
5793
6384
|
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6385
|
+
if (
|
|
6386
|
+
cachedSplit &&
|
|
6387
|
+
centerTime >= cachedSplit.minTime &&
|
|
6388
|
+
centerTime <= cachedSplit.maxTime
|
|
6389
|
+
) {
|
|
6390
|
+
return cachedSplit.items;
|
|
6391
|
+
}
|
|
5799
6392
|
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
6393
|
+
const narrowedItems = await fetchSplitItems(
|
|
6394
|
+
itemsUrl,
|
|
6395
|
+
query ?? "",
|
|
6396
|
+
centerDatetime,
|
|
6397
|
+
maxNumber,
|
|
6398
|
+
);
|
|
6399
|
+
|
|
6400
|
+
if (!narrowedItems.length) {
|
|
6401
|
+
return narrowedItems;
|
|
6402
|
+
}
|
|
6403
|
+
const datetimeProperty = getDatetimeProperty(narrowedItems);
|
|
6404
|
+
if (!datetimeProperty) {
|
|
6405
|
+
return narrowedItems;
|
|
6406
|
+
}
|
|
6407
|
+
|
|
6408
|
+
const times = narrowedItems
|
|
6409
|
+
.map((i) =>
|
|
6410
|
+
i.properties[datetimeProperty]
|
|
6411
|
+
? new Date(i.properties[datetimeProperty]).getTime()
|
|
6412
|
+
: null,
|
|
6413
|
+
)
|
|
6414
|
+
.filter((t) => t !== null);
|
|
6415
|
+
if (!times.length) {
|
|
6416
|
+
return narrowedItems;
|
|
6417
|
+
}
|
|
6418
|
+
splitItemsCache.set(splitCacheKey, {
|
|
6419
|
+
items: narrowedItems,
|
|
6420
|
+
minTime: Math.min(...times),
|
|
6421
|
+
maxTime: Math.max(...times),
|
|
6422
|
+
});
|
|
6423
|
+
|
|
6424
|
+
return narrowedItems;
|
|
5806
6425
|
}
|
|
6426
|
+
|
|
6427
|
+
itemsCache.set(cacheKey, items);
|
|
6428
|
+
return items;
|
|
5807
6429
|
}
|
|
5808
6430
|
|
|
5809
|
-
|
|
5810
|
-
|
|
6431
|
+
urlQuery.set("limit", maxNumber.toString());
|
|
6432
|
+
|
|
6433
|
+
const allItems = await axios
|
|
6434
|
+
.get(itemsUrl + "?" + urlQuery.toString())
|
|
6435
|
+
.then((resp) => resp.data.features)
|
|
6436
|
+
.catch((err) => {
|
|
6437
|
+
console.error(err);
|
|
6438
|
+
return [];
|
|
6439
|
+
});
|
|
6440
|
+
items.splice(0, items.length, ...allItems);
|
|
6441
|
+
itemsCache.set(cacheKey, items);
|
|
5811
6442
|
return items;
|
|
5812
6443
|
}
|
|
5813
6444
|
/**
|
|
@@ -5846,4 +6477,273 @@ function extractEoxLegendLink(link) {
|
|
|
5846
6477
|
return extraProperties;
|
|
5847
6478
|
}
|
|
5848
6479
|
|
|
5849
|
-
|
|
6480
|
+
/**
|
|
6481
|
+
* Locate the first sub-schema whose `format` matches by walking `properties`
|
|
6482
|
+
* and the `oneOf` / `allOf` / `anyOf` combinators. Returns the schema path
|
|
6483
|
+
* (array of keys/indices) from the root schema to the matched node, or
|
|
6484
|
+
* undefined if not found. Empty array means the input schema itself matched.
|
|
6485
|
+
*
|
|
6486
|
+
* @param {Record<string, any> | null | undefined} schema
|
|
6487
|
+
* @param {string} [format="bands"]
|
|
6488
|
+
* @returns {(string | number)[] | undefined}
|
|
6489
|
+
*/
|
|
6490
|
+
function getBandsProperty(schema, format = "bands") {
|
|
6491
|
+
if (!schema || typeof schema !== "object") return undefined;
|
|
6492
|
+
if (schema.format === format) return [];
|
|
6493
|
+
|
|
6494
|
+
if (schema.properties) {
|
|
6495
|
+
for (const key of Object.keys(schema.properties)) {
|
|
6496
|
+
const sub = getBandsProperty(schema.properties[key], format);
|
|
6497
|
+
if (sub) return ["properties", key, ...sub];
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6501
|
+
for (const combinator of ["oneOf", "allOf", "anyOf"]) {
|
|
6502
|
+
if (!Array.isArray(schema[combinator])) continue;
|
|
6503
|
+
for (let i = 0; i < schema[combinator].length; i++) {
|
|
6504
|
+
const sub = getBandsProperty(schema[combinator][i], format);
|
|
6505
|
+
if (sub) return [combinator, i, ...sub];
|
|
6506
|
+
}
|
|
6507
|
+
}
|
|
6508
|
+
|
|
6509
|
+
return undefined;
|
|
6510
|
+
}
|
|
6511
|
+
|
|
6512
|
+
/**
|
|
6513
|
+
* Checks whether a GeoZarr layer's bands changed in the jsonform output and,
|
|
6514
|
+
* if so, rebuilds the source with the new 3 selected bands.
|
|
6515
|
+
* Gamma and rescale are handled automatically by `applyUpdatedStyles` via
|
|
6516
|
+
* `updateStyleVariables` — this function only manages source reconstruction.
|
|
6517
|
+
* Uses the existing source constructor to avoid import-version mismatches.
|
|
6518
|
+
*
|
|
6519
|
+
* @param {import("ol/layer/Layer").default} olLayer - Layer from layerConfig:change event
|
|
6520
|
+
* @param {Record<string, any>} jsonformValue - Current jsonform output
|
|
6521
|
+
* @returns {boolean} true if the source was rebuilt
|
|
6522
|
+
*/
|
|
6523
|
+
function updateGeoZarrBands(olLayer, jsonformValue) {
|
|
6524
|
+
/** @type {import("@eox/map/src/layers").EOxLayerType<"WebGLTile","GeoZarr">} */
|
|
6525
|
+
const jsonLayer = olLayer.get("_jsonDefinition");
|
|
6526
|
+
const updatedBands = jsonformValue.bands;
|
|
6527
|
+
const isGeoZarr =
|
|
6528
|
+
jsonLayer?.type === "WebGLTile" && jsonLayer?.source?.type === "GeoZarr";
|
|
6529
|
+
if (!jsonLayer || !jsonLayer.source || !isGeoZarr || !updatedBands) {
|
|
6530
|
+
return false;
|
|
6531
|
+
}
|
|
6532
|
+
|
|
6533
|
+
const oldBands = jsonLayer.source?.bands;
|
|
6534
|
+
if (JSON.stringify(updatedBands) === JSON.stringify(oldBands)) {
|
|
6535
|
+
return false;
|
|
6536
|
+
}
|
|
6537
|
+
jsonLayer.source.bands = [...updatedBands];
|
|
6538
|
+
olLayer.setSource(
|
|
6539
|
+
new window.eoxMapAdvancedOlSources.GeoZarr(jsonLayer.source),
|
|
6540
|
+
);
|
|
6541
|
+
return true;
|
|
6542
|
+
}
|
|
6543
|
+
|
|
6544
|
+
/**
|
|
6545
|
+
* Safely appends query parameters to a URL string, preserving templates like {z}/{x}/{y}
|
|
6546
|
+
* @param {string} url
|
|
6547
|
+
* @param {Record<string, string>} params
|
|
6548
|
+
* @returns {string}
|
|
6549
|
+
*/
|
|
6550
|
+
function appendQueryParams(url, params) {
|
|
6551
|
+
const [base, query] = url.split("?");
|
|
6552
|
+
const searchParams = new URLSearchParams(query || "");
|
|
6553
|
+
|
|
6554
|
+
for (const [key, val] of Object.entries(params)) {
|
|
6555
|
+
if (val !== undefined && val !== null && val !== "") {
|
|
6556
|
+
searchParams.set(key, val);
|
|
6557
|
+
} else {
|
|
6558
|
+
searchParams.delete(key);
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6562
|
+
const newQuery = searchParams.toString();
|
|
6563
|
+
return newQuery ? `${base}?${newQuery}` : base;
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6566
|
+
/**
|
|
6567
|
+
* Checks whether a VectorTile layer's URL needs to be updated based on jsonform output.
|
|
6568
|
+
* If the style's jsonform schema has properties with `url_key` defined, their values
|
|
6569
|
+
* are injected as query parameters into the source URL.
|
|
6570
|
+
*
|
|
6571
|
+
* @param {import("ol/layer/Layer").default} olLayer - Layer from layerConfig:change event
|
|
6572
|
+
* @param {Record<string, any>} jsonformValue - Current jsonform output
|
|
6573
|
+
* @returns {boolean} true if the URL was updated
|
|
6574
|
+
*/
|
|
6575
|
+
function updateLayerUrl(olLayer, jsonformValue) {
|
|
6576
|
+
const jsonLayer = olLayer.get("_jsonDefinition");
|
|
6577
|
+
if (!jsonLayer || jsonLayer.type !== "VectorTile") {
|
|
6578
|
+
return false;
|
|
6579
|
+
}
|
|
6580
|
+
|
|
6581
|
+
const schema = jsonLayer.properties?.layerConfig?.schema;
|
|
6582
|
+
const urlKeys = extractUrlKeys(schema);
|
|
6583
|
+
|
|
6584
|
+
if (Object.keys(urlKeys).length === 0) {
|
|
6585
|
+
return false;
|
|
6586
|
+
}
|
|
6587
|
+
|
|
6588
|
+
let originalUrl = olLayer.get("originalUrl") || jsonLayer.source?.url;
|
|
6589
|
+
|
|
6590
|
+
if (!originalUrl || typeof originalUrl !== "string") {
|
|
6591
|
+
return false;
|
|
6592
|
+
}
|
|
6593
|
+
|
|
6594
|
+
if (!olLayer.get("originalUrl")) {
|
|
6595
|
+
olLayer.set("originalUrl", originalUrl);
|
|
6596
|
+
}
|
|
6597
|
+
|
|
6598
|
+
/** @type {Record<string, string>} */
|
|
6599
|
+
const queryParamsToInject = {};
|
|
6600
|
+
for (const [propName, urlKey] of Object.entries(urlKeys)) {
|
|
6601
|
+
queryParamsToInject[urlKey] = jsonformValue[propName];
|
|
6602
|
+
}
|
|
6603
|
+
|
|
6604
|
+
const newUrl = appendQueryParams(originalUrl, queryParamsToInject);
|
|
6605
|
+
|
|
6606
|
+
if (jsonLayer.source?.url) {
|
|
6607
|
+
if (jsonLayer.source.url === newUrl) {
|
|
6608
|
+
return false;
|
|
6609
|
+
}
|
|
6610
|
+
jsonLayer.source.url = newUrl;
|
|
6611
|
+
if (olLayer.get("injectedUrl") === newUrl) {
|
|
6612
|
+
return false;
|
|
6613
|
+
}
|
|
6614
|
+
const source = olLayer.getSource();
|
|
6615
|
+
olLayer.set("injectedUrl", newUrl);
|
|
6616
|
+
if (source && "setUrl" in source) {
|
|
6617
|
+
/** @type {any} */ (source).setUrl(newUrl);
|
|
6618
|
+
return true;
|
|
6619
|
+
}
|
|
6620
|
+
if (source && "setUrls" in source) {
|
|
6621
|
+
/** @type {any} */ (source).setUrls([newUrl]);
|
|
6622
|
+
return true;
|
|
6623
|
+
}
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6626
|
+
return false;
|
|
6627
|
+
}
|
|
6628
|
+
|
|
6629
|
+
/**
|
|
6630
|
+
* Applies titiler upscaling to an XYZ tile URL based on the matched endpoint config.
|
|
6631
|
+
* - titiler v1: appends `@2x` to the `{y}` tile coordinate
|
|
6632
|
+
* - titiler v2: adds `tilesize=512` query parameter (v2 removed the `@2x` suffix)
|
|
6633
|
+
* Plain strings in the config default to v1 behavior for backward compatibility.
|
|
6634
|
+
*
|
|
6635
|
+
* @param {string} url - The XYZ tile URL template
|
|
6636
|
+
* @param {Array<string | { url: string; titilerVersion?: 1 | 2 }>} upscalingEndpoints
|
|
6637
|
+
* @returns {{ url: string; tileSize: [number, number] } | null} null if no endpoint matches
|
|
6638
|
+
*/
|
|
6639
|
+
function applyTitilerUpscaling(url, upscalingEndpoints) {
|
|
6640
|
+
const match = upscalingEndpoints.find((entry) => {
|
|
6641
|
+
const endpointUrl = typeof entry === "string" ? entry : entry.url;
|
|
6642
|
+
return url.includes(endpointUrl);
|
|
6643
|
+
});
|
|
6644
|
+
|
|
6645
|
+
if (!match) {
|
|
6646
|
+
return null;
|
|
6647
|
+
}
|
|
6648
|
+
|
|
6649
|
+
const version = typeof match === "string" ? 1 : (match.titilerVersion ?? 1);
|
|
6650
|
+
|
|
6651
|
+
if (version === 2) {
|
|
6652
|
+
const [base, query] = url.split("?");
|
|
6653
|
+
const params = new URLSearchParams(query);
|
|
6654
|
+
params.set("tilesize", "512");
|
|
6655
|
+
return { url: `${base}?${params.toString()}`, tileSize: [512, 512] };
|
|
6656
|
+
}
|
|
6657
|
+
|
|
6658
|
+
return { url: url.replace("{y}", "{y}@2x"), tileSize: [512, 512] };
|
|
6659
|
+
}
|
|
6660
|
+
|
|
6661
|
+
/**
|
|
6662
|
+
* Picks the render presets for a collection, preferring client-provided config
|
|
6663
|
+
* (`options.renders[collectionId]`) and falling back to the collection's own STAC
|
|
6664
|
+
* `renders` extension when no config entry exists.
|
|
6665
|
+
* @param {import("stac-ts").StacCollection | null | undefined} collection
|
|
6666
|
+
* @param {Record<string, Record<string, import("@/types").Render>> | undefined} [configRenders]
|
|
6667
|
+
* @returns {Record<string, import("@/types").Render> | undefined}
|
|
6668
|
+
*/
|
|
6669
|
+
function resolveRenders(collection, configRenders) {
|
|
6670
|
+
const config = collection?.id ? configRenders?.[collection.id] : undefined;
|
|
6671
|
+
if (config) return config;
|
|
6672
|
+
return /** @type {Record<string, import("@/types").Render>|undefined} */ (
|
|
6673
|
+
collection?.renders ?? undefined
|
|
6674
|
+
);
|
|
6675
|
+
}
|
|
6676
|
+
|
|
6677
|
+
/**
|
|
6678
|
+
* TiTiler expects rescale as [min,max] pairs; chunks a flat numeric list
|
|
6679
|
+
* into pairs (e.g. [0,0.4,0,0.1] -> [[0,0.4],[0,0.1]]). Nested input passes through.
|
|
6680
|
+
* @param {number[]|number[][]|undefined} rescale - flat or nested rescale values
|
|
6681
|
+
* @returns {number[][]|undefined} rescale as [min,max] pairs
|
|
6682
|
+
*/
|
|
6683
|
+
function normalizeRescale(rescale) {
|
|
6684
|
+
if (!rescale?.length || Array.isArray(rescale[0])) {
|
|
6685
|
+
return /** @type {number[][]|undefined} */ (rescale);
|
|
6686
|
+
}
|
|
6687
|
+
const pairs = [];
|
|
6688
|
+
for (let i = 0; i < rescale.length; i += 2) {
|
|
6689
|
+
pairs.push(/** @type {number[]} */ (rescale).slice(i, i + 2));
|
|
6690
|
+
}
|
|
6691
|
+
return pairs;
|
|
6692
|
+
}
|
|
6693
|
+
|
|
6694
|
+
/**
|
|
6695
|
+
* Drops NaN nodata values; NaN is already the implicit fill for float data,
|
|
6696
|
+
* so forwarding `nodata=nan` to TiTiler is redundant.
|
|
6697
|
+
* @param {string|number|undefined} nodata - nodata value from render/asset metadata
|
|
6698
|
+
* @returns {string|number|undefined} nodata, or undefined when it is NaN
|
|
6699
|
+
*/
|
|
6700
|
+
function normalizeNodata(nodata) {
|
|
6701
|
+
if (typeof nodata === "number" && Number.isNaN(nodata)) return undefined;
|
|
6702
|
+
if (typeof nodata === "string" && nodata.trim().toLowerCase() === "nan")
|
|
6703
|
+
return undefined;
|
|
6704
|
+
return nodata;
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
/**
|
|
6708
|
+
* Serializes an object into a TiTiler query string. Arrays repeat the key per
|
|
6709
|
+
* element (TiTiler list params, e.g. `assets=a&assets=b`); nested elements
|
|
6710
|
+
* comma-join (`rescale: [[0,1],[0,2]]` -> `rescale=0,1&rescale=0,2`); objects
|
|
6711
|
+
* are JSON-encoded. Shared by the render-extension and mosaic paths.
|
|
6712
|
+
* @param {Record<string,any>} obj
|
|
6713
|
+
* @returns {string}
|
|
6714
|
+
*/
|
|
6715
|
+
function encodeURLObject(obj) {
|
|
6716
|
+
let str = "";
|
|
6717
|
+
for (const key in obj) {
|
|
6718
|
+
const value = obj[key];
|
|
6719
|
+
if (value === null || value === undefined || value === "") {
|
|
6720
|
+
continue;
|
|
6721
|
+
}
|
|
6722
|
+
|
|
6723
|
+
const valueType = Array.isArray(value) ? "array" : typeof value;
|
|
6724
|
+
|
|
6725
|
+
switch (valueType) {
|
|
6726
|
+
case "array": {
|
|
6727
|
+
for (const val of value) {
|
|
6728
|
+
if (Array.isArray(val)) {
|
|
6729
|
+
str += `${key}=${val.join(",")}&`;
|
|
6730
|
+
} else {
|
|
6731
|
+
str += `${key}=${encodeURIComponent(val)}&`;
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
break;
|
|
6735
|
+
}
|
|
6736
|
+
case "object": {
|
|
6737
|
+
str += `${key}=${encodeURI(JSON.stringify(value))}&`;
|
|
6738
|
+
break;
|
|
6739
|
+
}
|
|
6740
|
+
default: {
|
|
6741
|
+
str += `${key}=${encodeURIComponent(value)}&`;
|
|
6742
|
+
break;
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
return str;
|
|
6747
|
+
}
|
|
6748
|
+
|
|
6749
|
+
export { compareChartSpec as $, findLayer as A, findLayersByLayerPrefix as B, replaceLayer as C, generateFeatures as D, dataThemesBrands as E, getLayers as F, extractLayerLegend as G, extractCollectionUrls as H, generateLinksFromItems as I, revokeCollectionBlobUrls as J, collectionsPalette as K, datetime as L, poi as M, activeTemplate as N, mapPosition as O, isFirstLoad as P, indicator as Q, currentUrl as R, currentCompareUrl as S, eodashCompareCollections as T, compareIndicator as U, comparePoi as V, eodashCollections as W, states as X, actions as Y, areChartsSeparateLayout as Z, chartSpec as _, extractRoles as a, loading as a0, includesProcess as a1, shouldShowChartWidget as a2, sanitizeBbox as a3, tooltipAdapter as a4, mapCompareEl as a5, updateGeoZarrBands as a6, updateLayerUrl as a7, layerControlFormValueCompare as a8, layerControlFormValue as a9, getColFromLayer as aa, isGlobe as ab, setMapProjFromCol as ac, setActiveTemplate as ad, availableMapProjection as ae, changeMapProjection as af, getCompareLayers as ag, compareChartData as ah, chartData as ai, axios$1 as aj, removeLayers as ak, removeUnneededProperties as al, extractEoxLegendLink as b, createLayerID as c, applyTitilerUpscaling as d, extractLayerConfig as e, fetchRasterForm as f, axios as g, resolveStyle as h, addTooltipInteraction as i, createAssetID as j, getBandsProperty as k, resolveRenders as l, mergeGeojsons as m, normalizeRescale as n, normalizeNodata as o, encodeURLObject as p, mapEl as q, registerProjection as r, getProjectionCode as s, isSTACItem as t, extractLayerTimeValues as u, fetchStyle as v, fetchApiItems as w, getDatetimeProperty as x, fetchPreAggregations as y, fetchAllStyles as z };
|