@eodash/eodash 5.1.0 → 5.3.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 (133) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -2
  2. package/core/client/components/EodashOverlay.vue +4 -5
  3. package/core/client/components/MobileLayout.vue +42 -21
  4. package/core/client/composables/index.js +54 -60
  5. package/core/client/eodashSTAC/EodashCollection.js +199 -108
  6. package/core/client/eodashSTAC/auth.js +86 -0
  7. package/core/client/eodashSTAC/createLayers.js +234 -4
  8. package/core/client/eodashSTAC/helpers.js +281 -59
  9. package/core/client/eodashSTAC/parquet.js +0 -13
  10. package/core/client/eodashSTAC/triggers.js +1 -1
  11. package/core/client/store/actions.js +14 -0
  12. package/core/client/store/stac.js +46 -8
  13. package/core/client/store/states.js +6 -0
  14. package/core/client/types.ts +206 -3
  15. package/core/client/utils/bands-editor/arithmetic.js +144 -0
  16. package/core/client/utils/bands-editor/colors.js +36 -0
  17. package/core/client/utils/bands-editor/dom.js +196 -0
  18. package/core/client/utils/bands-editor/exampleSchema.json +1320 -0
  19. package/core/client/utils/bands-editor/index.js +68 -0
  20. package/core/client/utils/bands-editor/rgb.js +102 -0
  21. package/core/client/utils/index.js +5 -2
  22. package/core/client/views/Dashboard.vue +1 -1
  23. package/core/client/vite-env.d.ts +122 -0
  24. package/dist/client/{DashboardLayout-ByVs1DrY.js → DashboardLayout-Cq15p4TH.js} +5 -6
  25. package/dist/client/{DynamicWebComponent-C3W7HSQm.js → DynamicWebComponent-Cv-fPRG1.js} +1 -1
  26. package/dist/client/{EodashDatePicker-BIAf1sMT.js → EodashDatePicker-CPlJwEIO.js} +20 -22
  27. package/dist/client/{EodashItemFilter-DPznh8UB.js → EodashItemFilter-Ydebgbjj.js} +46 -31
  28. package/dist/client/EodashLayerControl-COhrkNEs.js +1517 -0
  29. package/dist/client/{EodashLayoutSwitcher-C5qTEffW.js → EodashLayoutSwitcher-pnKhTRZV.js} +4 -4
  30. package/dist/client/EodashMapBtns-Cj0Fx119.js +301 -0
  31. package/dist/client/{EodashStacInfo-CSvvF2jI.js → EodashStacInfo-Dadkg_Nj.js} +1 -1
  32. package/dist/client/EodashTimeSlider-CpoHX0S7.js +53 -0
  33. package/dist/client/{EodashTools-Cv1SXQ5y.js → EodashTools-UGBG7KC9.js} +10 -7
  34. package/dist/client/{ExportState-D-iuwaad.js → ExportState-GtJkAqeZ.js} +145 -121
  35. package/dist/client/{Footer-CyF0zRAk.js → Footer-D3ZPG5c4.js} +1 -1
  36. package/dist/client/{Header-CgD8jDKU.js → Header-z6AK-wpN.js} +2 -3
  37. package/dist/client/MobileLayout-BXNsNftb.js +118 -0
  38. package/dist/client/{PopUp-BsYLvWch.js → PopUp-BbQdjENV.js} +79 -44
  39. package/dist/client/{ProcessList-C2xsLU2_.js → ProcessList-C6VsdsYI.js} +18 -12
  40. package/dist/client/{VImg-OHe8YTs2.js → VImg-CxaMSB99.js} +203 -5
  41. package/dist/client/{VMain-PryTLU4a.js → VMain-Ds7yw0wj.js} +1 -1
  42. package/dist/client/{VTooltip-DZ0fjpB3.js → VTooltip-Cze6CEVh.js} +2 -3
  43. package/dist/client/{WidgetsContainer-B9LBadcC.js → WidgetsContainer-D66bj-JJ.js} +1 -1
  44. package/dist/client/asWebComponent-CWbNRdf9.js +8895 -0
  45. package/dist/client/{async-DkSu_u2K.js → async-BA7oWCMX.js} +69 -5
  46. package/dist/client/easing-CH0-9wR8.js +35 -0
  47. package/dist/client/eo-dash.js +1 -1
  48. package/dist/client/{VOverlay-yUn7p-Uf.js → forwardRefs-BUfxOIo-.js} +308 -28
  49. package/dist/client/{handling-CgmFXkW6.js → handling-DlNTtKB-.js} +27 -6
  50. package/dist/client/{helpers-Dy0Q13tP.js → helpers-CtE0W7iu.js} +595 -278
  51. package/dist/client/{index-skjhlH8u.js → index-CeEZIjO6.js} +26 -13
  52. package/dist/client/{index-Ch_HchK3.js → index-CsKbRDeN.js} +238 -77
  53. package/dist/client/{index-Dqj4tbx2.js → index-D4_NRKrf.js} +2 -2
  54. package/dist/client/index-DeECc3lV.js +571 -0
  55. package/dist/client/material-symbols-outlined.woff2 +0 -0
  56. package/dist/client/material-symbols-rounded.woff2 +0 -0
  57. package/dist/client/material-symbols-sharp.woff2 +0 -0
  58. package/dist/client/material-symbols-subset.woff2 +0 -0
  59. package/dist/client/templates.js +106 -49
  60. package/dist/client/{transition-C98Yn4Vo.js → transition-Byvp3L6Y.js} +1 -1
  61. package/dist/node/cli.js +6 -6
  62. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +24 -10
  63. package/dist/types/core/client/eodashSTAC/auth.d.ts +7 -0
  64. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +15 -3
  65. package/dist/types/core/client/eodashSTAC/helpers.d.ts +51 -15
  66. package/dist/types/core/client/plugins/vuetify.d.ts +14 -14
  67. package/dist/types/core/client/store/actions.d.ts +2 -0
  68. package/dist/types/core/client/store/stac.d.ts +16 -7
  69. package/dist/types/core/client/store/states.d.ts +4 -0
  70. package/dist/types/core/client/types.d.ts +171 -3
  71. package/dist/types/core/client/utils/bands-editor/arithmetic.d.ts +8 -0
  72. package/dist/types/core/client/utils/bands-editor/colors.d.ts +15 -0
  73. package/dist/types/core/client/utils/bands-editor/dom.d.ts +42 -0
  74. package/dist/types/core/client/utils/bands-editor/index.d.ts +20 -0
  75. package/dist/types/core/client/utils/bands-editor/rgb.d.ts +15 -0
  76. package/dist/types/core/client/utils/index.d.ts +1 -1
  77. package/dist/types/templates/baseConfig.d.ts +87 -1
  78. package/dist/types/templates/compare.d.ts +0 -25
  79. package/dist/types/templates/expert.d.ts +17 -21
  80. package/dist/types/templates/explore.d.ts +67 -0
  81. package/dist/types/templates/index.d.ts +1 -1
  82. package/dist/types/templates/{light.d.ts → lite.d.ts} +9 -0
  83. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +21 -0
  84. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +49 -0
  85. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +4 -0
  86. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +12 -0
  87. package/dist/types/widgets/EodashItemCatalog/types.d.ts +14 -0
  88. package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +6 -0
  89. package/dist/types/widgets/EodashMap/index.vue.d.ts +114 -0
  90. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  91. package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
  92. package/dist/types/widgets/EodashProcess/methods/async.d.ts +1 -0
  93. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  94. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +7 -0
  95. package/dist/types/widgets/EodashTools.vue.d.ts +10 -10
  96. package/dist/types/widgets/ExportState.vue.d.ts +2 -0
  97. package/package.json +31 -28
  98. package/templates/baseConfig.js +10 -5
  99. package/templates/compare.js +2 -22
  100. package/templates/expert.js +19 -18
  101. package/templates/explore.js +62 -0
  102. package/templates/index.js +1 -1
  103. package/templates/{light.js → lite.js} +11 -2
  104. package/widgets/EodashDatePicker.vue +15 -18
  105. package/widgets/EodashItemCatalog/index.vue +161 -0
  106. package/widgets/EodashItemCatalog/methods/filters.js +216 -0
  107. package/widgets/EodashItemCatalog/methods/handlers.js +50 -0
  108. package/widgets/EodashItemCatalog/methods/map.js +144 -0
  109. package/widgets/EodashItemCatalog/types.ts +15 -0
  110. package/widgets/EodashItemFilter.vue +35 -28
  111. package/widgets/EodashLayerControl.vue +10 -6
  112. package/widgets/EodashLayoutSwitcher.vue +1 -1
  113. package/widgets/EodashMap/EodashMapBtns.vue +278 -0
  114. package/widgets/EodashMap/index.vue +263 -38
  115. package/widgets/EodashMap/methods/create-layers-config.js +9 -6
  116. package/widgets/EodashMap/methods/index.js +27 -13
  117. package/widgets/EodashProcess/ProcessList.vue +13 -1
  118. package/widgets/EodashProcess/index.vue +17 -1
  119. package/widgets/EodashProcess/methods/async.js +22 -1
  120. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +25 -3
  121. package/widgets/EodashProcess/methods/handling.js +2 -0
  122. package/widgets/EodashProcess/methods/outputs.js +1 -0
  123. package/widgets/EodashProcess/methods/utils.js +45 -1
  124. package/widgets/EodashTimeSlider.vue +40 -0
  125. package/widgets/EodashTools.vue +7 -3
  126. package/widgets/ExportState.vue +53 -22
  127. package/dist/client/EodashLayerControl-Bhxjw4V2.js +0 -154
  128. package/dist/client/EodashMapBtns-WoGq8MuV.js +0 -173
  129. package/dist/client/MobileLayout-EKQ_kpSh.js +0 -1226
  130. package/dist/client/asWebComponent-By_7_JjS.js +0 -19193
  131. package/dist/client/forwardRefs-BXxrv98s.js +0 -272
  132. package/dist/client/index-BuhOHXKv.js +0 -199
  133. package/widgets/EodashMapBtns.vue +0 -155
