@eodash/eodash 5.2.0 → 5.3.1

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.
Files changed (119) hide show
  1. package/core/client/components/DashboardLayout.vue +0 -1
  2. package/core/client/composables/index.js +53 -59
  3. package/core/client/eodashSTAC/EodashCollection.js +196 -94
  4. package/core/client/eodashSTAC/auth.js +86 -0
  5. package/core/client/eodashSTAC/createLayers.js +204 -4
  6. package/core/client/eodashSTAC/helpers.js +254 -62
  7. package/core/client/eodashSTAC/parquet.js +0 -13
  8. package/core/client/eodashSTAC/triggers.js +1 -1
  9. package/core/client/store/actions.js +14 -0
  10. package/core/client/store/stac.js +46 -8
  11. package/core/client/store/states.js +6 -0
  12. package/core/client/types.ts +206 -3
  13. package/core/client/utils/bands-editor/arithmetic.js +144 -0
  14. package/core/client/utils/bands-editor/colors.js +36 -0
  15. package/core/client/utils/bands-editor/dom.js +196 -0
  16. package/core/client/utils/bands-editor/exampleSchema.json +1320 -0
  17. package/core/client/utils/bands-editor/index.js +68 -0
  18. package/core/client/utils/bands-editor/rgb.js +102 -0
  19. package/core/client/utils/index.js +5 -2
  20. package/core/client/views/Dashboard.vue +1 -1
  21. package/core/client/vite-env.d.ts +122 -0
  22. package/dist/client/{DashboardLayout-Dq9Kfe6O.js → DashboardLayout-BAstYnhU.js} +4 -5
  23. package/dist/client/{DynamicWebComponent-DCBMXskE.js → DynamicWebComponent-7v4_DFqP.js} +1 -1
  24. package/dist/client/{EodashDatePicker-DtngxU6s.js → EodashDatePicker-IVHLv9UN.js} +20 -22
  25. package/dist/client/{EodashItemFilter-ClQebJQt.js → EodashItemFilter-BPMpnXjo.js} +46 -31
  26. package/dist/client/EodashLayerControl-CSnQh2tb.js +1517 -0
  27. package/dist/client/{EodashLayoutSwitcher-DQ8SfVDd.js → EodashLayoutSwitcher-CPpGM8Pb.js} +4 -4
  28. package/dist/client/EodashMapBtns-C_jyUJ2x.js +301 -0
  29. package/dist/client/{EodashStacInfo-Dt1nF06x.js → EodashStacInfo-DjuWc0Iz.js} +1 -1
  30. package/dist/client/EodashTimeSlider-CDh9Lf02.js +53 -0
  31. package/dist/client/{EodashTools-DV5ykmWc.js → EodashTools-DSvDUUlL.js} +10 -7
  32. package/dist/client/{ExportState-B6zZQUmE.js → ExportState-BhjxS0jG.js} +145 -120
  33. package/dist/client/{Footer-DNhXs8k6.js → Footer-C3PPcdjv.js} +1 -1
  34. package/dist/client/{Header-BjhN5JY4.js → Header-E5NbT7HE.js} +2 -2
  35. package/dist/client/MobileLayout-DY7OHr1k.js +118 -0
  36. package/dist/client/{PopUp-CgpvNr3o.js → PopUp-CSPXdqKI.js} +79 -43
  37. package/dist/client/{ProcessList-vecpxThi.js → ProcessList-C3HV7G0b.js} +5 -6
  38. package/dist/client/{VImg-CETuikH2.js → VImg-FoXcOnWF.js} +6 -3
  39. package/dist/client/{VMain-Ci9DyaGU.js → VMain-Ck2g1QOG.js} +1 -1
  40. package/dist/client/{VTooltip-J4ac48X7.js → VTooltip-F_1Zcvhp.js} +2 -2
  41. package/dist/client/{WidgetsContainer-CCML4TyV.js → WidgetsContainer-Cq9uZEuN.js} +1 -1
  42. package/dist/client/asWebComponent-DZeEbWG0.js +8895 -0
  43. package/dist/client/{async-B7jIrM53.js → async-Dk79llLt.js} +2 -2
  44. package/dist/client/easing-CH0-9wR8.js +35 -0
  45. package/dist/client/eo-dash.js +1 -1
  46. package/dist/client/{forwardRefs-BQclvjMq.js → forwardRefs-BbvoXHtj.js} +58 -45
  47. package/dist/client/{handling-BS24aG1q.js → handling-DxucYlYh.js} +12 -6
  48. package/dist/client/{helpers-wXK7Ywio.js → helpers-CI_7CUmn.js} +568 -281
  49. package/dist/client/index-BO5uGfUe.js +571 -0
  50. package/dist/client/{index-9KR-G20t.js → index-C13BiO9C.js} +2 -2
  51. package/dist/client/{index-4UCzZi8B.js → index-DcCcdbgR.js} +26 -13
  52. package/dist/client/{index-B2XpdgR6.js → index-KrGHjH-_.js} +63 -36
  53. package/dist/client/templates.js +82 -15
  54. package/dist/client/{transition-yBii4fu6.js → transition-Ctkv90El.js} +1 -1
  55. package/dist/node/cli.js +6 -6
  56. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +24 -10
  57. package/dist/types/core/client/eodashSTAC/auth.d.ts +7 -0
  58. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +15 -3
  59. package/dist/types/core/client/eodashSTAC/helpers.d.ts +47 -16
  60. package/dist/types/core/client/plugins/vuetify.d.ts +14 -14
  61. package/dist/types/core/client/store/actions.d.ts +2 -0
  62. package/dist/types/core/client/store/stac.d.ts +16 -7
  63. package/dist/types/core/client/store/states.d.ts +4 -0
  64. package/dist/types/core/client/types.d.ts +170 -2
  65. package/dist/types/core/client/utils/bands-editor/arithmetic.d.ts +8 -0
  66. package/dist/types/core/client/utils/bands-editor/colors.d.ts +15 -0
  67. package/dist/types/core/client/utils/bands-editor/dom.d.ts +42 -0
  68. package/dist/types/core/client/utils/bands-editor/index.d.ts +20 -0
  69. package/dist/types/core/client/utils/bands-editor/rgb.d.ts +15 -0
  70. package/dist/types/core/client/utils/index.d.ts +1 -1
  71. package/dist/types/templates/baseConfig.d.ts +87 -1
  72. package/dist/types/templates/expert.d.ts +6 -6
  73. package/dist/types/templates/explore.d.ts +67 -0
  74. package/dist/types/templates/index.d.ts +1 -1
  75. package/dist/types/templates/{light.d.ts → lite.d.ts} +5 -5
  76. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +21 -0
  77. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +49 -0
  78. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +4 -0
  79. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +12 -0
  80. package/dist/types/widgets/EodashItemCatalog/types.d.ts +14 -0
  81. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +2 -0
  82. package/dist/types/widgets/EodashMap/index.vue.d.ts +108 -2
  83. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  84. package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
  85. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  86. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +7 -0
  87. package/dist/types/widgets/EodashTools.vue.d.ts +10 -10
  88. package/dist/types/widgets/ExportState.vue.d.ts +2 -0
  89. package/package.json +28 -27
  90. package/templates/baseConfig.js +10 -5
  91. package/templates/compare.js +2 -2
  92. package/templates/expert.js +5 -5
  93. package/templates/explore.js +62 -0
  94. package/templates/index.js +1 -1
  95. package/templates/{light.js → lite.js} +1 -1
  96. package/widgets/EodashDatePicker.vue +15 -18
  97. package/widgets/EodashItemCatalog/index.vue +161 -0
  98. package/widgets/EodashItemCatalog/methods/filters.js +216 -0
  99. package/widgets/EodashItemCatalog/methods/handlers.js +50 -0
  100. package/widgets/EodashItemCatalog/methods/map.js +144 -0
  101. package/widgets/EodashItemCatalog/types.ts +15 -0
  102. package/widgets/EodashItemFilter.vue +35 -28
  103. package/widgets/EodashLayerControl.vue +10 -6
  104. package/widgets/EodashLayoutSwitcher.vue +1 -1
  105. package/widgets/EodashMap/EodashMapBtns.vue +18 -9
  106. package/widgets/EodashMap/index.vue +22 -12
  107. package/widgets/EodashMap/methods/create-layers-config.js +9 -6
  108. package/widgets/EodashMap/methods/index.js +27 -13
  109. package/widgets/EodashProcess/index.vue +17 -1
  110. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +9 -3
  111. package/widgets/EodashProcess/methods/handling.js +2 -0
  112. package/widgets/EodashProcess/methods/outputs.js +1 -0
  113. package/widgets/EodashTimeSlider.vue +40 -0
  114. package/widgets/EodashTools.vue +7 -3
  115. package/widgets/ExportState.vue +53 -22
  116. package/dist/client/EodashLayerControl-BLBds28C.js +0 -154
  117. package/dist/client/EodashMapBtns-B89_YBDw.js +0 -326
  118. package/dist/client/MobileLayout-JelB6w1G.js +0 -118
  119. package/dist/client/asWebComponent-ZyEzWOOf.js +0 -19092
