@eodash/eodash 5.0.0-rc.3 → 5.1.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 (155) hide show
  1. package/README.md +1 -0
  2. package/core/client/App.vue +8 -2
  3. package/core/client/asWebComponent.js +5 -5
  4. package/core/client/components/DashboardLayout.vue +42 -25
  5. package/core/client/components/EodashOverlay.vue +1 -1
  6. package/core/client/components/ErrorAlert.vue +2 -2
  7. package/core/client/components/Footer.vue +4 -4
  8. package/core/client/components/Header.vue +3 -3
  9. package/core/client/components/MobileLayout.vue +9 -10
  10. package/core/client/composables/DefineEodash.js +38 -43
  11. package/core/client/composables/DefineTemplate.js +4 -2
  12. package/core/client/composables/DefineWidgets.js +14 -8
  13. package/core/client/composables/index.js +273 -23
  14. package/core/client/eodashSTAC/EodashCollection.js +80 -47
  15. package/core/client/eodashSTAC/helpers.js +136 -27
  16. package/core/client/eodashSTAC/parquet.js +145 -0
  17. package/core/client/eodashSTAC/triggers.js +6 -3
  18. package/core/client/plugins/index.js +4 -3
  19. package/core/client/plugins/vuetify.js +3 -0
  20. package/core/client/store/actions.js +21 -4
  21. package/core/client/store/stac.js +93 -56
  22. package/core/client/store/states.js +15 -5
  23. package/core/client/types.ts +59 -43
  24. package/core/client/utils/index.js +79 -0
  25. package/core/client/utils/keys.js +2 -2
  26. package/core/client/utils/states.js +30 -5
  27. package/core/client/views/Dashboard.vue +36 -32
  28. package/core/client/vite-env.d.ts +7 -0
  29. package/dist/client/{DashboardLayout-t_PavJPO.js → DashboardLayout-ByVs1DrY.js} +23 -12
  30. package/dist/client/{DynamicWebComponent-y07rVJch.js → DynamicWebComponent-C3W7HSQm.js} +1 -1
  31. package/dist/client/{EodashDatePicker-CcOfyzGD.js → EodashDatePicker-BIAf1sMT.js} +59 -32
  32. package/dist/client/{EodashItemFilter-B9HCvIMi.js → EodashItemFilter-DPznh8UB.js} +20 -10
  33. package/dist/client/{EodashLayerControl-KStn7Nb_.js → EodashLayerControl-Bhxjw4V2.js} +29 -16
  34. package/dist/client/EodashLayoutSwitcher-C5qTEffW.js +61 -0
  35. package/dist/client/EodashMapBtns-WoGq8MuV.js +173 -0
  36. package/dist/client/{EodashStacInfo-C_hDy6Pd.js → EodashStacInfo-CSvvF2jI.js} +3 -18
  37. package/dist/client/{EodashTools-BXflvRf8.js → EodashTools-Cv1SXQ5y.js} +13 -13
  38. package/dist/client/{ExportState-C0QRemK1.js → ExportState-D-iuwaad.js} +58 -52
  39. package/dist/client/{Footer-7VGyGUAs.js → Footer-CyF0zRAk.js} +15 -13
  40. package/dist/client/{Header-BQJnXHYq.js → Header-CgD8jDKU.js} +33 -28
  41. package/dist/client/{MobileLayout-b8nQ-Vyl.js → MobileLayout-EKQ_kpSh.js} +69 -60
  42. package/dist/client/{PopUp-DgNrh9Df.js → PopUp-BsYLvWch.js} +19 -10
  43. package/dist/client/ProcessList-C2xsLU2_.js +191 -0
  44. package/dist/client/{VImg-D4eT3IQ1.js → VImg-OHe8YTs2.js} +24 -24
  45. package/dist/client/{VMain-C3hN2-H3.js → VMain-PryTLU4a.js} +7 -7
  46. package/dist/client/{VOverlay-tAeNygaA.js → VOverlay-yUn7p-Uf.js} +64 -27
  47. package/dist/client/{VTooltip-B0Q3iHMZ.js → VTooltip-DZ0fjpB3.js} +13 -10
  48. package/dist/client/{WidgetsContainer-CtDHfCYf.js → WidgetsContainer-B9LBadcC.js} +1 -1
  49. package/dist/client/asWebComponent-By_7_JjS.js +19193 -0
  50. package/dist/client/async-DkSu_u2K.js +740 -0
  51. package/dist/client/eo-dash.js +1 -1
  52. package/dist/client/{forwardRefs-CIFAqXaZ.js → forwardRefs-BXxrv98s.js} +31 -4
  53. package/dist/client/handling-CgmFXkW6.js +1212 -0
  54. package/dist/client/helpers-Dy0Q13tP.js +4534 -0
  55. package/dist/client/{index-DvcUndod.js → index-BuhOHXKv.js} +2 -4
  56. package/dist/client/{index-BQ16n4Sk.js → index-Ch_HchK3.js} +39 -32
  57. package/dist/client/{index-Cv7HBz49.js → index-Dqj4tbx2.js} +2 -2
  58. package/dist/client/index-skjhlH8u.js +376 -0
  59. package/dist/client/{ssrBoot-BP7SYRyC.js → ssrBoot-Zgc_Ttvi.js} +2 -2
  60. package/dist/client/templates.js +850 -0
  61. package/dist/client/transition-C98Yn4Vo.js +40 -0
  62. package/dist/node/cli.js +16 -6
  63. package/dist/node/types.d.ts +1 -1
  64. package/dist/types/core/client/App.vue.d.ts +2 -2
  65. package/dist/types/core/client/asWebComponent.d.ts +1 -1
  66. package/dist/types/core/client/components/DynamicWebComponent.vue.d.ts +1 -3
  67. package/dist/types/core/client/components/Footer.vue.d.ts +1 -105
  68. package/dist/types/core/client/components/IframeWrapper.vue.d.ts +1 -1
  69. package/dist/types/core/client/components/MobileLayout.vue.d.ts +1 -324
  70. package/dist/types/core/client/composables/DefineEodash.d.ts +2 -2
  71. package/dist/types/core/client/composables/DefineTemplate.d.ts +1 -1
  72. package/dist/types/core/client/composables/DefineWidgets.d.ts +4 -4
  73. package/dist/types/core/client/composables/index.d.ts +24 -2
  74. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +9 -6
  75. package/dist/types/core/client/eodashSTAC/helpers.d.ts +20 -5
  76. package/dist/types/core/client/eodashSTAC/parquet.d.ts +2 -0
  77. package/dist/types/core/client/plugins/vuetify.d.ts +7 -4
  78. package/dist/types/core/client/store/actions.d.ts +3 -2
  79. package/dist/types/core/client/store/stac.d.ts +16 -13
  80. package/dist/types/core/client/store/states.d.ts +14 -4
  81. package/dist/types/core/client/types.d.ts +45 -30
  82. package/dist/types/core/client/utils/index.d.ts +2 -0
  83. package/dist/types/core/client/utils/keys.d.ts +4 -4
  84. package/dist/types/core/client/utils/states.d.ts +59 -47
  85. package/dist/types/core/client/views/Dashboard.vue.d.ts +2 -2
  86. package/dist/types/templates/baseConfig.d.ts +4 -0
  87. package/dist/types/templates/compare.d.ts +210 -0
  88. package/dist/types/templates/expert.d.ts +151 -0
  89. package/dist/types/templates/index.d.ts +6 -0
  90. package/dist/types/templates/light.d.ts +145 -0
  91. package/dist/types/widgets/EodashDatePicker.vue.d.ts +1 -458
  92. package/dist/types/widgets/EodashItemFilter.vue.d.ts +3 -3
  93. package/dist/types/widgets/EodashLayerControl.vue.d.ts +14 -7
  94. package/dist/types/widgets/EodashLayoutSwitcher.vue.d.ts +1 -3
  95. package/dist/types/widgets/EodashMap/index.vue.d.ts +1 -4
  96. package/dist/types/widgets/EodashMapBtns.vue.d.ts +8 -8
  97. package/dist/types/widgets/EodashProcess/ProcessList.vue.d.ts +8 -1
  98. package/dist/types/widgets/EodashProcess/index.vue.d.ts +8 -4
  99. package/dist/types/widgets/EodashProcess/methods/async.d.ts +18 -18
  100. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +3 -2
  101. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/index.d.ts +1 -0
  102. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.d.ts +6 -0
  103. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.d.ts +4 -0
  104. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +5 -0
  105. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/index.d.ts +1 -0
  106. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +12 -5
  107. package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +72 -41
  108. package/dist/types/widgets/EodashProcess/methods/utils.d.ts +41 -21
  109. package/dist/types/widgets/EodashProcess/states.d.ts +11 -0
  110. package/dist/types/widgets/EodashProcess/types.d.ts +41 -0
  111. package/dist/types/widgets/EodashStacInfo.vue.d.ts +14 -14
  112. package/dist/types/widgets/EodashTools.vue.d.ts +3 -3
  113. package/dist/types/widgets/ExportState.vue.d.ts +1 -1
  114. package/dist/types/widgets/PopUp.vue.d.ts +11 -16
  115. package/dist/types/widgets/WidgetsContainer.vue.d.ts +3 -6
  116. package/package.json +53 -45
  117. package/templates/baseConfig.js +68 -0
  118. package/templates/compare.js +162 -0
  119. package/templates/expert.js +123 -0
  120. package/templates/index.js +8 -0
  121. package/templates/light.js +130 -0
  122. package/widgets/EodashDatePicker.vue +80 -31
  123. package/widgets/EodashItemFilter.vue +26 -11
  124. package/widgets/EodashLayerControl.vue +20 -11
  125. package/widgets/EodashLayoutSwitcher.vue +6 -3
  126. package/widgets/EodashMap/index.vue +3 -8
  127. package/widgets/EodashMap/methods/create-layers-config.js +4 -3
  128. package/widgets/EodashMap/methods/index.js +33 -23
  129. package/widgets/EodashMapBtns.vue +83 -41
  130. package/widgets/EodashProcess/ProcessList.vue +34 -10
  131. package/widgets/EodashProcess/index.vue +55 -20
  132. package/widgets/EodashProcess/methods/async.js +77 -59
  133. package/widgets/EodashProcess/methods/composables.js +21 -14
  134. package/widgets/EodashProcess/methods/custom-endpoints/chart/index.js +35 -0
  135. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +275 -0
  136. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +116 -0
  137. package/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.js +94 -0
  138. package/widgets/EodashProcess/methods/custom-endpoints/layers/index.js +33 -0
  139. package/widgets/EodashProcess/methods/handling.js +127 -80
  140. package/widgets/EodashProcess/methods/outputs.js +376 -125
  141. package/widgets/EodashProcess/methods/utils.js +398 -10
  142. package/widgets/EodashProcess/states.js +13 -0
  143. package/widgets/EodashProcess/types.ts +46 -0
  144. package/widgets/EodashStacInfo.vue +2 -17
  145. package/widgets/EodashTools.vue +13 -13
  146. package/widgets/WidgetsContainer.vue +1 -1
  147. package/core/client/eodash.js +0 -454
  148. package/dist/client/EodashLayoutSwitcher-DqeFO3RN.js +0 -52
  149. package/dist/client/EodashMapBtns-5BF27qJB.js +0 -131
  150. package/dist/client/ProcessList-C62SOVO6.js +0 -484
  151. package/dist/client/asWebComponent-BJ2NWunV.js +0 -12479
  152. package/dist/client/eo-dash.css +0 -5
  153. package/dist/client/index-Da5xXX6Q.js +0 -780
  154. package/dist/client/transition-Cdb4K27U.js +0 -37
  155. package/dist/types/core/client/eodash.d.ts +0 -8
