@eodash/eodash 5.0.0 → 5.2.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 (162) 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 +43 -26
  5. package/core/client/components/EodashOverlay.vue +5 -6
  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 +47 -27
  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 +84 -62
  15. package/core/client/eodashSTAC/createLayers.js +30 -0
  16. package/core/client/eodashSTAC/helpers.js +159 -28
  17. package/core/client/eodashSTAC/parquet.js +145 -0
  18. package/core/client/eodashSTAC/triggers.js +6 -3
  19. package/core/client/plugins/index.js +4 -3
  20. package/core/client/plugins/vuetify.js +3 -0
  21. package/core/client/store/actions.js +21 -4
  22. package/core/client/store/stac.js +93 -56
  23. package/core/client/store/states.js +15 -5
  24. package/core/client/types.ts +59 -43
  25. package/core/client/utils/index.js +79 -0
  26. package/core/client/utils/keys.js +2 -2
  27. package/core/client/utils/states.js +30 -5
  28. package/core/client/views/Dashboard.vue +36 -32
  29. package/core/client/vite-env.d.ts +7 -0
  30. package/dist/client/{DashboardLayout-CkWvOMOW.js → DashboardLayout-Dq9Kfe6O.js} +24 -13
  31. package/dist/client/{DynamicWebComponent-DYBbpvUK.js → DynamicWebComponent-DCBMXskE.js} +1 -1
  32. package/dist/client/{EodashDatePicker-CALmW3SI.js → EodashDatePicker-DtngxU6s.js} +59 -32
  33. package/dist/client/{EodashItemFilter-DlQiE713.js → EodashItemFilter-ClQebJQt.js} +20 -10
  34. package/dist/client/{EodashLayerControl-DEzEbft7.js → EodashLayerControl-BLBds28C.js} +29 -16
  35. package/dist/client/EodashLayoutSwitcher-DQ8SfVDd.js +61 -0
  36. package/dist/client/EodashMapBtns-B89_YBDw.js +326 -0
  37. package/dist/client/{EodashStacInfo-DPPxDkF6.js → EodashStacInfo-Dt1nF06x.js} +3 -18
  38. package/dist/client/{EodashTools-CUaL9s4H.js → EodashTools-DV5ykmWc.js} +13 -13
  39. package/dist/client/{ExportState-DjyIZVhl.js → ExportState-B6zZQUmE.js} +57 -52
  40. package/dist/client/{Footer-DyL0JoWt.js → Footer-DNhXs8k6.js} +15 -13
  41. package/dist/client/{Header-B5Dgty9l.js → Header-BjhN5JY4.js} +32 -28
  42. package/dist/client/MobileLayout-JelB6w1G.js +118 -0
  43. package/dist/client/{PopUp-BfB8s_ki.js → PopUp-CgpvNr3o.js} +18 -10
  44. package/dist/client/ProcessList-vecpxThi.js +198 -0
  45. package/dist/client/{VImg-FD1WVphJ.js → VImg-CETuikH2.js} +221 -26
  46. package/dist/client/{VMain-DJKG4SvM.js → VMain-Ci9DyaGU.js} +7 -7
  47. package/dist/client/{VTooltip-CfeefrXI.js → VTooltip-J4ac48X7.js} +12 -10
  48. package/dist/client/{WidgetsContainer-C2TaTdb6.js → WidgetsContainer-CCML4TyV.js} +1 -1
  49. package/dist/client/asWebComponent-ZyEzWOOf.js +19092 -0
  50. package/dist/client/async-B7jIrM53.js +804 -0
  51. package/dist/client/eo-dash.js +1 -1
  52. package/dist/client/{VOverlay-BzOdRu9h.js → forwardRefs-BQclvjMq.js} +332 -28
  53. package/dist/client/handling-BS24aG1q.js +1227 -0
  54. package/dist/client/helpers-wXK7Ywio.js +4556 -0
  55. package/dist/client/index-4UCzZi8B.js +376 -0
  56. package/dist/client/{index-4CT7Tz83.js → index-9KR-G20t.js} +2 -2
  57. package/dist/client/{index-CIHH_3dW.js → index-B2XpdgR6.js} +227 -86
  58. package/dist/client/material-symbols-outlined.woff2 +0 -0
  59. package/dist/client/material-symbols-rounded.woff2 +0 -0
  60. package/dist/client/material-symbols-sharp.woff2 +0 -0
  61. package/dist/client/material-symbols-subset.woff2 +0 -0
  62. package/dist/client/{ssrBoot-BP7SYRyC.js → ssrBoot-Zgc_Ttvi.js} +2 -2
  63. package/dist/client/templates.js +840 -0
  64. package/dist/client/transition-yBii4fu6.js +40 -0
  65. package/dist/node/cli.js +16 -6
  66. package/dist/node/types.d.ts +1 -1
  67. package/dist/types/core/client/App.vue.d.ts +2 -2
  68. package/dist/types/core/client/asWebComponent.d.ts +1 -1
  69. package/dist/types/core/client/components/DynamicWebComponent.vue.d.ts +1 -3
  70. package/dist/types/core/client/components/Footer.vue.d.ts +1 -105
  71. package/dist/types/core/client/components/IframeWrapper.vue.d.ts +1 -1
  72. package/dist/types/core/client/components/MobileLayout.vue.d.ts +1 -324
  73. package/dist/types/core/client/composables/DefineEodash.d.ts +2 -2
  74. package/dist/types/core/client/composables/DefineTemplate.d.ts +1 -1
  75. package/dist/types/core/client/composables/DefineWidgets.d.ts +4 -4
  76. package/dist/types/core/client/composables/index.d.ts +24 -2
  77. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +9 -6
  78. package/dist/types/core/client/eodashSTAC/helpers.d.ts +25 -5
  79. package/dist/types/core/client/eodashSTAC/parquet.d.ts +2 -0
  80. package/dist/types/core/client/plugins/vuetify.d.ts +7 -4
  81. package/dist/types/core/client/store/actions.d.ts +3 -2
  82. package/dist/types/core/client/store/stac.d.ts +16 -13
  83. package/dist/types/core/client/store/states.d.ts +14 -4
  84. package/dist/types/core/client/types.d.ts +46 -31
  85. package/dist/types/core/client/utils/index.d.ts +2 -0
  86. package/dist/types/core/client/utils/keys.d.ts +4 -4
  87. package/dist/types/core/client/utils/states.d.ts +59 -47
  88. package/dist/types/core/client/views/Dashboard.vue.d.ts +2 -2
  89. package/dist/types/templates/baseConfig.d.ts +4 -0
  90. package/dist/types/templates/compare.d.ts +185 -0
  91. package/dist/types/templates/expert.d.ts +147 -0
  92. package/dist/types/templates/index.d.ts +6 -0
  93. package/dist/types/templates/light.d.ts +154 -0
  94. package/dist/types/widgets/EodashDatePicker.vue.d.ts +1 -458
  95. package/dist/types/widgets/EodashItemFilter.vue.d.ts +3 -3
  96. package/dist/types/widgets/EodashLayerControl.vue.d.ts +14 -7
  97. package/dist/types/widgets/EodashLayoutSwitcher.vue.d.ts +1 -3
  98. package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +12 -8
  99. package/dist/types/widgets/EodashMap/index.vue.d.ts +9 -4
  100. package/dist/types/widgets/EodashProcess/ProcessList.vue.d.ts +8 -1
  101. package/dist/types/widgets/EodashProcess/index.vue.d.ts +8 -4
  102. package/dist/types/widgets/EodashProcess/methods/async.d.ts +19 -18
  103. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +3 -2
  104. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/index.d.ts +1 -0
  105. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.d.ts +6 -0
  106. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.d.ts +4 -0
  107. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +5 -0
  108. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/index.d.ts +1 -0
  109. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +12 -5
  110. package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +72 -41
  111. package/dist/types/widgets/EodashProcess/methods/utils.d.ts +41 -21
  112. package/dist/types/widgets/EodashProcess/states.d.ts +11 -0
  113. package/dist/types/widgets/EodashProcess/types.d.ts +41 -0
  114. package/dist/types/widgets/EodashStacInfo.vue.d.ts +14 -14
  115. package/dist/types/widgets/EodashTools.vue.d.ts +3 -3
  116. package/dist/types/widgets/ExportState.vue.d.ts +1 -1
  117. package/dist/types/widgets/PopUp.vue.d.ts +11 -16
  118. package/dist/types/widgets/WidgetsContainer.vue.d.ts +3 -6
  119. package/package.json +55 -45
  120. package/templates/baseConfig.js +68 -0
  121. package/templates/compare.js +142 -0
  122. package/templates/expert.js +124 -0
  123. package/templates/index.js +8 -0
  124. package/templates/light.js +139 -0
  125. package/widgets/EodashDatePicker.vue +80 -31
  126. package/widgets/EodashItemFilter.vue +26 -11
  127. package/widgets/EodashLayerControl.vue +20 -11
  128. package/widgets/EodashLayoutSwitcher.vue +6 -3
  129. package/widgets/EodashMap/EodashMapBtns.vue +269 -0
  130. package/widgets/EodashMap/index.vue +255 -45
  131. package/widgets/EodashMap/methods/create-layers-config.js +4 -3
  132. package/widgets/EodashMap/methods/index.js +33 -23
  133. package/widgets/EodashProcess/ProcessList.vue +47 -11
  134. package/widgets/EodashProcess/index.vue +55 -20
  135. package/widgets/EodashProcess/methods/async.js +99 -60
  136. package/widgets/EodashProcess/methods/composables.js +21 -14
  137. package/widgets/EodashProcess/methods/custom-endpoints/chart/index.js +35 -0
  138. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +275 -0
  139. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +132 -0
  140. package/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.js +94 -0
  141. package/widgets/EodashProcess/methods/custom-endpoints/layers/index.js +33 -0
  142. package/widgets/EodashProcess/methods/handling.js +127 -80
  143. package/widgets/EodashProcess/methods/outputs.js +376 -125
  144. package/widgets/EodashProcess/methods/utils.js +442 -10
  145. package/widgets/EodashProcess/states.js +13 -0
  146. package/widgets/EodashProcess/types.ts +46 -0
  147. package/widgets/EodashStacInfo.vue +2 -17
  148. package/widgets/EodashTools.vue +13 -13
  149. package/widgets/WidgetsContainer.vue +1 -1
  150. package/core/client/eodash.js +0 -454
  151. package/dist/client/EodashLayoutSwitcher-CDeCV8F-.js +0 -52
  152. package/dist/client/EodashMapBtns-CktQCfa-.js +0 -131
  153. package/dist/client/MobileLayout-CRsg_5Q4.js +0 -1217
  154. package/dist/client/ProcessList-DTefwQZx.js +0 -484
  155. package/dist/client/asWebComponent-CLhcT715.js +0 -12479
  156. package/dist/client/eo-dash.css +0 -5
  157. package/dist/client/forwardRefs-Bon_Kku1.js +0 -245
  158. package/dist/client/index-Bm9cbtx5.js +0 -201
  159. package/dist/client/index-DiGDvTQU.js +0 -780
  160. package/dist/client/transition-C5I57hn6.js +0 -37
  161. package/dist/types/core/client/eodash.d.ts +0 -8
  162. package/widgets/EodashMapBtns.vue +0 -113
