@eodash/eodash 5.7.0 → 5.8.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 (119) hide show
  1. package/core/client/components/EodashOverlay.vue +27 -4
  2. package/core/client/composables/DefineEodash.js +0 -1
  3. package/core/client/composables/DefineWidgets.js +3 -1
  4. package/core/client/eodashSTAC/EodashCollection.js +87 -24
  5. package/core/client/eodashSTAC/cql.js +64 -0
  6. package/core/client/eodashSTAC/createLayers.js +353 -187
  7. package/core/client/eodashSTAC/helpers.js +694 -110
  8. package/core/client/eodashSTAC/mosaic.js +422 -0
  9. package/core/client/plugins/axios.js +31 -1
  10. package/core/client/store/actions.js +1 -1
  11. package/core/client/store/stac.js +54 -4
  12. package/core/client/store/states.js +20 -2
  13. package/core/client/types.ts +152 -28
  14. package/core/client/utils/bands-editor/exampleSchema.json +50 -50
  15. package/core/client/utils/states.js +10 -0
  16. package/dist/client/{DashboardLayout-kRsmsNW5.js → DashboardLayout-DEs4f_Vy.js} +2 -2
  17. package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-PvnV88BA.js} +1 -1
  18. package/dist/client/EodashChart-D-3AGKZY.js +260 -0
  19. package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-Dm_goZzs.js} +3 -3
  20. package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-CJy3zYzD.js} +2 -1
  21. package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-rL8UnpkA.js} +55 -33
  22. package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
  23. package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-CG7u2Aq8.js} +227 -112
  24. package/dist/client/EodashStacInfo-CJhhxthD.js +169 -0
  25. package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-CWyK8QJT.js} +4 -4
  26. package/dist/client/{ExportState-DbOm8hBI.js → ExportState-BXhy5TOU.js} +20 -174
  27. package/dist/client/{Footer-BKg-VVkj.js → Footer-DZPvnhcT.js} +1 -1
  28. package/dist/client/{Header-DCrwCvmY.js → Header-CiGsDE_a.js} +2 -2
  29. package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BJHW7x5V.js} +2 -2
  30. package/dist/client/{PopUp-9uimfK49.js → PopUp-7_XnhvCc.js} +4 -168
  31. package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-LL_UCbvc.js} +5 -5
  32. package/dist/client/VDivider-DWYM0WUr.js +168 -0
  33. package/dist/client/{VImg-CrmB76ZY.js → VImg-B75dgw2K.js} +2 -2
  34. package/dist/client/{VMain-OKZNpcEG.js → VMain-DvMmOL4f.js} +1 -1
  35. package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-ClWHUmWL.js} +2 -2
  36. package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-BqaWs4ch.js} +1 -1
  37. package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-IZanwL-A.js} +677 -379
  38. package/dist/client/{async-DSKj6g1f.js → async-CiNIOBVz.js} +31 -29
  39. package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-3S3y_kZg.js} +17 -17
  40. package/dist/client/dialog-transition-c4ZSnzac.js +170 -0
  41. package/dist/client/eo-dash.js +1 -1
  42. package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-B-c1_6yo.js} +8 -87
  43. package/dist/client/{handling-BrcDDlgS.js → handling-NvY8Csry.js} +3 -4
  44. package/dist/client/{helpers-DjZVl_WF.js → helpers-BFuh19CP.js} +1094 -194
  45. package/dist/client/{index-1JKE68n3.js → index-1wMAyuNL.js} +219 -67
  46. package/dist/client/{index-CSG_JoOA.js → index-B8jGJSTA.js} +62 -56
  47. package/dist/client/{index-Cu3-13Xg.js → index-BKQQJkhy.js} +167 -61
  48. package/dist/client/index-D0jUyol8.js +3167 -0
  49. package/dist/client/{index-CUjl-ZsI.js → index-KGxz5Egv.js} +2 -2
  50. package/dist/client/mosaic-vHvSuih6.js +459 -0
  51. package/dist/client/templates.js +251 -121
  52. package/dist/client/{transition-OHcz5Qq1.js → transition-BkyUsZh_.js} +1 -1
  53. package/dist/node/cli.js +5 -5
  54. package/dist/node/types.d.ts +4 -0
  55. package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
  56. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +57 -25
  57. package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
  58. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +42 -23
  59. package/dist/types/core/client/eodashSTAC/helpers.d.ts +118 -19
  60. package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
  61. package/dist/types/core/client/store/stac.d.ts +38 -8
  62. package/dist/types/core/client/store/states.d.ts +24 -2
  63. package/dist/types/core/client/types.d.ts +128 -24
  64. package/dist/types/core/client/utils/states.d.ts +12 -0
  65. package/dist/types/templates/baseConfig.d.ts +1 -100
  66. package/dist/types/templates/compare.d.ts +32 -3
  67. package/dist/types/templates/expert.d.ts +18 -0
  68. package/dist/types/templates/explore.d.ts +10 -1
  69. package/dist/types/templates/lite.d.ts +11 -0
  70. package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
  71. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
  72. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
  73. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
  74. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
  75. package/dist/types/widgets/EodashLayerControl.vue.d.ts +35 -16
  76. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +14 -10
  77. package/dist/types/widgets/EodashMap/index.vue.d.ts +86 -37
  78. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  79. package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
  80. package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
  81. package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
  82. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  83. package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
  84. package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
  85. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
  86. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
  87. package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
  88. package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
  89. package/package.json +34 -34
  90. package/templates/baseConfig.js +37 -6
  91. package/templates/compare.js +61 -7
  92. package/templates/expert.js +24 -2
  93. package/templates/explore.js +12 -5
  94. package/templates/lite.js +13 -0
  95. package/widgets/EodashChart.vue +162 -21
  96. package/widgets/EodashItemCatalog/index.vue +363 -35
  97. package/widgets/EodashItemCatalog/methods/filters.js +134 -70
  98. package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
  99. package/widgets/EodashItemCatalog/methods/map.js +141 -28
  100. package/widgets/EodashLayerControl.vue +51 -25
  101. package/widgets/EodashMap/EodashMapBtns.vue +109 -48
  102. package/widgets/EodashMap/index.vue +91 -105
  103. package/widgets/EodashMap/methods/btns.js +64 -31
  104. package/widgets/EodashMap/methods/create-layers-config.js +16 -16
  105. package/widgets/EodashMap/methods/index.js +71 -8
  106. package/widgets/EodashMap/methods/use-map-loading.js +58 -0
  107. package/widgets/EodashProcess/index.vue +136 -16
  108. package/widgets/EodashProcess/methods/composables.js +13 -0
  109. package/widgets/EodashProcess/methods/outputs.js +0 -1
  110. package/widgets/EodashProcess/methods/utils.js +29 -27
  111. package/widgets/EodashProcess/states.js +1 -1
  112. package/widgets/EodashStacInfo.vue +64 -2
  113. package/widgets/EodashTimeSlider/index.vue +58 -2
  114. package/widgets/EodashTimeSlider/methods/index.js +5 -48
  115. package/widgets/EodashTimeSlider/types.ts +7 -17
  116. package/widgets/ExportState.vue +13 -4
  117. package/dist/client/EodashChart-CLDqE93C.js +0 -144
  118. package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
  119. package/dist/client/index-5Jhsa8Xe.js +0 -570
