@eodash/eodash 5.6.1 → 5.7.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 (81) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -1
  2. package/core/client/composables/index.js +6 -2
  3. package/core/client/eodashSTAC/EodashCollection.js +5 -25
  4. package/core/client/eodashSTAC/createLayers.js +23 -9
  5. package/core/client/eodashSTAC/helpers.js +33 -0
  6. package/core/client/types.ts +8 -1
  7. package/core/client/utils/index.js +4 -0
  8. package/dist/client/{DashboardLayout-CqMb7OXc.js → DashboardLayout-kRsmsNW5.js} +5 -5
  9. package/dist/client/{DynamicWebComponent-DqKqTf7l.js → DynamicWebComponent-CnKhYo0K.js} +1 -1
  10. package/dist/client/{EodashChart-PkUQfH0k.js → EodashChart-CLDqE93C.js} +4 -4
  11. package/dist/client/{EodashDatePicker-DLJGdTtX.js → EodashDatePicker-CMPDT-ce.js} +3 -3
  12. package/dist/client/{EodashItemFilter-Dk8KjFcX.js → EodashItemFilter-BpdA8QQa.js} +1 -1
  13. package/dist/client/{EodashLayerControl-CC1Cogrn.js → EodashLayerControl-BHpDA4_0.js} +25 -7
  14. package/dist/client/{EodashLayoutSwitcher-5WZYd6rr.js → EodashLayoutSwitcher-B1fRUt7G.js} +3 -3
  15. package/dist/client/EodashMapBtns-Cpned0tc.js +478 -0
  16. package/dist/client/{EodashStacInfo-B90wobPc.js → EodashStacInfo-BQspAHKH.js} +1 -1
  17. package/dist/client/{EodashTools-CGZtTh45.js → EodashTools-1zHz26gv.js} +4 -4
  18. package/dist/client/{ExportState-Ce0ZnrX_.js → ExportState-DbOm8hBI.js} +4 -4
  19. package/dist/client/{Footer-CnZqEiop.js → Footer-BKg-VVkj.js} +1 -1
  20. package/dist/client/{Header-Dr5PBIm8.js → Header-DCrwCvmY.js} +2 -2
  21. package/dist/client/MobileLayout-vWSHpluo.js +118 -0
  22. package/dist/client/{PopUp-DjvE_71e.js → PopUp-9uimfK49.js} +2 -2
  23. package/dist/client/{ProcessList-kf3olI7W.js → ProcessList-CTTIZa06.js} +4 -4
  24. package/dist/client/{VImg-Bj6uWt6j.js → VImg-CrmB76ZY.js} +2 -2
  25. package/dist/client/{VMain-Cm0UmKGi.js → VMain-OKZNpcEG.js} +1 -1
  26. package/dist/client/{VTooltip-DbBu-ky3.js → VTooltip-CJw2FyWk.js} +2 -2
  27. package/dist/client/{WidgetsContainer-CfLnd44U.js → WidgetsContainer-BaWOnE5R.js} +1 -1
  28. package/dist/client/{asWebComponent-DfULAbpq.js → asWebComponent-BHWpMuE3.js} +47 -39
  29. package/dist/client/{async-CIbKXEEe.js → async-DSKj6g1f.js} +21 -10
  30. package/dist/client/create-layers-config-CBoYedqB.js +182 -0
  31. package/dist/client/eo-dash.js +1 -1
  32. package/dist/client/{forwardRefs-yj3VWOK5.js → forwardRefs-DH51zPUr.js} +2 -2
  33. package/dist/client/{handling-s8xt2JwF.js → handling-BrcDDlgS.js} +123 -85
  34. package/dist/client/{helpers-DAVGYl_f.js → helpers-DjZVl_WF.js} +2273 -1500
  35. package/dist/client/{index-4TrjfJ32.js → index-1JKE68n3.js} +49 -194
  36. package/dist/client/{index-CugS__t5.js → index-5Jhsa8Xe.js} +3 -3
  37. package/dist/client/index-CSG_JoOA.js +436 -0
  38. package/dist/client/{index-CYz6dT7K.js → index-CUjl-ZsI.js} +2 -2
  39. package/dist/client/{index-Bu8BMA9q.js → index-Cu3-13Xg.js} +69 -83
  40. package/dist/client/material-symbols-outlined.woff2 +0 -0
  41. package/dist/client/material-symbols-rounded.woff2 +0 -0
  42. package/dist/client/material-symbols-sharp.woff2 +0 -0
  43. package/dist/client/material-symbols-subset.woff2 +0 -0
  44. package/dist/client/templates.js +1 -1
  45. package/dist/client/{transition-DaOsiD4u.js → transition-OHcz5Qq1.js} +1 -1
  46. package/dist/node/cli.js +1 -1
  47. package/dist/types/core/client/composables/index.d.ts +1 -3
  48. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +6 -7
  49. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +3 -3
  50. package/dist/types/core/client/eodashSTAC/helpers.d.ts +1 -0
  51. package/dist/types/core/client/types.d.ts +8 -1
  52. package/dist/types/templates/baseConfig.d.ts +12 -0
  53. package/dist/types/widgets/EodashLayerControl.vue.d.ts +1 -1
  54. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +9 -0
  55. package/dist/types/widgets/EodashMap/index.vue.d.ts +7 -0
  56. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -8
  57. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +1 -1
  58. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +13 -8
  59. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +34 -0
  60. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +39 -0
  61. package/dist/types/widgets/EodashTimeSlider/types.d.ts +62 -0
  62. package/package.json +32 -32
  63. package/widgets/EodashLayerControl.vue +22 -3
  64. package/widgets/EodashMap/EodashMapBtns.vue +46 -1
  65. package/widgets/EodashMap/index.vue +17 -5
  66. package/widgets/EodashMap/methods/create-layers-config.js +2 -0
  67. package/widgets/EodashMap/methods/index.js +23 -5
  68. package/widgets/EodashProcess/index.vue +1 -1
  69. package/widgets/EodashProcess/methods/async.js +19 -8
  70. package/widgets/EodashProcess/methods/composables.js +63 -77
  71. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +9 -5
  72. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +7 -4
  73. package/widgets/EodashProcess/methods/handling.js +105 -72
  74. package/widgets/EodashTimeSlider/index.vue +152 -0
  75. package/widgets/EodashTimeSlider/methods/index.js +279 -0
  76. package/widgets/EodashTimeSlider/types.ts +64 -0
  77. package/dist/client/EodashMapBtns-BHrSDNoC.js +0 -435
  78. package/dist/client/EodashTimeSlider-5vm7F3ju.js +0 -53
  79. package/dist/client/MobileLayout-DyMjaeif.js +0 -118
  80. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +0 -15
  81. package/widgets/EodashTimeSlider.vue +0 -40