@@ -7,17 +7,54 @@ import {
7
7
  currentUrl,
8
8
  datetime,
9
9
  indicator,
10
+ mapEl,
10
11
  mapPosition,
12
+ poi,
11
13
  } from "@/store/states";
12
- import eodash from "@/eodash";
13
- import { useTheme } from "vuetify/lib/framework.mjs";
14
- import { inject, onMounted, onUnmounted, watch } from "vue";
14
+ import { useTheme } from "vuetify";
15
+ import { inject, nextTick, onMounted, onUnmounted, watch } from "vue";
15
16
  import { useSTAcStore } from "@/store/stac";
16
17
  import log from "loglevel";
17
18
  import { eodashKey, eoxLayersKey } from "@/utils/keys";
18
- import { useEventBus } from "@vueuse/core";
19
- import { posIsSetFromUrl } from "@/utils/states";
19
+ import { useEventBus, useMutationObserver } from "@vueuse/core";
20
+ import { isFirstLoad } from "@/utils/states";
20
21
  import { setCollectionsPalette } from "@/utils";
22
+ import mustache from "mustache";
23
+ import { toAbsolute } from "stac-js/src/http.js";
24
+ import axios from "@/plugins/axios";
25
+
26
+ /**
27
+ /** @type {import('@/types').Eodash | null}*/
28
+
29
+ let _eodash = null;
30
+
31
+ /**
32
+ * Call this once in a top-level component to inject and store the reactive eodash object.
33
+ * @throws {Error} If eodash is not found in the inject context
34
+ */
35
+ export function provideEodashInstance() {
36
+ const injected = inject(eodashKey);
37
+ if (!injected) {
38
+ throw new Error(
39
+ "Missing injected eodash – did you forget to call provideEodashInstance in a component?",
40
+ );
41
+ }
42
+ _eodash = injected;
43
+ }
44
+
45
+ /**
46
+ * Access the reactive eodash configuration anywhere after it has been provided.
47
+ * @returns {import('@/types').Eodash | null}
48
+ * @throws {Error} If eodash was not yet provided
49
+ */
50
+ export function useEodash() {
51
+ if (!_eodash) {
52
+ throw new Error(
53
+ "Eodash not yet available – call provideEodashInstance() first.",
54
+ );
55
+ }
56
+ return _eodash;
57
+ }
21
58
 
