@eodash/eodash 5.5.2 → 5.6.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.
Files changed (61) hide show
  1. package/core/client/App.vue +1 -1
  2. package/core/client/eodashSTAC/EodashCollection.js +7 -4
  3. package/core/client/eodashSTAC/auth.js +19 -9
  4. package/core/client/eodashSTAC/createLayers.js +130 -24
  5. package/core/client/eodashSTAC/helpers.js +23 -56
  6. package/core/client/store/stac.js +9 -0
  7. package/core/client/types.ts +1 -0
  8. package/dist/client/{DashboardLayout-BAB1tPv0.js → DashboardLayout-DkKDhfgH.js} +3 -3
  9. package/dist/client/{DynamicWebComponent-qVeu4sqS.js → DynamicWebComponent-BrpkA-e3.js} +2 -2
  10. package/dist/client/{EodashChart-CXnNxlhk.js → EodashChart-p7_1FRlF.js} +5 -5
  11. package/dist/client/{EodashDatePicker-KyJXPgAq.js → EodashDatePicker-HK0Jl7wQ.js} +4 -4
  12. package/dist/client/{EodashItemFilter-CAlyUnje.js → EodashItemFilter-BYQDA78B.js} +2 -2
  13. package/dist/client/{EodashLayerControl-D8VobuZJ.js → EodashLayerControl-NcrNncGn.js} +18 -4
  14. package/dist/client/{EodashLayoutSwitcher-DI6uGF_7.js → EodashLayoutSwitcher-CC2Znd0I.js} +4 -4
  15. package/dist/client/EodashMapBtns-BJSh7qT-.js +435 -0
  16. package/dist/client/{EodashStacInfo-Cb28jVEr.js → EodashStacInfo-BA-0UjvT.js} +42 -7
  17. package/dist/client/{EodashTimeSlider-UIpnhO4N.js → EodashTimeSlider-GDv1hbw7.js} +3 -3
  18. package/dist/client/{EodashTools-vxReGN8p.js → EodashTools-CWmoH0g_.js} +5 -5
  19. package/dist/client/{ExportState-D6CBpKUU.js → ExportState-CfAfX4RN.js} +9 -7
  20. package/dist/client/{Footer-CUgMupXk.js → Footer-DLGxQTZt.js} +2 -2
  21. package/dist/client/{Header-BZ53ACvp.js → Header-eyEyyzD6.js} +10 -6
  22. package/dist/client/{IframeWrapper-XzQDZy0T.js → IframeWrapper-BgM9aU8f.js} +1 -1
  23. package/dist/client/MobileLayout-RzLOyXwL.js +118 -0
  24. package/dist/client/{PopUp-C6HJH8wM.js → PopUp-WNs6Io2N.js} +3 -3
  25. package/dist/client/{ProcessList-DQjvv7Cc.js → ProcessList-RzVqbd5E.js} +5 -5
  26. package/dist/client/{VImg-CWCCg87G.js → VImg-GKUTFoV-.js} +51 -22
  27. package/dist/client/{VMain-CAzXMN4O.js → VMain-D7_72mHr.js} +1 -1
  28. package/dist/client/{VTooltip-B1_FzBl_.js → VTooltip-BbmvtvYw.js} +2 -2
  29. package/dist/client/{WidgetsContainer-Bc8pGq_X.js → WidgetsContainer-Dw5d7SOW.js} +2 -2
  30. package/dist/client/asWebComponent-DDt7V3Bl.js +9383 -0
  31. package/dist/client/{async-Byq11ZKl.js → async-CT3KEV52.js} +24 -33
  32. package/dist/client/eo-dash.js +1 -1
  33. package/dist/client/{forwardRefs-D3na5VY2.js → forwardRefs-CVzxyoSq.js} +2 -2
  34. package/dist/client/{handling-7IVxQxtQ.js → handling-C285F_Mq.js} +3 -3
  35. package/dist/client/{helpers-Bi-qmnbZ.js → helpers-CZFH0EVN.js} +349 -316
  36. package/dist/client/{index-Ch4PgFGP.js → index-B-m7V3Kb.js} +24 -11
  37. package/dist/client/{index-FQrkgCu_.js → index-B_6H-N14.js} +7 -7
  38. package/dist/client/{index-Dk5_G7Aw.js → index-CF3ZMzTz.js} +4 -4
  39. package/dist/client/{index-CJOvBxMx.js → index-DgOXD7wd.js} +2 -2
  40. package/dist/client/templates.js +6 -15
  41. package/dist/client/{transition-Bp5R_Vzd.js → transition-C2gnrWg0.js} +1 -1
  42. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +2 -5
  43. package/dist/types/core/client/eodashSTAC/auth.d.ts +6 -2
  44. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +1 -4
  45. package/dist/types/core/client/eodashSTAC/helpers.d.ts +1 -2
  46. package/dist/types/core/client/store/stac.d.ts +4 -1
  47. package/dist/types/core/client/types.d.ts +1 -0
  48. package/dist/types/templates/baseConfig.d.ts +1 -0
  49. package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
  50. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  51. package/dist/types/widgets/EodashStacInfo.vue.d.ts +140 -25
  52. package/package.json +31 -31
  53. package/templates/baseConfig.js +5 -14
  54. package/widgets/EodashMap/EodashMapBtns.vue +15 -12
  55. package/widgets/EodashMap/index.vue +5 -1
  56. package/widgets/EodashMap/methods/index.js +13 -4
  57. package/widgets/EodashProcess/methods/utils.js +22 -31
  58. package/widgets/EodashStacInfo.vue +40 -5
  59. package/dist/client/EodashMapBtns-BjF8Yrpd.js +0 -433
  60. package/dist/client/MobileLayout-jnu24OJM.js +0 -118
  61. package/dist/client/asWebComponent-C-6K08Kk.js +0 -9206