@@ -24,7 +24,7 @@
24
24
  <script setup>
25
25
  import "color-legend-element";
26
26
  import "@eox/timecontrol";
27
- import { computed, ref } from "vue";
27
+ import { computed, ref, watch } from "vue";
28
28
  import { mapEl, mapCompareEl } from "@/store/states";
29
29
  import { getColFromLayer } from "@/eodashSTAC/helpers";
30
30
  import {
@@ -36,6 +36,7 @@ import {
36
36
  import { storeToRefs } from "pinia";
37
37
  import { useSTAcStore } from "@/store/stac";
38
38
  import { bandsEditorInterface } from "@/utils/bands-editor";
39
+ import { useEmitLayersUpdate } from "@/composables";
39
40
 
40
41
  if (!customElements.get("eox-layercontrol")) {
41
42
  await import("@eox/layercontrol");
@@ -70,6 +71,7 @@ const config = {
70
71
  };
71
72
 
72
73
  const { selectedCompareStac, selectedStac } = storeToRefs(useSTAcStore());
74
+
73
75
  const showControls = computed(() => {
74
76
  if (props.map === "second") {
75
77
  return mapCompareEl.value !== null && selectedCompareStac.value !== null;
@@ -84,9 +86,20 @@ const mapElement = props.map === "second" ? mapCompareEl : mapEl;
84
86
  /** @type { import("vue").Ref<HTMLElement & Record<string,any> | null>} */
85
87
  const eoxLayercontrol = ref(null);
86
88
 
89
+ // eox-timecontrol re-fires datetime:updated after layer reassignment;
90
+ // dedupe by (collectionId, datetime)
91
+ const processedDatetimes = new Map();
92
+ watch([selectedStac, selectedCompareStac], () => processedDatetimes.clear());
93
+
87
94
  /** @param {CustomEvent<{layer:import('ol/layer').Layer; datetime:string;}>} evt */
88
95
  const handleDatetimeUpdate = async (evt) => {
89
96
  const { layer, datetime } = evt.detail;
97
+ const collectionId = layer.get("id")?.split(";:;")[0] ?? layer.get("id");
98
+ if (processedDatetimes.get(collectionId) === datetime) return;
99
+ // First event per collection is eox-timecontrol's mount echo.
100
+ const isFirstEvent = !processedDatetimes.has(collectionId);
101
+ processedDatetimes.set(collectionId, datetime);
102
+ if (isFirstEvent) return;
90
103
 
91
104
  const ec = await getColFromLayer(eodashCols, layer);
92
105
 
@@ -97,9 +110,10 @@ const handleDatetimeUpdate = async (evt) => {
97
110
  updatedLayers = await ec.updateLayerJson(
98
111
  datetime,
99
112
  layer.get("id"),
100
- props.map,
113
+ mapElement.value?.layers ?? [],
101
114
  );
102
115
  }
116
+ if (!updatedLayers?.length) return;
103
117
  /** @type {Record<String,any>[] | undefined} */
104
118
  const dataLayers = updatedLayers?.find(
105
119
  (l) => l?.properties?.id === "AnalysisGroup",
@@ -111,9 +125,14 @@ const handleDatetimeUpdate = async (evt) => {
111
125
  dl.properties.layerControlExpand = true;
112
126
  dl.properties.layerControlToolsExpand = true;
113
127
  });
114
- // assign layers to the map
115
128
  /** @type {HTMLElement & Record<string,any>} */
116
129
  (mapElement.value).layers = updatedLayers;
130
+ // Emit after layer assignment so listeners resolve against the new layer.
131
+ await useEmitLayersUpdate(
132
+ props.map === "second" ? "compareLayertime:updated" : "layertime:updated",
133
+ mapElement.value,
134
+ updatedLayers,
135
+ );
117
136
  }
118
137
  };
119
138
 
@@ -80,6 +80,28 @@
80
80
  {{ isGlobe ? "switch to 2D" : "switch to 3D" }}
81
81
  </div>
82
82
  </button>
83
+ <button
84
+ v-if="enableFeedback && eodash?.brand?.feedback"
85
+ class="primary small circle small-elevate"
86
+ @click="showFeedback = !showFeedback"
87
+ >
88
+ <i class="small"
89
+ ><svg viewBox="0 0 24 24">
90
+ <path :d="mdiMessageQuestion" />
91
+ </svg>
92
+ </i>
93
+ <div class="tooltip left">Provide Feedback</div>
94
+ </button>
95
+ <div
96
+ v-if="showFeedback && enableFeedback && eodash?.brand?.feedback"
97
+ class="feedback-container small-elevate"
98
+ >
99
+ <eox-feedback
100
+ :endpoint="eodash?.brand?.feedback?.endpoint"
101
+ .schema="eodash?.brand?.feedback?.schema"
102
+ @close="showFeedback = false"
103
+ ></eox-feedback>
104
+ </div>
83
105
  <eox-geosearch
84
106
  v-if="mapEl && !isGlobe && enableSearch"
85
107
  :for="mapEl"
@@ -110,7 +132,7 @@
110
132
  </div>
111
133
  </template>
112
134
  <script setup>
113
- import { useTransparentPanel } from "@/composables";
135
+ import { useTransparentPanel, useEodash } from "@/composables";
114
136
  import { changeMapProjection } from "@/store/actions";
115
137
  import {
116
138
  activeTemplate,
@@ -129,6 +151,7 @@ import {
129
151
  mdiPlus,
130
152
  mdiStarFourPointsCircleOutline,
131
153
  mdiEarth,
154
+ mdiMessageQuestion,
132
155
  } from "@mdi/js";
133
156
  import ExportState from "^/ExportState.vue";
134
157
  import { computed, ref } from "vue";
@@ -146,6 +169,12 @@ import {
146
169
  } from "./methods/btns";
147
170
  import "@eox/geosearch";
148
171
 
172
+ if (!customElements.get("eox-feedback")) {
173
+ //@ts-expect-error will be fixed in https://github.com/EOX-A/EOxElements/pull/2238
174
+ await import("@eox/feedback");
175
+ }
176
+ const eodash = useEodash();
177
+
149
178
  const {
150
179
  compareIndicators,
151
180
  changeProjection,
@@ -155,6 +184,7 @@ const {
155
184
  enableZoom,
156
185
  searchParams,
157
186
  enableGlobe,
187
+ enableFeedback,
158
188
  } = defineProps({
159
189
  exportMap: {
160
190
  type: Boolean,
@@ -193,6 +223,10 @@ const {
193
223
  type: Boolean,
194
224
  default: true,
195
225
  },
226
+ enableFeedback: {
227
+ type: Boolean,
228
+ default: true,
229
+ },
196
230
  });
197
231
 
198
232
  const { smAndDown } = useDisplay();
@@ -200,6 +234,7 @@ const popupWidth = computed(() => (smAndDown.value ? "80%" : "70%"));
200
234
  const popupHeight = computed(() => (smAndDown.value ? "90%" : "70%"));
201
235
 
202
236
  const showMapState = ref(false);
237
+ const showFeedback = ref(false);
203
238
  const isInCompareMode = computed(
204
239
  () =>
205
240
  activeTemplate.value ===
@@ -246,6 +281,16 @@ const opencageUrl = `https://api.opencagedata.com/geocode/v1/json?key=${opencage
246
281
  margin-bottom: 5px;
247
282
  background-color: var(--primary);
248
283
  }
284
+
285
+ .feedback-container {
286
+ position: absolute;
287
+ right: 70px;
288
+ pointer-events: auto;
289
+ z-index: 20;
290
+ border-radius: 8px;
291
+ min-width: 300px;
292
+ }
293
+
249
294
  /* Make sure buttons have pointer event */
250
295
  .geosearch-detached {
251
296
  pointer-events: auto !important;
@@ -61,6 +61,7 @@
61
61
  :enableZoom="(indicator || compareIndicator || poi) ? btnsProps.enableZoom : false
62
62
  "
63
63
  :enableGlobe="(indicator || compareIndicator || poi) ? btnsProps.enableGlobe : false"
64
+ :enableFeedback="(indicator || compareIndicator || poi) ? btnsProps.enableFeedback : false"
64
65
  :searchParams="btnsProps.searchParams"
65
66
  />
66
67
  </div>
@@ -69,7 +70,7 @@
69
70
  <script setup>
70
71
  import "@eox/map";
71
72
  import "@eox/map/src/plugins/advancedLayersAndSources";
72
- import { computed, onMounted, ref, toRaw, useTemplateRef } from "vue";
73
+ import { nextTick, computed, onMounted, ref, toRaw, useTemplateRef } from "vue";
73
74
  import {
74
75
  datetime,
75
76
  mapEl,
@@ -142,6 +143,7 @@ const props = defineProps({
142
143
  * searchParams?: object;
143
144
  * enableZoom?: boolean;
144
145
  * enableGlobe?: boolean;
146
+ * enableFeedback?: boolean;
145
147
  * enableCompareIndicators?: boolean | {
146
148
  * compareTemplate?:string;
147
149
  * fallbackTemplate?:string;
@@ -157,6 +159,7 @@ const props = defineProps({
157
159
  enableSearch: true,
158
160
  enableZoom: true,
159
161
  enableGlobe: true,
162
+ enableFeedback: true,
160
163
  searchParams: {},
161
164
  }),
162
165
  },
@@ -200,6 +203,7 @@ const btnsProps = computed(() => ({
200
203
  enableSearch: props.btns.enableSearch ?? true,
201
204
  enableZoom: props.btns.enableZoom ?? true,
202
205
  enableGlobe: props.btns.enableGlobe ?? true,
206
+ enableFeedback: props.btns.enableFeedback ?? true,
203
207
  searchParams: props.btns.searchParams,
204
208
  }));
205
209
 
@@ -241,7 +245,11 @@ const controls = computed(() => {
241
245
  controlsObj.MousePosition = {
242
246
  projection: "EPSG:4326",
243
247
  coordinateFormat: (/** @type {[number, number]} */ c) => {
244
- return `${c[1].toFixed(3)} °N, ${c[0].toFixed(3)} °E`;
248
+ const lat = c[1];
249
+ const lng = c[0];
250
+ const latStr = `${Math.abs(lat).toFixed(3)}°${lat >= 0 ? "N" : "S"}`;
251
+ const lngStr = `${Math.abs(lng).toFixed(3)}°${lng >= 0 ? "E" : "W"}`;
252
+ return `${latStr}, ${lngStr}`;
245
253
  },
246
254
  target: cursorCoordsRef.value,
247
255
  };
@@ -276,10 +284,10 @@ const eoxMapCompareLayers = ref([
276
284
  },
277
285
  ]);
278
286
 
279
- const animationOptions = {
280
- duration: 1200,
287
+ const animationOptions = ref({
288
+ duration: 0, // Initially set to 0 for an instant "jump"
281
289
  easing: inAndOut,
282
- };
290
+ });
283
291
 
284
292
  /** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
285
293
  const eoxMap = ref(null);
@@ -330,6 +338,10 @@ onMounted(() => {
330
338
  props.zoomToExtent,
331
339
  selectedItem,
332
340
  );
341
+ // After the initial mount and "jump", set the animation duration for subsequent flyTo calls
342
+ nextTick(() => {
343
+ animationOptions.value.duration = 1200;
344
+ });
333
345
  });
334
346
 
335
347
  useUpdateTooltipProperties(eodashCollections, tooltipProperties);
@@ -10,6 +10,7 @@ import log from "loglevel";
10
10
  * } selectedIndicator
11
11
  * @param {EodashCollection[]} eodashCols
12
12
  * @param {string | import("stac-ts").StacItem | null} [timeOrItem] - time as a string, or a stac item
13
+ * @returns {Promise<Record<string, any>[]>}
13
14
  */
14
15
 
15
16
  export const createLayersConfig = async (
@@ -68,6 +69,7 @@ export const createLayersConfig = async (
68
69
  if (targetLayer) {
69
70
  if (Array.isArray(vl.roles) && vl.roles.includes("disable")) {
70
71
  targetLayer.properties.visible = false;
72
+ targetLayer.properties.layerControlExpand = false;
71
73
  } else if (Array.isArray(vl.roles) && vl.roles.includes("hidden")) {
72
74
  targetLayer.properties.layerControlHide = true;
73
75
  }
@@ -83,6 +83,10 @@ export const useInitMap = (
83
83
  const watching = selectedItem
84
84
  ? [selectedIndicator, datetime, selectedItem]
85
85
  : [selectedIndicator, datetime];
86
+ // Tags datetime values we set ourselves so the watcher skips its own echo.
87
+ /** @type {string | null} */
88
+ let internalDatetime = null;
89
+
86
90
  const stopIndicatorWatcher = watch(
87
91
  watching,
88
92
  async (updated, previous) => {
@@ -96,6 +100,16 @@ export const useInitMap = (
96
100
  );
97
101
 
98
102
  if (updatedStac) {
103
+ if (
104
+ internalDatetime !== null &&
105
+ updatedTime === internalDatetime &&
106
+ updatedStac?.id === previousStac?.id &&
107
+ updatedItem?.id === previousItem?.id
108
+ ) {
109
+ internalDatetime = null;
110
+ return;
111
+ }
112
+
99
113
  log.debug(
100
114
  "Selected Indicator watch triggered",
101
115
  updatedStac,
@@ -145,7 +159,6 @@ export const useInitMap = (
145
159
  JSON.parse(JSON.stringify(layersCollection)),
146
160
  );
147
161
  mapLayers.value = layersCollection;
148
-
149
162
  useEmitLayersUpdate(
150
163
  mapElement.value?.id === "compare"
151
164
  ? "compareTime:updated"
@@ -167,22 +180,27 @@ export const useInitMap = (
167
180
  endInterval,
168
181
  );
169
182
  }
183
+ let resolvedTime = updatedItem ?? updatedTime;
170
184
  if (
171
185
  !updatedItem &&
172
186
  endInterval !== null &&
173
187
  endInterval.toISOString() !== datetime.value &&
174
188
  !isFirstLoad.value
175
189
  ) {
176
- datetime.value = endInterval.toISOString();
190
+ resolvedTime = endInterval.toISOString();
191
+ internalDatetime = resolvedTime;
192
+ datetime.value = resolvedTime;
177
193
  } else if (isFirstLoad.value && !datetime.value && endInterval) {
178
- datetime.value = endInterval.toISOString();
194
+ resolvedTime = endInterval.toISOString();
195
+ internalDatetime = resolvedTime;
196
+ datetime.value = resolvedTime;
179
197
  }
180
198
 
181
199
  /** @type {Record<string,any>[]} */
182
200
  layersCollection = await createLayersConfig(
183
201
  updatedStac,
184
202
  eodashCols,
185
- updatedItem ?? updatedTime,
203
+ resolvedTime,
186
204
  );
187
205
 
188
206
  if (zoomToExtent) {
@@ -251,7 +269,7 @@ export const useUpdateTooltipProperties = (
251
269
  * @param {string} evt */
252
270
  const listenTo = (evt) =>
253
271
  enableCompare ? evt.includes("compare") : !evt.includes("compare");
254
- useOnLayersUpdate(async (evt, _payload) => {
272
+ useOnLayersUpdate(async (evt) => {
255
273
  if (!listenTo(evt)) {
256
274
  return;
257
275
  }
@@ -119,7 +119,7 @@ const jsonformKey = computed(
119
119
 
120
120
  useInitProcess({
121
121
  selectedStac: currentSelectedStac,
122
- mapElement: mapElement.value,
122
+ mapElement,
123
123
  jsonformSchema,
124
124
  isProcessed,
125
125
  processResults,
@@ -106,16 +106,27 @@ export async function updateJobsStatus(jobs, indicator) {
106
106
  jobsUrls.map((url) =>
107
107
  axios
108
108
  .get(url, { params: { t: Date.now() } })
109
- .then((response) => response.data),
109
+ .then((response) => response.data)
110
+ .catch((error) => {
111
+ if (axios.isAxiosError(error)) {
112
+ console.warn(
113
+ `Job URL not accessible: ${url}. Maybe was deleted from the processing endpoint or endpoint is unavailable. Skipping...`,
114
+ );
115
+ return null;
116
+ }
117
+ throw error;
118
+ }),
110
119
  ),
111
120
  );
112
- jobResults.sort((a, b) => {
113
- return (
114
- new Date(b.job_start_datetime).getTime() -
115
- new Date(a.job_start_datetime).getTime()
116
- );
117
- });
118
- jobs.value.splice(0, jobs.value.length, ...jobResults);
121
+ const validResults = jobResults
122
+ .filter((job) => job !== null)
123
+ .sort((a, b) => {
124
+ return (
125
+ new Date(b.job_start_datetime).getTime() -
126
+ new Date(a.job_start_datetime).getTime()
127
+ );
128
+ });
129
+ jobs.value.splice(0, jobs.value.length, ...validResults);
119
130
  }
120
131
 
121
132
  /**
@@ -1,8 +1,7 @@
1
1
  import { initProcess, updateJsonformIdentifier } from "./handling";
2
- import { useEventBus } from "@vueuse/core";
3
- import { nextTick, onMounted, watch } from "vue";
4
- import { eoxLayersKey } from "@/utils/keys";
2
+ import { nextTick, onMounted, onUnmounted, watch } from "vue";
5
3
  import { useOnLayersUpdate } from "@/composables";
4
+ import { getCompareLayers, getLayers } from "@/store/actions";
6
5
  /**
7
6
  * Composable resposible of timing the Initialization of the process
8
7
  *
@@ -15,7 +14,7 @@ import { useOnLayersUpdate } from "@/composables";
15
14
  * @param {import("vue").Ref<boolean>} params.isProcessed
16
15
  * @param {import("vue").Ref<boolean>} params.loading
17
16
  * @param {import("vue").Ref<boolean>} params.isPolling
18
- * @param {import("@eox/map").EOxMap | null} params.mapElement
17
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} params.mapElement
19
18
  */
20
19
  export const useInitProcess = ({
21
20
  selectedStac,
@@ -26,60 +25,45 @@ export const useInitProcess = ({
26
25
  isPolling,
27
26
  mapElement,
28
27
  }) => {
29
- const layersEvents = useEventBus(eoxLayersKey);
30
-
31
28
  onMounted(async () => {
32
- // wait for the layers to be rendered
33
- if ((mapElement?.layers.length ?? 0) > 1) {
34
- await initProcess({
35
- enableCompare: mapElement?.id === "compare",
36
- selectedStac,
37
- jsonformSchema,
38
- isProcessed,
39
- processResults,
40
- loading,
41
- isPolling,
42
- });
43
- } else {
44
- layersEvents.once(async () => {
45
- await initProcess({
46
- enableCompare: mapElement?.id === "compare",
47
- selectedStac,
48
- jsonformSchema,
49
- isProcessed,
50
- loading,
51
- processResults,
52
- isPolling,
53
- });
54
- });
55
- }
29
+ await initProcess({
30
+ enableCompare: mapElement.value?.id === "compare",
31
+ selectedStac,
32
+ jsonformSchema,
33
+ isProcessed,
34
+ processResults,
35
+ loading,
36
+ isPolling,
37
+ mapElement: mapElement.value,
38
+ });
56
39
  });
57
- const enableCompare = mapElement?.id === "compare";
58
- const evtKey = enableCompare ? "compareLayers:updated" : "layers:updated";
59
- useOnLayersUpdate(async (evt, _payload) => {
60
- if (
61
- evt == "layertime:updated" ||
62
- evt == "compareLayertime:updated" ||
63
- evt == "time:updated" ||
64
- evt == "compareTime:updated"
65
- ) {
66
- const shouldMainJsonFormUpdate =
67
- ["layertime:updated", "time:updated"].includes(evt) && !enableCompare;
68
- const shouldCompareJsonFormUpdate =
69
- ["compareLayertime:updated", "compareTime:updated"].includes(evt) &&
70
- enableCompare;
71
- // we need to update jsonform on time change in cases when the feature selection layer was time-based, so that it attaches to a correct new layer
72
- if (shouldMainJsonFormUpdate || shouldCompareJsonFormUpdate) {
73
- const newJsonForm = await updateJsonformIdentifier({
74
- jsonformSchema: jsonformSchema.value,
75
- // @ts-expect-error TODO payload coming from time update events is not an object with layers property
76
- newLayers: _payload,
77
- enableCompare,
78
- });
40
+
41
+ useOnLayersUpdate(async (evt) => {
42
+ const enableCompare = mapElement.value?.id === "compare";
43
+ const layerUpdatedKey = enableCompare
44
+ ? "compareLayers:updated"
45
+ : "layers:updated";
46
+ const timeUpdatedKeys = enableCompare
47
+ ? ["compareLayertime:updated", "compareTime:updated"]
48
+ : ["layertime:updated", "time:updated"];
49
+
50
+ if (timeUpdatedKeys.includes(evt)) {
51
+ // we need to update jsonform on time change in cases
52
+ // when the feature selection layer was time-based,
53
+ // so that it attaches to a correct new layer
54
+ const newJsonForm = await updateJsonformIdentifier({
55
+ jsonformSchema: jsonformSchema.value,
56
+ newLayers: enableCompare ? getCompareLayers() : getLayers(),
57
+ enableCompare,
58
+ mapElement: mapElement.value,
59
+ });
60
+
61
+ if (newJsonForm) {
79
62
  jsonformSchema.value = newJsonForm;
80
63
  }
81
64
  }
82
- if (evt !== evtKey) {
65
+
66
+ if (evt !== layerUpdatedKey) {
83
67
  return;
84
68
  }
85
69
  await initProcess({
@@ -90,6 +74,7 @@ export const useInitProcess = ({
90
74
  processResults,
91
75
  loading,
92
76
  isPolling,
77
+ mapElement: mapElement.value,
93
78
  });
94
79
  });
95
80
  };
@@ -109,38 +94,39 @@ export function useAutoExec(
109
94
  startProcess,
110
95
  ) {
111
96
  /**
112
- * @param {CustomEvent} _e
97
+ * @param {any} _e
113
98
  **/
114
99
  const onJsonFormChange = async (_e) => {
115
100
  await startProcess();
116
101
  };
117
102
 
118
- const addEventListener = async () => {
119
- await nextTick(() => {
120
- //@ts-expect-error TODO
121
- jsonformEl.value?.addEventListener("change", onJsonFormChange);
122
- });
123
- };
124
- const removeEventListener = () => {
125
- //@ts-expect-error TODO
126
- jsonformEl.value?.removeEventListener("change", onJsonFormChange);
127
- };
128
-
129
103
  watch(jsonformSchema, (updatedSchema) => {
130
104
  autoExec.value = updatedSchema?.options?.["execute"] || false;
131
105
  });
132
106
 
133
- onMounted(() => {
134
- watch(
135
- autoExec,
136
- async (exec) => {
137
- if (exec) {
138
- await addEventListener();
139
- } else {
140
- removeEventListener();
141
- }
142
- },
143
- { immediate: true },
144
- );
107
+ // Re-attach listener whenever the element or autoExec changes
108
+ const stop = watch(
109
+ [autoExec, jsonformEl],
110
+ async ([exec, el], [_prevExec, prevEl]) => {
111
+ // Cleanup if previous element existed
112
+ if (prevEl) {
113
+ prevEl.removeEventListener("change", onJsonFormChange);
114
+ }
115
+ // Add if enabled and we have an element
116
+ if (exec && el) {
117
+ // remove listener before adding to avoid duplicates in case of element change
118
+ el.removeEventListener("change", onJsonFormChange);
119
+ await nextTick();
120
+ el.addEventListener("change", onJsonFormChange);
121
+ }
122
+ },
123
+ { immediate: true },
124
+ );
125
+
126
+ onUnmounted(() => {
127
+ if (jsonformEl.value) {
128
+ jsonformEl.value.removeEventListener("change", onJsonFormChange);
129
+ }
130
+ stop();
145
131
  });
146
132
  }
@@ -58,7 +58,7 @@ export async function handleSentinelHubProcess({
58
58
  );
59
59
 
60
60
  // fetch data from sentinel hub
61
- return await Promise.all(
61
+ const results = await Promise.all(
62
62
  timePairs.map(([to, from]) => {
63
63
  return fetchSentinelHubData({
64
64
  url: endpoint,
@@ -67,14 +67,18 @@ export async function handleSentinelHubProcess({
67
67
  from,
68
68
  to,
69
69
  exampleLink: evalScriptLink,
70
- }).catch((err) =>
70
+ }).catch((err) => {
71
71
  console.error(
72
72
  "[eodash] Error while fetching data from sentinel hub endpoint:",
73
73
  err,
74
- ),
75
- );
74
+ );
75
+ return [];
76
+ });
76
77
  }),
77
- ).then((data) => data.flat().map((data) => data.outputs.data));
78
+ )
79
+ .then((data) => data.filter((result) => result.length > 0))
80
+ .then((data) => data.flat().map((data) => data.outputs.data));
81
+ return results;
78
82
  }
79
83
 
80
84
  /**
@@ -37,7 +37,7 @@ export async function handleVedaEndpoint({
37
37
  vedaLink,
38
38
  );
39
39
  // TODO: convert jsonform bbox type to geojson in the schema to avoid the conversion here
40
- return await Promise.all(
40
+ const results = await Promise.all(
41
41
  configs.map(({ endpoint, datetime }) => {
42
42
  const url = new URL(vedaEndpoint);
43
43
  const key = vedaLink.endpoint === "veda_stac" ? "ids" : "url";
@@ -56,14 +56,17 @@ export async function handleVedaEndpoint({
56
56
  fetchedSats.date = datetime;
57
57
  return fetchedSats;
58
58
  })
59
- .catch((resp) =>
59
+ .catch((resp) => {
60
60
  console.error(
61
61
  "[eodash] Error while fetching data from veda endpoint:",
62
62
  resp,
63
- ),
64
- );
63
+ );
64
+ return null;
65
+ });
65
66
  }),
66
67
  );
68
+ // Filter out the nulls (failed requests) before returning
69
+ return results.filter((result) => result !== null);
67
70
  }
68
71
 
69
72
  /**