22
59
  /**
23
60
  * Creates an absolute URL from a relative link and assignes it to `currentUrl`
@@ -28,8 +65,11 @@ import { setCollectionsPalette } from "@/utils";
28
65
  * @returns {import("vue").Ref<string>} - Returns `currentUrl`
29
66
  * @see {@link '@/store/states.js'}
30
67
  */
31
- export const useAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
32
- if (!rel || rel.includes("http")) {
68
+ export const useAbsoluteUrl = (
69
+ rel = "",
70
+ base = inject(eodashKey)?.stacEndpoint,
71
+ ) => {
72
+ if (!rel || rel.includes("http") || !base) {
33
73
  currentUrl.value = rel;
34
74
  return currentUrl;
35
75
  }
@@ -57,9 +97,12 @@ export const useAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
57
97
  * @returns {import("vue").Ref<string>} - Returns `currentUrl`
58
98
  * @see {@link '@/store/states.js'}
59
99
  */
60
- export const useCompareAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
61
- if (!rel || rel.includes("http")) {
62
- currentCompareUrl.value = base;
100
+ export const useCompareAbsoluteUrl = (
101
+ rel = "",
102
+ base = inject(eodashKey)?.stacEndpoint,
103
+ ) => {
104
+ if (!rel || rel.includes("http") || !base) {
105
+ currentCompareUrl.value = rel;
63
106
  return currentCompareUrl;
64
107
  }
65
108
 
@@ -119,6 +162,7 @@ export const useURLSearchParametersSync = () => {
119
162
  // Analyze currently set url params when first loaded and set them in the store
120
163
  if (window.location.search) {
121
164
  const searchParams = new URLSearchParams(window.location.search);
165
+ const store = useSTAcStore();
122
166
 
123
167
  /** @type {number | undefined} */
124
168
  let x,
@@ -134,11 +178,54 @@ export const useURLSearchParametersSync = () => {
134
178
  }
135
179
  case "indicator": {
136
180
  log.debug("Found indicator key in url");
137
- const { loadSelectedSTAC, stac } = useSTAcStore();
138
- const match = stac?.find((link) => link.id == value);
181
+ const eodash = inject(eodashKey);
182
+ const match = store.stac?.find(
183
+ (link) => useGetSubCodeId(link) == value,
184
+ );
139
185
  if (match) {
140
186
  log.debug("Found match, loading stac item", match);
141
- await loadSelectedSTAC(match.href);
187
+ if (searchParams.has("poi")) {
188
+ const indicatorUrl = toAbsolute(
189
+ match.href,
190
+ eodash?.stacEndpoint ?? "",
191
+ );
192
+ // fetch indicator stac collection without rendering it
193
+ /** @type {import("stac-ts").StacCollection} */
194
+ const indicatorStac = await axios
195
+ .get(indicatorUrl)
196
+ .then((resp) => resp.data);
197
+ poi.value = searchParams.get("poi") ?? "";
198
+ // find the process link
199
+ const poiMatch = indicatorStac?.links.find(
200
+ (link) =>
201
+ link.rel === "service" &&
202
+ link.type === "application/json; profile=collection",
203
+ );
204
+ if (poiMatch) {
205
+ log.debug("Found poi match, setting poi", poiMatch);
206
+ // render poi value into the link href
207
+ /** @type {any} */
208
+ let viewForMustache = poi.value;
209
+ const tokens = mustache.parse(poiMatch.href);
210
+ const keyToken = tokens.find(
211
+ (token) => token[0] === "name" && token[1] !== ".",
212
+ );
213
+ // Construct the view object
214
+ if (keyToken) {
215
+ const keyName = keyToken[1];
216
+ viewForMustache = { [keyName]: poi.value };
217
+ }
218
+ /** @type {string} */
219
+ const poiUrl = mustache.render(
220
+ poiMatch.href,
221
+ viewForMustache,
222
+ );
223
+ const poiAbsoluteUrl = toAbsolute(poiUrl, indicatorUrl);
224
+ await store.loadSelectedSTAC(poiAbsoluteUrl, true);
225
+ }
226
+ } else {
227
+ await store.loadSelectedSTAC(match.href);
228
+ }
142
229
  }
143
230
  break;
144
231
  }
@@ -173,19 +260,25 @@ export const useURLSearchParametersSync = () => {
173
260
  if (x && y && z) {
174
261
  log.debug("Coordinates found, applying map poisition", x, y, z);
175
262
  mapPosition.value = [x, y, z];
176
- if (!posIsSetFromUrl.value) {
177
- posIsSetFromUrl.value = true;
263
+ if (mapEl.value) {
264
+ mapEl.value.center = [x, y];
265
+ mapEl.value.zoom = z;
178
266
  }
179
267
  }
268
+
269
+ if (!isFirstLoad.value) {
270
+ isFirstLoad.value = true;
271
+ }
180
272
  }
181
273
 
182
274
  watch(
183
- [indicator, mapPosition, datetime, activeTemplate],
275
+ [indicator, mapPosition, datetime, activeTemplate, poi],
184
276
  ([
185
277
  updatedIndicator,
186
278
  updatedMapPosition,
187
279
  updatedDatetime,
188
280
  updatedTemplate,
281
+ updatedPoi,
189
282
  ]) => {
190
283
  if ("URLSearchParams" in window) {
191
284
  const searchParams = new URLSearchParams(window.location.search);
@@ -205,6 +298,14 @@ export const useURLSearchParametersSync = () => {
205
298
  if (updatedTemplate) {
206
299
  searchParams.set("template", updatedTemplate);
207
300
  }
301
+ if (!updatedPoi) {
302
+ if (searchParams.has("poi")) {
303
+ searchParams.delete("poi");
304
+ }
305
+ } else {
306
+ searchParams.set("poi", updatedPoi);
307
+ }
308
+
208
309
  const newRelativePathQuery =
209
310
  window.location.pathname + "?" + searchParams.toString();
210
311
  history.pushState(null, "", newRelativePathQuery);
@@ -218,25 +319,28 @@ export const useURLSearchParametersSync = () => {
218
319
  * Converts eox-layout-item to transparent
219
320
  *
220
321
  * @param {import("vue").Ref<HTMLElement|null>} root - components root element ref*/
221
- export const makePanelTransparent = (root) => {
322
+ export const useTransparentPanel = (root) => {
222
323
  onMounted(() => {
223
- const eoxItem = root.value?.parentElement;
224
- if (eoxItem?.tagName === "EOX-LAYOUT-ITEM") {
225
- eoxItem.classList.remove("bg-surface");
324
+ const backgroundItem = root.value?.parentElement;
325
+ if (backgroundItem?.classList.contains("bg-surface")) {
326
+ backgroundItem.classList.remove("bg-surface");
226
327
  }
227
328
  });
228
329
  };
229
330
 
230
331
  export const useGetTemplates = () => {
231
- const eodash = /** @type {import("@/types").Eodash} */ (inject(eodashKey));
232
- return "template" in eodash ? [] : Object.keys(eodash.templates);
332
+ const eodash = inject(eodashKey);
333
+ if (!eodash) {
334
+ return [];
335
+ }
336
+ return "template" in eodash ? [] : Object.keys(eodash?.templates ?? {});
233
337
  };
234
338
 
235
339
  /**
236
340
  * Listens to the `layers:updated` and `time:updated` events and calls
237
341
  *
238
342
  * @param {import("@vueuse/core").EventBusListener<
239
- * "layers:updated"|"time:updated",
343
+ * import("@/types").LayersEventBusKeys,
240
344
  * {layers:Record<string,any>[]| undefined}
241
345
  * >} listener
242
346
  */
@@ -249,3 +353,149 @@ export const useOnLayersUpdate = (listener) => {
249
353
  unsubscribe();
250
354
  });
251
355
  };
356
+ /**
357
+ * @param {import("@/types").LayersEventBusKeys} event
358
+ * @param {import("@eox/map").EOxMap | null} mapEl
359
+ * @param {Record<string,any>[]} layers
360
+ */
361
+ export const useEmitLayersUpdate = async (event, mapEl, layers) => {
362
+ if (!mapEl) {
363
+ return;
364
+ }
365
+
366
+ const layersEvents = useEventBus(eoxLayersKey);
367
+
368
+ const emit = async () =>
369
+ mapEl?.updateComplete.then(async () => {
370
+ await nextTick(() => {
371
+ layersEvents.emit(event, layers);
372
+ });
373
+ });
374
+
375
+ const dl = /** @type {import("ol/layer").Group} */ (
376
+ mapEl.getLayerById("AnalysisGroup")
377
+ );
378
+ await new Promise((res, _rej) => {
379
+ if (!dl) {
380
+ mapEl.map.getLayers().once("add", async () => {
381
+ await emit();
382
+ res(true);
383
+ });
384
+ return;
385
+ }
386
+
387
+ dl.getLayers().once("change", async () => {
388
+ await emit();
389
+ res(true);
390
+ });
391
+ });
392
+ };
393
+
394
+ /**
395
+ * @param {import("stac-ts").StacCollection | import("stac-ts").StacLink | import("stac-ts").StacItem | null} collection
396
+ * @returns {string} - Returns the collection id or subcode if `useSubCode` is enabled
397
+ */
398
+ export const useGetSubCodeId = (collection) => {
399
+ const eodash = useEodash();
400
+
401
+ if (!collection) {
402
+ return "";
403
+ }
404
+
405
+ if (eodash?.options?.useSubCode) {
406
+ return typeof collection.subcode === "string"
407
+ ? collection.subcode
408
+ : /** @type {string} */ (collection.id);
409
+ }
410
+ return /** @type {string} */ (collection.id);
411
+ };
412
+
413
+ /**
414
+ * Composable: Adopt Vuetify styles into eo-dash shadow root
415
+ * - Safe no-op outside Web Component context
416
+ * - Cleans up observers on unmount
417
+ */
418
+ /**
419
+ * @param {string[]} [keyWords=["vuetify"]] - list of case-insensitive substrings to match against <style> element IDs
420
+ */
421
+ export function useAdoptStyles(keyWords = ["vuetify"]) {
422
+ /** @type {Array<() => void>} */
423
+ let stops = [];
424
+
425
+ const eoDash =
426
+ /** @type {HTMLElement & { shadowRoot: ShadowRoot | null }} */ (
427
+ document.querySelector("eo-dash")
428
+ );
429
+ if (!eoDash || !eoDash.shadowRoot) return;
430
+ // Require Constructable Stylesheets support
431
+ if (typeof CSSStyleSheet === "undefined") return;
432
+
433
+ const head = document.head;
434
+ const shadow = eoDash.shadowRoot;
435
+ /** @type {Map<string, CSSStyleSheet>} */
436
+ const sheetsById = new Map();
437
+
438
+ /**
439
+ * @param {string} id
440
+ * @param {string} cssText
441
+ */
442
+ const updateSheet = (id, cssText) => {
443
+ let sheet = sheetsById.get(id);
444
+ if (!sheet) {
445
+ sheet = new CSSStyleSheet();
446
+ sheet.replaceSync(cssText);
447
+ sheetsById.set(id, sheet);
448
+ shadow.adoptedStyleSheets = [...shadow.adoptedStyleSheets, sheet];
449
+ } else {
450
+ sheet.replaceSync(cssText);
451
+ }
452
+ };
453
+
454
+ /**
455
+ * @param {HTMLStyleElement} el
456
+ */
457
+ const handleStyleEl = (el) => {
458
+ const id = el.id || "";
459
+ const idLc = id.toLowerCase();
460
+ if (!keyWords.some((s) => idLc.includes(String(s).toLowerCase()))) return;
461
+ const cssText = (el.textContent || "").replaceAll(":root", ":host");
462
+ if (!cssText) return;
463
+
464
+ updateSheet(id, cssText);
465
+
466
+ const observer = useMutationObserver(
467
+ el,
468
+ () => {
469
+ const nextCss = (el.textContent || "").replaceAll(":root", ":host");
470
+ if (nextCss) updateSheet(id, nextCss);
471
+ },
472
+ { childList: true, characterData: true, subtree: true },
473
+ );
474
+ if (observer?.stop) stops.push(() => observer.stop());
475
+ };
476
+
477
+ /**
478
+ * @param {Node} node
479
+ */
480
+ const handleNode = (node) => {
481
+ if (node instanceof HTMLStyleElement) handleStyleEl(node);
482
+ };
483
+
484
+ head.childNodes.forEach(handleNode);
485
+
486
+ const headObserver = useMutationObserver(
487
+ head,
488
+ (mutations) => {
489
+ mutations?.forEach((m) => {
490
+ m.addedNodes?.forEach(handleNode);
491
+ });
492
+ },
493
+ { childList: true },
494
+ );
495
+ if (headObserver?.stop) stops.push(() => headObserver.stop());
496
+
497
+ onUnmounted(() => {
498
+ stops.forEach((stop) => stop());
499
+ stops = [];
500
+ });
501
+ }
@@ -7,6 +7,7 @@ import {
7
7
  fetchStyle,
8
8
  findLayer,
9
9
  generateFeatures,
10
+ getDatetimeProperty,
10
11
  replaceLayer,
11
12
  } from "./helpers";
12
13
  import {
@@ -57,10 +58,10 @@ export class EodashCollection {
57
58
  /**
58
59
  * @type {import("stac-ts").StacLink | undefined}
59
60
  **/
60
- let stacItem;
61
+ let itemLink;
61
62
 
62
63
  /**
63
- * @type {import("stac-ts").StacCollection | undefined}
64
+ * @type {import("stac-ts").StacCollection | import("stac-ts").StacItem | undefined}
64
65
  **/
65
66
  let stac;
66
67
  // TODO get auxiliary layers from collection
@@ -70,22 +71,26 @@ export class EodashCollection {
70
71
  // Load collectionstac if not yet initialized
71
72
  stac = await this.fetchCollection();
72
73
 
73
- const isGeoDB = stac?.endpointtype === "GeoDB";
74
+ const isObservationPoint = stac?.endpointtype === "GeoDB";
74
75
 
75
76
  if (linkOrDate instanceof Date) {
76
77
  // if collectionStac not yet initialized we do it here
77
- stacItem = this.getItem(linkOrDate);
78
+ itemLink = this.getItem(linkOrDate);
78
79
  } else {
79
- stacItem = linkOrDate;
80
+ itemLink = linkOrDate;
81
+ }
82
+ let stacItemUrl = "";
83
+ if (itemLink?.href.startsWith("blob:")) {
84
+ stacItemUrl = itemLink.href;
85
+ } else {
86
+ stacItemUrl = itemLink
87
+ ? toAbsolute(itemLink.href, this.#collectionUrl)
88
+ : this.#collectionUrl;
80
89
  }
81
-
82
- const stacItemUrl = stacItem
83
- ? toAbsolute(stacItem.href, this.#collectionUrl)
84
- : this.#collectionUrl;
85
90
 
86
91
  stac = await axios.get(stacItemUrl).then((resp) => resp.data);
87
92
 
88
- if (!stacItem) {
93
+ if (!itemLink) {
89
94
  // no specific item was requested; render last item
90
95
  this.#collectionStac = new Collection(stac);
91
96
  this.selectedItem = this.getItem();
@@ -105,7 +110,12 @@ export class EodashCollection {
105
110
  const title =
106
111
  this.#collectionStac?.title || this.#collectionStac?.id || "";
107
112
  layersJson.unshift(
108
- ...(await this.buildJsonArray(item, stacItemUrl, title, isGeoDB)),
113
+ ...(await this.buildJsonArray(
114
+ item,
115
+ stacItemUrl,
116
+ title,
117
+ isObservationPoint,
118
+ )),
109
119
  );
110
120
  return layersJson;
111
121
  }
@@ -115,20 +125,19 @@ export class EodashCollection {
115
125
  * @param {import("stac-ts").StacItem} item
116
126
  * @param {string} itemUrl
117
127
  * @param {string} title
118
- * @param {boolean} isGeoDB
128
+ * @param {boolean} isObservationPoint
119
129
  * @param {string} [itemDatetime]
120
130
  * @returns {Promise<Record<string,any>[]>} layers
121
131
  * */
122
- async buildJsonArray(item, itemUrl, title, isGeoDB, itemDatetime) {
132
+ async buildJsonArray(item, itemUrl, title, isObservationPoint, itemDatetime) {
123
133
  log.debug(
124
134
  "Building JSON array",
125
135
  item,
126
136
  itemUrl,
127
137
  title,
128
- isGeoDB,
138
+ isObservationPoint,
129
139
  itemDatetime,
130
140
  );
131
- await this.fetchCollection();
132
141
  // registering top level indicator projection
133
142
  const indicatorProjection =
134
143
  item?.["proj:epsg"] || item?.["eodash:proj4_def"];
@@ -140,8 +149,8 @@ export class EodashCollection {
140
149
 
141
150
  const jsonArray = [];
142
151
 
143
- if (isGeoDB) {
144
- // handled by getGeoDBLayer
152
+ if (isObservationPoint) {
153
+ // handled by getObservationPointsLayer
145
154
  return [];
146
155
  }
147
156
 
@@ -158,7 +167,9 @@ export class EodashCollection {
158
167
 
159
168
  const layerDatetime = extractLayerDatetime(
160
169
  this.getItems(),
161
- item.properties?.datetime ?? itemDatetime,
170
+ item.properties?.datetime ??
171
+ item.properties.start_datetime ??
172
+ itemDatetime,
162
173
  );
163
174
 
164
175
  const dataAssets = Object.keys(item?.assets ?? {}).reduce((data, ast) => {
@@ -191,6 +202,7 @@ export class EodashCollection {
191
202
  ...extraProperties,
192
203
  ...(this.color && { color: this.color }),
193
204
  };
205
+
194
206
  const links = await createLayersFromLinks(
195
207
  this.#collectionStac?.id ?? "",
196
208
  title,
@@ -198,6 +210,7 @@ export class EodashCollection {
198
210
  layerDatetime,
199
211
  extraProperties,
200
212
  );
213
+
201
214
  jsonArray.push(
202
215
  ...links,
203
216
  ...(await createLayersFromAssets(
@@ -247,14 +260,21 @@ export class EodashCollection {
247
260
  return this.#collectionStac;
248
261
  }
249
262
 
263
+ /**
264
+ * Returns all item links sorted by datetime ascendingly
265
+ */
250
266
  getItems() {
267
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
268
+ const items = this.#collectionStac?.links.filter((i) => i.rel === "item");
269
+ if (!datetimeProperty) {
270
+ return items;
271
+ }
251
272
  return (
252
- this.#collectionStac?.links
253
- .filter((i) => i.rel === "item")
273
+ items
254
274
  // sort by `datetime`, where oldest is first in array
255
- .sort((a, b) =>
256
- /** @type {number} */ (a.datetime) <
257
- /** @type {number} */ (b.datetime)
275
+ ?.sort((a, b) =>
276
+ /** @type {number} */ (a[datetimeProperty]) <
277
+ /** @type {number} */ (b[datetimeProperty])
258
278
  ? -1
259
279
  : 1,
260
280
  )
@@ -262,22 +282,16 @@ export class EodashCollection {
262
282
  }
263
283
 
264
284
  getDates() {
265
- return (
266
- this.#collectionStac?.links
267
- .filter((i) => i.rel === "item")
268
- // sort by `datetime`, where oldest is first in array
269
- .sort((a, b) =>
270
- /** @type {number} */ (a.datetime) <
271
- /** @type {number} */ (b.datetime)
272
- ? -1
273
- : 1,
274
- )
275
- .map((i) => new Date(/** @type {number} */ (i.datetime)))
285
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
286
+ if (!datetimeProperty) {
287
+ return [];
288
+ }
289
+ return this.getItems()?.map(
290
+ (i) => new Date(/** @type {number} */ (i[datetimeProperty])),
276
291
  );
277
292
  }
278
293
 
279
294
  async getExtent() {
280
- await this.fetchCollection();
281
295
  return this.#collectionStac?.extent;
282
296
  }
283
297
 
@@ -287,14 +301,19 @@ export class EodashCollection {
287
301
  * @param {Date} [date]
288
302
  **/
289
303
  getItem(date) {
304
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
305
+ if (!datetimeProperty) {
306
+ // in case no datetime property is found, return the first item
307
+ return this.getItems()?.[0];
308
+ }
290
309
  return date
291
310
  ? this.getItems()?.sort((a, b) => {
292
311
  const distanceA = Math.abs(
293
- new Date(/** @type {number} */ (a.datetime)).getTime() -
312
+ new Date(/** @type {number} */ (a[datetimeProperty])).getTime() -
294
313
  date.getTime(),
295
314
  );
296
315
  const distanceB = Math.abs(
297
- new Date(/** @type {number} */ (b.datetime)).getTime() -
316
+ new Date(/** @type {number} */ (b[datetimeProperty])).getTime() -
298
317
  date.getTime(),
299
318
  );
300
319
  return distanceA - distanceB;
@@ -323,12 +342,16 @@ export class EodashCollection {
323
342
  */
324
343
  async updateLayerJson(datetime, layer, map) {
325
344
  await this.fetchCollection();
326
-
345
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
346
+ if (!datetimeProperty) {
347
+ console.warn("[eodash] no datetime property found in collection");
348
+ return;
349
+ }
327
350
  // get the link of the specified date
328
351
  const specifiedLink = this.getItems()?.find(
329
352
  (item) =>
330
- typeof item.datetime === "string" &&
331
- new Date(item.datetime).toISOString() === datetime,
353
+ typeof item[datetimeProperty] === "string" &&
354
+ new Date(item[datetimeProperty]).toISOString() === datetime,
332
355
  );
333
356
 
334
357
  if (!specifiedLink) {
@@ -348,12 +371,13 @@ export class EodashCollection {
348
371
  }
349
372
 
350
373
  /** @type {string | undefined} */
351
- const oldLayerID = findLayer(currentLayers, layer)?.properties.id;
374
+ const oldLayerID = findLayer(currentLayers, layer)?.properties?.id;
352
375
 
353
376
  if (!oldLayerID) {
354
377
  return;
355
378
  }
356
379
 
380
+ //@ts-expect-error TODO
357
381
  const updatedLayers = replaceLayer(currentLayers, oldLayerID, newLayers);
358
382
 
359
383
  return updatedLayers;
@@ -395,16 +419,18 @@ export class EodashCollection {
395
419
  }
396
420
 
397
421
  /**
398
- * Returns GeoDB layer from a list of EodashCollections
422
+ * Returns Observation points layer from a list of EodashCollections
399
423
  *
400
424
  * @param {EodashCollection[]} eodashCollections
401
425
  *
402
426
  **/
403
- static getGeoDBLayer(eodashCollections) {
427
+ static getObservationPointsLayer(eodashCollections) {
404
428
  const allFeatures = [];
405
429
  for (const collection of eodashCollections) {
406
- const isGeoDB = collection.#collectionStac?.endpointtype === "GeoDB";
407
- if (!isGeoDB) {
430
+ const isObservationPoint =
431
+ collection.#collectionStac?.endpointtype === "GeoDB" ||
432
+ /**@type {boolean} */ (collection.#collectionStac?.locations);
433
+ if (!isObservationPoint) {
408
434
  continue;
409
435
  }
410
436
  const collectionFeatures = generateFeatures(
@@ -414,6 +440,7 @@ export class EodashCollection {
414
440
  geoDBID: collection.#collectionStac?.geoDBID,
415
441
  themes: collection.#collectionStac?.themes ?? [],
416
442
  },
443
+ collection.#collectionStac?.locations ? "child" : "item",
417
444
  ).features;
418
445
 
419
446
  if (collectionFeatures.length) {
@@ -475,13 +502,19 @@ export class EodashCollection {
475
502
  "circle-radius": 10,
476
503
  "circle-fill-color": "#00417077",
477
504
  "circle-stroke-color": "#004170",
478
- "fill-color": "#0417077",
505
+ "fill-color": "#00417077",
479
506
  "stroke-color": "#004170",
480
507
  },
481
508
  },
482
509
  ];
483
510
  })(),
484
- interactions: [],
511
+ interactions: (() => {
512
+ const oldLayer = findLayer([...getLayers()], "geodb-collection");
513
+ if (!oldLayer || !oldLayer.interactions?.length) {
514
+ return [];
515
+ }
516
+ return [...oldLayer.interactions];
517
+ })(),
485
518
  };
486
519
  }
487
520
  }