@eodash/eodash 5.6.1 → 5.7.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 (133) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -1
  2. package/core/client/components/EodashOverlay.vue +27 -4
  3. package/core/client/composables/DefineEodash.js +0 -1
  4. package/core/client/composables/DefineWidgets.js +3 -1
  5. package/core/client/composables/index.js +6 -2
  6. package/core/client/eodashSTAC/EodashCollection.js +79 -45
  7. package/core/client/eodashSTAC/cql.js +64 -0
  8. package/core/client/eodashSTAC/createLayers.js +192 -125
  9. package/core/client/eodashSTAC/helpers.js +423 -97
  10. package/core/client/eodashSTAC/mosaic.js +416 -0
  11. package/core/client/plugins/axios.js +31 -1
  12. package/core/client/store/actions.js +1 -1
  13. package/core/client/store/stac.js +25 -4
  14. package/core/client/store/states.js +20 -2
  15. package/core/client/types.ts +155 -29
  16. package/core/client/utils/bands-editor/exampleSchema.json +50 -50
  17. package/core/client/utils/index.js +4 -0
  18. package/core/client/utils/states.js +10 -0
  19. package/dist/client/{DashboardLayout-CqMb7OXc.js → DashboardLayout-ePbdvUTE.js} +5 -5
  20. package/dist/client/{DynamicWebComponent-DqKqTf7l.js → DynamicWebComponent-g5SdRbjY.js} +1 -1
  21. package/dist/client/{EodashChart-PkUQfH0k.js → EodashChart-ChrkA5oS.js} +7 -6
  22. package/dist/client/{EodashDatePicker-DLJGdTtX.js → EodashDatePicker-CY1TrsDo.js} +3 -3
  23. package/dist/client/{EodashItemFilter-Dk8KjFcX.js → EodashItemFilter-DzshHDlS.js} +2 -1
  24. package/dist/client/{EodashLayerControl-CC1Cogrn.js → EodashLayerControl-DMCeMkSd.js} +70 -22
  25. package/dist/client/{EodashLayoutSwitcher-5WZYd6rr.js → EodashLayoutSwitcher-iJ8mX1rF.js} +3 -3
  26. package/dist/client/EodashMapBtns-C7Cr7q5Y.js +592 -0
  27. package/dist/client/EodashStacInfo-K7-iuXa7.js +169 -0
  28. package/dist/client/{EodashTools-CGZtTh45.js → EodashTools-BEK7e7JM.js} +4 -4
  29. package/dist/client/{ExportState-Ce0ZnrX_.js → ExportState-CqIzfMYQ.js} +6 -168
  30. package/dist/client/{Footer-CnZqEiop.js → Footer-1P0cmBOZ.js} +1 -1
  31. package/dist/client/{Header-Dr5PBIm8.js → Header-CkkBaBNh.js} +2 -2
  32. package/dist/client/MobileLayout-BPf49kDU.js +118 -0
  33. package/dist/client/{PopUp-DjvE_71e.js → PopUp-DPZq5SWI.js} +4 -168
  34. package/dist/client/{ProcessList-kf3olI7W.js → ProcessList-DuI6WpS1.js} +5 -5
  35. package/dist/client/VDivider-Bj9LG_JV.js +168 -0
  36. package/dist/client/{VImg-Bj6uWt6j.js → VImg-Ddv5OyMz.js} +2 -2
  37. package/dist/client/{VMain-Cm0UmKGi.js → VMain-7yqk_qC3.js} +1 -1
  38. package/dist/client/{VTooltip-DbBu-ky3.js → VTooltip-2hAJyEoa.js} +2 -2
  39. package/dist/client/{WidgetsContainer-CfLnd44U.js → WidgetsContainer-2DOrlwCB.js} +1 -1
  40. package/dist/client/{asWebComponent-DfULAbpq.js → asWebComponent-BlFN2lkB.js} +493 -335
  41. package/dist/client/{async-CIbKXEEe.js → async-B2H8ZwUK.js} +21 -10
  42. package/dist/client/create-layers-config-u_vn5hPk.js +182 -0
  43. package/dist/client/dialog-transition-BrMkMiNa.js +170 -0
  44. package/dist/client/eo-dash.js +1 -1
  45. package/dist/client/{forwardRefs-yj3VWOK5.js → forwardRefs-DAlror6d.js} +8 -87
  46. package/dist/client/{handling-s8xt2JwF.js → handling-B6I1V5eG.js} +123 -85
  47. package/dist/client/{helpers-DAVGYl_f.js → helpers-CBqIUSXJ.js} +3034 -1649
  48. package/dist/client/index-BWq1ZKaV.js +3168 -0
  49. package/dist/client/{index-CYz6dT7K.js → index-CJ9UI3ln.js} +2 -2
  50. package/dist/client/index-DXLz4sFR.js +442 -0
  51. package/dist/client/{index-4TrjfJ32.js → index-DhKU6KqM.js} +230 -231
  52. package/dist/client/{index-Bu8BMA9q.js → index-DjQlbWbJ.js} +70 -83
  53. package/dist/client/material-symbols-outlined.woff2 +0 -0
  54. package/dist/client/material-symbols-rounded.woff2 +0 -0
  55. package/dist/client/material-symbols-sharp.woff2 +0 -0
  56. package/dist/client/material-symbols-subset.woff2 +0 -0
  57. package/dist/client/mosaic-BFYLEwyx.js +455 -0
  58. package/dist/client/templates.js +250 -119
  59. package/dist/client/{transition-DaOsiD4u.js → transition-D_W6Wn9v.js} +1 -1
  60. package/dist/node/cli.js +5 -5
  61. package/dist/node/types.d.ts +4 -0
  62. package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
  63. package/dist/types/core/client/composables/index.d.ts +1 -3
  64. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +62 -31
  65. package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
  66. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +44 -25
  67. package/dist/types/core/client/eodashSTAC/helpers.d.ts +78 -18
  68. package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
  69. package/dist/types/core/client/store/stac.d.ts +31 -7
  70. package/dist/types/core/client/store/states.d.ts +24 -2
  71. package/dist/types/core/client/types.d.ts +131 -25
  72. package/dist/types/core/client/utils/states.d.ts +12 -0
  73. package/dist/types/templates/baseConfig.d.ts +1 -88
  74. package/dist/types/templates/compare.d.ts +32 -3
  75. package/dist/types/templates/expert.d.ts +18 -0
  76. package/dist/types/templates/explore.d.ts +9 -1
  77. package/dist/types/templates/lite.d.ts +11 -0
  78. package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
  79. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
  80. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
  81. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
  82. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
  83. package/dist/types/widgets/EodashLayerControl.vue.d.ts +36 -17
  84. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +23 -10
  85. package/dist/types/widgets/EodashMap/index.vue.d.ts +91 -35
  86. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -8
  87. package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
  88. package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
  89. package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
  90. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +1 -1
  91. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +13 -8
  92. package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
  93. package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
  94. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +54 -0
  95. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +34 -0
  96. package/dist/types/widgets/EodashTimeSlider/types.d.ts +52 -0
  97. package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
  98. package/package.json +46 -46
  99. package/templates/baseConfig.js +36 -6
  100. package/templates/compare.js +62 -7
  101. package/templates/expert.js +23 -0
  102. package/templates/explore.js +11 -5
  103. package/templates/lite.js +14 -0
  104. package/widgets/EodashChart.vue +1 -0
  105. package/widgets/EodashItemCatalog/index.vue +363 -35
  106. package/widgets/EodashItemCatalog/methods/filters.js +134 -70
  107. package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
  108. package/widgets/EodashItemCatalog/methods/map.js +142 -28
  109. package/widgets/EodashLayerControl.vue +64 -14
  110. package/widgets/EodashMap/EodashMapBtns.vue +153 -47
  111. package/widgets/EodashMap/index.vue +101 -105
  112. package/widgets/EodashMap/methods/btns.js +37 -5
  113. package/widgets/EodashMap/methods/create-layers-config.js +18 -16
  114. package/widgets/EodashMap/methods/index.js +89 -8
  115. package/widgets/EodashMap/methods/use-map-loading.js +52 -0
  116. package/widgets/EodashProcess/index.vue +2 -1
  117. package/widgets/EodashProcess/methods/async.js +19 -8
  118. package/widgets/EodashProcess/methods/composables.js +63 -77
  119. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +9 -5
  120. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +7 -4
  121. package/widgets/EodashProcess/methods/handling.js +105 -72
  122. package/widgets/EodashProcess/states.js +1 -1
  123. package/widgets/EodashStacInfo.vue +64 -2
  124. package/widgets/EodashTimeSlider/index.vue +208 -0
  125. package/widgets/EodashTimeSlider/methods/index.js +236 -0
  126. package/widgets/EodashTimeSlider/types.ts +54 -0
  127. package/dist/client/EodashMapBtns-BHrSDNoC.js +0 -435
  128. package/dist/client/EodashStacInfo-B90wobPc.js +0 -107
  129. package/dist/client/EodashTimeSlider-5vm7F3ju.js +0 -53
  130. package/dist/client/MobileLayout-DyMjaeif.js +0 -118
  131. package/dist/client/index-CugS__t5.js +0 -570
  132. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +0 -15
  133. package/widgets/EodashTimeSlider.vue +0 -40
