@eodash/eodash 5.7.0 → 5.7.1

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 (113) 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 +74 -20
  5. package/core/client/eodashSTAC/cql.js +64 -0
  6. package/core/client/eodashSTAC/createLayers.js +169 -116
  7. package/core/client/eodashSTAC/helpers.js +391 -98
  8. package/core/client/eodashSTAC/mosaic.js +416 -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 +25 -4
  12. package/core/client/store/states.js +20 -2
  13. package/core/client/types.ts +147 -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-ePbdvUTE.js} +2 -2
  17. package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-g5SdRbjY.js} +1 -1
  18. package/dist/client/{EodashChart-CLDqE93C.js → EodashChart-ChrkA5oS.js} +7 -6
  19. package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-CY1TrsDo.js} +3 -3
  20. package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-DzshHDlS.js} +2 -1
  21. package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-DMCeMkSd.js} +50 -20
  22. package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-iJ8mX1rF.js} +3 -3
  23. package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-C7Cr7q5Y.js} +200 -86
  24. package/dist/client/EodashStacInfo-K7-iuXa7.js +169 -0
  25. package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-BEK7e7JM.js} +4 -4
  26. package/dist/client/{ExportState-DbOm8hBI.js → ExportState-CqIzfMYQ.js} +6 -168
  27. package/dist/client/{Footer-BKg-VVkj.js → Footer-1P0cmBOZ.js} +1 -1
  28. package/dist/client/{Header-DCrwCvmY.js → Header-CkkBaBNh.js} +2 -2
  29. package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BPf49kDU.js} +2 -2
  30. package/dist/client/{PopUp-9uimfK49.js → PopUp-DPZq5SWI.js} +4 -168
  31. package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-DuI6WpS1.js} +5 -5
  32. package/dist/client/VDivider-Bj9LG_JV.js +168 -0
  33. package/dist/client/{VImg-CrmB76ZY.js → VImg-Ddv5OyMz.js} +2 -2
  34. package/dist/client/{VMain-OKZNpcEG.js → VMain-7yqk_qC3.js} +1 -1
  35. package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-2hAJyEoa.js} +2 -2
  36. package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-2DOrlwCB.js} +1 -1
  37. package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-BlFN2lkB.js} +456 -306
  38. package/dist/client/{async-DSKj6g1f.js → async-B2H8ZwUK.js} +2 -2
  39. package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-u_vn5hPk.js} +17 -17
  40. package/dist/client/dialog-transition-BrMkMiNa.js +170 -0
  41. package/dist/client/eo-dash.js +1 -1
  42. package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-DAlror6d.js} +8 -87
  43. package/dist/client/{handling-BrcDDlgS.js → handling-B6I1V5eG.js} +3 -3
  44. package/dist/client/{helpers-DjZVl_WF.js → helpers-CBqIUSXJ.js} +794 -182
  45. package/dist/client/index-BWq1ZKaV.js +3168 -0
  46. package/dist/client/{index-CUjl-ZsI.js → index-CJ9UI3ln.js} +2 -2
  47. package/dist/client/{index-CSG_JoOA.js → index-DXLz4sFR.js} +62 -56
  48. package/dist/client/{index-1JKE68n3.js → index-DhKU6KqM.js} +211 -67
  49. package/dist/client/{index-Cu3-13Xg.js → index-DjQlbWbJ.js} +7 -6
  50. package/dist/client/mosaic-BFYLEwyx.js +455 -0
  51. package/dist/client/templates.js +250 -119
  52. package/dist/client/{transition-OHcz5Qq1.js → transition-D_W6Wn9v.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 +77 -18
  60. package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
  61. package/dist/types/core/client/store/stac.d.ts +31 -7
  62. package/dist/types/core/client/store/states.d.ts +24 -2
  63. package/dist/types/core/client/types.d.ts +123 -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 +9 -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/states.d.ts +1 -1
  83. package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
  84. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
  85. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
  86. package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
  87. package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
  88. package/package.json +33 -33
  89. package/templates/baseConfig.js +36 -6
  90. package/templates/compare.js +62 -7
  91. package/templates/expert.js +23 -0
  92. package/templates/explore.js +11 -5
  93. package/templates/lite.js +14 -0
  94. package/widgets/EodashChart.vue +1 -0
  95. package/widgets/EodashItemCatalog/index.vue +363 -35
  96. package/widgets/EodashItemCatalog/methods/filters.js +134 -70
  97. package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
  98. package/widgets/EodashItemCatalog/methods/map.js +142 -28
  99. package/widgets/EodashLayerControl.vue +42 -11
  100. package/widgets/EodashMap/EodashMapBtns.vue +109 -48
  101. package/widgets/EodashMap/index.vue +89 -105
  102. package/widgets/EodashMap/methods/btns.js +37 -5
  103. package/widgets/EodashMap/methods/create-layers-config.js +16 -16
  104. package/widgets/EodashMap/methods/index.js +71 -8
  105. package/widgets/EodashMap/methods/use-map-loading.js +52 -0
  106. package/widgets/EodashProcess/index.vue +1 -0
  107. package/widgets/EodashProcess/states.js +1 -1
  108. package/widgets/EodashStacInfo.vue +64 -2
  109. package/widgets/EodashTimeSlider/index.vue +58 -2
  110. package/widgets/EodashTimeSlider/methods/index.js +5 -48
  111. package/widgets/EodashTimeSlider/types.ts +7 -17
  112. package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
  113. 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) => {
@@ -204,6 +207,10 @@ export class EodashCollection {
204
207
  timeControlValues,
205
208
  timeControlProperty: "TIME",
206
209
  }),