@@ -24,7 +24,7 @@ defineProps({
24
24
  },
25
25
  });
26
26
 
27
- window.setEodashLoglevel("DEBUG");
27
+ // window.setEodashLoglevel("DEBUG")
28
28
 
29
29
  const error = ref("");
30
30
  const isWebComponent = !!document.querySelector("eo-dash");
@@ -11,9 +11,9 @@ import {
11
11
  getDatetimeProperty,
12
12
  isSTACItem,
13
13
  findLayersByLayerPrefix,
14
- replaceLayersInStructure,
15
14
  extractLayerLegend,
16
15
  extractLayerTimeValues,
16
+ replaceLayer,
17
17
  } from "./helpers";
18
18
  import {
19
19
  getLayers,
@@ -195,7 +195,9 @@ export class EodashCollection {
195
195
 
196
196
  const isSupported =
197
197
  item.links.some((link) =>
198
- ["wms", "xyz", "wmts", "vector-tile"].includes(link.rel),
198
+ ["wms", "xyz", "wmts", "vector-tile", "mapbox-style-document"].includes(
199
+ link.rel,
200
+ ),
199
201
  ) || Object.keys(dataAssets).length;
200
202
 
201
203
  if (isSupported) {
@@ -453,9 +455,10 @@ export class EodashCollection {
453
455
  if (!toBeReplacedLayers) {
454
456
  return;
455
457
  }
456
- const updatedLayers = replaceLayersInStructure(
458
+ const updatedLayers = replaceLayer(
457
459
  currentLayers,
458
- toBeReplacedLayers,
460
+ /** @type {string[]} */ (toBeReplacedLayers.map((l) => l.properties?.id)),
461
+ //@ts-expect-error createLayersJson is not typed correctly
459
462
  newLayers,
460
463
  );
461
464
 
@@ -2,17 +2,22 @@
2
2
  * Generic handler for possible authentications schemes as defined in STAC authentication extension.
3
3
  * @param {import("@/types").StacAuthItem } item
4
4
  * @param {import("@/types").StacAuthLink | import("@/types").StacAuthAsset} linkOrAsset
5
- * @returns {string}
5
+ * @param { object | undefined } optionsObject // generic object to pass options to handlers and modify them if needed
6
+ * @returns {{url: string, optionsObject: object | undefined}}
6
7
  */
7
- export function handleAuthenticationOfLink(item, linkOrAsset) {
8
+ export function handleAuthenticationOfLink(item, linkOrAsset, optionsObject) {
8
9
  // browse through all authentication refs on a link to find a first one we support
9
10
  for (const authRef of linkOrAsset["auth:refs"] || []) {
10
11
  const authSchemes = item["auth:schemes"];
11
12
  if (authRef in authSchemes) {
12
13
  switch (authSchemes[authRef].type) {
13
14
  case "apiKey": {
14
- //@ts-expect-error TODO
15
- return handleApiKeyBasedAuth(authSchemes[authRef], linkOrAsset.href);
15
+ return handleApiKeyBasedAuth(
16
+ //@ts-expect-error TODO
17
+ authSchemes[authRef],
18
+ linkOrAsset.href,
19
+ optionsObject,
20
+ );
16
21
  }
17
22
  // case "signedUrl":
18
23
  // case "s3":
@@ -28,15 +33,16 @@ export function handleAuthenticationOfLink(item, linkOrAsset) {
28
33
  }
29
34
  }
30
35
  }
31
- return linkOrAsset.href;
36
+ return { url: linkOrAsset.href, optionsObject };
32
37
  }
33
38
  /**
34
39
  * Generic handler for possible authentications schemes as defined in STAC authentication extension.
35
40
  * @param {import("@/types").ApiKeyAuthScheme } schemeDef
36
41
  * @param { string } href
37
- * @returns { string }
42
+ * @param { object | undefined } optionsObject
43
+ * @returns { {url: string, optionsObject: object | undefined} }
38
44
  */
39
- function handleApiKeyBasedAuth(schemeDef, href) {
45
+ function handleApiKeyBasedAuth(schemeDef, href, optionsObject) {
40
46
  // add token to query parameters of href
41
47
  let url = href;
42
48
  switch (schemeDef.in) {
@@ -45,7 +51,11 @@ function handleApiKeyBasedAuth(schemeDef, href) {
45
51
  const envVar = "EODASH_" + apiKey;
46
52
  const envValue = process.env[envVar];
47
53
  if (envValue) {
48
- url = setQueryParam(href, apiKey, envValue);
54
+ if (typeof optionsObject !== "undefined") {
55
+ optionsObject = { ...optionsObject, apiKey: envValue };
56
+ } else {
57
+ url = setQueryParam(href, apiKey, envValue);
58
+ }
49
59
  } else {
50
60
  console.error(
51
61
  `env variable ${envVar} for authentication parameter ${apiKey} not set`,
@@ -56,7 +66,7 @@ function handleApiKeyBasedAuth(schemeDef, href) {
56
66
  default:
57
67
  console.error("eodash does not support any referenced handler");
58
68
  }
59
- return url;
69
+ return { url, optionsObject };
60
70
  }
61
71
 
62
72
  /**
@@ -15,6 +15,24 @@ import {
15
15
  } from "./helpers";
16
16
  import { handleAuthenticationOfLink } from "./auth";
17
17
  import log from "loglevel";
18
+ import { useSTAcStore } from "@/store/stac";
19
+
20
+ /**
21
+ * Build a WMTS GetCapabilities URL from a base endpoint URL.
22
+ * If the URL already points to a capabilities document, return as-is.
23
+ *
24
+ * @param {string} href
25
+ * @returns {string}
26
+ */
27
+ function buildCapabilitiesUrl(href) {
28
+ if (href.includes("GetCapabilities") || href.endsWith(".xml")) {
29
+ return href;
30
+ }
31
+ const url = new URL(href);
32
+ url.searchParams.set("service", "WMTS");
33
+ url.searchParams.set("request", "GetCapabilities");
34
+ return url.toString();
35
+ }
18
36
 
19
37
  /**
20
38
  * @param {string} collectionId
@@ -210,10 +228,10 @@ export async function createLayersFromAssets(
210
228
  const projection = getProjectionCode(assetProjection) || "EPSG:4326";
211
229
  // in case we merge them, we pass urls, else just single url
212
230
  const urlsObject = {
213
- url: fgbSource,
214
- // TODO uncomment this once fgb merging supported on source
215
- // url: stacObject?.["eodash:merge_assets"] === false ? fgbSource : undefined,
216
- // urls: stacObject?.["eodash:merge_assets"] !== false ? fgbSources : undefined,
231
+ url:
232
+ stacObject?.["eodash:merge_assets"] === false
233
+ ? fgbSource
234
+ : fgbSources,
217
235
  };
218
236
  const layer = {
219
237
  type: "Vector",
@@ -242,9 +260,7 @@ export async function createLayersFromAssets(
242
260
  addTooltipInteraction(layer, style);
243
261
  jsonArray.push(layer);
244
262
  // if we merged assets (default yes), then we can break from this loop
245
- // TODO uncomment this once fgb merging supported on source
246
- // if (stacObject?.["eodash:merge_assets"] !== false)
247
- // break
263
+ if (stacObject?.["eodash:merge_assets"] !== false) break;
248
264
  }
249
265
  }
250
266
 
@@ -271,7 +287,7 @@ export async function createLayersFromAssets(
271
287
  const sources =
272
288
  stacObject?.["eodash:merge_assets"] !== false
273
289
  ? geoTIFFSources
274
- : geotiffSource;
290
+ : [geotiffSource];
275
291
  const layer = {
276
292
  type: "WebGLTile",
277
293
  source: {
@@ -282,7 +298,7 @@ export async function createLayersFromAssets(
282
298
  },
283
299
  properties: {
284
300
  id: assetLayerId,
285
- title,
301
+ title: assets[assetName]?.title || title,
286
302
  layerConfig,
287
303
  layerDatetime,
288
304
  },
@@ -325,7 +341,8 @@ export const createLayersFromLinks = async (
325
341
  const xyzArray = item.links.filter((l) => l.rel === "xyz") ?? [];
326
342
  const vectorTileArray =
327
343
  item.links.filter((l) => l.rel === "vector-tile") ?? [];
328
-
344
+ const mapboxStyleDocumentArray =
345
+ item.links.filter((l) => l.rel === "mapbox-style-document") ?? [];
329
346
  // Taking projection code from main map view, as main view defines
330
347
  // projection for comparison map
331
348
  const viewProjectionCode = mapEl?.value?.projection || "EPSG:3857";
@@ -375,6 +392,15 @@ export const createLayersFromLinks = async (
375
392
  },
376
393
  },
377
394
  };
395
+ if (
396
+ // @ts-expect-error missing type definition, can be accessed like this
397
+ wmsLink.roles?.includes("baselayer") ||
398
+ // @ts-expect-error missing type definition, can be accessed like this
399
+ wmsLink.roles?.includes("overlay")
400
+ ) {
401
+ // @ts-expect-error no type for eox-map
402
+ json.preload = Infinity;
403
+ }
378
404
  if ("wms:version" in wmsLink) {
379
405
  // @ts-expect-error no type for eox-map
380
406
  json.source.params["VERSION"] = wmsLink["wms:version"];
@@ -431,7 +457,7 @@ export const createLayersFromLinks = async (
431
457
  const dimensions = /** @type { {style:any} & Record<string,any> } */ (
432
458
  wmtsLink["wmts:dimensions"] || {}
433
459
  );
434
- let { style, ...dimensionsWithoutStyle } = { ...dimensions };
460
+ let { style, matrixSet, ...dimensionsWithoutStyle } = { ...dimensions };
435
461
  let extractedStyle = /** @type { string } */ (style || "default");
436
462
 
437
463
  // TODO, this does not yet work between layer time changes because we do not get
@@ -475,10 +501,8 @@ export const createLayersFromLinks = async (
475
501
  },
476
502
  };
477
503
  } else {
478
- log.debug(
479
- "Warning: WMTS Layer from capabilities added, function needs to be updated",
480
- linkId,
481
- );
504
+ log.debug("WMTS Layer from capabilities added", linkId);
505
+
482
506
  json = {
483
507
  type: "Tile",
484
508
  properties: {
@@ -487,15 +511,11 @@ export const createLayersFromLinks = async (
487
511
  layerDatetime,
488
512
  },
489
513
  source: {
490
- type: "WMTS",
491
- url: wmtsLink.href,
514
+ type: "WMTSCapabilities",
515
+ url: buildCapabilitiesUrl(wmtsLink.href),
492
516
  layer: wmtsLink["wmts:layer"],
493
517
  style: extractedStyle,
494
- matrixSet: wmtsLink.matrixSet || "EPSG:3857",
495
- projection: projectionCode,
496
- tileGrid: {
497
- tileSize: [512, 512],
498
- },
518
+ ...(matrixSet ? { matrixSet } : {}),
499
519
  attributions: wmtsLink.attribution,
500
520
  dimensions: dimensionsWithoutStyle,
501
521
  },
@@ -556,6 +576,16 @@ export const createLayersFromLinks = async (
556
576
  xyzUrl = `${base}?${params.toString()}`;
557
577
  }
558
578
 
579
+ const { supportedUpscalingEndpoints } = useSTAcStore();
580
+ const isUpscalingSupported = supportedUpscalingEndpoints.some(
581
+ (/** @type {string} */ endpoint) => xyzUrl.includes(endpoint),
582
+ );
583
+
584
+ // Add sharding for s2maps automatically
585
+ if (xyzUrl.includes("s2maps-tiles.eu")) {
586
+ xyzUrl = xyzUrl.replace("s2maps-tiles.eu", "{a-e}.s2maps-tiles.eu");
587
+ }
588
+
559
589
  log.debug("XYZ Layer added", linkId);
560
590
  let json = {
561
591
  type: "Tile",
@@ -568,11 +598,26 @@ export const createLayersFromLinks = async (
568
598
  },
569
599
  source: {
570
600
  type: "XYZ",
571
- url: xyzUrl,
601
+ url: isUpscalingSupported ? xyzUrl.replace("{y}", "{y}@2x") : xyzUrl,
572
602
  projection: projectionCode,
573
603
  attributions: xyzLink.attribution,
574
604
  },
575
605
  };
606
+ if (isUpscalingSupported) {
607
+ // @ts-expect-error tileGrid is added here and supported in eox-map layer definition
608
+ json.source.tileGrid = {
609
+ tileSize: [512, 512],
610
+ };
611
+ }
612
+ if (
613
+ // @ts-expect-error missing type definition, can be accessed like this
614
+ xyzLink.roles?.includes("baselayer") ||
615
+ // @ts-expect-error missing type definition, can be accessed like this
616
+ xyzLink.roles?.includes("overlay")
617
+ ) {
618
+ // @ts-expect-error no type for eox-map
619
+ json.preload = Infinity;
620
+ }
576
621
 
577
622
  extractRoles(json.properties, xyzLink);
578
623
  if (extraProperties !== null) {
@@ -610,10 +655,12 @@ export const createLayersFromLinks = async (
610
655
 
611
656
  let href = vectorTileLink.href;
612
657
  if ("auth:schemes" in item && "auth:refs" in vectorTileLink) {
613
- href = handleAuthenticationOfLink(
658
+ const { url } = handleAuthenticationOfLink(
614
659
  /** @type { import("@/types").StacAuthItem} */ (item),
615
660
  /** @type { import("@/types").StacAuthLink} */ (vectorTileLink),
661
+ undefined,
616
662
  );
663
+ href = url;
617
664
  }
618
665
  const json = {
619
666
  type: "VectorTile",
@@ -655,6 +702,65 @@ export const createLayersFromLinks = async (
655
702
  }
656
703
  jsonArray.push(json);
657
704
  }
705
+
706
+ for (const mapboxStyleDocumentLink of mapboxStyleDocumentArray ?? []) {
707
+ const mapboxStyleDocumentLinkProjection =
708
+ /** @type {number | string | {name: string, def: string} | undefined} */
709
+ (
710
+ mapboxStyleDocumentLink?.["proj:epsg"] ||
711
+ mapboxStyleDocumentLink?.["eodash:proj4_def"]
712
+ );
713
+
714
+ await registerProjection(mapboxStyleDocumentLinkProjection);
715
+ const projectionCode = getProjectionCode(
716
+ mapboxStyleDocumentLinkProjection || "EPSG:3857",
717
+ );
718
+ const linkId = createLayerID(
719
+ collectionId,
720
+ item.id,
721
+ mapboxStyleDocumentLink,
722
+ viewProjectionCode,
723
+ );
724
+ log.debug("Mapbox Style Document Layer added", linkId);
725
+
726
+ let href = mapboxStyleDocumentLink.href;
727
+ let applyOptions = mapboxStyleDocumentLink?.applyOptions || {};
728
+ if ("auth:schemes" in item && "auth:refs" in mapboxStyleDocumentLink) {
729
+ const { url, optionsObject } = handleAuthenticationOfLink(
730
+ /** @type { import("@/types").StacAuthItem} */ (item),
731
+ /** @type { import("@/types").StacAuthLink} */ (
732
+ mapboxStyleDocumentLink
733
+ ),
734
+ applyOptions,
735
+ );
736
+ applyOptions = /** @type { object } */ (optionsObject);
737
+ href = url;
738
+ }
739
+ const json = {
740
+ type: "MapboxStyle",
741
+ properties: {
742
+ id: linkId,
743
+ title: mapboxStyleDocumentLink.title || title || item.id,
744
+ roles: mapboxStyleDocumentLink.roles,
745
+ layerDatetime,
746
+ mapboxStyle: href,
747
+ projection: projectionCode,
748
+ attributions: mapboxStyleDocumentLink.attribution,
749
+ applyOptions,
750
+ },
751
+ interactions: [],
752
+ };
753
+ extractRoles(json.properties, mapboxStyleDocumentLink);
754
+ if (extraProperties !== null) {
755
+ json.properties = {
756
+ ...json.properties,
757
+ ...extraProperties,
758
+ ...extractEoxLegendLink(mapboxStyleDocumentLink),
759
+ };
760
+ }
761
+ jsonArray.push(json);
762
+ }
763
+
658
764
  return jsonArray;
659
765
  };
660
766
  /**
@@ -392,39 +392,35 @@ export const findLayer = (layers, layer) => {
392
392
  };
393
393
 
394
394
  /**
395
- * Removes a list of layers (and nested ones) from a layer/group structure,
396
- * and inserts new layers in place of the first removed one.
395
+ * Removes one or more layers (by id) from a layer/group structure and inserts
396
+ * new layers in place of the first removed one.
397
+ * Returns a new array reference at every level that changed (immutable).
397
398
  *
398
- * @param {import("@eox/map").EoxLayer[]} layers - Array of layers or groups.
399
- * @param {import("@eox/map").EoxLayer[]} toRemove - Array of layers to remove (by id).
400
- * @param {Record<string, any>[]} toInsert - Array of layers to insert instead.
401
- * @returns {Record<string, any>[]} New array with replacements applied.
399
+ * @param {import("@eox/map").EoxLayer[]} layers - Current layer array.
400
+ * @param {string | string[]} toRemove - Id(s) of layers to remove.
401
+ * @param {import("@eox/map").EoxLayer[]} toInsert - Layers to insert in place of the first removed one.
402
+ * @returns {import("@eox/map").EoxLayer[]}
402
403
  */
403
- export const replaceLayersInStructure = (layers, toRemove, toInsert) => {
404
- const result = [];
404
+ export const replaceLayer = (layers, toRemove, toInsert) => {
405
+ const removeIds = new Set(Array.isArray(toRemove) ? toRemove : [toRemove]);
405
406
  let inserted = false;
407
+ const result = [];
406
408
 
407
409
  for (const layer of layers) {
408
410
  if (layer.type === "Group" && Array.isArray(layer.layers)) {
409
- // Recurse into group layers
410
- const newGroupLayers = replaceLayersInStructure(
411
- layer.layers,
412
- toRemove,
413
- toInsert,
411
+ const newGroupLayers = replaceLayer(layer.layers, toRemove, toInsert);
412
+ // Only create a new object reference if children changed
413
+ result.push(
414
+ newGroupLayers !== layer.layers
415
+ ? { ...layer, layers: newGroupLayers }
416
+ : layer,
414
417
  );
415
- result.push({ ...layer, layers: newGroupLayers });
416
418
  continue;
417
419
  }
418
420
 
419
- // Match by reference or id
420
421
  const id = layer?.properties?.id;
421
- const isToRemove = toRemove.some((rem) => {
422
- const remId = rem?.properties?.id;
423
- return remId === id;
424
- });
425
422
 
426
- if (isToRemove) {
427
- // On first removed occurrence, insert the new layers
423
+ if (id && removeIds.has(id)) {
428
424
  if (!inserted) {
429
425
  result.push(...toInsert);
430
426
  inserted = true;
@@ -433,43 +429,14 @@ export const replaceLayersInStructure = (layers, toRemove, toInsert) => {
433
429
  continue;
434
430
  }
435
431
 
436
- result.unshift(layer);
437
- }
438
-
439
- return result;
440
- };
441
-
442
- /**
443
- * Removes the layer with the id provided and injects an array of layers in its position
444
- * @param {import("@eox/map").EoxLayer[]} currentLayers
445
- * @param {string} oldLayer - id of the layer to be replaced
446
- * @param {import("@eox/map").EoxLayer[]} newLayers - array of layers to replace the old layer
447
- * @returns {import("@eox/map").EoxLayer[]}
448
- */
449
- export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
450
- const oldLayerIdx = currentLayers.findIndex(
451
- (l) => l.properties?.id === oldLayer,
452
- );
453
-
454
- if (oldLayerIdx !== -1) {
455
- log.debug(
456
- "Replacing layer",
457
- oldLayer,
458
- "with",
459
- newLayers.map((l) => l.properties?.id),
460
- );
461
- currentLayers.splice(oldLayerIdx, 1, ...newLayers);
432
+ result.push(layer);
462
433
  }
463
434
 
464
- for (const l of currentLayers) {
465
- if (l.type === "Group") {
466
- const updatedGroupLyrs = replaceLayer(l.layers, oldLayer, newLayers);
467
- if (updatedGroupLyrs?.length) {
468
- l.layers = updatedGroupLyrs;
469
- }
470
- }
471
- }
472
- return currentLayers;
435
+ // If nothing changed, return the original reference to avoid unnecessary re-renders
436
+ return result.length === layers.length &&
437
+ result.every((l, i) => l === layers[i])
438
+ ? layers
439
+ : result;
473
440
  };
474
441
 
475
442
  /**
@@ -29,6 +29,12 @@ export const useSTAcStore = defineStore("stac", () => {
29
29
  const rasterEndpoint = ref(null);
30
30
  const isApi = ref(false);
31
31
 
32
+ /**
33
+ * List of supported endpoints for upscaling
34
+ * @type {import("vue").Ref<string[]>}
35
+ */
36
+ const supportedUpscalingEndpoints = ref([]);
37
+
32
38
  /**
33
39
  * Links of the root STAC catalog
34
40
  *
@@ -77,6 +83,8 @@ export const useSTAcStore = defineStore("stac", () => {
77
83
  stacEndpoint.value = endpoint.endpoint;
78
84
  isApi.value = endpoint.api ?? false;
79
85
  rasterEndpoint.value = endpoint.rasterEndpoint ?? null;
86
+ supportedUpscalingEndpoints.value =
87
+ endpoint.supportedUpscalingEndpoints ?? [];
80
88
  }
81
89
 
82
90
  /**
@@ -249,5 +257,6 @@ export const useSTAcStore = defineStore("stac", () => {
249
257
  selectedStac,
250
258
  selectedCompareStac,
251
259
  selectedItem,
260
+ supportedUpscalingEndpoints,
252
261
  };
253
262
  });
@@ -331,6 +331,7 @@ export type StacEndpoint =
331
331
  api?: boolean;
332
332
  rasterEndpoint?: string;
333
333
  vectorEndpoint?: string;
334
+ supportedUpscalingEndpoints?: string[];
334
335
  };
335
336
 
336
337
  /** @group Eodash */
@@ -1,7 +1,7 @@
1
- import { createBlock, openBlock, withCtx, createElementVNode, unref, createElementBlock, createCommentVNode, normalizeStyle, Suspense, resolveDynamicComponent, mergeProps, Fragment, renderList, Transition } from 'vue';
1
+ import { openBlock, createBlock, withCtx, createElementVNode, unref, createElementBlock, normalizeStyle, Suspense, resolveDynamicComponent, mergeProps, createCommentVNode, Fragment, renderList, Transition } from 'vue';
2
2
  import '@eox/layout';
3
- import { _ as _export_sfc, u as useDefineTemplate } from './asWebComponent-C-6K08Kk.js';
4
- import { V as VMain } from './VMain-CAzXMN4O.js';
3
+ import { _ as _export_sfc, u as useDefineTemplate } from './asWebComponent-DDt7V3Bl.js';
4
+ import { V as VMain } from './VMain-D7_72mHr.js';
5
5
 
6
6
  const _style_0 = ".panel[data-v-9c8477cc]{position:relative;overflow:auto;z-index:1;pointer-events:none}.pointer[data-v-9c8477cc]{pointer-events:all}.bg-panel[data-v-9c8477cc]{z-index:0;border-radius:0!important}.fade-enter-active[data-v-9c8477cc],.fade-leave-active[data-v-9c8477cc]{transition:opacity .25s ease}.fade-enter-from[data-v-9c8477cc],.fade-leave-to[data-v-9c8477cc]{opacity:0}.bg-surface[data-v-9c8477cc],.bg-primary[data-v-9c8477cc]{backdrop-filter:blur(10px)!important;border-radius:8px;border:none;box-shadow:0 0 1px #18274b38,0 6px 12px -6px #18274b1f,0 8px 24px -4px #18274b14;max-height:100%;overflow:auto;scrollbar-color:rgba(var(--v-theme-on-surface),.2) transparent;scrollbar-width:thin}.bg-surface[data-v-9c8477cc]{background-color:rgba(var(--v-theme-surface),var(--v-surface-opacity, .8))!important}.bg-primary[data-v-9c8477cc]{background-color:rgba(var(--v-theme-primary),var(--v-primary-opacity, .8))!important}";
7
7
 
@@ -1,5 +1,5 @@
1
- import { withAsyncContext, ref, onMounted, onUnmounted, createElementBlock, openBlock, createBlock, resolveDynamicComponent, mergeProps } from 'vue';
2
- import { E as useSTAcStore } from './asWebComponent-C-6K08Kk.js';
1
+ import { withAsyncContext, ref, onMounted, onUnmounted, openBlock, createElementBlock, createBlock, resolveDynamicComponent, mergeProps } from 'vue';
2
+ import { H as useSTAcStore } from './asWebComponent-DDt7V3Bl.js';
3
3
 
4
4
  const _hoisted_1 = { class: "d-flex flex-column fill-height overflow-auto" };
5
5
 
@@ -1,10 +1,10 @@
1
- import { computed, ref, useTemplateRef, onMounted, onBeforeUnmount, createElementBlock, openBlock, createElementVNode, withDirectives, createCommentVNode, unref, normalizeStyle, toRaw } from 'vue';
1
+ import { computed, ref, useTemplateRef, onMounted, onBeforeUnmount, openBlock, createElementBlock, createElementVNode, withDirectives, unref, createCommentVNode, normalizeStyle, toRaw } from 'vue';
2
2
  import '@eox/chart';
3
- import { o as onChartClick } from './handling-7IVxQxtQ.js';
4
- import { Z as compareChartData, _ as chartData, O as compareChartSpec, N as chartSpec, M as areChartsSeparateLayout } from './helpers-Bi-qmnbZ.js';
5
- import { _ as _export_sfc, S as getOverlayParent } from './asWebComponent-C-6K08Kk.js';
3
+ import { o as onChartClick } from './handling-C285F_Mq.js';
4
+ import { Z as compareChartData, _ as chartData, O as compareChartSpec, N as chartSpec, M as areChartsSeparateLayout } from './helpers-CZFH0EVN.js';
5
+ import { _ as _export_sfc, W as getOverlayParent } from './asWebComponent-DDt7V3Bl.js';
6
6
  import { mdiArrowCollapse, mdiArrowExpand } from '@mdi/js';
7
- import { T as Tooltip } from './index-CJOvBxMx.js';
7
+ import { T as Tooltip } from './index-DgOXD7wd.js';
8
8
 
9
9
  const _style_0 = ".chart-frame[data-v-c9dee45e]{position:relative}.chart-toggle[data-v-c9dee45e]{position:absolute;top:18px;right:46px;z-index:2;cursor:pointer}";
10
10
 
@@ -1,11 +1,11 @@
1
- import { useCssVars, ref, useTemplateRef, customRef, computed, reactive, watch, onMounted, createElementBlock, openBlock, createVNode, unref, createSlots, withCtx, createElementVNode, withDirectives, createCommentVNode, createBlock, mergeProps, toHandlers } from 'vue';
1
+ import { useCssVars, useTemplateRef, customRef, ref, computed, reactive, watch, onMounted, openBlock, createElementBlock, createVNode, unref, createSlots, withCtx, createElementVNode, withDirectives, createBlock, createCommentVNode, mergeProps, toHandlers } from 'vue';
2
2
  import { DatePicker } from 'v-calendar';
3
- import { _ as _export_sfc, D as useDisplay, E as useSTAcStore, F as useTransparentPanel, G as VBtn, H as VIcon, I as eodashCollections, J as eodashCompareCollections } from './asWebComponent-C-6K08Kk.js';
4
- import { C as datetime } from './helpers-Bi-qmnbZ.js';
3
+ import { _ as _export_sfc, G as useDisplay, H as useSTAcStore, I as useTransparentPanel, J as VBtn, K as VIcon, L as eodashCollections, M as eodashCompareCollections } from './asWebComponent-DDt7V3Bl.js';
4
+ import { B as datetime } from './helpers-CZFH0EVN.js';
5
5
  import { mdiRayEndArrow, mdiRayStartArrow } from '@mdi/js';
6
6
  import log from 'loglevel';
7
7
  import { storeToRefs } from 'pinia';
8
- import { T as Tooltip } from './index-CJOvBxMx.js';
8
+ import { T as Tooltip } from './index-DgOXD7wd.js';
9
9
 
10
10
  const _style_0 = ".vc-popover-content{--vc-nav-hover-bg: rgba(var(--v-theme-on-surface), .1);--vc-nav-item-active-color: rgb(var(--v-theme-on-secondary));--vc-nav-item-active-bg: rgba(var(--v-theme-secondary), .8);--vc-focus-ring: 0 0 0 2px rgba(var(--v-theme-secondary), .5)}.vc-container{--vc-day-content-hover-bg: rgba(var(--v-theme-on-surface), .2);--vc-focus-ring: 0 0 0 2px rgba(var(--v-theme-secondary), .4);--vc-header-arrow-hover-bg: rgba(var(--v-theme-secondary), .1)}.vc-attr{--vc-accent-600: rgba(var(--v-theme-secondary), .8)}.datePicker{--vc-day-content-hover-bg: red}@media(min-width:960px){.datePicker{position:absolute;bottom:0;left:0;right:0;margin-inline:auto;width:fit-content}}.vc-day-content{color:#5e5e5e;font-weight:400}.vc-highlight-content-solid{color:#fff!important}.vc-popover-content-wrapper{transform:var(--v518b583c)!important}.vc-date-picker-content,.datePicker{backdrop-filter:blur(10px)!important;border-radius:8px;border:none;box-shadow:0 0 1px #18274b38,0 6px 12px -6px #18274b1f,0 8px 24px -4px #18274b14;background-color:rgba(var(--v-theme-surface),var(--v-surface-opacity, .8))!important}.vc-popover-caret.direction-top.align-left{clip-path:polygon(0% 0%,100% 0%,0% 100%,0% 100%)}.vc-bordered{border:none}";
11
11
 
@@ -1,5 +1,5 @@
1
- import { withAsyncContext, computed, ref, createElementBlock, openBlock, mergeProps, unref, createElementVNode, toDisplayString } from 'vue';
2
- import { _ as _export_sfc, E as useSTAcStore, Q as isFirstLoad } from './asWebComponent-C-6K08Kk.js';
1
+ import { withAsyncContext, computed, ref, openBlock, createElementBlock, mergeProps, unref, createElementVNode, toDisplayString } from 'vue';
2
+ import { _ as _export_sfc, H as useSTAcStore, T as isFirstLoad } from './asWebComponent-DDt7V3Bl.js';
3
3
 
4
4
  const _style_0 = "eox-itemfilter[data-v-88d0abc7]{--form-flex-direction: row}@media(max-width:768px){eox-itemfilter[data-v-88d0abc7]{--form-flex-direction: column}}";
5
5
 
@@ -1,8 +1,8 @@
1
- import { withAsyncContext, computed, ref, createElementBlock, openBlock, createCommentVNode, mergeProps, unref, renderSlot, createElementVNode, toDisplayString } from 'vue';
1
+ import { withAsyncContext, computed, ref, openBlock, createElementBlock, mergeProps, unref, renderSlot, createElementVNode, toDisplayString, createCommentVNode } from 'vue';
2
2
  import 'color-legend-element';
3
3
  import '@eox/timecontrol';
4
- import { S as mapCompareEl, j as mapEl, T as getColFromLayer } from './helpers-Bi-qmnbZ.js';
5
- import { _ as _export_sfc, E as useSTAcStore, M as layerControlFormValueCompare, N as layerControlFormValue, J as eodashCompareCollections, I as eodashCollections } from './asWebComponent-C-6K08Kk.js';
4
+ import { S as mapCompareEl, i as mapEl, T as getColFromLayer } from './helpers-CZFH0EVN.js';
5
+ import { _ as _export_sfc, H as useSTAcStore, P as layerControlFormValueCompare, Q as layerControlFormValue, M as eodashCompareCollections, L as eodashCollections } from './asWebComponent-DDt7V3Bl.js';
6
6
  import { storeToRefs } from 'pinia';
7
7
 
8
8
  /**
@@ -236,7 +236,7 @@ class AbstractEditor {
236
236
  const editor = this.jsoneditor.getEditor(path);
237
237
  const value = editor ? editor.getValue() : undefined;
238
238
 
239
- if (!editor || !editor.dependenciesFulfilled || !value) {
239
+ if (!editor || !editor.dependenciesFulfilled || value === undefined || value === null) {
240
240
  this.dependenciesFulfilled = false;
241
241
  } else if (Array.isArray(choices)) {
242
242
  this.dependenciesFulfilled = choices.some(choice => {
@@ -581,6 +581,20 @@ class AbstractEditor {
581
581
  }
582
582
  }
583
583
 
584
+ purify (val) {
585
+ if (typeof val !== 'string') {
586
+ return val
587
+ }
588
+
589
+ if (window.DOMPurify) {
590
+ val = window.DOMPurify.sanitize(val);
591
+ } else {
592
+ val = this.cleanText(val);
593
+ }
594
+
595
+ return val
596
+ }
597
+
584
598
  getHeaderText (titleOnly) {
585
599
  if (this.header_text) return this.header_text
586
600
  else if (titleOnly) return this.translateProperty(this.schema.title)
@@ -1,8 +1,8 @@
1
- import { ref, createElementBlock, openBlock, createBlock, unref, withCtx, createVNode, mergeProps } from 'vue';
2
- import { E as activeTemplate } from './helpers-Bi-qmnbZ.js';
1
+ import { ref, openBlock, createElementBlock, unref, createBlock, withCtx, createVNode, mergeProps } from 'vue';
2
+ import { D as activeTemplate } from './helpers-CZFH0EVN.js';
3
3
  import { mdiViewDashboard } from '@mdi/js';
4
- import { D as useDisplay, F as useTransparentPanel, H as VIcon } from './asWebComponent-C-6K08Kk.js';
5
- import { V as VTooltip } from './VTooltip-B1_FzBl_.js';
4
+ import { G as useDisplay, I as useTransparentPanel, K as VIcon } from './asWebComponent-DDt7V3Bl.js';
5
+ import { V as VTooltip } from './VTooltip-BbmvtvYw.js';
6
6
 
7
7
  const _sfc_main = {
8
8
  __name: 'EodashLayoutSwitcher',