@@ -1,188 +1,63 @@
1
- import { onMounted, onUnmounted, watch, computed, withAsyncContext, useTemplateRef, ref, toRaw, openBlock, createElementBlock, createElementVNode, unref, normalizeStyle, createCommentVNode, createVNode } from 'vue';
1
+ import { watch, onUnmounted, onMounted, computed, withAsyncContext, useTemplateRef, ref, toRaw, nextTick, openBlock, createElementBlock, createElementVNode, unref, normalizeStyle, createCommentVNode, createVNode } from 'vue';
2
2
  import { transformExtent } from '@eox/map';
3
3
  import '@eox/map/src/plugins/advancedLayersAndSources';
4
- import { U as setMapProjFromCol, E as mapPosition, R as sanitizeBbox, i as mapEl, S as mapCompareEl, F as indicator, I as compareIndicator, C as poi, V as isGlobe, B as datetime } from './helpers-DAVGYl_f.js';
4
+ import { Y as loading, a6 as isGlobe, a7 as setMapProjFromCol, L as isFirstLoad, K as mapPosition, $ as sanitizeBbox, n as mapEl, a1 as mapCompareEl, a0 as tooltipAdapter, M as indicator, Q as compareIndicator, I as poi, a4 as layerControlFormValue, a3 as layerControlFormValueCompare, H as datetime, P as eodashCompareCollections, S as eodashCollections } from './helpers-CBqIUSXJ.js';
5
5
  import { storeToRefs } from 'pinia';