210
+ ...(!!this.#collectionStac?.["eodash:layerExclusive"] && {
211
+ layerControlExclusive: true,
212
+ layerControlExpand: false,
213
+ }),
207
214
  };
208
215
 
209
216
  const links = await createLayersFromLinks(
@@ -224,6 +231,7 @@ export class EodashCollection {
224
231
  item,
225
232
  layerDatetime,
226
233
  extraProperties,
234
+ this.#collectionStac,
227
235
  )),
228
236
  ...((this.rasterEndpoint &&
229
237
  (await createLayerFromRender(
@@ -279,24 +287,33 @@ export class EodashCollection {
279
287
 
280
288
  /**
281
289
  * Returns all item links sorted by datetime ascendingly
282
- * @param {boolean} [fields=false] if true, fetch items from API with only properties
290
+ * @param {boolean} [fields=false] if true, fetch items from API with only properties.datetime to optimize performance
283
291
  * @param {boolean} [first] - if true, returns the first page of items only (for API collections)
292
+ * @param {string | Date} [centerDatetime] - Date to center the search around if items exceed maxNumber
284
293
  * @returns {Promise<import("stac-ts").StacLink[] | import("stac-ts").StacItem[] | undefined>}
285
294
  */
286
- async getItems(fields = false, first = false) {
295
+ async getItems(fields = false, first = false, centerDatetime = undefined) {
287
296
  const items = this.#collectionStac?.links.filter((i) => i.rel === "item");
288
-
289
- if (this.isAPI && !items?.length) {
297
+ if (this.isAPI && !items?.length && !first) {
290
298
  const itemUrl = this.#collectionUrl + "/items";
291
299
  if (fields) {
292
300
  return await fetchApiItems(
293
301
  itemUrl,
294
- `fields=properties,-assets,-geometry,-links,-bbox`,
302
+ `fields=properties.datetime,-assets,-geometry,-links,-bbox`,
295
303
  100,
296
304
  first,
305
+ 1000,
306
+ centerDatetime,
297
307
  );
298
308
  }
299
- return await fetchApiItems(itemUrl, undefined, 100, first);
309
+ return await fetchApiItems(
310
+ itemUrl,
311
+ undefined,
312
+ 100,
313
+ first,
314
+ 100,
315
+ centerDatetime,
316
+ );
300
317
  }
301
318
 
302
319
  const datetimeProperty = getDatetimeProperty(this.#collectionStac?.links);
@@ -315,9 +332,34 @@ export class EodashCollection {
315
332
  );
316
333
  }
317
334
 
318
- async getDates() {
319
- const items = await this.getItems(true, false);
335
+ /**
336
+ * Returns all available dates for the collection, sorted as the source
337
+ * provides. Tries the daily `pre-aggregation` link first (cheap, single
338
+ * request), falls back to fetching items via the API. Use anywhere the
339
+ * collection's date list is needed (date picker, mosaic time control,
340
+ * single-item time slider).
341
+ *
342
+ * NOTE: only the `daily` aggregation interval is consumed today. Other
343
+ * intervals (`monthly`, `hourly`) fall through to the API path.
344
+ *
345
+ * @param {string | Date} [centerDatetime]
346
+ * @returns {Promise<Date[]>}
347
+ */
348
+ async getDates(centerDatetime) {
349
+ await this.fetchCollection();
350
+
351
+ const aggregation = await fetchPreAggregations(
352
+ this.#collectionStac,
353
+ this.#collectionUrl,
354
+ );
355
+ const datetimeAgg = aggregation?.aggregations?.find(
356
+ (/** @type {any} */ a) => a.key?.startsWith("datetime_") || a.interval,
357
+ );
358
+ if (datetimeAgg?.buckets) {
359
+ return datetimeAgg.buckets.map((/** @type {any} */ b) => new Date(b.key));
360
+ }
320
361
 
362
+ const items = await this.getItems(true, false, centerDatetime);
321
363
  const datetimeProperty = getDatetimeProperty(items);
322
364
  if (!datetimeProperty || !items?.length) {
323
365
  return [];
@@ -343,21 +385,33 @@ export class EodashCollection {
343
385
  * @return {Promise<import("stac-ts").StacItem | import("stac-ts").StacLink | undefined>} item
344
386
  **/
345
387
  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
388
+ let items = await this.getItems(false, true);
389
+ if (!(date instanceof Date) || isNaN(date.getTime())) {
390
+ // in case no date was provided, return the last item
349
391
  return items && items.at(-1);
350
392
  }
351
393
 
352
- const items = await this.getItems();
394
+ if (this.isAPI) {
395
+ const urlArr = this.#collectionUrl.split("/").slice(0, -2);
396
+ const searchURL = urlArr.join("/") + "/search";
397
+ const targetItem = await axios
398
+ .get(searchURL, {
399
+ params: {
400
+ collections: this.#collectionStac?.id,
401
+ datetime: `../${date.toISOString()}`,
402
+ limit: 1,
403
+ sortby: "-datetime",
404
+ },
405
+ })
406
+ .then((resp) => resp.data?.features?.[0]);
407
+ return targetItem;
408
+ }
353
409
  const datetimeProperty = getDatetimeProperty(items);
354
410
  if (!datetimeProperty) {
355
- // in case no datetime property is found, return the last item
356
- const items = await this.getItems(false, true);
357
411
  // in case no datetime property is found, return the last item
358
412
  return items && items.at(-1);
359
413
  }
360
- return (await this.getItems())?.sort((a, b) => {
414
+ return items?.sort((a, b) => {
361
415
  const distanceA = Math.abs(
362
416
  new Date(
363
417
  /** @type {number} */ (
@@ -404,7 +458,7 @@ export class EodashCollection {
404
458
  async updateLayerJson(datetime, layerId, currentLayers) {
405
459
  await this.fetchCollection();
406
460
  const datetimeProperty = getDatetimeProperty(
407
- await this.getItems(true, true),
461
+ await this.getItems(false, true),
408
462
  );
409
463
  if (!datetimeProperty) {
410
464
  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
+ }