@@ -2,20 +2,28 @@ import { Collection, Item } from "stac-js";
2
2
  import { toAbsolute } from "stac-js/src/http.js";
3
3
  import {
4
4
  extractLayerConfig,
5
- extractLayerDatetime,
6
5
  extractRoles,
6
+ fetchApiItems,
7
7
  fetchStyle,
8
+ fetchAllStyles,
8
9
  findLayer,
9
10
  generateFeatures,
10
11
  getDatetimeProperty,
12
+ isSTACItem,
11
13
  replaceLayer,
14
+ extractLayerLegend,
15
+ extractLayerTimeValues,
12
16
  } from "./helpers";
13
17
  import {
14
18
  getLayers,
15
19
  getCompareLayers,
16
20
  registerProjection,
17
21
  } from "@/store/actions";
18
- import { createLayersFromAssets, createLayersFromLinks } from "./createLayers";
22
+ import {
23
+ createLayerFromRender,
24
+ createLayersFromAssets,
25
+ createLayersFromLinks,
26
+ } from "./createLayers";
19
27
  import axios from "@/plugins/axios";
20
28
  import log from "loglevel";
21
29
  import { dataThemesBrands } from "@/utils/states";
@@ -23,6 +31,10 @@ import { dataThemesBrands } from "@/utils/states";
23
31
  export class EodashCollection {
24
32
  #collectionUrl = "";
25
33
 
34
+ isAPI = true;
35
+ /** @type {string | null} */
36
+ rasterEndpoint = null;
37
+
26
38
  /** @type {import("stac-ts").StacCollection | undefined} */
27
39
  #collectionStac;
28
40
 
@@ -44,81 +56,87 @@ export class EodashCollection {
44
56
  return this.#collectionStac;
45
57
  }
46
58
 
47
- /** @param {string} collectionUrl */
48
- constructor(collectionUrl) {
59
+ /**
60
+ * @param {string} collectionUrl
61
+ * @param {boolean} isAPI
62
+ * @param {string | null} rasterEndpoint
63
+ */
64
+ constructor(collectionUrl, isAPI = true, rasterEndpoint = null) {
49
65
  this.#collectionUrl = collectionUrl;
66
+ this.isAPI = isAPI;
67
+ this.rasterEndpoint = rasterEndpoint;
50
68
  }
51
69
 
52
70
  /**
53
71
  * @async
54
- * @param {import('stac-ts').StacLink | Date} [linkOrDate]
55
- * @returns
72
+ * @param {import("stac-ts").StacItem | import('stac-ts').StacLink | Date } [itemOrDate]
73
+ * @returns {Promise<Record<string,any>[]>} layers
56
74
  */
57
- createLayersJson = async (linkOrDate) => {
75
+ createLayersJson = async (itemOrDate) => {
58
76
  /**
59
- * @type {import("stac-ts").StacLink | undefined}
77
+ * @type {import("stac-ts").StacLink | import("stac-ts").StacItem | undefined}
60
78
  **/
61
- let itemLink;
79
+ let itemOrItemLink;
62
80
 
63
- /**
64
- * @type {import("stac-ts").StacCollection | import("stac-ts").StacItem | undefined}
65
- **/
66
- let stac;
67
81
  // TODO get auxiliary layers from collection
68
82
  /** @type {Record<string,any>[]} */
69
83
  let layersJson = [];
70
84
 
71
- // Load collectionstac if not yet initialized
72
- stac = await this.fetchCollection();
85
+ // Load collectionstac if not yet initialized // TODO
86
+ await this.fetchCollection();
73
87
 
74
- const isObservationPoint = stac?.endpointtype === "GeoDB";
88
+ const isObservationPoint = this.#collectionStac?.endpointtype === "GeoDB";
75
89
 
76
- if (linkOrDate instanceof Date) {
90
+ if (itemOrDate instanceof Date) {
77
91
  // if collectionStac not yet initialized we do it here
78
- itemLink = this.getItem(linkOrDate);
92
+ itemOrItemLink = await this.getItem(itemOrDate);
79
93
  } else {
80
- itemLink = linkOrDate;
94
+ itemOrItemLink = itemOrDate;
81
95
  }
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;
89
- }
90
-
91
- stac = await axios.get(stacItemUrl).then((resp) => resp.data);
92
-
93
- if (!itemLink) {
94
- // no specific item was requested; render last item
95
- this.#collectionStac = new Collection(stac);
96
- this.selectedItem = this.getItem();
97
96
 
98
- if (this.selectedItem) {
99
- layersJson = await this.createLayersJson(this.selectedItem);
97
+ let stacItemUrl = "";
98
+ if (isSTACItem(itemOrItemLink)) {
99
+ this.selectedItem = itemOrItemLink;
100
+ stacItemUrl = this.#collectionUrl + `/items/${this.selectedItem.id}`;
101
+ } else if (itemOrItemLink) {
102
+ if (itemOrItemLink?.href?.startsWith("blob:")) {
103
+ stacItemUrl = itemOrItemLink.href;
104
+ // Use native fetch for blob URLs to avoid axios/cache interceptor issues
105
+ this.selectedItem = await fetch(stacItemUrl).then(
106
+ async (resp) => await resp.json(),
107
+ );
100
108
  } else {
109
+ stacItemUrl = toAbsolute(itemOrItemLink.href, this.#collectionUrl);
110
+ this.selectedItem = await axios
111
+ .get(stacItemUrl)
112
+ .then((resp) => resp.data);
113
+ }
114
+ } else if (!this.selectedItem) {
115
+ this.selectedItem = await this.getItem();
116
+ if (!this.selectedItem) {
101
117
  console.warn(
102
118
  "[eodash] the selected collection does not include any items",
103
119
  );
120
+ return [];
121
+ } else if (this.selectedItem) {
122
+ // if no specific item was requested, we create layers from the latest item
123
+ return this.createLayersJson(this.selectedItem);
104
124
  }
105
- return [];
106
- } else {
107
- // specific item was requested
108
- const item = new Item(stac);
109
- this.selectedItem = item;
110
- const title =
111
- this.#collectionStac?.title || this.#collectionStac?.id || "";
112
- layersJson.unshift(
113
- ...(await this.buildJsonArray(
114
- item,
115
- stacItemUrl,
116
- title,
117
- isObservationPoint,
118
- )),
119
- );
120
- return layersJson;
121
125
  }
126
+
127
+ // specific item was requested
128
+ const item = new Item(this.selectedItem);
129
+ this.selectedItem = item;
130
+ const title = this.#collectionStac?.title || this.#collectionStac?.id || "";
131
+ layersJson.unshift(
132
+ ...(await this.buildJsonArray(
133
+ item,
134
+ stacItemUrl,
135
+ title,
136
+ isObservationPoint,
137
+ )),
138
+ );
139
+ return layersJson;
122
140
  };
123
141
 
124
142
  /**
@@ -130,6 +148,11 @@ export class EodashCollection {
130
148
  * @returns {Promise<Record<string,any>[]>} layers
131
149
  * */
132
150
  async buildJsonArray(item, itemUrl, title, isObservationPoint, itemDatetime) {
151
+ if (!item) {
152
+ console.warn("[eodash] no item provided to buildJsonArray");
153
+ return [];
154
+ }
155
+
133
156
  log.debug(
134
157
  "Building JSON array",
135
158
  item,
@@ -160,13 +183,21 @@ export class EodashCollection {
160
183
  // will try to extract anything it supports but for which we have
161
184
  // less control.
162
185
 
186
+ const rasterformURL = /** @type {string|undefined} */ (
187
+ this.#collectionStac?.["eodash:rasterform"]
188
+ );
189
+ /** @type {import("@/types").EodashRasterJSONForm|undefined} */
190
+ const rasterForm = rasterformURL
191
+ ? await axios.get(rasterformURL).then((resp) => resp.data)
192
+ : undefined;
163
193
  let { layerConfig, style } = extractLayerConfig(
164
194
  this.#collectionStac?.id ?? "",
165
195
  await fetchStyle(item, itemUrl),
196
+ rasterForm,
166
197
  );
167
198
 
168
- const layerDatetime = extractLayerDatetime(
169
- this.getItems(),
199
+ const { layerDatetime, timeControlValues } = extractLayerTimeValues(
200
+ await this.getItems(),
170
201
  item.properties?.datetime ??
171
202
  item.properties.start_datetime ??
172
203
  itemDatetime,
@@ -179,40 +210,43 @@ export class EodashCollection {
179
210
  return data;
180
211
  }, /** @type {Record<string,import('stac-ts').StacAsset>} */ ({}));
181
212
  const isSupported =
182
- item.links.some((link) => ["wms", "xyz", "wmts"].includes(link.rel)) ||
213
+ item.links.some((link) => ["wms", "xyz", "wmts", "vector-tile"].includes(link.rel)) ||
183
214
  Object.keys(dataAssets).length;
184
215
 
185
216
  if (isSupported) {
186
217
  // Checking for potential legend asset
187
- let extraProperties = null;
188
- if (this.#collectionStac?.assets?.legend?.href) {
189
- extraProperties = {
190
- description: `<div style="width: 100%">
191
- <img src="${this.#collectionStac.assets.legend.href}" style="max-height:70px; margin-top:-15px; margin-bottom:-20px;" />
192
- </div>`,
193
- };
194
- }
195
- // Check if collection has eox:colorlegend definition, if yes overwrite legend description
196
- if (this.#collectionStac && this.#collectionStac["eox:colorlegend"]) {
197
- extraProperties = {
198
- layerLegend: this.#collectionStac["eox:colorlegend"],
199
- };
200
- }
218
+ let extraProperties = extractLayerLegend(this.#collectionStac);
201
219
  extraProperties = {
202
220
  ...extraProperties,
203
221
  ...(this.color && { color: this.color }),
222
+ ...(timeControlValues && {
223
+ timeControlValues,
224
+ timeControlProperty: "TIME",
225
+ }),
204
226
  };
205
227
 
206
228
  const links = await createLayersFromLinks(
207
229
  this.#collectionStac?.id ?? "",
208
230
  title,
209
231
  item,
232
+ itemUrl,
210
233
  layerDatetime,
211
234
  extraProperties,
212
235
  );
213
236
 
214
237
  jsonArray.push(
215
- ...links,
238
+ ...((this.rasterEndpoint &&
239
+ createLayerFromRender(
240
+ this.rasterEndpoint,
241
+ this.#collectionStac,
242
+ item,
243
+ {
244
+ ...extraProperties,
245
+ ...(layerConfig && { layerConfig }),
246
+ ...(layerDatetime && { layerDatetime }),
247
+ },
248
+ )) ||
249
+ []),
216
250
  ...(await createLayersFromAssets(
217
251
  this.#collectionStac?.id ?? "",
218
252
  title || this.#collectionStac?.title || item.id,
@@ -223,6 +257,8 @@ export class EodashCollection {
223
257
  layerDatetime,
224
258
  extraProperties,
225
259
  )),
260
+ // We add the links after the assets so they are layered underneath assets
261
+ ...links,
226
262
  );
227
263
  } else {
228
264
  // fallback to STAC
@@ -251,21 +287,38 @@ export class EodashCollection {
251
287
 
252
288
  async fetchCollection() {
253
289
  if (!this.#collectionStac) {
254
- log.debug("Fetching collection file", this.#collectionUrl);
255
290
  const col = await axios
256
291
  .get(this.#collectionUrl)
257
292
  .then((resp) => resp.data);
258
293
  this.#collectionStac = new Collection(col);
294
+ log.debug("Fetching collection file", this.#collectionUrl);
259
295
  }
260
296
  return this.#collectionStac;
261
297
  }
262
298
 
263
299
  /**
264
300
  * Returns all item links sorted by datetime ascendingly
301
+ * @param {boolean} [fields=false] if true, fetch items from API with only properties
302
+ * @param {boolean} [first] - if true, returns the first page of items only (for API collections)
303
+ * @returns {Promise<import("stac-ts").StacLink[] | import("stac-ts").StacItem[] | undefined>}
265
304
  */
266
- getItems() {
267
- const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
305
+ async getItems(fields = false, first = false) {
268
306
  const items = this.#collectionStac?.links.filter((i) => i.rel === "item");
307
+
308
+ if (this.isAPI && !items?.length) {
309
+ const itemUrl = this.#collectionUrl + "/items";
310
+ if (fields) {
311
+ return await fetchApiItems(
312
+ itemUrl,
313
+ `fields=properties,-assets,-geometry,-links,-bbox`,
314
+ 100,
315
+ first,
316
+ );
317
+ }
318
+ return await fetchApiItems(itemUrl, undefined, 100, first);
319
+ }
320
+
321
+ const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
269
322
  if (!datetimeProperty) {
270
323
  return items;
271
324
  }
@@ -281,14 +334,21 @@ export class EodashCollection {
281
334
  );
282
335
  }
283
336
 
284
- getDates() {
285
- const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
286
- if (!datetimeProperty) {
337
+ async getDates() {
338
+ const items = await this.getItems(true, false);
339
+
340
+ const datetimeProperty = getDatetimeProperty(items);
341
+ if (!datetimeProperty || !items?.length) {
287
342
  return [];
288
343
  }
289
- return this.getItems()?.map(
290
- (i) => new Date(/** @type {number} */ (i[datetimeProperty])),
291
- );
344
+
345
+ const mapToDates = this.isAPI
346
+ ? //@ts-expect-error todo
347
+ (i) =>
348
+ new Date(/** @type {string} */ (i.properties?.[datetimeProperty]))
349
+ : //@ts-expect-error todo
350
+ (i) => new Date(/** @type {string} */ (i[datetimeProperty]));
351
+ return items?.map(mapToDates) || [];
292
352
  }
293
353
 
294
354
  async getExtent() {
@@ -299,38 +359,58 @@ export class EodashCollection {
299
359
  * Get closest Item Link from a certain date,
300
360
  * get the latest if no date provided
301
361
  * @param {Date} [date]
362
+ * @return {Promise<import("stac-ts").StacItem | import("stac-ts").StacLink | undefined>} item
302
363
  **/
303
- getItem(date) {
304
- const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
364
+ async getItem(date) {
365
+ if (!date) {
366
+ return (await this.getItems(false, true))?.[0];
367
+ }
368
+
369
+ const items = await this.getItems();
370
+ const datetimeProperty = getDatetimeProperty(items);
305
371
  if (!datetimeProperty) {
306
372
  // in case no datetime property is found, return the first item
307
- return this.getItems()?.[0];
373
+ return (await this.getItems(false, true))?.[0];
308
374
  }
309
- return date
310
- ? this.getItems()?.sort((a, b) => {
311
- const distanceA = Math.abs(
312
- new Date(/** @type {number} */ (a[datetimeProperty])).getTime() -
313
- date.getTime(),
314
- );
315
- const distanceB = Math.abs(
316
- new Date(/** @type {number} */ (b[datetimeProperty])).getTime() -
317
- date.getTime(),
318
- );
319
- return distanceA - distanceB;
320
- })[0]
321
- : this.getItems()?.at(-1);
375
+ return (await this.getItems())?.sort((a, b) => {
376
+ const distanceA = Math.abs(
377
+ new Date(
378
+ /** @type {number} */ (
379
+ //@ts-expect-error TODO
380
+ this.isAPI ? a.properties[datetimeProperty] : a[datetimeProperty]
381
+ ),
382
+ ).getTime() - date.getTime(),
383
+ );
384
+ const distanceB = Math.abs(
385
+ new Date(
386
+ /** @type {number} */ (
387
+ //@ts-expect-error TODO
388
+ this.isAPI ? b.properties[datetimeProperty] : b[datetimeProperty]
389
+ ),
390
+ ).getTime() - date.getTime(),
391
+ );
392
+ return distanceA - distanceB;
393
+ })[0];
322
394
  }
323
395
 
324
396
  async getToolTipProperties() {
325
397
  if (!(this.selectedItem instanceof Item)) {
326
398
  return [];
327
399
  }
328
- let styles = await fetchStyle(
400
+ // get all style links, which could contribute by tooltip config and aggregate them
401
+ const styles = await fetchAllStyles(
329
402
  this.selectedItem,
330
403
  `${this.#collectionUrl}/${this.selectedItem.id}`,
331
404
  );
332
- const { tooltip } = styles || { tooltip: [] };
333
- this.#tooltipProperties = tooltip ?? [];
405
+ // get only unique ids to avoid duplicates
406
+ const aggregatedTooltips = [
407
+ ...new Map(
408
+ styles
409
+ .flatMap(style => style.tooltip || [])
410
+ .map(entry => [entry.id, entry])
411
+ ).values()
412
+ ];
413
+ this.#tooltipProperties = aggregatedTooltips ?? [];
334
414
  return this.#tooltipProperties;
335
415
  }
336
416
 
@@ -342,17 +422,15 @@ export class EodashCollection {
342
422
  */
343
423
  async updateLayerJson(datetime, layer, map) {
344
424
  await this.fetchCollection();
345
- const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
425
+ const datetimeProperty = getDatetimeProperty(
426
+ await this.getItems(true, true),
427
+ );
346
428
  if (!datetimeProperty) {
347
429
  console.warn("[eodash] no datetime property found in collection");
348
430
  return;
349
431
  }
350
432
  // get the link of the specified date
351
- const specifiedLink = this.getItems()?.find(
352
- (item) =>
353
- typeof item[datetimeProperty] === "string" &&
354
- new Date(item[datetimeProperty]).toISOString() === datetime,
355
- );
433
+ const specifiedLink = await this.getItem(new Date(datetime));
356
434
 
357
435
  if (!specifiedLink) {
358
436
  console.warn(
@@ -361,9 +439,22 @@ export class EodashCollection {
361
439
  );
362
440
  return;
363
441
  }
364
-
365
- // create json layers from the item
366
- const newLayers = await this.createLayersJson(specifiedLink);
442
+ /** @type {Record<string, any>[]} */
443
+ let newLayers = [];
444
+ if (isSTACItem(specifiedLink)) {
445
+ // if specifiedLink is an item, we create layers from it
446
+ newLayers = await this.buildJsonArray(
447
+ specifiedLink,
448
+ this.#collectionUrl + `/items/${specifiedLink.id}`,
449
+ this.#collectionStac?.title || this.#collectionStac?.id || "",
450
+ this.#collectionStac?.endpointtype === "GeoDB" ||
451
+ !!this.#collectionStac?.locations,
452
+ datetime,
453
+ );
454
+ } else {
455
+ // create json layers from the item
456
+ newLayers = await this.createLayersJson(specifiedLink);
457
+ }
367
458
 
368
459
  let currentLayers = getLayers();
369
460
  if (map === "second") {
@@ -402,10 +493,10 @@ export class EodashCollection {
402
493
  );
403
494
 
404
495
  return [
496
+ //@ts-expect-error indicator instead of item
405
497
  ...(await createLayersFromLinks(
406
498
  indicator?.id ?? "",
407
499
  indicator?.title || indicator.id,
408
- //@ts-expect-error indicator instead of item
409
500
  indicator,
410
501
  )),
411
502
  ...(await createLayersFromAssets(
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Generic handler for possible authentications schemes as defined in STAC authentication extension.
3
+ * @param {import("@/types").StacAuthItem } item
4
+ * @param {import("@/types").StacAuthLink | import("@/types").StacAuthAsset} linkOrAsset
5
+ * @returns {string}
6
+ */
7
+ export function handleAuthenticationOfLink(item, linkOrAsset) {
8
+ // browse through all authentication refs on a link to find a first one we support
9
+ for (const authRef of linkOrAsset["auth:refs"] || []) {
10
+ const authSchemes = item["auth:schemes"];
11
+ if (authRef in authSchemes) {
12
+ switch (authSchemes[authRef].type) {
13
+ case "apiKey": {
14
+ //@ts-expect-error TODO
15
+ return handleApiKeyBasedAuth(authSchemes[authRef], linkOrAsset.href);
16
+ }
17
+ // case "signedUrl":
18
+ // case "s3":
19
+ // case "http":
20
+ // case "openIdConnect":
21
+ // case "apiKey":
22
+ // case "oauth2":
23
+ // todo add more handlers when needed
24
+ default:
25
+ console.error(`eodash does not support referenced authentication scheme ${authRef}`);
26
+ }
27
+ }
28
+ }
29
+ return linkOrAsset.href;
30
+ }
31
+ /**
32
+ * Generic handler for possible authentications schemes as defined in STAC authentication extension.
33
+ * @param {import("@/types").ApiKeyAuthScheme } schemeDef
34
+ * @param { string } href
35
+ * @returns { string }
36
+ */
37
+ function handleApiKeyBasedAuth(schemeDef, href) {
38
+ // add token to query parameters of href
39
+ let url = href;
40
+ switch (schemeDef.in) {
41
+ case "query": {
42
+ const apiKey = schemeDef.name;
43
+ const envVar = "EODASH_" + apiKey;
44
+ const envValue = process.env[envVar];
45
+ if (envValue) {
46
+ url = setQueryParam(href, apiKey, envValue);
47
+ } else {
48
+ console.error(`env variable ${envVar} for authentication parameter ${apiKey} not set`);
49
+ }
50
+ break
51
+ }
52
+ default:
53
+ console.error('eodash does not support any referenced handler');
54
+ }
55
+ return url;
56
+ }
57
+
58
+ /**
59
+ * Inserts or replaces a query parameter in a URL string (without escaping special characters).
60
+ *
61
+ * @param {string} url - Input URL (may contain special characters)
62
+ * @param {string} key - Query parameter key (e.g. "token", "authCode")
63
+ * @param {string} value - Value to set for the key
64
+ * @returns {string} - Updated URL string
65
+ */
66
+ function setQueryParam(url, key, value) {
67
+ // Split off any fragment (#something)
68
+ const [base, hash] = url.split("#", 2);
69
+
70
+ // Regex to detect existing key, respecting ? or &
71
+ const pattern = new RegExp(`([?&])${key}=[^&#]*`, "i");
72
+
73
+ if (pattern.test(base)) {
74
+ // Replace existing key=value
75
+ url = base.replace(pattern, `$1${key}=${value}`);
76
+ } else {
77
+ // Append as new key=value
78
+ const joiner = base.includes("?") ? "&" : "?";
79
+ url = `${base}${joiner}${key}=${value}`;
80
+ }
81
+
82
+ // Reattach fragment if present
83
+ if (hash) url += "#" + hash;
84
+
85
+ return url;
86
+ }