@@ -54,7 +54,7 @@ const isUndefined = typeOfTest('undefined');
54
54
  */
55
55
  function isBuffer(val) {
56
56
  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
57
- && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val);
57
+ && isFunction$1(val.constructor.isBuffer) && val.constructor.isBuffer(val);
58
58
  }
59
59
 
60
60
  /**
@@ -99,7 +99,7 @@ const isString = typeOfTest('string');
99
99
  * @param {*} val The value to test
100
100
  * @returns {boolean} True if value is a Function, otherwise false
101
101
  */
102
- const isFunction = typeOfTest('function');
102
+ const isFunction$1 = typeOfTest('function');
103
103
 
104
104
  /**
105
105
  * Determine if a value is a Number
@@ -155,7 +155,7 @@ const isEmptyObject = (val) => {
155
155
  if (!isObject(val) || isBuffer(val)) {
156
156
  return false;
157
157
  }
158
-
158
+
159
159
  try {
160
160
  return Object.keys(val).length === 0 && Object.getPrototypeOf(val) === Object.prototype;
161
161
  } catch (e) {
@@ -207,7 +207,7 @@ const isFileList = kindOfTest('FileList');
207
207
  *
208
208
  * @returns {boolean} True if value is a Stream, otherwise false
209
209
  */
210
- const isStream = (val) => isObject(val) && isFunction(val.pipe);
210
+ const isStream = (val) => isObject(val) && isFunction$1(val.pipe);
211
211
 
212
212
  /**
213
213
  * Determine if a value is a FormData
@@ -220,10 +220,10 @@ const isFormData = (thing) => {
220
220
  let kind;
221
221
  return thing && (
222
222
  (typeof FormData === 'function' && thing instanceof FormData) || (
223
- isFunction(thing.append) && (
223
+ isFunction$1(thing.append) && (
224
224
  (kind = kindOf(thing)) === 'formdata' ||
225
225
  // detect form-data instance
226
- (kind === 'object' && isFunction(thing.toString) && thing.toString() === '[object FormData]')
226
+ (kind === 'object' && isFunction$1(thing.toString) && thing.toString() === '[object FormData]')
227
227
  )
228
228
  )
229
229
  )
@@ -348,7 +348,7 @@ const isContextDefined = (context) => !isUndefined(context) && context !== _glob
348
348
  * @returns {Object} Result of all merge properties
349
349
  */