@@ -7,7 +7,9 @@ import {
7
7
  fetchStyle,
8
8
  findLayer,
9
9
  generateFeatures,
10
+ getDatetimeProperty,
10
11
  replaceLayer,
12
+ extractLayerLegend,
11
13
  } from "./helpers";
12
14
  import {
13
15
  getLayers,
@@ -57,10 +59,10 @@ export class EodashCollection {
57
59
  /**
58
60
  * @type {import("stac-ts").StacLink | undefined}
59
61
  **/
60
- let stacItem;
62
+ let itemLink;
61
63
 
62
64
  /**
63
- * @type {import("stac-ts").StacCollection | undefined}
65
+ * @type {import("stac-ts").StacCollection | import("stac-ts").StacItem | undefined}
64
66
  **/
65
67
  let stac;
66
68
  // TODO get auxiliary layers from collection
@@ -70,22 +72,26 @@ export class EodashCollection {
70
72
  // Load collectionstac if not yet initialized
71
73
  stac = await this.fetchCollection();
72
74
 
73
- const isGeoDB = stac?.endpointtype === "GeoDB";
75
+ const isObservationPoint = stac?.endpointtype === "GeoDB";
74
76
 
75
77
  if (linkOrDate instanceof Date) {
76
78
  // if collectionStac not yet initialized we do it here
77
- stacItem = this.getItem(linkOrDate);
79
+ itemLink = this.getItem(linkOrDate);
78
80
  } else {
79
- stacItem = linkOrDate;
81
+ itemLink = linkOrDate;
82
+ }
83
+ let stacItemUrl = "";
84
+ if (itemLink?.href.startsWith("blob:")) {
85
+ stacItemUrl = itemLink.href;
86
+ } else {
87
+ stacItemUrl = itemLink
88
+ ? toAbsolute(itemLink.href, this.#collectionUrl)
89
+ : this.#collectionUrl;
80
90
  }
81
-
82
- const stacItemUrl = stacItem
83
- ? toAbsolute(stacItem.href, this.#collectionUrl)
84
- : this.#collectionUrl;
85
91
 
86
92
  stac = await axios.get(stacItemUrl).then((resp) => resp.data);
87
93
 
88
- if (!stacItem) {
94
+ if (!itemLink) {
89
95
  // no specific item was requested; render last item
90
96
  this.#collectionStac = new Collection(stac);
91
97
  this.selectedItem = this.getItem();
@@ -105,7 +111,12 @@ export class EodashCollection {
105
111
  const title =
106
112
  this.#collectionStac?.title || this.#collectionStac?.id || "";
107
113
  layersJson.unshift(
108
- ...(await this.buildJsonArray(item, stacItemUrl, title, isGeoDB)),
114
+ ...(await this.buildJsonArray(
115
+ item,
116
+ stacItemUrl,
117
+ title,
118
+ isObservationPoint,
119
+ )),
109
120
  );
110
121
  return layersJson;
111
122
  }
@@ -115,20 +126,19 @@ export class EodashCollection {
115
126
  * @param {import("stac-ts").StacItem} item
116
127
  * @param {string} itemUrl
117
128
  * @param {string} title
118
- * @param {boolean} isGeoDB
129
+ * @param {boolean} isObservationPoint
119
130
  * @param {string} [itemDatetime]
120
131
  * @returns {Promise<Record<string,any>[]>} layers
121
132
  * */
122
- async buildJsonArray(item, itemUrl, title, isGeoDB, itemDatetime) {
133
+ async buildJsonArray(item, itemUrl, title, isObservationPoint, itemDatetime) {
123
134
  log.debug(
124
135
  "Building JSON array",
125
136
  item,
126
137
  itemUrl,
127
138
  title,
128
- isGeoDB,
139
+ isObservationPoint,
129
140
  itemDatetime,
130
141
  );
131
- await this.fetchCollection();
132
142
  // registering top level indicator projection
133
143
  const indicatorProjection =
134
144
  item?.["proj:epsg"] || item?.["eodash:proj4_def"];
@@ -140,8 +150,8 @@ export class EodashCollection {
140
150
 
141
151
  const jsonArray = [];
142
152
 
143
- if (isGeoDB) {
144
- // handled by getGeoDBLayer
153
+ if (isObservationPoint) {
154
+ // handled by getObservationPointsLayer
145
155
  return [];
146
156
  }
147
157
 
@@ -158,7 +168,9 @@ export class EodashCollection {
158
168
 
159
169
  const layerDatetime = extractLayerDatetime(
160
170
  this.getItems(),
161
- item.properties?.datetime ?? itemDatetime,
171
+ item.properties?.datetime ??
172
+ item.properties.start_datetime ??
173
+ itemDatetime,
162
174
  );
163
175
 
164
176
  const dataAssets = Object.keys(item?.assets ?? {}).reduce((data, ast) => {
@@ -173,24 +185,12 @@ export class EodashCollection {
173
185
 
174
186
  if (isSupported) {
175
187
  // Checking for potential legend asset
176
- let extraProperties = null;
177
- if (this.#collectionStac?.assets?.legend?.href) {
178
- extraProperties = {
179
- description: `<div style="width: 100%">
180
- <img src="${this.#collectionStac.assets.legend.href}" style="max-height:70px; margin-top:-15px; margin-bottom:-20px;" />
181
- </div>`,
182
- };
183
- }
184
- // Check if collection has eox:colorlegend definition, if yes overwrite legend description
185
- if (this.#collectionStac && this.#collectionStac["eox:colorlegend"]) {
186
- extraProperties = {
187
- layerLegend: this.#collectionStac["eox:colorlegend"],
188
- };
189
- }
188
+ let extraProperties = extractLayerLegend(this.#collectionStac);
190
189
  extraProperties = {
191
190
  ...extraProperties,
192
191
  ...(this.color && { color: this.color }),
193
192
  };
193
+
194
194
  const links = await createLayersFromLinks(
195
195
  this.#collectionStac?.id ?? "",
196
196
  title,
@@ -198,8 +198,8 @@ export class EodashCollection {
198
198
  layerDatetime,
199
199
  extraProperties,
200
200
  );
201
+
201
202
  jsonArray.push(
202
- ...links,
203
203
  ...(await createLayersFromAssets(
204
204
  this.#collectionStac?.id ?? "",
205
205
  title || this.#collectionStac?.title || item.id,
@@ -210,6 +210,8 @@ export class EodashCollection {
210
210
  layerDatetime,
211
211
  extraProperties,
212
212
  )),
213
+ ...links,
214
+ // We add the links after the assets so they are layered underneath assets
213
215
  );
214
216
  } else {
215
217
  // fallback to STAC
@@ -247,14 +249,21 @@ export class EodashCollection {
247
249
  return this.#collectionStac;
248
250
  }
249
251
 
252
+ /**
253
+ * Returns all item links sorted by datetime ascendingly
254
+ */
250
255
  getItems() {
256
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
257
+ const items = this.#collectionStac?.links.filter((i) => i.rel === "item");
258
+ if (!datetimeProperty) {
259
+ return items;
260
+ }
251
261
  return (
252
- this.#collectionStac?.links
253
- .filter((i) => i.rel === "item")
262
+ items
254
263
  // sort by `datetime`, where oldest is first in array
255
- .sort((a, b) =>
256
- /** @type {number} */ (a.datetime) <
257
- /** @type {number} */ (b.datetime)
264
+ ?.sort((a, b) =>
265
+ /** @type {number} */ (a[datetimeProperty]) <
266
+ /** @type {number} */ (b[datetimeProperty])
258
267
  ? -1
259
268
  : 1,
260
269
  )
@@ -262,22 +271,16 @@ export class EodashCollection {
262
271
  }
263
272
 
264
273
  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)))
274
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
275
+ if (!datetimeProperty) {
276
+ return [];
277
+ }
278
+ return this.getItems()?.map(
279
+ (i) => new Date(/** @type {number} */ (i[datetimeProperty])),
276
280
  );
277
281
  }
278
282
 
279
283
  async getExtent() {
280
- await this.fetchCollection();
281
284
  return this.#collectionStac?.extent;
282
285
  }
283
286
 
@@ -287,14 +290,19 @@ export class EodashCollection {
287
290
  * @param {Date} [date]
288
291
  **/
289
292
  getItem(date) {
293
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
294
+ if (!datetimeProperty) {
295
+ // in case no datetime property is found, return the first item
296
+ return this.getItems()?.[0];
297
+ }
290
298
  return date
291
299
  ? this.getItems()?.sort((a, b) => {
292
300
  const distanceA = Math.abs(
293
- new Date(/** @type {number} */ (a.datetime)).getTime() -
301
+ new Date(/** @type {number} */ (a[datetimeProperty])).getTime() -
294
302
  date.getTime(),
295
303
  );
296
304
  const distanceB = Math.abs(
297
- new Date(/** @type {number} */ (b.datetime)).getTime() -
305
+ new Date(/** @type {number} */ (b[datetimeProperty])).getTime() -
298
306
  date.getTime(),
299
307
  );
300
308
  return distanceA - distanceB;
@@ -323,12 +331,16 @@ export class EodashCollection {
323
331
  */
324
332
  async updateLayerJson(datetime, layer, map) {
325
333
  await this.fetchCollection();
326
-
334
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
335
+ if (!datetimeProperty) {
336
+ console.warn("[eodash] no datetime property found in collection");
337
+ return;
338
+ }
327
339
  // get the link of the specified date
328
340
  const specifiedLink = this.getItems()?.find(
329
341
  (item) =>
330
- typeof item.datetime === "string" &&
331
- new Date(item.datetime).toISOString() === datetime,
342
+ typeof item[datetimeProperty] === "string" &&
343
+ new Date(item[datetimeProperty]).toISOString() === datetime,
332
344
  );
333
345
 
334
346
  if (!specifiedLink) {
@@ -348,12 +360,13 @@ export class EodashCollection {
348
360
  }
349
361
 
350
362
  /** @type {string | undefined} */
351
- const oldLayerID = findLayer(currentLayers, layer)?.properties.id;
363
+ const oldLayerID = findLayer(currentLayers, layer)?.properties?.id;
352
364
 
353
365
  if (!oldLayerID) {
354
366
  return;
355
367
  }
356
368
 
369
+ //@ts-expect-error TODO
357
370
  const updatedLayers = replaceLayer(currentLayers, oldLayerID, newLayers);
358
371
 
359
372
  return updatedLayers;
@@ -395,16 +408,18 @@ export class EodashCollection {
395
408
  }
396
409
 
397
410
  /**
398
- * Returns GeoDB layer from a list of EodashCollections
411
+ * Returns Observation points layer from a list of EodashCollections
399
412
  *
400
413
  * @param {EodashCollection[]} eodashCollections
401
414
  *
402
415
  **/
403
- static getGeoDBLayer(eodashCollections) {
416
+ static getObservationPointsLayer(eodashCollections) {
404
417
  const allFeatures = [];
405
418
  for (const collection of eodashCollections) {
406
- const isGeoDB = collection.#collectionStac?.endpointtype === "GeoDB";
407
- if (!isGeoDB) {
419
+ const isObservationPoint =
420
+ collection.#collectionStac?.endpointtype === "GeoDB" ||
421
+ /**@type {boolean} */ (collection.#collectionStac?.locations);
422
+ if (!isObservationPoint) {
408
423
  continue;
409
424
  }
410
425
  const collectionFeatures = generateFeatures(
@@ -414,6 +429,7 @@ export class EodashCollection {
414
429
  geoDBID: collection.#collectionStac?.geoDBID,
415
430
  themes: collection.#collectionStac?.themes ?? [],
416
431
  },
432
+ collection.#collectionStac?.locations ? "child" : "item",
417
433
  ).features;
418
434
 
419
435
  if (collectionFeatures.length) {
@@ -475,13 +491,19 @@ export class EodashCollection {
475
491
  "circle-radius": 10,
476
492
  "circle-fill-color": "#00417077",
477
493
  "circle-stroke-color": "#004170",
478
- "fill-color": "#0417077",
494
+ "fill-color": "#00417077",
479
495
  "stroke-color": "#004170",
480
496
  },
481
497
  },
482
498
  ];
483
499
  })(),
484
- interactions: [],
500
+ interactions: (() => {
501
+ const oldLayer = findLayer([...getLayers()], "geodb-collection");
502
+ if (!oldLayer || !oldLayer.interactions?.length) {
503
+ return [];
504
+ }
505
+ return [...oldLayer.interactions];
506
+ })(),
485
507
  };
486
508
  }
487
509
  }
@@ -95,6 +95,36 @@ export async function createLayersFromAssets(
95
95
  url: assets[ast].href,
96
96
  attributions: assets[ast].attribution,
97
97
  });
98
+ } else if (assets[ast]?.type === "application/geodb+json") {
99
+ const responseData = await (await fetch(assets[ast].href)).json();
100
+ if (
101
+ !responseData ||
102
+ !Array.isArray(responseData) ||
103
+ responseData.length === 0
104
+ ) {
105
+ console.error(
106
+ "[eodash] GeoDB response data is not in expected format",
107
+ responseData,
108
+ );
109
+ continue;
110
+ }
111
+ const features = responseData.map((item) => {
112
+ return {
113
+ type: "Feature",
114
+ geometry: item.geometry,
115
+ // we pass the item making sure to remove geometry to avoid duplication
116
+ properties: { ...item, geometry: undefined },
117
+ };
118
+ });
119
+ const geojson = {
120
+ type: "FeatureCollection",
121
+ features: features,
122
+ };
123
+ geoJsonSources.push(
124
+ encodeURI(
125
+ "data:application/json;charset=utf-8," + JSON.stringify(geojson),
126
+ ),
127
+ );
98
128
  }
99
129
  }
100
130
 
@@ -6,14 +6,15 @@ import { getStyleVariablesState } from "./triggers.js";
6
6
  /**
7
7
  * @param {import("stac-ts").StacLink[]} [links]
8
8
  * @param {Record<string,any>} [extraProperties]
9
+ * @param {string} [rel = "item"]
9
10
  **/
10
- export function generateFeatures(links, extraProperties = {}) {
11
+ export function generateFeatures(links, extraProperties = {}, rel = "item") {
11
12
  /**
12
13
  * @type {import("geojson").Feature[]}
13
14
  */
14
15
  const features = [];
15
16
  links?.forEach((element) => {
16
- if (element.rel === "item" && "latlng" in element) {
17
+ if (element.rel === rel && "latlng" in element) {
17
18
  const [lat, lon] = /** @type {string} */ (element.latlng)
18
19
  .split(",")
19
20
  .map((it) => Number(it));
@@ -177,9 +178,9 @@ export const extractLayerDatetime = (links, currentStep) => {
177
178
  }
178
179
 
179
180
  // check if links has a datetime value
180
- // TODO: consider datetime ranges
181
- const hasDatetime = links.some((l) => typeof l.datetime === "string");
182
- if (!hasDatetime) {
181
+ const dateProperty = getDatetimeProperty(links);
182
+
183
+ if (!dateProperty) {
183
184
  return undefined;
184
185
  }
185
186
 
@@ -187,11 +188,10 @@ export const extractLayerDatetime = (links, currentStep) => {
187
188
  const controlValues = [];
188
189
  try {
189
190
  currentStep = new Date(currentStep).toISOString();
190
-
191
191
  links.reduce((vals, link) => {
192
- if (link.datetime && link.rel === "item") {
192
+ if (link[dateProperty] && link.rel === "item") {
193
193
  vals.push(
194
- new Date(/** @type {string} */ (link.datetime)).toISOString(),
194
+ new Date(/** @type {string} */ (link[dateProperty])).toISOString(),
195
195
  );
196
196
  }
197
197
  return vals;
@@ -207,7 +207,12 @@ export const extractLayerDatetime = (links, currentStep) => {
207
207
 
208
208
  // item datetime is not included in the item links datetime
209
209
  if (!controlValues.includes(currentStep)) {
210
- return undefined;
210
+ const currentStepTime = new Date(currentStep).getTime();
211
+ currentStep = controlValues.reduce((a, b) => {
212
+ const aDiff = Math.abs(new Date(a).getTime() - currentStepTime);
213
+ const bDiff = Math.abs(new Date(b).getTime() - currentStepTime);
214
+ return bDiff < aDiff ? b : a;
215
+ });
211
216
  }
212
217
 
213
218
  return {
@@ -216,15 +221,15 @@ export const extractLayerDatetime = (links, currentStep) => {
216
221
  slider: true,
217
222
  navigation: true,
218
223
  play: false,
219
- displayFormat: "DD.MM.YYYY HH:MM",
224
+ displayFormat: "DD.MM.YYYY HH:mm",
220
225
  };
221
226
  };
222
227
 
223
228
  /**
224
229
  * Find JSON layer by ID
225
230
  * @param {string} layer
226
- * @param {Record<string, any>[]} layers
227
- * @returns {Record<string,any> | undefined}
231
+ * @param {import("@eox/map").EoxLayer[]} layers
232
+ * @returns {import("@eox/map").EoxLayer | undefined}
228
233
  **/
229
234
  export const findLayer = (layers, layer) => {
230
235
  for (const lyr of layers) {
@@ -235,7 +240,7 @@ export const findLayer = (layers, layer) => {
235
240
  }
236
241
  return found;
237
242
  }
238
- if (lyr.properties.id === layer) {
243
+ if (lyr.properties?.id === layer) {
239
244
  return lyr;
240
245
  }
241
246
  }
@@ -243,14 +248,14 @@ export const findLayer = (layers, layer) => {
243
248
 
244
249
  /**
245
250
  * Removes the layer with the id provided and injects an array of layers in its position
246
- * @param {Record<string,any>[]} currentLayers
251
+ * @param {import("@eox/map").EoxLayer[]} currentLayers
247
252
  * @param {string} oldLayer - id of the layer to be replaced
248
- * @param {Record<string,any>[]} newLayers - array of layers to replace the old layer
249
- * @returns {Record<string,any>[] | undefined}
253
+ * @param {import("@eox/map").EoxLayer[]} newLayers - array of layers to replace the old layer
254
+ * @returns {import("@eox/map").EoxLayer[]}
250
255
  */
251
256
  export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
252
257
  const oldLayerIdx = currentLayers.findIndex(
253
- (l) => l.properties.id === oldLayer,
258
+ (l) => l.properties?.id === oldLayer,
254
259
  );
255
260
 
256
261
  if (oldLayerIdx !== -1) {
@@ -258,10 +263,9 @@ export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
258
263
  "Replacing layer",
259
264
  oldLayer,
260
265
  "with",
261
- newLayers.map((l) => l.properties.id),
266
+ newLayers.map((l) => l.properties?.id),
262
267
  );
263
268
  currentLayers.splice(oldLayerIdx, 1, ...newLayers);
264
- return currentLayers;
265
269
  }
266
270
 
267
271
  for (const l of currentLayers) {
@@ -269,10 +273,10 @@ export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
269
273
  const updatedGroupLyrs = replaceLayer(l.layers, oldLayer, newLayers);
270
274
  if (updatedGroupLyrs?.length) {
271
275
  l.layers = updatedGroupLyrs;
272
- return currentLayers;
273
276
  }
274
277
  }
275
278
  }
279
+ return currentLayers;
276
280
  };
277
281
 
278
282
  /**
@@ -281,18 +285,21 @@ export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
281
285
  * @param {import('../eodashSTAC/EodashCollection.js').EodashCollection[]} indicators
282
286
  * @param {import('ol/layer').Layer} layer
283
287
  */
284
- export const getColFromLayer = async (indicators, layer) => {
288
+ export const getColFromLayer = (indicators, layer) => {
285
289
  // init cols
286
- const collections = await Promise.all(
287
- indicators.map((ind) => ind.fetchCollection()),
288
- );
290
+ const collections = indicators.map((ind) => ind.collectionStac);
289
291
  const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
290
292
 
291
293
  const chosen = collections.find((col) => {
292
294
  const isInd =
293
- col.id === collectionId &&
294
- col.links?.some(
295
- (link) => link.rel === "item" && link.href.includes(itemId),
295
+ col?.id === collectionId &&
296
+ col?.links?.some(
297
+ (link) =>
298
+ link.rel === "item" &&
299
+ (link.href.includes(itemId) ||
300
+ link.id === itemId ||
301
+ //@ts-expect-error attaching the item in link when parsing .parquet items, see @/eodashSTAC/parquet.js
302
+ (link["item"] && link["item"].id === itemId)),
296
303
  );
297
304
  return isInd;
298
305
  });
@@ -306,7 +313,7 @@ export const getColFromLayer = async (indicators, layer) => {
306
313
  * @param {string} collectionId
307
314
  * @param {string} itemId
308
315
  * @param {import('stac-ts').StacLink} link
309
- * @param {string} projectionCode
316
+ * @param {string | import("ol/proj").ProjectionLike} projectionCode
310
317
  *
311
318
  */
312
319
  export const createLayerID = (collectionId, itemId, link, projectionCode) => {
@@ -419,6 +426,87 @@ export async function mergeGeojsons(geojsonUrls) {
419
426
  );
420
427
  }
421
428
 
429
+ /**
430
+ *
431
+ * @param {import("stac-ts").StacItem[]} items
432
+ */
433
+ export function generateLinksFromItems(items) {
434
+ /**
435
+ * @param {string|Date} datetime
436
+ * @returns
437
+ */
438
+ function formateDatetime(datetime) {
439
+ if (datetime instanceof Date) {
440
+ return datetime.toISOString();
441
+ }
442
+ if (typeof datetime === "string") {
443
+ const date = new Date(datetime);
444
+ return date.toISOString();
445
+ }
446
+ return datetime;
447
+ }
448
+
449
+ return items.map((item) => {
450
+ const itemBlob = new Blob([JSON.stringify(item)], {
451
+ type: "application/geo+json",
452
+ });
453
+ // urls are revoked when updating the collection. see updateEodashCollections in "../utils/index.js"
454
+ const itemUrl = URL.createObjectURL(itemBlob);
455
+ return {
456
+ id: item.id,
457
+ rel: "item",
458
+ type: "application/geo+json",
459
+ title: item.id,
460
+ href: itemUrl,
461
+ ...(item.properties.datetime && {
462
+ datetime: formateDatetime(item.properties.datetime),
463
+ }),
464
+ ...(item.properties.start_datetime && {
465
+ start_datetime: formateDatetime(item.properties.start_datetime),
466
+ }),
467
+ ...(item.properties.end_datetime && {
468
+ end_datetime: formateDatetime(item.properties.end_datetime),
469
+ }),
470
+ //@ts-expect-error projection extension
471
+ ...(item.properties?.["proj:epsg"] && {
472
+ "proj:epsg": /** @type {number} **/ (item.properties["proj:epsg"]),
473
+ }),
474
+ //@ts-expect-error eodash projection
475
+ ...(item.properties?.["eodash:proj4_def"] && {
476
+ "eodash:proj4_def": item.properties["eodash:proj4_def"],
477
+ }),
478
+
479
+ ...(item.geometry?.type == "Point" &&
480
+ item.geometry?.coordinates.length && {
481
+ latlng: item.geometry.coordinates.reverse().join(","),
482
+ }),
483
+ ...(Object.values(item.assets ?? {}).some(
484
+ (asset) =>
485
+ asset.href.startsWith("s3://veda-data-store") &&
486
+ asset.type === "image/tiff; application=geotiff",
487
+ ) && {
488
+ cog_href: Object.values(item.assets ?? {}).find((asset) =>
489
+ asset.href.startsWith("s3://veda-data-store"),
490
+ )?.href,
491
+ }),
492
+ };
493
+ });
494
+ }
495
+
496
+ /**
497
+ * @param {import("../eodashSTAC/EodashCollection.js").EodashCollection} collection
498
+ */
499
+ export function revokeCollectionBlobUrls(collection) {
500
+ collection.collectionStac?.links.forEach((link) => {
501
+ if (!(link.rel === "item" && link.type === "application/geo+json")) {
502
+ return;
503
+ }
504
+ if (link.href.startsWith("blob:")) {
505
+ URL.revokeObjectURL(link.href);
506
+ }
507
+ });
508
+ }
509
+
422
510
  /**
423
511
  * adds tooltip to the layer if the style has tooltip property
424
512
  * @param {Record<string,any>} layer
@@ -441,3 +529,46 @@ export const addTooltipInteraction = (layer, style) => {
441
529
  ];
442
530
  }
443
531
  };
532
+
533
+ /**
534
+ *
535
+ * @param {import("stac-ts").StacLink[]} [links]
536
+ */
537
+ export function getDatetimeProperty(links) {
538
+ if (!links?.length) {
539
+ return undefined;
540
+ }
541
+ // TODO: consider other properties for datetime ranges
542
+ const datetimeProperties = ["datetime", "start_datetime", "end_datetime"];
543
+ for (const prop of datetimeProperties) {
544
+ const propExists = links.some(
545
+ (l) => l[prop] && typeof l[prop] === "string",
546
+ );
547
+ if (!propExists) {
548
+ continue;
549
+ }
550
+ return prop;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * @param {import ("stac-ts").StacCollection | undefined | null} collection
556
+ * @returns {object}
557
+ */
558
+ export function extractLayerLegend(collection) {
559
+ let extraProperties = {};
560
+ if (collection?.assets?.legend?.href) {
561
+ extraProperties = {
562
+ description: `<div style="width: 100%">
563
+ <img src="${collection.assets.legend.href}" style="max-height:70px; margin-top:-15px; margin-bottom:-20px;" />
564
+ </div>`,
565
+ };
566
+ }
567
+ // Check if collection has eox:colorlegend definition, if yes overwrite legend description
568
+ if (collection && collection["eox:colorlegend"]) {
569
+ extraProperties = {
570
+ layerLegend: collection["eox:colorlegend"],
571
+ };
572
+ }
573
+ return extraProperties;
574
+ }