6
- import { R as EodashCollection, H as useSTAcStore, S as useEmitLayersUpdate, T as isFirstLoad, N as useOnLayersUpdate, _ as _export_sfc, G as useDisplay, a as useLayout, Q as layerControlFormValue, P as layerControlFormValueCompare, M as eodashCompareCollections, L as eodashCollections } from './asWebComponent-DfULAbpq.js';
6
+ import { H as useSTAcStore, aa as useEmitLayersUpdate, L as useOnLayersUpdate, _ as _export_sfc, G as useDisplay, a as useLayout } from './asWebComponent-BlFN2lkB.js';
7
+ import { c as createLayersConfig } from './create-layers-config-u_vn5hPk.js';
7
8
  import log from 'loglevel';
8
9
  import { i as inAndOut } from './easing-CH0-9wR8.js';
9
10
  import mustache from 'mustache';
10
- import EodashMapBtns from './EodashMapBtns-BHrSDNoC.js';
11
+ import EodashMapBtns from './EodashMapBtns-C7Cr7q5Y.js';
11
12
 
12
13
  /**
13
- * Creates full layer configuration from indicator and time information
14
- * @param {import("stac-ts").StacCatalog
15
- * | import("stac-ts").StacCollection
16
- * | import("stac-ts").StacItem
17
- * | null
18
- * } selectedIndicator
19
- * @param {EodashCollection[]} eodashCols
20
- * @param {string | import("stac-ts").StacItem | null} [timeOrItem] - time as a string, or a stac item
14
+ * Tracks map loading state at the source level via `loading.activeLoads`,
15
+ * the same counter used by the axios plugin. Re-attaches source listeners
16
+ * whenever eox-map fires `layerschanged`, using `getFlatLayersArray`.
17
+ *
18
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
19
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} compareMapElement
21
20
  */