350
350
  function merge(/* obj1, obj2, obj3, ... */) {
351
- const {caseless} = isContextDefined(this) && this || {};
351
+ const {caseless, skipUndefined} = isContextDefined(this) && this || {};
352
352
  const result = {};
353
353
  const assignValue = (val, key) => {
354
354
  const targetKey = caseless && findKey(result, key) || key;
@@ -358,7 +358,7 @@ function merge(/* obj1, obj2, obj3, ... */) {
358
358
  result[targetKey] = merge({}, val);
359
359
  } else if (isArray(val)) {
360
360
  result[targetKey] = val.slice();
361
- } else {
361
+ } else if (!skipUndefined || !isUndefined(val)) {
362
362
  result[targetKey] = val;
363
363
  }
364
364
  };
@@ -381,7 +381,7 @@ function merge(/* obj1, obj2, obj3, ... */) {
381
381
  */
382
382
  const extend = (a, b, thisArg, {allOwnKeys}= {}) => {
383
383
  forEach(b, (val, key) => {
384
- if (thisArg && isFunction(val)) {
384
+ if (thisArg && isFunction$1(val)) {
385
385
  a[key] = bind(val, thisArg);
386
386
  } else {
387
387
  a[key] = val;
@@ -597,13 +597,13 @@ const reduceDescriptors = (obj, reducer) => {
597
597
  const freezeMethods = (obj) => {
598
598
  reduceDescriptors(obj, (descriptor, name) => {
599
599
  // skip restricted props in strict mode
600
- if (isFunction(obj) && ['arguments', 'caller', 'callee'].indexOf(name) !== -1) {
600
+ if (isFunction$1(obj) && ['arguments', 'caller', 'callee'].indexOf(name) !== -1) {
601
601
  return false;
602
602
  }
603
603
 
604
604
  const value = obj[name];
605
605
 
606
- if (!isFunction(value)) return;
606
+ if (!isFunction$1(value)) return;
607
607
 
608
608
  descriptor.enumerable = false;
609
609
 
@@ -640,6 +640,8 @@ const toFiniteNumber = (value, defaultValue) => {
640
640
  return value != null && Number.isFinite(value = +value) ? value : defaultValue;
641
641
  };
642
642
 
643
+
644
+
643
645
  /**
644
646
  * If the thing is a FormData object, return true, otherwise return false.
645
647
  *
@@ -648,7 +650,7 @@ const toFiniteNumber = (value, defaultValue) => {
648
650
  * @returns {boolean}
649
651
  */
650
652
  function isSpecCompliantForm(thing) {
651
- return !!(thing && isFunction(thing.append) && thing[toStringTag] === 'FormData' && thing[iterator]);
653
+ return !!(thing && isFunction$1(thing.append) && thing[toStringTag] === 'FormData' && thing[iterator]);
652
654
  }
653
655
 
654
656
  const toJSONObject = (obj) => {
@@ -690,7 +692,7 @@ const toJSONObject = (obj) => {
690
692
  const isAsyncFn = kindOfTest('AsyncFunction');
691
693
 
692
694
  const isThenable = (thing) =>
693
- thing && (isObject(thing) || isFunction(thing)) && isFunction(thing.then) && isFunction(thing.catch);
695
+ thing && (isObject(thing) || isFunction$1(thing)) && isFunction$1(thing.then) && isFunction$1(thing.catch);
694
696
 
695
697
  // original code
696
698
  // https://github.com/DigitalBrainJS/AxiosPromise/blob/16deab13710ec09779922131f3fa5954320f83ab/lib/utils.js#L11-L34
@@ -714,7 +716,7 @@ const _setImmediate = ((setImmediateSupported, postMessageSupported) => {
714
716
  })(`axios@${Math.random()}`, []) : (cb) => setTimeout(cb);
715
717
  })(
716
718
  typeof setImmediate === 'function',
717
- isFunction(_global.postMessage)
719
+ isFunction$1(_global.postMessage)
718
720
  );
719
721
 
720
722
  const asap = typeof queueMicrotask !== 'undefined' ?
@@ -723,7 +725,7 @@ const asap = typeof queueMicrotask !== 'undefined' ?
723
725
  // *********************
724
726
 
725
727
 
726
- const isIterable = (thing) => thing != null && isFunction(thing[iterator]);
728
+ const isIterable = (thing) => thing != null && isFunction$1(thing[iterator]);
727
729
 
728
730
 
729
731
  const utils$1 = {
@@ -747,7 +749,7 @@ const utils$1 = {
747
749
  isFile,
748
750
  isBlob,
749
751
  isRegExp,
750
- isFunction,
752
+ isFunction: isFunction$1,
751
753
  isStream,
752
754
  isURLSearchParams,
753
755
  isTypedArray,
@@ -873,11 +875,18 @@ AxiosError.from = (error, code, config, request, response, customProps) => {
873
875
  return prop !== 'isAxiosError';
874
876
  });
875
877
 
876
- AxiosError.call(axiosError, error.message, code, config, request, response);
878
+ const msg = error && error.message ? error.message : 'Error';
879
+
880
+ // Prefer explicit code; otherwise copy the low-level error's code (e.g. ECONNREFUSED)
881
+ const errCode = code == null && error ? error.code : code;
882
+ AxiosError.call(axiosError, msg, errCode, config, request, response);
877
883
 
878
- axiosError.cause = error;
884
+ // Chain the original error on the standard field; non-enumerable to avoid JSON noise
885
+ if (error && axiosError.cause == null) {
886
+ Object.defineProperty(axiosError, 'cause', { value: error, configurable: true });
887
+ }
879
888
 
880
- axiosError.name = error.name;
889
+ axiosError.name = (error && error.name) || 'Error';
881
890
 
882
891
  customProps && Object.assign(axiosError, customProps);
883
892
 
@@ -1168,9 +1177,7 @@ function encode(val) {
1168
1177
  replace(/%3A/gi, ':').
1169
1178
  replace(/%24/g, '$').
1170
1179
  replace(/%2C/gi, ',').
1171
- replace(/%20/g, '+').
1172
- replace(/%5B/gi, '[').
1173
- replace(/%5D/gi, ']');
1180
+ replace(/%20/g, '+');
1174
1181
  }
1175
1182
 
1176
1183
  /**
@@ -1573,7 +1580,7 @@ const defaults = {
1573
1580
  const strictJSONParsing = !silentJSONParsing && JSONRequested;
1574
1581
 
1575
1582
  try {
1576
- return JSON.parse(data);
1583
+ return JSON.parse(data, this.parseReviver);
1577
1584
  } catch (e) {
1578
1585
  if (strictJSONParsing) {
1579
1586
  if (e.name === 'SyntaxError') {
@@ -2394,7 +2401,7 @@ function mergeConfig(config1, config2) {
2394
2401
  const resolveConfig = (config) => {
2395
2402
  const newConfig = mergeConfig({}, config);
2396
2403
 
2397
- let {data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth} = newConfig;
2404
+ let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
2398
2405
 
2399
2406
  newConfig.headers = headers = AxiosHeaders.from(headers);
2400
2407
 
@@ -2407,17 +2414,21 @@ const resolveConfig = (config) => {
2407
2414
  );
2408
2415
  }
2409
2416
 
2410
- let contentType;
2411
-
2412
2417
  if (utils$1.isFormData(data)) {
2413
2418
  if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
2414
- headers.setContentType(undefined); // Let the browser set it
2415
- } else if ((contentType = headers.getContentType()) !== false) {
2416
- // fix semicolon duplication issue for ReactNative FormData implementation
2417
- const [type, ...tokens] = contentType ? contentType.split(';').map(token => token.trim()).filter(Boolean) : [];
2418
- headers.setContentType([type || 'multipart/form-data', ...tokens].join('; '));
2419
+ headers.setContentType(undefined); // browser handles it
2420
+ } else if (utils$1.isFunction(data.getHeaders)) {
2421
+ // Node.js FormData (like form-data package)
2422
+ const formHeaders = data.getHeaders();
2423
+ // Only set safe headers to avoid overwriting security headers
2424
+ const allowedHeaders = ['content-type', 'content-length'];
2425
+ Object.entries(formHeaders).forEach(([key, val]) => {
2426
+ if (allowedHeaders.includes(key.toLowerCase())) {
2427
+ headers.set(key, val);
2428
+ }
2429
+ });
2419
2430
  }
2420
- }
2431
+ }
2421
2432
 
2422
2433
  // Add xsrf header
2423
2434
  // This is only done if running in a standard browser environment.
@@ -2534,15 +2545,18 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
2534
2545
  };
2535
2546
 
2536
2547
  // Handle low level network errors
2537
- request.onerror = function handleError() {
2538
- // Real errors are hidden from us by the browser
2539
- // onerror should only fire if it's a network error
2540
- reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
2541
-
2542
- // Clean up request
2543
- request = null;
2548
+ request.onerror = function handleError(event) {
2549
+ // Browsers deliver a ProgressEvent in XHR onerror
2550
+ // (message may be empty; when present, surface it)
2551
+ // See https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/error_event
2552
+ const msg = event && event.message ? event.message : 'Network Error';
2553
+ const err = new AxiosError(msg, AxiosError.ERR_NETWORK, config, request);
2554
+ // attach the underlying event for consumers who want details
2555
+ err.event = event || null;
2556
+ reject(err);
2557
+ request = null;
2544
2558
  };
2545
-
2559
+
2546
2560
  // Handle timeout
2547
2561
  request.ontimeout = function handleTimeout() {
2548
2562
  let timeoutErrorMessage = _config.timeout ? 'timeout of ' + _config.timeout + 'ms exceeded' : 'timeout exceeded';
@@ -2756,14 +2770,18 @@ const trackStream = (stream, chunkSize, onProgress, onFinish) => {
2756
2770
  })
2757
2771
  };
2758
2772
 
2759
- const isFetchSupported = typeof fetch === 'function' && typeof Request === 'function' && typeof Response === 'function';
2760
- const isReadableStreamSupported = isFetchSupported && typeof ReadableStream === 'function';
2773
+ const DEFAULT_CHUNK_SIZE = 64 * 1024;
2774
+
2775
+ const {isFunction} = utils$1;
2776
+
2777
+ const globalFetchAPI = (({Request, Response}) => ({
2778
+ Request, Response
2779
+ }))(utils$1.global);
2780
+
2781
+ const {
2782
+ ReadableStream: ReadableStream$1, TextEncoder
2783
+ } = utils$1.global;
2761
2784
 
2762
- // used only inside the fetch adapter
2763
- const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
2764
- ((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
2765
- async (str) => new Uint8Array(await new Response(str).arrayBuffer())
2766
- );
2767
2785
 
2768
2786
  const test = (fn, ...args) => {
2769
2787
  try {
@@ -2773,211 +2791,268 @@ const test = (fn, ...args) => {
2773
2791
  }
2774
2792
  };
2775
2793
 
2776
- const supportsRequestStream = isReadableStreamSupported && test(() => {
2777
- let duplexAccessed = false;
2794
+ const factory = (env) => {
2795
+ env = utils$1.merge.call({
2796
+ skipUndefined: true
2797
+ }, globalFetchAPI, env);
2778
2798
 
2779
- const hasContentType = new Request(platform.origin, {
2780
- body: new ReadableStream(),
2781
- method: 'POST',
2782
- get duplex() {
2783
- duplexAccessed = true;
2784
- return 'half';
2785
- },
2786
- }).headers.has('Content-Type');
2799
+ const {fetch: envFetch, Request, Response} = env;
2800
+ const isFetchSupported = envFetch ? isFunction(envFetch) : typeof fetch === 'function';
2801
+ const isRequestSupported = isFunction(Request);
2802
+ const isResponseSupported = isFunction(Response);
2787
2803
 
2788
- return duplexAccessed && !hasContentType;
2789
- });
2804
+ if (!isFetchSupported) {
2805
+ return false;
2806
+ }
2790
2807
 
2791
- const DEFAULT_CHUNK_SIZE = 64 * 1024;
2808
+ const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream$1);
2792
2809
 
2793
- const supportsResponseStream = isReadableStreamSupported &&
2794
- test(() => utils$1.isReadableStream(new Response('').body));
2810
+ const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
2811
+ ((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
2812
+ async (str) => new Uint8Array(await new Request(str).arrayBuffer())
2813
+ );
2795
2814
 
2815
+ const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
2816
+ let duplexAccessed = false;
2796
2817
 
2797
- const resolvers = {
2798
- stream: supportsResponseStream && ((res) => res.body)
2799
- };
2818
+ const hasContentType = new Request(platform.origin, {
2819
+ body: new ReadableStream$1(),
2820
+ method: 'POST',
2821
+ get duplex() {
2822
+ duplexAccessed = true;
2823
+ return 'half';
2824
+ },
2825
+ }).headers.has('Content-Type');
2800
2826
 
2801
- isFetchSupported && (((res) => {
2802
- ['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
2803
- !resolvers[type] && (resolvers[type] = utils$1.isFunction(res[type]) ? (res) => res[type]() :
2804
- (_, config) => {
2805
- throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
2806
- });
2827
+ return duplexAccessed && !hasContentType;
2807
2828
  });
2808
- })(new Response));
2809
2829
 
2810
- const getBodyLength = async (body) => {
2811
- if (body == null) {
2812
- return 0;
2813
- }
2830
+ const supportsResponseStream = isResponseSupported && isReadableStreamSupported &&
2831
+ test(() => utils$1.isReadableStream(new Response('').body));
2814
2832
 
2815
- if(utils$1.isBlob(body)) {
2816
- return body.size;
2817
- }
2833
+ const resolvers = {
2834
+ stream: supportsResponseStream && ((res) => res.body)
2835
+ };
2818
2836
 
2819
- if(utils$1.isSpecCompliantForm(body)) {
2820
- const _request = new Request(platform.origin, {
2821
- method: 'POST',
2822
- body,
2837
+ isFetchSupported && ((() => {
2838
+ ['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
2839
+ !resolvers[type] && (resolvers[type] = (res, config) => {
2840
+ let method = res && res[type];
2841
+
2842
+ if (method) {
2843
+ return method.call(res);
2844
+ }
2845
+
2846
+ throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
2847
+ });
2823
2848
  });
2824
- return (await _request.arrayBuffer()).byteLength;
2825
- }
2849
+ })());
2826
2850
 
2827
- if(utils$1.isArrayBufferView(body) || utils$1.isArrayBuffer(body)) {
2828
- return body.byteLength;
2829
- }
2851
+ const getBodyLength = async (body) => {
2852
+ if (body == null) {
2853
+ return 0;
2854
+ }
2830
2855
 
2831
- if(utils$1.isURLSearchParams(body)) {
2832
- body = body + '';
2833
- }
2856
+ if (utils$1.isBlob(body)) {
2857
+ return body.size;
2858
+ }
2834
2859
 
2835
- if(utils$1.isString(body)) {
2836
- return (await encodeText(body)).byteLength;
2837
- }
2838
- };
2860
+ if (utils$1.isSpecCompliantForm(body)) {
2861
+ const _request = new Request(platform.origin, {
2862
+ method: 'POST',
2863
+ body,
2864
+ });
2865
+ return (await _request.arrayBuffer()).byteLength;
2866
+ }
2839
2867
 
2840
- const resolveBodyLength = async (headers, body) => {
2841
- const length = utils$1.toFiniteNumber(headers.getContentLength());
2868
+ if (utils$1.isArrayBufferView(body) || utils$1.isArrayBuffer(body)) {
2869
+ return body.byteLength;
2870
+ }
2842
2871
 
2843
- return length == null ? getBodyLength(body) : length;
2844
- };
2872
+ if (utils$1.isURLSearchParams(body)) {
2873
+ body = body + '';
2874
+ }
2845
2875
 
2846
- const fetchAdapter = isFetchSupported && (async (config) => {
2847
- let {
2848
- url,
2849
- method,
2850
- data,
2851
- signal,
2852
- cancelToken,
2853
- timeout,
2854
- onDownloadProgress,
2855
- onUploadProgress,
2856
- responseType,
2857
- headers,
2858
- withCredentials = 'same-origin',
2859
- fetchOptions
2860
- } = resolveConfig(config);
2861
-
2862
- responseType = responseType ? (responseType + '').toLowerCase() : 'text';
2863
-
2864
- let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
2865
-
2866
- let request;
2867
-
2868
- const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
2876
+ if (utils$1.isString(body)) {
2877
+ return (await encodeText(body)).byteLength;
2878
+ }
2879
+ };
2880
+
2881
+ const resolveBodyLength = async (headers, body) => {
2882
+ const length = utils$1.toFiniteNumber(headers.getContentLength());
2883
+
2884
+ return length == null ? getBodyLength(body) : length;
2885
+ };
2886
+
2887
+ return async (config) => {
2888
+ let {
2889
+ url,
2890
+ method,
2891
+ data,
2892
+ signal,
2893
+ cancelToken,
2894
+ timeout,
2895
+ onDownloadProgress,
2896
+ onUploadProgress,
2897
+ responseType,
2898
+ headers,
2899
+ withCredentials = 'same-origin',
2900
+ fetchOptions
2901
+ } = resolveConfig(config);
2902
+
2903
+ let _fetch = envFetch || fetch;
2904
+
2905
+ responseType = responseType ? (responseType + '').toLowerCase() : 'text';
2906
+
2907
+ let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
2908
+
2909
+ let request = null;
2910
+
2911
+ const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
2869
2912
  composedSignal.unsubscribe();
2870
- });
2913
+ });
2871
2914
 
2872
- let requestContentLength;
2915
+ let requestContentLength;
2873
2916
 
2874
- try {
2875
- if (
2876
- onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
2877
- (requestContentLength = await resolveBodyLength(headers, data)) !== 0
2878
- ) {
2879
- let _request = new Request(url, {
2880
- method: 'POST',
2881
- body: data,
2882
- duplex: "half"
2883
- });
2917
+ try {
2918
+ if (
2919
+ onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
2920
+ (requestContentLength = await resolveBodyLength(headers, data)) !== 0
2921
+ ) {
2922
+ let _request = new Request(url, {
2923
+ method: 'POST',
2924
+ body: data,
2925
+ duplex: "half"
2926
+ });
2884
2927
 
2885
- let contentTypeHeader;
2928
+ let contentTypeHeader;
2886
2929
 
2887
- if (utils$1.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
2888
- headers.setContentType(contentTypeHeader);
2889
- }
2930
+ if (utils$1.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
2931
+ headers.setContentType(contentTypeHeader);
2932
+ }
2890
2933
 
2891
- if (_request.body) {
2892
- const [onProgress, flush] = progressEventDecorator(
2893
- requestContentLength,
2894
- progressEventReducer(asyncDecorator(onUploadProgress))
2895
- );
2934
+ if (_request.body) {
2935
+ const [onProgress, flush] = progressEventDecorator(
2936
+ requestContentLength,
2937
+ progressEventReducer(asyncDecorator(onUploadProgress))
2938
+ );
2896
2939
 
2897
- data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
2940
+ data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
2941
+ }
2898
2942
  }
2899
- }
2900
2943
 
2901
- if (!utils$1.isString(withCredentials)) {
2902
- withCredentials = withCredentials ? 'include' : 'omit';
2903
- }
2944
+ if (!utils$1.isString(withCredentials)) {
2945
+ withCredentials = withCredentials ? 'include' : 'omit';
2946
+ }
2904
2947
 
2905
- // Cloudflare Workers throws when credentials are defined
2906
- // see https://github.com/cloudflare/workerd/issues/902
2907
- const isCredentialsSupported = "credentials" in Request.prototype;
2908
- request = new Request(url, {
2909
- ...fetchOptions,
2910
- signal: composedSignal,
2911
- method: method.toUpperCase(),
2912
- headers: headers.normalize().toJSON(),
2913
- body: data,
2914
- duplex: "half",
2915
- credentials: isCredentialsSupported ? withCredentials : undefined
2916
- });
2948
+ // Cloudflare Workers throws when credentials are defined
2949
+ // see https://github.com/cloudflare/workerd/issues/902
2950
+ const isCredentialsSupported = isRequestSupported && "credentials" in Request.prototype;
2917
2951
 
2918
- let response = await fetch(request, fetchOptions);
2952
+ const resolvedOptions = {
2953
+ ...fetchOptions,
2954
+ signal: composedSignal,
2955
+ method: method.toUpperCase(),
2956
+ headers: headers.normalize().toJSON(),
2957
+ body: data,
2958
+ duplex: "half",
2959
+ credentials: isCredentialsSupported ? withCredentials : undefined
2960
+ };
2919
2961
 
2920
- const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
2962
+ request = isRequestSupported && new Request(url, resolvedOptions);
2921
2963
 
2922
- if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
2923
- const options = {};
2964
+ let response = await (isRequestSupported ? _fetch(request, fetchOptions) : _fetch(url, resolvedOptions));
2924
2965
 
2925
- ['status', 'statusText', 'headers'].forEach(prop => {
2926
- options[prop] = response[prop];
2927
- });
2966
+ const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
2928
2967
 
2929
- const responseContentLength = utils$1.toFiniteNumber(response.headers.get('content-length'));
2968
+ if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
2969
+ const options = {};
2930
2970
 
2931
- const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
2932
- responseContentLength,
2933
- progressEventReducer(asyncDecorator(onDownloadProgress), true)
2934
- ) || [];
2971
+ ['status', 'statusText', 'headers'].forEach(prop => {
2972
+ options[prop] = response[prop];
2973
+ });
2935
2974
 
2936
- response = new Response(
2937
- trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
2938
- flush && flush();
2939
- unsubscribe && unsubscribe();
2940
- }),
2941
- options
2942
- );
2943
- }
2975
+ const responseContentLength = utils$1.toFiniteNumber(response.headers.get('content-length'));
2944
2976
 
2945
- responseType = responseType || 'text';
2977
+ const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
2978
+ responseContentLength,
2979
+ progressEventReducer(asyncDecorator(onDownloadProgress), true)
2980
+ ) || [];
2946
2981
 
2947
- let responseData = await resolvers[utils$1.findKey(resolvers, responseType) || 'text'](response, config);
2982
+ response = new Response(
2983
+ trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
2984
+ flush && flush();
2985
+ unsubscribe && unsubscribe();
2986
+ }),
2987
+ options
2988
+ );
2989
+ }
2948
2990
 
2949
- !isStreamResponse && unsubscribe && unsubscribe();
2991
+ responseType = responseType || 'text';
2950
2992
 
2951
- return await new Promise((resolve, reject) => {
2952
- settle(resolve, reject, {
2953
- data: responseData,
2954
- headers: AxiosHeaders.from(response.headers),
2955
- status: response.status,
2956
- statusText: response.statusText,
2957
- config,
2958
- request
2959
- });
2960
- })
2961
- } catch (err) {
2962
- unsubscribe && unsubscribe();
2963
-
2964
- if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
2965
- throw Object.assign(
2966
- new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
2967
- {
2968
- cause: err.cause || err
2969
- }
2970
- )
2993
+ let responseData = await resolvers[utils$1.findKey(resolvers, responseType) || 'text'](response, config);
2994
+
2995
+ !isStreamResponse && unsubscribe && unsubscribe();
2996
+
2997
+ return await new Promise((resolve, reject) => {
2998
+ settle(resolve, reject, {
2999
+ data: responseData,
3000
+ headers: AxiosHeaders.from(response.headers),
3001
+ status: response.status,
3002
+ statusText: response.statusText,
3003
+ config,
3004
+ request
3005
+ });
3006
+ })
3007
+ } catch (err) {
3008
+ unsubscribe && unsubscribe();
3009
+
3010
+ if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
3011
+ throw Object.assign(
3012
+ new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
3013
+ {
3014
+ cause: err.cause || err
3015
+ }
3016
+ )
3017
+ }
3018
+
3019
+ throw AxiosError.from(err, err && err.code, config, request);
2971
3020
  }
3021
+ }
3022
+ };
3023
+
3024
+ const seedCache = new Map();
3025
+
3026
+ const getFetch = (config) => {
3027
+ let env = config ? config.env : {};
3028
+ const {fetch, Request, Response} = env;
3029
+ const seeds = [
3030
+ Request, Response, fetch
3031
+ ];
3032
+
3033
+ let len = seeds.length, i = len,
3034
+ seed, target, map = seedCache;
2972
3035
 
2973
- throw AxiosError.from(err, err && err.code, config, request);
3036
+ while (i--) {
3037
+ seed = seeds[i];
3038
+ target = map.get(seed);
3039
+
3040
+ target === undefined && map.set(seed, target = (i ? new Map() : factory(env)));
3041
+
3042
+ map = target;
2974
3043
  }
2975
- });
3044
+
3045
+ return target;
3046
+ };
3047
+
3048
+ getFetch();
2976
3049
 
2977
3050
  const knownAdapters = {
2978
3051
  http: httpAdapter,
2979
3052
  xhr: xhrAdapter,
2980
- fetch: fetchAdapter
3053
+ fetch: {
3054
+ get: getFetch,
3055
+ }
2981
3056
  };
2982
3057
 
2983
3058
  utils$1.forEach(knownAdapters, (fn, value) => {
@@ -2996,7 +3071,7 @@ const renderReason = (reason) => `- ${reason}`;
2996
3071
  const isResolvedHandle = (adapter) => utils$1.isFunction(adapter) || adapter === null || adapter === false;
2997
3072
 
2998
3073
  const adapters = {
2999
- getAdapter: (adapters) => {
3074
+ getAdapter: (adapters, config) => {
3000
3075
  adapters = utils$1.isArray(adapters) ? adapters : [adapters];
3001
3076
 
3002
3077
  const {length} = adapters;
@@ -3019,7 +3094,7 @@ const adapters = {
3019
3094
  }
3020
3095
  }
3021
3096
 
3022
- if (adapter) {
3097
+ if (adapter && (utils$1.isFunction(adapter) || (adapter = adapter.get(config)))) {
3023
3098
  break;
3024
3099
  }
3025
3100
 
@@ -3087,7 +3162,7 @@ function dispatchRequest(config) {
3087
3162
  config.headers.setContentType('application/x-www-form-urlencoded', false);
3088
3163
  }
3089
3164
 
3090
- const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
3165
+ const adapter = adapters.getAdapter(config.adapter || defaults.adapter, config);
3091
3166
 
3092
3167
  return adapter(config).then(function onAdapterResolution(response) {
3093
3168
  throwIfCancellationRequested(config);
@@ -3121,7 +3196,7 @@ function dispatchRequest(config) {
3121
3196
  });
3122
3197
  }
3123
3198
 
3124
- const VERSION = "1.11.0";
3199
+ const VERSION = "1.12.2";
3125
3200
 
3126
3201
  const validators$1 = {};
3127
3202
 
@@ -3377,8 +3452,6 @@ class Axios {
3377
3452
 
3378
3453
  let newConfig = config;
3379
3454
 
3380
- i = 0;
3381
-
3382
3455
  while (i < len) {
3383
3456
  const onFulfilled = requestInterceptorChain[i++];
3384
3457
  const onRejected = requestInterceptorChain[i++];
@@ -3800,10 +3873,18 @@ const poi = ref("");
3800
3873
  */
3801
3874
  const comparePoi = ref("");
3802
3875
 
3876
+ /** @type {import("vue").Ref<import("@eox/chart").EOxChart | null>} */
3877
+ const chartEl = shallowRef(null);
3878
+
3879
+ /** @type {import("vue").Ref<import("@eox/chart").EOxChart | null>} */
3880
+ const compareChartEl = shallowRef(null);
3881
+
3803
3882
  const states = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3804
3883
  __proto__: null,
3805
3884
  activeTemplate,
3806
3885
  availableMapProjection,
3886
+ chartEl,
3887
+ compareChartEl,
3807
3888
  compareIndicator,
3808
3889
  comparePoi,
3809
3890
  currentCompareUrl,
@@ -3830,6 +3911,18 @@ const getLayers = () => mapEl.value?.layers.toReversed() ?? [];
3830
3911
  const getCompareLayers = () =>
3831
3912
  mapCompareEl.value?.layers.toReversed() ?? [];
3832
3913
 
3914
+ /**
3915
+ * Returns the current chart spec from {@link chartEl}
3916
+ * @returns {import("vega-embed").VisualizationSpec | null}
3917
+ */
3918
+ const getChartSpec = () => chartEl.value?.spec ?? null;
3919
+
3920
+ /**
3921
+ * Returns the current chart spec from {@link compareChartEl}
3922
+ * @returns {import("vega-embed").VisualizationSpec | null}
3923
+ */
3924
+ const getCompareChartSpec = () => compareChartEl.value?.spec ?? null;
3925
+
3833
3926
  /**
3834
3927
  * Register EPSG projection in `eox-map`
3835
3928
  * @param {string|number|{name: string, def: string, extent?:number[]}} [projection]*/
@@ -3906,6 +3999,8 @@ const includesProcess = (collection, compare = false) => {
3906
3999
  const actions = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3907
4000
  __proto__: null,
3908
4001
  changeMapProjection,
4002
+ getChartSpec,
4003
+ getCompareChartSpec,
3909
4004
  getCompareLayers,
3910
4005
  getLayers,
3911
4006
  includesProcess,
@@ -3973,14 +4068,14 @@ function getStyleVariablesState(collectionId, variables) {
3973
4068
  return variables;
3974
4069
  }
3975
4070
  const matchingLayer = analysisGroup.layers?.find((layer) => {
3976
- const [collection, ..._other] = layer.properties?.id.split(";:;") ?? [
4071
+ const [collection] = layer.properties?.id.split(";:;") ?? [
3977
4072
  "",
3978
4073
  "",
3979
4074
  "",
3980
4075
  ];
3981
4076
  return (
3982
4077
  collection === collectionId &&
3983
- ["Vector", "WebGLTile"].includes(layer?.type ?? "")
4078
+ ["Vector", "WebGLTile", "VectorTile"].includes(layer?.type ?? "")
3984
4079
  );
3985
4080
  });
3986
4081
 
@@ -4054,14 +4149,28 @@ function generateFeatures(links, extraProperties = {}, rel = "item") {
4054
4149
  *
4055
4150
  * @param {string} collectionId
4056
4151
  * @param { import("@/types").EodashStyleJson} [style]
4152
+ * @param {Record<string,any>} [rasterJsonform]
4057
4153
  * */
4058
- function extractLayerConfig(collectionId, style) {
4059
- if (!style) {
4154
+ function extractLayerConfig(collectionId, style, rasterJsonform) {
4155
+ if (!style && !rasterJsonform) {
4060
4156
  return { layerConfig: undefined, style: undefined };
4061
4157
  }
4062
- style = { ...style };
4158
+ if (style) {
4159
+ style = { ...style };
4160
+ }
4063
4161
 
4064
- if (Object.keys(style.variables ?? {}).length) {
4162
+ if (rasterJsonform) {
4163
+ return {
4164
+ layerConfig: {
4165
+ schema: rasterJsonform.jsonform,
4166
+ legend: rasterJsonform.legend,
4167
+ type: "tileUrl",
4168
+ },
4169
+ style,
4170
+ };
4171
+ }
4172
+
4173
+ if (style?.variables && Object.keys(style.variables ?? {}).length) {
4065
4174
  style.variables = getStyleVariablesState(collectionId, style.variables);
4066
4175
  }
4067
4176
 
@@ -4069,6 +4178,7 @@ function extractLayerConfig(collectionId, style) {
4069
4178
  let layerConfig = undefined;
4070
4179
 
4071
4180
  if (style?.jsonform) {
4181
+ // this explicitly sets legend only if jsonform is configured
4072
4182
  layerConfig = { schema: style.jsonform, type: "style" };
4073
4183
  delete style.jsonform;
4074
4184
  if (style?.legend) {
@@ -4083,7 +4193,25 @@ function extractLayerConfig(collectionId, style) {
4083
4193
 
4084
4194
  return { layerConfig, style };
4085
4195
  }
4086
-
4196
+ /**
4197
+ *
4198
+ * @param {number[]} bbox
4199
+ * @returns
4200
+ */
4201
+ const sanitizeBbox = (bbox) => {
4202
+ if (!bbox || !bbox.length || bbox.length !== 4) {
4203
+ return [0, 0, 0, 0];
4204
+ }
4205
+ let [minX, minY, maxX, maxY] = bbox;
4206
+ // Normalize longitudes to be within -180 to 180
4207
+ minX = Math.max(((minX + 180) % 360) - 180, -180);
4208
+ maxX = Math.min(((maxX - 180) % 360) + 180, 180);
4209
+ // Normalize latitudes to be within -90 to 90
4210
+ minY = Math.max(((minY + 90) % 180) - 90, -90);
4211
+ maxY = Math.min(((maxY - 90) % 180) + 90, 90);
4212
+
4213
+ return [minX, minY, maxX, maxY];
4214
+ };
4087
4215
  /**
4088
4216
  * Function to extract collection urls from an indicator
4089
4217
  * @param {import("stac-ts").StacCatalog
@@ -4108,6 +4236,10 @@ function extractCollectionUrls(stacObject, basepath) {
4108
4236
  return collectionUrls;
4109
4237
  }
4110
4238
  children.forEach((link) => {
4239
+ if (link.href.startsWith("http")) {
4240
+ collectionUrls.push(link.href);
4241
+ return;
4242
+ }
4111
4243
  collectionUrls.push(toAbsolute(link.href, basepath));
4112
4244
  });
4113
4245
  return collectionUrls;
@@ -4123,6 +4255,8 @@ const extractRoles = (properties, linkOrAsset) => {
4123
4255
  roles?.forEach((role) => {
4124
4256
  if (role === "visible") {
4125
4257
  properties.visible = true;
4258
+ } else if (role === "invisible") {
4259
+ properties.visible = false;
4126
4260
  }
4127
4261
  if (role === "overlay" || role === "baselayer") {
4128
4262
  properties.group = role;
@@ -4132,13 +4266,19 @@ const extractRoles = (properties, linkOrAsset) => {
4132
4266
  };
4133
4267
 
4134
4268
  /**
4135
- * Extracts style JSON from a STAC Item
4269
+ * Extracts a single non-link style JSON from a STAC Item optionally for a selected key mapping
4136
4270
  * @param {import("stac-ts").StacItem} item
4137
4271
  * @param {string} itemUrl
4138
- * @returns
4272
+ * @param {string | undefined} key
4273
+ * @returns
4139
4274
  **/
4140
- const fetchStyle = async (item, itemUrl) => {
4141
- const styleLink = item.links.find((link) => link.rel.includes("style"));
4275
+ const fetchStyle = async (item, itemUrl, key=undefined) => {
4276
+ let styleLink = null;
4277
+ if (key) {
4278
+ styleLink = item.links.find((link) => link.rel.includes("style") && link["links:keys"] && /** @type {Array<string>} */ (link["links:keys"]).includes(key) );
4279
+ } else {
4280
+ styleLink = item.links.find((link) => link.rel.includes("style") && !link["links:keys"]);
4281
+ }
4142
4282
  if (styleLink) {
4143
4283
  let url = "";
4144
4284
  if (styleLink.href.startsWith("http")) {
@@ -4155,6 +4295,27 @@ const fetchStyle = async (item, itemUrl) => {
4155
4295
  }
4156
4296
  };
4157
4297
 
4298
+ /**
4299
+ * Fetches all style JSONs from a STAC Item and returns an array with style objects
4300
+ * @param {import("stac-ts").StacItem} item
4301
+ * @param {string} itemUrl
4302
+ * @returns { Promise <Array<import("@/types").EodashStyleJson>>}
4303
+ **/
4304
+ const fetchAllStyles = async (item, itemUrl) => {
4305
+ const styleLinks = item.links.filter((link) => link.rel.includes("style"));
4306
+ const fetchPromises = styleLinks.map(async (link) => {
4307
+ let url = link.href.startsWith("http")
4308
+ ? link.href
4309
+ : toAbsolute(link.href, itemUrl);
4310
+
4311
+ const styleJson = await axios.get(url).then((resp) => resp.data);
4312
+ log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
4313
+ return styleJson;
4314
+ });
4315
+ const results = await Promise.all(fetchPromises);
4316
+ return results;
4317
+ };
4318
+
4158
4319
  /**
4159
4320
  * Return projection code which is to be registered in `eox-map`
4160
4321
  * @param {string|number|{name: string, def: string}} [projection]
@@ -4177,60 +4338,90 @@ const getProjectionCode = (projection) => {
4177
4338
 
4178
4339
  /**
4179
4340
  * Extracts layercontrol LayerDatetime property from STAC Links
4180
- * @param {import("stac-ts").StacLink[]} [links]
4341
+ * @param {import("stac-ts").StacLink[] | import("stac-ts").StacItem[] | undefined} [items]
4181
4342
  * @param {string|null} [currentStep]
4182
4343
  **/
4183
- const extractLayerDatetime = (links, currentStep) => {
4184
- if (!currentStep || !links?.length) {
4185
- return undefined;
4344
+ const extractLayerTimeValues = (items, currentStep) => {
4345
+ if (!currentStep || !items?.length) {
4346
+ return { layerDatetime: undefined, timeControlValues: undefined };
4186
4347
  }
4187
4348
 
4188
- // check if links has a datetime value
4189
- const dateProperty = getDatetimeProperty(links);
4349
+ // check if items has a datetime value
4350
+ const dateProperty = getDatetimeProperty(items);
4190
4351
 
4191
4352
  if (!dateProperty) {
4192
- return undefined;
4353
+ return { layerDatetime: undefined, timeControlValues: undefined };
4193
4354
  }
4194
-
4195
- /** @type {string[]} */
4196
- const controlValues = [];
4355
+ /** @type {{date:string;itemId:string}[]} */
4356
+ const timeValues = [];
4197
4357
  try {
4198
- currentStep = new Date(currentStep).toISOString();
4199
- links.reduce((vals, link) => {
4358
+ /**
4359
+ * @param {typeof timeValues} vals
4360
+ * @param {import("stac-ts").StacLink} link
4361
+ */
4362
+ const reduceLinks = (vals, link) => {
4200
4363
  if (link[dateProperty] && link.rel === "item") {
4201
- vals.push(
4202
- new Date(/** @type {string} */ (link[dateProperty])).toISOString(),
4203
- );
4364
+ vals.push({
4365
+ itemId: /** @type {string} */ (link.id),
4366
+ date: new Date(
4367
+ /** @type {string} */ (link[dateProperty]),
4368
+ ).toISOString(),
4369
+ });
4370
+ }
4371
+ return vals;
4372
+ };
4373
+
4374
+ /**
4375
+ *
4376
+ * @param {typeof timeValues} vals
4377
+ * @param {import("stac-ts").StacItem} item
4378
+ */
4379
+ const reduceItems = (vals, item) => {
4380
+ const date = item.properties?.[dateProperty];
4381
+ if (date) {
4382
+ vals.push({
4383
+ itemId: /** @type {string} */ (item.id),
4384
+ date: new Date(/** @type {string} */ (date)).toISOString(),
4385
+ ...item.properties,
4386
+ });
4204
4387
  }
4205
4388
  return vals;
4206
- }, controlValues);
4389
+ };
4390
+ currentStep = new Date(currentStep).toISOString();
4391
+ //@ts-expect-error TODO
4392
+ items.reduce(isSTACItem(items[0]) ? reduceItems : reduceLinks, timeValues);
4207
4393
  } catch (e) {
4208
4394
  console.warn("[eodash] not supported datetime format was provided", e);
4209
- return undefined;
4395
+ return { layerDatetime: undefined, timeControlValues: undefined };
4210
4396
  }
4211
- // not enough controlValues
4212
- if (controlValues.length <= 1) {
4213
- return undefined;
4397
+ // not enough timeValues
4398
+ if (timeValues.length <= 1) {
4399
+ return { layerDatetime: undefined, timeControlValues: undefined };
4214
4400
  }
4215
4401
 
4216
4402
  // item datetime is not included in the item links datetime
4217
- if (!controlValues.includes(currentStep)) {
4403
+ if (!timeValues.some((val) => val.date === currentStep)) {
4218
4404
  const currentStepTime = new Date(currentStep).getTime();
4219
- currentStep = controlValues.reduce((a, b) => {
4220
- const aDiff = Math.abs(new Date(a).getTime() - currentStepTime);
4221
- const bDiff = Math.abs(new Date(b).getTime() - currentStepTime);
4222
- return bDiff < aDiff ? b : a;
4223
- });
4405
+ currentStep = timeValues.reduce((time, step) => {
4406
+ const aDiff = Math.abs(new Date(time).getTime() - currentStepTime);
4407
+ const bDiff = Math.abs(new Date(step.date).getTime() - currentStepTime);
4408
+ return bDiff < aDiff ? step.date : time;
4409
+ }, timeValues[0].date);
4224
4410
  }
4225
4411
 
4226
- return {
4227
- controlValues,
4412
+ const layerDatetime = {
4413
+ controlValues: timeValues.map((d) => d.date).sort(),
4228
4414
  currentStep,
4229
4415
  slider: true,
4230
4416
  navigation: true,
4231
4417
  play: false,
4232
4418
  displayFormat: "DD.MM.YYYY HH:mm",
4233
4419
  };
4420
+
4421
+ return {
4422
+ layerDatetime,
4423
+ timeControlValues: timeValues,
4424
+ };
4234
4425
  };
4235
4426
 
4236
4427
  /**
@@ -4293,25 +4484,17 @@ const replaceLayer = (currentLayers, oldLayer, newLayers) => {
4293
4484
  * @param {import('../eodashSTAC/EodashCollection.js').EodashCollection[]} indicators
4294
4485
  * @param {import('ol/layer').Layer} layer
4295
4486
  */
4296
- const getColFromLayer = (indicators, layer) => {
4297
- // init cols
4298
- const collections = indicators.map((ind) => ind.collectionStac);
4299
- const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
4300
-
4301
- const chosen = collections.find((col) => {
4302
- const isInd =
4303
- col?.id === collectionId &&
4304
- col?.links?.some(
4305
- (link) =>
4306
- link.rel === "item" &&
4307
- (link.href.includes(itemId) ||
4308
- link.id === itemId ||
4309
- //@ts-expect-error attaching the item in link when parsing .parquet items, see @/eodashSTAC/parquet.js
4310
- (link["item"] && link["item"].id === itemId)),
4311
- );
4312
- return isInd;
4313
- });
4314
- return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
4487
+ const getColFromLayer = async (indicators, layer) => {
4488
+ const [collectionId, itemId] = layer.get("id").split(";:;");
4489
+
4490
+ for (const ind of indicators) {
4491
+ if (ind.collectionStac?.id !== collectionId) continue;
4492
+ const items = await ind.getItems();
4493
+ const itemIds = items?.map(item => item.id || item.datetime) ?? [];
4494
+ if (itemIds.includes(itemId)) {
4495
+ return ind;
4496
+ }
4497
+ }
4315
4498
  };
4316
4499
 
4317
4500
  /**
@@ -4326,14 +4509,14 @@ const getColFromLayer = (indicators, layer) => {
4326
4509
  */
4327
4510
  const createLayerID = (collectionId, itemId, link, projectionCode) => {
4328
4511
  const linkId = link.id || link.title || link.href;
4329
- let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${linkId ?? ""};:;${projectionCode}`;
4512
+ let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${linkId ?? ""};:;${projectionCode ?? ""}`;
4330
4513
  // If we are looking at base layers and overlays we remove the collection and item part
4331
4514
  // as we want to make sure tiles are not reloaded when switching layers
4332
4515
  if (
4333
4516
  /** @type {string[]} */
4334
4517
  (link.roles)?.find((r) => ["baselayer", "overlay"].includes(r))
4335
4518
  ) {
4336
- lId = `${linkId ?? ""};:;${projectionCode}`;
4519
+ lId = `${linkId ?? ""};:;${projectionCode ?? ""}`;
4337
4520
  }
4338
4521
  log.debug("Generated Layer ID", lId);
4339
4522
  return lId;
@@ -4393,12 +4576,20 @@ async function mergeGeojsons(geojsonUrls) {
4393
4576
  features: [],
4394
4577
  };
4395
4578
  await Promise.all(
4396
- geojsonUrls.map((url) =>
4397
- axios.get(url).then((resp) => {
4579
+ geojsonUrls.map((url) => {
4580
+ // Use native fetch for blob URLs to avoid axios/cache interceptor issues
4581
+ if (url.startsWith("blob:")) {
4582
+ return fetch(url)
4583
+ .then(async (resp) => await resp.json())
4584
+ .then((geojson) => {
4585
+ merged.features.push(...(geojson.features ?? []));
4586
+ });
4587
+ }
4588
+ return axios.get(url).then((resp) => {
4398
4589
  const geojson = resp.data;
4399
4590
  merged.features.push(...(geojson.features ?? []));
4400
- }),
4401
- ),
4591
+ });
4592
+ }),
4402
4593
  );
4403
4594
 
4404
4595
  return encodeURI(
@@ -4512,16 +4703,37 @@ const addTooltipInteraction = (layer, style) => {
4512
4703
 
4513
4704
  /**
4514
4705
  *
4515
- * @param {import("stac-ts").StacLink[]} [links]
4706
+ * @param {import("stac-ts").StacLink[] | import("stac-ts").StacItem[] |undefined |null} [linksOrItems]
4516
4707
  */
4517
- function getDatetimeProperty(links) {
4518
- if (!links?.length) {
4708
+ function getDatetimeProperty(linksOrItems) {
4709
+ if (!linksOrItems?.length) {
4519
4710
  return undefined;
4520
4711
  }
4712
+ const first = linksOrItems[0];
4713
+ let checkProperties = false;
4714
+ if (isSTACItem(first)) {
4715
+ checkProperties = true;
4716
+ }
4717
+
4521
4718
  // TODO: consider other properties for datetime ranges
4522
4719
  const datetimeProperties = ["datetime", "start_datetime", "end_datetime"];
4720
+ if (checkProperties) {
4721
+ for (const prop of datetimeProperties) {
4722
+ const propExists = linksOrItems.some(
4723
+ (l) =>
4724
+ //@ts-expect-error TODO
4725
+ l["properties"]?.[prop] &&
4726
+ //@ts-expect-error TODO
4727
+ typeof l["properties"]?.[prop] === "string",
4728
+ );
4729
+ if (!propExists) {
4730
+ continue;
4731
+ }
4732
+ return prop;
4733
+ }
4734
+ }
4523
4735
  for (const prop of datetimeProperties) {
4524
- const propExists = links.some(
4736
+ const propExists = linksOrItems.some(
4525
4737
  (l) => l[prop] && typeof l[prop] === "string",
4526
4738
  );
4527
4739
  if (!propExists) {
@@ -4530,7 +4742,82 @@ function getDatetimeProperty(links) {
4530
4742
  return prop;
4531
4743
  }
4532
4744
  }
4745
+ /**
4746
+ *
4747
+ * @param {*} stacObject
4748
+ * @returns {stacObject is import("stac-ts").StacItem}
4749
+ */
4750
+ function isSTACItem(stacObject) {
4751
+ return (
4752
+ stacObject &&
4753
+ typeof stacObject === "object" &&
4754
+ stacObject.collection &&
4755
+ stacObject.id &&
4756
+ stacObject.properties &&
4757
+ typeof stacObject.properties === "object"
4758
+ );
4759
+ }
4760
+
4761
+ /**
4762
+ * Fetch all STAC items from a STAC API endpoint.
4763
+ * @param {string} itemsUrl
4764
+ * @param {string} [query]
4765
+ * @param {number} [limit=100] - The maximum number of items to fetch per request.
4766
+ * @param {boolean} [returnFirst] - If true, only the first page of results will be returned.
4767
+ * @param {number} [maxNumber=1000] - if the matched number of items exceed this, only the first page will be returned.
4768
+ */
4769
+ async function fetchApiItems(
4770
+ itemsUrl,
4771
+ query,
4772
+ limit = 100,
4773
+ returnFirst = false,
4774
+ maxNumber = 1000,
4775
+ ) {
4776
+ itemsUrl = itemsUrl.includes("?") ? itemsUrl.split("?")[0] : itemsUrl;
4777
+ itemsUrl += query ? `?limit=${limit}&${query}` : `?limit=${limit}`;
4778
+
4779
+ const itemsFeatureCollection = await axios
4780
+ .get(itemsUrl)
4781
+ .then((resp) => resp.data);
4782
+ /** @type {import("stac-ts").StacItem[]} */
4783
+ const items = itemsFeatureCollection.features;
4784
+ const nextLink = itemsFeatureCollection.links?.find(
4785
+ //@ts-expect-error TODO: itemsFeatureCollection is not typed
4786
+ (link) => link.rel === "next",
4787
+ );
4788
+
4789
+ if (!nextLink || returnFirst) {
4790
+ return items;
4791
+ }
4792
+ /** @type {number} */
4793
+ const matchedItems = itemsFeatureCollection.numberMatched;
4794
+ // Avoid fetching too many items
4795
+ if (matchedItems >= maxNumber) {
4796
+ console.warn(
4797
+ `[eodash] The number of items matched (${matchedItems}) exceeds the maximum allowed (${maxNumber})`,
4798
+ );
4799
+ return items;
4800
+ }
4533
4801
 
4802
+ let [nextLinkURL, nextLinkQuery] = nextLink.href.split("?");
4803
+ nextLinkQuery = nextLinkQuery.replace(/limit=\d+/, "");
4804
+ if (query) {
4805
+ const queryParams = new URLSearchParams(query);
4806
+ const nextLinkParams = new URLSearchParams(nextLinkQuery);
4807
+
4808
+ for (const key of nextLinkParams.keys()) {
4809
+ queryParams.delete(key);
4810
+ }
4811
+ const remainingQuery = queryParams.toString();
4812
+ if (remainingQuery) {
4813
+ nextLinkQuery += `&${remainingQuery}`;
4814
+ }
4815
+ }
4816
+
4817
+ const nextPage = await fetchApiItems(nextLinkURL, nextLinkQuery);
4818
+ items.push(...nextPage);
4819
+ return items;
4820
+ }
4534
4821
  /**
4535
4822
  * @param {import ("stac-ts").StacCollection | undefined | null} collection
4536
4823
  * @returns {object}
@@ -4553,4 +4840,4 @@ function extractLayerLegend(collection) {
4553
4840
  return extraProperties;
4554
4841
  }
4555
4842
 
4556
- export { activeTemplate as A, mapPosition as B, indicator as C, compareIndicator as D, comparePoi as E, states as F, actions as G, includesProcess as H, mapCompareEl as I, getColFromLayer as J, setMapProjFromCol as K, availableMapProjection as L, changeMapProjection as M, setActiveTemplate as N, axios$1 as O, removeUnneededProperties as P, createAssetID as a, addTooltipInteraction as b, createLayerID as c, mapEl as d, extractRoles as e, axios as f, getProjectionCode as g, extractLayerConfig as h, fetchStyle as i, extractLayerDatetime as j, getDatetimeProperty as k, getLayers as l, mergeGeojsons as m, getCompareLayers as n, findLayer as o, replaceLayer as p, generateFeatures as q, registerProjection as r, extractLayerLegend as s, extractCollectionUrls as t, generateLinksFromItems as u, revokeCollectionBlobUrls as v, currentCompareUrl as w, currentUrl as x, datetime as y, poi as z };
4843
+ export { datetime as A, poi as B, activeTemplate as C, mapPosition as D, indicator as E, currentCompareUrl as F, compareIndicator as G, comparePoi as H, states as I, actions as J, includesProcess as K, sanitizeBbox as L, mapCompareEl as M, getColFromLayer as N, setMapProjFromCol as O, availableMapProjection as P, changeMapProjection as Q, setActiveTemplate as R, compareChartEl as S, chartEl as T, axios$1 as U, removeUnneededProperties as V, getChartSpec as W, extractLayerConfig as a, addTooltipInteraction as b, createLayerID as c, createAssetID as d, extractRoles as e, fetchStyle as f, getProjectionCode as g, mapEl as h, isSTACItem as i, axios as j, extractLayerTimeValues as k, fetchApiItems as l, mergeGeojsons as m, getDatetimeProperty as n, fetchAllStyles as o, getLayers as p, getCompareLayers as q, registerProjection as r, findLayer as s, replaceLayer as t, generateFeatures as u, extractLayerLegend as v, extractCollectionUrls as w, generateLinksFromItems as x, revokeCollectionBlobUrls as y, currentUrl as z };