@@ -1,5 +1,13 @@
1
1
  <template>
2
2
  <div class="eodash-overlay">
3
+ <div class="progress-bar-wrapper">
4
+ <v-progress-linear
5
+ :active="loading.value"
6
+ color="secondary"
7
+ indeterminate
8
+ absolute
9
+ ></v-progress-linear>
10
+ </div>
3
11
  <p>
4
12
  <a href="https://github.com/eodash/eodash" target="_blank"
5
13
  >eodash v{{ version }}</a
@@ -12,12 +20,18 @@
12
20
  </div>
13
21
  </template>
14
22
  <script setup>
15
- import { useTheme } from "vuetify";
23
+ import { useTheme, useLayout } from "vuetify";
16
24
  import pkgJson from "../../../package.json";
25
+ import { loading } from "@/store/states";
26
+ import { computed } from "vue";
27
+
17
28
  const { computedThemes } = useTheme();
29
+ const { mainRect } = useLayout();
18
30
 
19
31
  const version = pkgJson.version;
20
32
 
33
+ const topPosition = computed(() => `${mainRect.value.top}px`);
34
+
21
35
  const eoxLogo = `<svg width="100%" height="100%" viewBox="0 0 355 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="ink_ext_XXXXXX"><path id="path5076" d="M152.986,40.52l-0.75,-0.019l-0.737,-0.056l-0.725,-0.093l-0.713,-0.127l-0.7,-0.162l-0.7,-0.196l-0.675,-0.228l-0.65,-0.261l-0.65,-0.292l-0.625,-0.319l-0.612,-0.352l-0.588,-0.377l-0.562,-0.407l-0.55,-0.431l-0.538,-0.457l-0.5,-0.481l-0.487,-0.505l-0.45,-0.528l-0.438,-0.55l-0.4,-0.57l-0.375,-0.59l-0.35,-0.609l-0.325,-0.627l-0.287,-0.645l-0.263,-0.66l-0.225,-0.676l-0.2,-0.69l-0.162,-0.704l-0.125,-0.717l-0.1,-0.728l-0.05,-0.739l-0.025,-0.748c0,-8.023 6.512,-14.532 14.537,-14.532c8.038,0 14.538,6.509 14.538,14.532c0,8.036 -6.5,14.544 -14.538,14.544Z" style="fill:none;"/><path id="path5078" d="M165.748,-0.002c-42.009,0 -76.06,18.985 -76.06,42.402c0,13.268 10.921,25.102 28.021,32.875c-8.081,-5.487 -13.403,-12.973 -14.584,-21.859c-2.685,-20.186 16.83,-40.82 45.148,-49.615c-14.687,6.665 -24.923,21.427 -24.923,38.599c0,23.218 18.648,42.046 41.773,42.383c-0.037,0.005 -0.087,0.015 -0.137,0.02c0.263,0 0.512,0.009 0.762,0.009c42.013,0 76.076,-18.984 76.076,-42.412c0,-13.261 -10.937,-25.097 -28.037,-32.871l0.75,0.522l0.736,0.531l0.713,0.543l0.7,0.555l0.687,0.566l0.664,0.578l0.649,0.588l0.625,0.6l0.613,0.609l0.588,0.621l0.574,0.631l0.562,0.643l0.538,0.652l0.513,0.662l0.5,0.672l0.475,0.684l0.449,0.693l0.438,0.703l0.425,0.713l0.387,0.723l0.375,0.73l0.352,0.74l0.336,0.75l0.3,0.76l0.287,0.77l0.264,0.777l0.25,0.787l0.211,0.795l0.188,0.805l0.175,0.812l0.151,0.82l0.125,0.831c2.675,20.19 -16.838,40.825 -45.151,49.619c14.688,-6.665 24.926,-21.426 24.926,-38.614c0,-23.202 -18.65,-42.026 -41.775,-42.367c0.05,-0.005 0.1,-0.015 0.15,-0.025c-0.262,0 -0.526,-0.01 -0.789,-0.01Zm-13.248,11.314c7.817,0 14.25,6.433 14.25,14.25c0,0.001 0,0.001 0,0.001c0,7.817 -6.433,14.249 -14.25,14.249c-7.817,0 -14.25,-6.432 -14.25,-14.249c0,-0.001 0,-0.001 0,-0.001c0,-7.817 6.433,-14.25 14.25,-14.25Z"
22
36
  style="fill:${computedThemes.value["dashboardTheme"].colors["on-secondary"]};fill-rule:nonzero;"/><path id="path5080" d="M213.786,9.529l0.75,0.521l0.738,0.532l0.712,0.544l0.7,0.555l0.688,0.566l0.662,0.577l0.65,0.588l0.625,0.6l0.613,0.61l0.587,0.62l0.575,0.631l0.563,0.643l0.537,0.652l0.513,0.663l0.5,0.672l0.475,0.684l0.45,0.692l0.437,0.703l0.425,0.712l0.388,0.723l0.375,0.731l0.35,0.741l0.337,0.75l0.3,0.76l0.288,0.769l0.262,0.777l0.25,0.786l0.213,0.797l0.187,0.804l0.175,0.812l0.15,0.821l0.125,0.83c2.675,20.19 -16.837,40.825 -45.15,49.619c14.688,-6.665 24.925,-21.426 24.925,-38.614c0,-23.202 -18.65,-42.025 -41.775,-42.367c0.05,-0.005 0.1,-0.015 0.15,-0.025c-0.262,0 -0.525,-0.01 -0.787,-0.01c-42.01,0 -76.062,18.985 -76.062,42.402c0,13.268 10.922,25.103 28.022,32.876c-8.081,-5.487 -13.403,-12.973 -14.584,-21.859c-2.685,-20.187 16.831,-40.82 45.149,-49.615c-14.688,6.665 -24.925,21.426 -24.925,38.598c0,23.218 18.65,42.046 41.775,42.383c-0.038,0.005 -0.088,0.015 -0.138,0.02c0.263,0 0.513,0.01 0.763,0.01c42.012,-0.001 76.075,-18.985 76.075,-42.413c0,-13.261 -10.938,-25.097 -28.038,-32.871Z" style="fill:none;"/><path id="path5082" d="M354.323,82.586l-41.999,-42.002l38.524,-38.515l-29.913,0l-23.562,23.559l-23.563,-23.559l-29.924,0l38.525,38.515l-42,42.002l29.913,0l27.049,-27.046l27.05,27.046l29.9,0"
23
37
  style="fill:${computedThemes.value["dashboardTheme"].colors["on-secondary"]};fill-rule:nonzero;"/><path id="path5084" d="M354.323,82.586l-41.999,-42.002l38.524,-38.515l-29.913,0l-23.562,23.559l-23.563,-23.559l-29.924,0l38.525,38.515l-42,42.002l29.913,0l27.049,-27.046l27.05,27.046l29.9,0Z" style="fill:none;"/><path id="path5086" d="M0,2.069l75.033,0l0,16.489l-51.629,0l0,14.931l40.258,0l0,16.495l-40.258,0l0,16.05l53.124,0l0,16.488l-76.528,0l0,-80.453"
@@ -26,7 +40,7 @@ const base64Logo = btoa(eoxLogo);
26
40
  </script>
27
41
  <style scoped lang="scss">
28
42
  .eodash-overlay {
29
- position: fixed;
43
+ position: absolute;
30
44
  top: 0;
31
45
  left: 0;
32
46
  width: 100%;
@@ -40,10 +54,19 @@ const base64Logo = btoa(eoxLogo);
40
54
  );
41
55
  z-index: 9999;
42
56
  pointer-events: none;
57
+
58
+ .progress-bar-wrapper {
59
+ position: absolute;
60
+ top: v-bind("topPosition");
61
+ left: 0;
62
+ width: 100%;
63
+ z-index: 10000;
64
+ }
65
+
43
66
  p {
44
67
  position: fixed;
45
- bottom: 6px;
46
- left: 24px;
68
+ bottom: 8px;
69
+ left: 162px;
47
70
  color: rgba(var(--v-theme-on-secondary), 1);
48
71
  }
49
72
  a {
@@ -7,7 +7,6 @@ import { inject, reactive } from "vue";
7
7
  *
8
8
  * @async
9
9
  * @param {string | undefined| (()=> Promise<import("@/types").Eodash>) | Function} runtimeConfig
10
- * @returns {Promise<import("@/types").Eodash | null | undefined>}
11
10
  * @see {@linkplain '@/eodash.js'}
12
11
  */
13
12
  export const useEodashRuntime = async (
@@ -88,7 +88,9 @@ export const useDefineWidgets = (widgetConfigs) => {
88
88
  config
89
89
  )?.defineWidget(updatedStac, updatedCompareStac);
90
90
  if (definedConfig) {
91
- definedConfig = reactive(definedConfig);
91
+ definedConfig = /** @type {import("@/types").StaticWidget} */ (
92
+ reactive(definedConfig)
93
+ );
92
94
  }
93
95
  definedWidget.value =
94
96
  definedConfig && definedWidget.value.id === definedConfig.id
@@ -4,6 +4,7 @@ import {
4
4
  extractLayerConfig,
5
5
  extractRoles,
6
6
  fetchApiItems,
7
+ fetchPreAggregations,
7
8
  fetchStyle,
8
9
  fetchAllStyles,
9
10
  findLayer,
@@ -173,11 +174,13 @@ export class EodashCollection {
173
174
  // will try to extract anything it supports but for which we have
174
175
  // less control.
175
176
 
176
- const { layerDatetime, timeControlValues } = extractLayerTimeValues(
177
- await this.getItems(),
177
+ const itemDate =
178
178
  item.properties?.datetime ??
179
- item.properties.start_datetime ??
180
- itemDatetime,
179
+ item.properties.start_datetime ??
180
+ itemDatetime;
181
+ const { layerDatetime, timeControlValues } = extractLayerTimeValues(
182
+ await this.getDates(itemDate),
183
+ itemDate,
181
184
  );
182
185
 
183
186
  const dataAssets = Object.keys(item?.assets ?? {}).reduce((data, ast) => {
@@ -189,9 +192,14 @@ export class EodashCollection {
189
192
 
190
193
  const isSupported =
191
194
  item.links.some((link) =>
192
- ["wms", "xyz", "wmts", "vector-tile", "mapbox-style-document"].includes(
193
- link.rel,
194
- ),
195
+ [
196
+ "wms",
197
+ "xyz",
198
+ "wmts",
199
+ "vector-tile",
200
+ "mapbox-style-document",
201
+ "tilejson",
202
+ ].includes(link.rel),
195
203
  ) || Object.keys(dataAssets).length;
196
204
 
197
205
  if (isSupported) {
@@ -204,6 +212,10 @@ export class EodashCollection {
204
212
  timeControlValues,
205
213
  timeControlProperty: "TIME",
206
214
  }),
215
+ ...(!!this.#collectionStac?.["eodash:layerExclusive"] && {
216
+ layerControlExclusive: true,
217
+ layerControlExpand: false,
218
+ }),
207
219
  };
208
220
 
209
221
  const links = await createLayersFromLinks(
@@ -224,6 +236,7 @@ export class EodashCollection {
224
236
  item,
225
237
  layerDatetime,
226
238
  extraProperties,
239
+ this.#collectionStac,
227
240
  )),
228
241
  ...((this.rasterEndpoint &&
229
242
  (await createLayerFromRender(
@@ -279,24 +292,33 @@ export class EodashCollection {
279
292
 
280
293
  /**
281
294
  * Returns all item links sorted by datetime ascendingly
282
- * @param {boolean} [fields=false] if true, fetch items from API with only properties
295
+ * @param {boolean} [fields=false] if true, fetch items from API with only properties.datetime to optimize performance
283
296
  * @param {boolean} [first] - if true, returns the first page of items only (for API collections)
297
+ * @param {string | Date} [centerDatetime] - Date to center the search around if items exceed maxNumber
284
298
  * @returns {Promise<import("stac-ts").StacLink[] | import("stac-ts").StacItem[] | undefined>}
285
299
  */
286
- async getItems(fields = false, first = false) {
300
+ async getItems(fields = false, first = false, centerDatetime = undefined) {
287
301
  const items = this.#collectionStac?.links.filter((i) => i.rel === "item");
288
-
289
- if (this.isAPI && !items?.length) {
302
+ if (this.isAPI && !items?.length && !first) {
290
303
  const itemUrl = this.#collectionUrl + "/items";
291
304
  if (fields) {
292
305
  return await fetchApiItems(
293
306
  itemUrl,
294
- `fields=properties,-assets,-geometry,-links,-bbox`,
307
+ `fields=properties.datetime,-assets,-geometry,-links,-bbox`,
295
308
  100,
296
309
  first,
310
+ 1000,
311
+ centerDatetime,
297
312
  );
298
313
  }
299
- return await fetchApiItems(itemUrl, undefined, 100, first);
314
+ return await fetchApiItems(
315
+ itemUrl,
316
+ undefined,
317
+ 100,
318
+ first,
319
+ 100,
320
+ centerDatetime,
321
+ );
300
322
  }
301
323
 
302
324
  const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
@@ -315,9 +337,36 @@ export class EodashCollection {
315
337
  );
316
338
  }
317
339
 
318
- async getDates() {
319
- const items = await this.getItems(true, false);
340
+ /**
341
+ * Returns all available dates for the collection, sorted as the source
342
+ * provides. Tries the daily `pre-aggregation` link first (cheap, single
343
+ * request), falls back to fetching items via the API. Use anywhere the
344
+ * collection's date list is needed (date picker, mosaic time control,
345
+ * single-item time slider).
346
+ *
347
+ * NOTE: only the `daily` aggregation interval is consumed today. Other
348
+ * intervals (`monthly`, `hourly`) fall through to the API path.
349
+ *
350
+ * @param {string | Date} [centerDatetime]
351
+ * @returns {Promise<Date[]>}
352
+ */
353
+ async getDates(centerDatetime) {
354
+ await this.fetchCollection();
355
+
356
+ const aggregation = await fetchPreAggregations(
357
+ this.#collectionStac,
358
+ this.#collectionUrl,
359
+ );
360
+ const datetimeAgg = aggregation?.aggregations?.find(
361
+ (/** @type {any} */ a) => a.key?.startsWith("datetime_") || a.interval,
362
+ );
363
+ if (datetimeAgg?.buckets) {
364
+ return datetimeAgg.buckets
365
+ .map((/** @type {any} */ b) => new Date(b.key))
366
+ .filter((/** @type {Date} */ d) => !isNaN(d.getTime()));
367
+ }
320
368
 
369
+ const items = await this.getItems(true, false, centerDatetime);
321
370
  const datetimeProperty = getDatetimeProperty(items);
322
371
  if (!datetimeProperty || !items?.length) {
323
372
  return [];
@@ -329,7 +378,9 @@ export class EodashCollection {
329
378
  new Date(/** @type {string} */ (i.properties?.[datetimeProperty]))
330
379
  : //@ts-expect-error todo
331
380
  (i) => new Date(/** @type {string} */ (i[datetimeProperty]));
332
- return items?.map(mapToDates) || [];
381
+ return (items ?? [])
382
+ .map(mapToDates)
383
+ .filter((/** @type {Date} */ d) => !isNaN(d.getTime()));
333
384
  }
334
385
 
335
386
  async getExtent() {
@@ -343,21 +394,33 @@ export class EodashCollection {
343
394
  * @return {Promise<import("stac-ts").StacItem | import("stac-ts").StacLink | undefined>} item
344
395
  **/
345
396
  async getItem(date) {
346
- if (!date) {
347
- const items = await this.getItems(false, true);
348
- // in case no datetime property is found, return the last item
397
+ let items = await this.getItems(false, true);
398
+ if (!(date instanceof Date) || isNaN(date.getTime())) {
399
+ // in case no date was provided, return the last item
349
400
  return items && items.at(-1);
350
401
  }
351
402
 
352
- const items = await this.getItems();
403
+ if (this.isAPI) {
404
+ const urlArr = this.#collectionUrl.split("/").slice(0, -2);
405
+ const searchURL = urlArr.join("/") + "/search";
406
+ const targetItem = await axios
407
+ .get(searchURL, {
408
+ params: {
409
+ collections: this.#collectionStac?.id,
410
+ datetime: `../${date.toISOString()}`,
411
+ limit: 1,
412
+ sortby: "-datetime",
413
+ },
414
+ })
415
+ .then((resp) => resp.data?.features?.[0]);
416
+ return targetItem;
417
+ }
353
418
  const datetimeProperty = getDatetimeProperty(items);
354
419
  if (!datetimeProperty) {
355
- // in case no datetime property is found, return the last item
356
- const items = await this.getItems(false, true);
357
420
  // in case no datetime property is found, return the last item
358
421
  return items && items.at(-1);
359
422
  }
360
- return (await this.getItems())?.sort((a, b) => {
423
+ return items?.sort((a, b) => {
361
424
  const distanceA = Math.abs(
362
425
  new Date(
363
426
  /** @type {number} */ (
@@ -404,7 +467,7 @@ export class EodashCollection {
404
467
  async updateLayerJson(datetime, layerId, currentLayers) {
405
468
  await this.fetchCollection();
406
469
  const datetimeProperty = getDatetimeProperty(
407
- await this.getItems(true, true),
470
+ await this.getItems(false, true),
408
471
  );
409
472
  if (!datetimeProperty) {
410
473
  console.warn("[eodash] no datetime property found in collection");
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Keys that are catalog based and have dedicated parameters
3
+ * sent to a remote API (e.g. TiTiler, eoAPI STAC search).
4
+ * @type {string[]}
5
+ */
6
+ const SKIP_KEYS = ["collection", "datetime", "bbox"];
7
+
8
+ /**
9
+ * Strips the "properties." prefix that eox-itemfilter catalog configs add to
10
+ * item property keys (e.g. "properties.eo:cloud_cover" -> "eo:cloud_cover").
11
+ * Keys without the prefix are returned unchanged.
12
+ * @param {string} key
13
+ * @returns {string}
14
+ */
15
+ function normalizeFilterKey(key) {
16
+ return key.startsWith("properties.") ? key.slice("properties.".length) : key;
17
+ }
18
+
19
+ /**
20
+ * Builds a CQL2-text filter string from an eox-itemfilter filters record.
21
+ *
22
+ * Handles range, multiselect, and select filter types.
23
+ * - Strips "properties." prefix from keys (catalog eox-itemfilter convention).
24
+ * - Double-quotes property names that contain non-word characters (e.g. "eo:cloud_cover").
25
+ * - Skips catalog-only keys (e.g. "collection").
26
+ *
27
+ * Compatible with both TiTiler's `filter` param and the STAC API `filter` param
28
+ * (both accept CQL2-text).
29
+ *
30
+ * @param {import("@/types").ItemFilterFilters | null | undefined} filters
31
+ * @returns {string}
32
+ */
33
+ export function buildCqlFilter(filters) {
34
+ if (!filters) return "";
35
+
36
+ /** @type {string[]} */
37
+ const parts = [];
38
+
39
+ for (const filter of Object.values(filters)) {
40
+ if (!filter?.key || SKIP_KEYS.includes(filter.key)) continue;
41
+
42
+ const key = normalizeFilterKey(filter.key);
43
+ const prop = /\W/.test(key) ? `"${key}"` : key;
44
+
45
+ if (filter.type === "range" && filter.state) {
46
+ const { min, max } = filter.state;
47
+ if (min != null && min > (filter.min ?? -Infinity)) {
48
+ parts.push(`${prop} >= ${min}`);
49
+ }
50
+ if (max != null && max < (filter.max ?? Infinity)) {
51
+ parts.push(`${prop} <= ${max}`);
52
+ }
53
+ } else if (
54
+ filter.type === "multiselect" &&
55
+ filter.stringifiedState?.length
56
+ ) {
57
+ parts.push(`${prop} IN (${filter.stringifiedState})`);
58
+ } else if (filter.type === "select" && filter.stringifiedState) {
59
+ parts.push(`${prop} = '${filter.stringifiedState}'`);
60
+ }
61
+ }
62
+
63
+ return parts.join(" AND ");
64
+ }