22
-
23
- const createLayersConfig = async (
24
- selectedIndicator,
25
- eodashCols,
26
- timeOrItem,
27
- ) => {
28
- log.debug(
29
- "Creating layers config",
30
- selectedIndicator,
31
- eodashCols,
32
- timeOrItem,
33
- );
34
- const layersCollection = [];
35
- const dataLayers = {
36
- type: "Group",
37
- properties: {
38
- id: "AnalysisGroup",
39
- title: "Data Layers",
40
- layerControlExpand: true,
41
- },
42
- layers: /** @type {Record<string,any>[]}*/ ([]),
21
+ const useMapLoading = (mapElement, compareMapElement) => {
22
+ const startHandler = () => {
23
+ loading.activeLoads++;
43
24
  };
44
-
45
- for (const ec of eodashCols) {
46
- /** @type {Record<string,any>[]} */
47
- let layers;
48
- if (timeOrItem) {
49
- const dateOrItem =
50
- typeof timeOrItem === "string" ? new Date(timeOrItem) : timeOrItem;
51
- layers = await ec.createLayersJson(dateOrItem);
52
- } else {
53
- layers = await ec.createLayersJson(undefined);
54
- }
55
- // Add expand to all analysis layers
56
- layers.forEach((dl) => {
57
- dl.properties.layerControlExpand = true;
58
- dl.properties.layerControlToolsExpand = true;
59
- });
60
- dataLayers.layers.push(...layers);
61
- }
62
-
63
- /* check for roles in indicator links to assign visibility property for the data layers */
64
- if (selectedIndicator?.links) {
65
- const visibilityLinks = selectedIndicator.links.filter(
66
- (link) =>
67
- Array.isArray(link.roles) &&
68
- (link.roles.includes("disable") || link.roles.includes("hidden")),
69
- );
70
- if (visibilityLinks.length > 0) {
71
- visibilityLinks.forEach((vl) => {
72
- const targetLayerId = vl.id;
73
- const targetLayer = dataLayers.layers.find(
74
- (dl) => dl.properties.id.split(";:;")[0] === targetLayerId,
75
- );
76
- if (targetLayer) {
77
- if (Array.isArray(vl.roles) && vl.roles.includes("disable")) {
78
- targetLayer.properties.visible = false;
79
- } else if (Array.isArray(vl.roles) && vl.roles.includes("hidden")) {
80
- targetLayer.properties.layerControlHide = true;
81
- }
82
- }
83
- });
84
- }
85
- }
86
- layersCollection.push(dataLayers);
87
- const indicatorLayers =
88
- //@ts-expect-error indicator is collection
89
- await EodashCollection.getIndicatorLayers(selectedIndicator);
90
- const observationPointsLayer =
91
- EodashCollection.getObservationPointsLayer(eodashCols);
92
- if (observationPointsLayer) {
93
- dataLayers.layers.push(observationPointsLayer);
94
- }
95
- const baseLayers = {
96
- type: "Group",
97
- properties: {
98
- id: "BaseLayersGroup",
99
- title: "Base Layers",
100
- },
101
- layers: /** @type {Record<string,any>[]}*/ ([]),
25
+ const endHandler = () => {
26
+ loading.activeLoads--;
102
27
  };
103
28
 
104
- const indicatorBaseLayers = indicatorLayers.filter(
105
- (l) => l.properties.group === "baselayer",
106
- );
29
+ /** @param {import("@eox/map").EOxMap | null} newMapEl */
30
+ const watcherHandler = (newMapEl) => {
31
+ if (!newMapEl) return;
107
32
 
108
- if (indicatorBaseLayers.length) {
109
- // Only one baselayer can be set to visible, let's first set all to
110
- // false that have not a dedicated property visible, then check
111
- // if there are more then one visible and only allow one
112
- let counter = 0;
113
- let lastPos = 0;
114
- for (let indx = 0; indx < indicatorBaseLayers.length; indx++) {
115
- const bl = indicatorBaseLayers[indx];
116
- if (!("visible" in bl.properties)) {
117
- bl.properties.visible = false;
118
- }
33
+ const hasStartListener = newMapEl.map
34
+ .getListeners("loadstart")
35
+ ?.some((listner) => listner === startHandler);
36
+ const hasEndListener = newMapEl.map
37
+ .getListeners("loadend")
38
+ ?.some((listner) => listner === endHandler);
119
39
 
120
- if (bl.properties.visible) {
121
- counter++;
122
- lastPos = indx;
123
- }
40
+ if (!hasStartListener) {
41
+ newMapEl.map.addEventListener("loadstart", startHandler);
124
42
  }
125
-
126
- // if none visible set the last one as visible
127
- if (counter === 0) {
128
- indicatorBaseLayers[0].properties.visible = true;
129
- }
130
-
131
- // disable all apart from last
132
- if (counter > 0) {
133
- indicatorBaseLayers.forEach((bl, indx) => {
134
- if (indx !== lastPos) {
135
- bl.properties.visible = false;
136
- } else {
137
- bl.properties.visible = true;
138
- }
139
- });
43
+ if (!hasEndListener) {
44
+ newMapEl.map.addEventListener("loadend", endHandler);
140
45
  }
141
-
142
- baseLayers.layers.push(...indicatorBaseLayers);
143
-
144
- // Add exclusive to baselayers and make sure only one is selected
145
- baseLayers.layers.forEach((bl) => {
146
- bl.properties.layerControlExclusive = true;
147
- });
148
- } else {
149
- // Default to some baselayer
150
- baseLayers.layers.push({
151
- type: "Tile",
152
- properties: {
153
- id: "osm",
154
- title: "Background",
155
- layerControlExclusive: true,
156
- },
157
- source: {
158
- type: "OSM",
159
- },
160
- });
161
- }
162
-
163
- if (baseLayers.layers.length) {
164
- layersCollection.unshift(baseLayers);
165
- }
166
-
167
- const overlayLayers = {
168
- type: "Group",
169
- properties: {
170
- id: "OverlayGroup",
171
- title: "Overlay Layers",
172
- },
173
- layers: /** @type {Record<string,any>[]}*/ ([]),
174
46
  };
47
+ const stopMainWatcher = watch(mapElement, watcherHandler, {
48
+ immediate: true,
49
+ });
50
+ const stopCompareWatcher = watch(compareMapElement, watcherHandler, {
51
+ immediate: true,
52
+ });
175
53
 
176
- const indicatorOverlays = indicatorLayers.filter(
177
- (l) => l.properties.group === "overlay",
178
- );
179
-
180
- if (indicatorOverlays.length) {
181
- overlayLayers.layers.push(...indicatorOverlays);
182
- layersCollection.push(overlayLayers);
183
- }
184
-
185
- return layersCollection;
54
+ onUnmounted(() => {
55
+ mapElement.value?.map.removeEventListener("loadstart", startHandler);
56
+ mapElement.value?.map.removeEventListener("loadend", endHandler);
57
+ stopMainWatcher();
58
+ stopCompareWatcher();
59
+ loading.activeLoads = 0;
60
+ });
186
61
  };
187
62
 
188
63
  /**
@@ -215,14 +90,52 @@ const useHandleMapMoveEnd = (mapElement, mapPosition) => {
215
90
  }
216
91
  };
217
92
 
93
+ const handleGlobeMoveEnd = () => {
94
+ const camera = mapElement.value?.globe?.planet?.camera;
95
+ const lonLat = camera?.getLonLat();
96
+ if (!lonLat) return;
97
+ const { lon, lat, height } = lonLat;
98
+ if (![lon, lat, height].some(Number.isNaN)) {
99
+ mapPosition.value = [lon, lat, height];
100
+ }
101
+ };
102
+
103
+ /** @type {{ events: { off: Function } } | null} */
104
+ let subscribedCamera = null;
105
+
106
+ const subscribeGlobe = (retries = 3) => {
107
+ if (!isGlobe.value) return; // toggled back out during retry
108
+ const camera = mapElement.value?.globe?.planet?.camera;
109
+ if (camera) {
110
+ if (subscribedCamera === camera) return;
111
+ camera.events.on("moveend", handleGlobeMoveEnd);
112
+ subscribedCamera = camera;
113
+ handleGlobeMoveEnd();
114
+ return;
115
+ }
116
+ if (retries > 0) setTimeout(() => subscribeGlobe(retries - 1), 50);
117
+ };
118
+ const unsubscribeGlobe = () => {
119
+ subscribedCamera?.events.off("moveend", handleGlobeMoveEnd);
120
+ subscribedCamera = null;
121
+ };
122
+
123
+ const stopGlobeWatch = watch(isGlobe, (globe) =>
124
+ globe ? subscribeGlobe() : unsubscribeGlobe(),
125
+ );
126
+
218
127
  onMounted(() => {
219
- /** @type {import('ol/Map').default} */
220
- (mapElement.value?.map)?.on("moveend", handleMoveEnd);
128
+ const map = mapElement.value?.map;
129
+ map?.on("moveend", handleMoveEnd);
130
+ // Seed mapPosition from the initial view
131
+ handleMoveEnd(/** @type {any} */ ({ map }));
221
132
  });
222
133
 
223
134
  onUnmounted(() => {
224
135
  /** @type {import('ol/Map').default} */
225
136
  (mapElement.value?.map)?.un("moveend", handleMoveEnd);
137
+ unsubscribeGlobe();
138
+ stopGlobeWatch();
226
139
  });
227
140
  };
228
141
 
@@ -237,6 +150,7 @@ const useHandleMapMoveEnd = (mapElement, mapPosition) => {
237
150
  * @param {import("vue").Ref<import("@eox/map").EOxMap| null>} partnerMap
238
151
  * @param {boolean} zoomToExtent
239
152
  * @param {import("vue").Ref<import("stac-ts").StacItem | import("stac-ts").StacLink | null>} [selectedItem]
153
+ * @param {Record<string, any>[]} [defaultBaseLayers]
240
154
  */
241
155
  const useInitMap = (
242
156
  mapElement,
@@ -247,6 +161,7 @@ const useInitMap = (
247
161
  partnerMap,
248
162
  zoomToExtent,
249
163
  selectedItem,
164
+ defaultBaseLayers,
250
165
  ) => {
251
166
  log.debug(
252
167
  "InitMap",
@@ -259,6 +174,10 @@ const useInitMap = (
259
174
  const watching = selectedItem
260
175
  ? [selectedIndicator, datetime, selectedItem]
261
176
  : [selectedIndicator, datetime];
177
+ // Tags datetime values we set ourselves so the watcher skips its own echo.
178
+ /** @type {string | null} */
179
+ let internalDatetime = null;
180
+
262
181
  const stopIndicatorWatcher = watch(
263
182
  watching,
264
183
  async (updated, previous) => {
@@ -272,6 +191,28 @@ const useInitMap = (
272
191
  );
273
192
 
274
193
  if (updatedStac) {
194
+ const isSameStac = updatedStac?.id === previousStac?.id;
195
+ const isSameItem = updatedItem?.id === previousItem?.id;
196
+
197
+ // Item deselect: overview render is owned by the item catalog widget.
198
+ const isItemDeselect =
199
+ previousItem &&
200
+ !updatedItem &&
201
+ isSameStac &&
202
+ updatedTime === previousTime;
203
+ if (isItemDeselect) return;
204
+
205
+ // Re-fire from our own datetime.value write, skip and clear
206
+ const isOwnDatetimeUpdate =
207
+ internalDatetime !== null &&
208
+ updatedTime === internalDatetime &&
209
+ isSameStac &&
210
+ isSameItem;
211
+ if (isOwnDatetimeUpdate) {
212
+ internalDatetime = null;
213
+ return;
214
+ }
215
+
275
216
  log.debug(
276
217
  "Selected Indicator watch triggered",
277
218
  updatedStac,
@@ -315,13 +256,14 @@ const useInitMap = (
315
256
  updatedStac,
316
257
  eodashCols,
317
258
  updatedItem ?? updatedTime,
259
+ defaultBaseLayers,
318
260
  );
261
+
319
262
  log.debug(
320
263
  "Assigned layers after changing time only",
321
264
  JSON.parse(JSON.stringify(layersCollection)),
322
265
  );
323
266
  mapLayers.value = layersCollection;
324
-
325
267
  useEmitLayersUpdate(
326
268
  mapElement.value?.id === "compare"
327
269
  ? "compareTime:updated"
@@ -338,27 +280,38 @@ const useInitMap = (
338
280
  if (interval && interval.length > 0 && interval[0].length > 1) {
339
281
  // @ts-expect-error this is the defined STAC structure
340
282
  endInterval = new Date(interval[0][1]);
283
+ // If end interval is in the future, set to now,
284
+ // for item fetching based on search endpoint
285
+ if (endInterval.getTime() > Date.now()) {
286
+ endInterval = new Date();
287
+ }
341
288
  log.debug(
342
289
  "Indicator load: found stac extent, setting time to latest value",
343
290
  endInterval,
344
291
  );
345
292
  }
293
+ let resolvedTime = updatedItem ?? updatedTime;
346
294
  if (
347
295
  !updatedItem &&
348
296
  endInterval !== null &&
349
297
  endInterval.toISOString() !== datetime.value &&
350
298
  !isFirstLoad.value
351
299
  ) {
352
- datetime.value = endInterval.toISOString();
300
+ resolvedTime = endInterval.toISOString();
301
+ internalDatetime = resolvedTime;
302
+ datetime.value = resolvedTime;
353
303
  } else if (isFirstLoad.value && !datetime.value && endInterval) {
354
- datetime.value = endInterval.toISOString();
304
+ resolvedTime = endInterval.toISOString();
305
+ internalDatetime = resolvedTime;
306
+ datetime.value = resolvedTime;
355
307
  }
356
308
 
357
309
  /** @type {Record<string,any>[]} */
358
310
  layersCollection = await createLayersConfig(
359
311
  updatedStac,
360
312
  eodashCols,
361
- updatedItem ?? updatedTime,
313
+ updatedItem ?? resolvedTime,
314
+ defaultBaseLayers,
362
315
  );
363
316
 
364
317
  if (zoomToExtent) {
@@ -427,7 +380,7 @@ const useUpdateTooltipProperties = (
427
380
  * @param {string} evt */
428
381
  const listenTo = (evt) =>
429
382
  enableCompare ? evt.includes("compare") : !evt.includes("compare");
430
- useOnLayersUpdate(async (evt, _payload) => {
383
+ useOnLayersUpdate(async (evt) => {
431
384
  if (!listenTo(evt)) {
432
385
  return;
433
386
  }
@@ -441,10 +394,10 @@ const useUpdateTooltipProperties = (
441
394
  });
442
395
  };
443
396
 
444
- const _style_0 = "#cursor-coordinates[data-v-df7611e7]{position:fixed;left:24px;bottom:54px;color:#000000e6;font-size:10px;font-family:var(--eox-body-font-family);background:#fffe;border-radius:4px;border:none;padding:0 3px;max-height:24px}@media(max-width:959px){#cursor-coordinates[data-v-df7611e7]{display:none}}#scale-line[data-v-df7611e7]{position:fixed;left:24px;bottom:28px;color:#fff}@media(max-width:959px){#scale-line[data-v-df7611e7]{bottom:102px}}[data-v-df7611e7] .ol-scale-line{background:#fffe!important;border-radius:4px!important;border:none!important;padding:0 3px 3px!important;font-size:10px!important;font-family:var(--eox-body-font-family);max-height:20px}[data-v-df7611e7] .ol-scale-line-inner{display:flex;justify-content:center;border:1px solid rgba(0,0,0,.5)!important;border-top:none!important;color:#333!important;font-weight:500!important;transform:translateY(1px)}.map-buttons-container[data-v-df7611e7]{position:fixed;left:0;width:100%;height:100%;display:grid;grid-template-columns:repeat(12,1fr);grid-template-rows:repeat(12,1fr);pointer-events:none;z-index:1}.map-buttons-container[data-v-df7611e7]>*{pointer-events:auto}";
397
+ const _style_0 = ".map-buttons-container[data-v-6d08ac44]{position:fixed;left:0;width:100%;height:100%;display:grid;grid-template-columns:repeat(12,1fr);grid-template-rows:repeat(12,1fr);pointer-events:none;z-index:1}.map-buttons-container[data-v-6d08ac44]>*{pointer-events:auto}";
445
398
 
446
399
  const _hoisted_1 = [".enabled"];
447
- const _hoisted_2 = [".center", ".zoom", ".layers", ".controls"];
400
+ const _hoisted_2 = [".animationOptions", ".center", ".zoom", ".layers", ".controls"];
448
401
  const _hoisted_3 = [".propertyTransform"];
449
402
  const _hoisted_4 = [".layers"];
450
403
  const _hoisted_5 = [".propertyTransform"];
@@ -463,42 +416,73 @@ const _hoisted_7 = {
463
416
  const _sfc_main = {
464
417
  __name: 'index',
465
418
  props: {
419
+ baseLayers: {
420
+ /** @type {import("vue").PropType<import("@eox/map").EoxLayer[]>} */
421
+ type: Array,
422
+ default: () => [
423
+ {
424
+ type: "Tile",
425
+ source: { type: "OSM" },
426
+ properties: {
427
+ id: "osm",
428
+ title: "Background",
429
+ layerControlExclusive: true,
430
+ },
431
+ },
432
+ ],
433
+ },
466
434
  enableCompare: {
467
435
  type: Boolean,
468
436
  default: false,
469
437
  },
470
- /** @type {import("vue").PropType<[number,number]>} */
438
+ /**
439
+ * Initial map center as [lon, lat]; falls back to the last stored map position.
440
+ * @default [15, 48]
441
+ */
471
442
  center: {
472
- //@ts-expect-error todo
473
- type: Array,
443
+ type: /** @type {import("vue").PropType<[number, number]>} */ (
444
+ /** @type {unknown} */ (Array)
445
+ ),
474
446
  default: () => [mapPosition.value?.[0] ?? 15, mapPosition.value?.[1] ?? 48],
475
447
  },
448
+ /**
449
+ * Initial zoom level; falls back to the last stored map position.
450
+ * @default 4
451
+ */
476
452
  zoom: {
477
453
  type: Number,
478
454
  default: mapPosition.value?.[2] ?? 4,
479
455
  },
456
+ /** Fly to the selected collection's spatial extent on selection. */
480
457
  zoomToExtent: {
481
458
  type: Boolean,
482
459
  default: true,
483
460
  },
461
+ /** Show live cursor coordinates as a fixed overlay at the bottom-left of the map. */
484
462
  enableCursorCoordinates: {
485
463
  type: Boolean,
486
464
  default: true,
487
465
  },
466
+ /** Show an OpenLayers scale-line control at the bottom-left of the map. */
488
467
  enableScaleLine: {
489
468
  type: Boolean,
490
469
  default: true,
491
470
  },
471
+ /** Grid position of the floating button toolbar. `x` accepts responsive `"mobile/tablet/desktop"` column notation. */
492
472
  btnsPosition: {
493
- type: Object,
473
+ type: /** @type {import("vue").PropType<{ x: string | number; y: number; gap: number }>} */ (
474
+ Object
475
+ ),
494
476
  default: () => ({
495
477
  x: "12/9/10",
496
478
  y: 1,
497
479
  gap: 16,
498
480
  }),
499
481
  },
482
+ /** Toolbar feature flags; set any flag to `false` to hide the corresponding button. */
500
483
  btns: {
501
- /** @type {import("vue").PropType<{
484
+ type: /**
485
+ * @type {import("vue").PropType<{
502
486
  * enableExportMap?: boolean;
503
487
  * enableChangeProjection?: boolean;
504
488
  * enableBackToPOIs?: boolean;
@@ -506,13 +490,14 @@ const _sfc_main = {
506
490
  * searchParams?: object;
507
491
  * enableZoom?: boolean;
508
492
  * enableGlobe?: boolean;
493
+ * enableMosaic?: boolean;
494
+ * enableFeedback?: boolean;
509
495
  * enableCompareIndicators?: boolean | {
510
496
  * compareTemplate?:string;
511
497
  * fallbackTemplate?:string;
512
498
  * itemFilterConfig?:InstanceType<import("../EodashItemFilter.vue").default>["$props"]
513
499
  * };
514
- * }> }*/
515
- type: Object,
500
+ * }> } */ (Object),
516
501
  default: () => ({
517
502
  enableExportMap: true,
518
503
  enableChangeProjection: true,
@@ -521,6 +506,8 @@ const _sfc_main = {
521
506
  enableSearch: true,
522
507
  enableZoom: true,
523
508
  enableGlobe: true,
509
+ enableMosaic: true,
510
+ enableFeedback: true,
524
511
  searchParams: {},
525
512
  }),
526
513
  },
@@ -569,6 +556,8 @@ const btnsProps = computed(() => ({
569
556
  enableSearch: props.btns.enableSearch ?? true,
570
557
  enableZoom: props.btns.enableZoom ?? true,
571
558
  enableGlobe: props.btns.enableGlobe ?? true,
559
+ enableMosaic: props.btns.enableMosaic ?? true,
560
+ enableFeedback: props.btns.enableFeedback ?? true,
572
561
  searchParams: props.btns.searchParams,
573
562
  }));
574
563
 
@@ -587,22 +576,13 @@ const cursorCoordsRef = useTemplateRef("cursor-coords");
587
576
  const tooltipProperties = ref([]);
588
577
  /** @type {import("vue").Ref<Exclude<import("@/types").EodashStyleJson["tooltip"], undefined>>} */
589
578
  const compareTooltipProperties = ref([]);
590
- /** @type {import("vue").ComputedRef<{
591
- Attribution: { collapsible: boolean };
592
- ScaleLine?: { target: HTMLElement };
593
- MousePosition?: { projection: string; coordinateFormat: (c: [number, number]) => string; target: HTMLElement };
594
- }>} */
579
+
595
580
  const controls = computed(() => {
596
- /** @type {{
597
- Attribution: { collapsible: boolean };
598
- ScaleLine?: { target: HTMLElement };
599
- MousePosition?: { projection: string; coordinateFormat: (c: [number, number]) => string; target: HTMLElement };
600
- }} */
601
- const controlsObj = {
581
+ const controlsObj = /** @type {import("@eox/map").ControlDictionary} */ ({
602
582
  Attribution: {
603
583
  collapsible: true,
604
584
  },
605
- };
585
+ });
606
586
 
607
587
  if (props.enableScaleLine && scaleLineRef.value) {
608
588
  controlsObj.ScaleLine = {
@@ -613,8 +593,8 @@ const controls = computed(() => {
613
593
  if (props.enableCursorCoordinates && cursorCoordsRef.value) {
614
594
  controlsObj.MousePosition = {
615
595
  projection: "EPSG:4326",
616
- coordinateFormat: (/** @type {[number, number]} */ c) => {
617
- return `${c[1].toFixed(3)} °N, ${c[0].toFixed(3)} °E`;
596
+ coordinateFormat: (c) => {
597
+ return `${c?.[1].toFixed(3)} °N, ${c?.[0].toFixed(3)} °E`;
618
598
  },
619
599
  target: cursorCoordsRef.value,
620
600
  };
@@ -626,38 +606,28 @@ const controls = computed(() => {
626
606
  const initialCenter = toRaw(props.center);
627
607
  const initialZoom = toRaw(mapPosition.value?.[2] ?? props.zoom);
628
608
  /** @type {import("vue").Ref<Record<string,any>[]>} */
629
- const eoxMapLayers = ref([
630
- {
631
- type: "Tile",
632
- source: { type: "OSM" },
633
- properties: {
634
- id: "osm",
635
- title: "Background",
636
- },
637
- },
638
- ]);
609
+ const eoxMapLayers = ref(
610
+ /** @type {Record<string,any>[]} */ (
611
+ structuredClone(toRaw(props.baseLayers))
612
+ ),
613
+ );
639
614
 
640
- /** @type {import("vue").Ref<Record<string,any>[]>} */
641
- const eoxMapCompareLayers = ref([
642
- {
643
- type: "Tile",
644
- source: { type: "OSM" },
645
- properties: {
646
- id: "osm",
647
- title: "Background",
648
- },
649
- },
650
- ]);
615
+ const eoxMapCompareLayers = ref(
616
+ /** @type {Record<string,any>[]} */ (
617
+ structuredClone(toRaw(props.baseLayers))
618
+ ),
619
+ );
651
620
 
652
- const animationOptions = {
653
- duration: 1200,
621
+ const animationOptions = ref({
622
+ duration: 0, // Initially set to 0 for an instant "jump"
654
623
  easing: inAndOut,
655
- };
624
+ });
656
625
 
657
626
  /** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
658
- const eoxMap = ref(null);
627
+ const eoxMap = useTemplateRef("eoxMap");
659
628
  /** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
660
- const compareMap = ref(null);
629
+ const compareMap = useTemplateRef("compareMap");
630
+
661
631
  const { selectedCompareStac } = storeToRefs(useSTAcStore());
662
632
  const showCompare = computed(() =>
663
633
  props.enableCompare && !!selectedCompareStac.value ? "" : "first",
@@ -666,10 +636,20 @@ const showCompare = computed(() =>
666
636
  useHandleMapMoveEnd(eoxMap, mapPosition);
667
637
 
668
638
  onMounted(() => {
669
- const { selectedCompareStac, selectedStac, selectedItem } =
670
- storeToRefs(useSTAcStore());
639
+ const {
640
+ selectedCompareStac,
641
+ selectedStac,
642
+ selectedItem,
643
+ selectedCompareItem,
644
+ } = storeToRefs(useSTAcStore());
645
+ if (!eoxMap.value) {
646
+ console.error("EOxMap reference is not available on mounted.");
647
+ return;
648
+ }
671
649
  // assign map Element state to eox map
672
650
  mapEl.value = eoxMap.value;
651
+ // enable terrain
652
+ mapEl.value.globeConfig.terrain = true;
673
653
 
674
654
  if (props.enableCompare) {
675
655
  mapCompareEl.value = compareMap.value;
@@ -684,6 +664,8 @@ onMounted(() => {
684
664
  eoxMapCompareLayers,
685
665
  eoxMap,
686
666
  false,
667
+ selectedCompareItem,
668
+ props.baseLayers,
687
669
  );
688
670
 
689
671
  useUpdateTooltipProperties(
@@ -702,17 +684,31 @@ onMounted(() => {
702
684
  compareMap,
703
685
  props.zoomToExtent,
704
686
  selectedItem,
687
+ props.baseLayers,
705
688
  );
689
+ // After the initial mount and "jump", set the animation duration for subsequent flyTo calls
690
+ nextTick(() => {
691
+ animationOptions.value.duration = 1200;
692
+ });
706
693
  });
707
694
 
695
+ // sync map loading with the global loading state
696
+ useMapLoading(eoxMap, compareMap);
697
+
708
698
  useUpdateTooltipProperties(eodashCollections, tooltipProperties);
709
699
 
710
700
  const mainTooltipStyles = computed(() => ({
711
- visibility: tooltipProperties.value.length ? "visible" : "hidden",
701
+ visibility:
702
+ tooltipProperties.value.length || !!tooltipAdapter.value
703
+ ? "visible"
704
+ : "hidden",
712
705
  }));
713
706
 
714
707
  const compareTooltipStyles = computed(() => ({
715
- visibility: compareTooltipProperties.value.length ? "visible" : "hidden",
708
+ visibility:
709
+ compareTooltipProperties.value.length || !!tooltipAdapter.value
710
+ ? "visible"
711
+ : "hidden",
716
712
  }));
717
713
  /**
718
714
  * @param {"main" | "compare"} map
@@ -738,6 +734,9 @@ const tooltipPropertyTransform = (map) => {
738
734
  (prop) => prop.id === param.key,
739
735
  );
740
736
  if (!tooltipProp) {
737
+ if (tooltipAdapter.value) {
738
+ return tooltipAdapter.value(param, map);
739
+ }
741
740
  return undefined;
742
741
  }
743
742
  if (typeof param.value === "object") {
@@ -749,7 +748,6 @@ const tooltipPropertyTransform = (map) => {
749
748
  : 4;
750
749
  param.value = Number(param.value).toFixed(decimals).toString();
751
750
  }
752
-
753
751
  return {
754
752
  key: tooltipProp.title || tooltipProp.id,
755
753
  value: param.value + " " + (tooltipProp.appendix || ""),
@@ -776,7 +774,7 @@ return (_ctx, _cache) => {
776
774
  ref_key: "eoxMap",
777
775
  ref: eoxMap,
778
776
  id: "main",
779
- ".animationOptions": animationOptions,
777
+ ".animationOptions": animationOptions.value,
780
778
  ".center": unref(initialCenter),
781
779
  ".zoom": unref(initialZoom),
782
780
  ".layers": eoxMapLayers.value,
@@ -829,14 +827,15 @@ return (_ctx, _cache) => {
829
827
  enableZoom: (unref(indicator) || unref(compareIndicator) || unref(poi)) ? btnsProps.value.enableZoom : false
830
828
  ,
831
829
  enableGlobe: (unref(indicator) || unref(compareIndicator) || unref(poi)) ? btnsProps.value.enableGlobe : false,
830
+ enableFeedback: (unref(indicator) || unref(compareIndicator) || unref(poi)) ? btnsProps.value.enableFeedback : false,
832
831
  searchParams: btnsProps.value.searchParams
833
- }, null, 8 /* PROPS */, ["style", "exportMap", "changeProjection", "compareIndicators", "backToPOIs", "enableSearch", "enableZoom", "enableGlobe", "searchParams"])
832
+ }, null, 8 /* PROPS */, ["style", "exportMap", "changeProjection", "compareIndicators", "backToPOIs", "enableSearch", "enableZoom", "enableGlobe", "enableFeedback", "searchParams"])
834
833
  ], 4 /* STYLE */)
835
834
  ]))
836
835
  }
837
836
  }
838
837
 
839
838
  };
840
- const index = /*#__PURE__*/_export_sfc(_sfc_main, [['styles',[_style_0]],['__scopeId',"data-v-df7611e7"]]);
839
+ const index = /*#__PURE__*/_export_sfc(_sfc_main, [['styles',[_style_0]],['__scopeId',"data-v-6d08ac44"]]);
841
840
 
842
841
  export { index as default };