@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
@@ -0,0 +1,1227 @@
1
+ import log from 'loglevel';
2
+ import { s as separateEndpointLinks, c as createTiffLayerDefinition, p as pollProcessStatus, e as extractAsyncResults, u as updateJobsStatus, a as creatAsyncProcessLayerDefinitions, g as getBboxProperty, b as generateTimePairs, f as updateJsonformSchemaTarget, h as extractGeometries, i as applyProcessLayersToMap } from './async-B7jIrM53.js';
3
+ import { f as axios, h as extractLayerConfig, D as compareIndicator, C as indicator, t as extractCollectionUrls, w as currentCompareUrl, x as currentUrl, u as generateLinksFromItems, k as getDatetimeProperty, y as datetime, E as comparePoi, z as poi } from './helpers-wXK7Ywio.js';
4
+ import mustache from 'mustache';
5
+ import { O as isFirstLoad, E as useSTAcStore, R as readParquetItems, S as useGetSubCodeId } from './asWebComponent-ZyEzWOOf.js';
6
+ import { toAbsolute } from 'stac-js/src/http.js';
7
+
8
+ ////// --- CHARTS --- //////
9
+ /**
10
+ * @param {object} options
11
+ * @param {import("stac-ts").StacLink[] | undefined} options.links
12
+ * @param {Record<string,any> | undefined} options.jsonformValue
13
+ * @param {string} options.specUrl
14
+ * @param {(input:import("^/EodashProcess/types").CustomEnpointInput)=>Promise<Record<string,any>[] | undefined | null>} [options.customEndpointsHandler]
15
+ * @param {import("vue").Ref<boolean>} options.isPolling
16
+ * @param {import("stac-ts").StacCollection} options.selectedStac
17
+ * @param {Record<string,any>} options.jsonformSchema
18
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} options.jobs
19
+ * @param {boolean} [options.enableCompare=false] - Whether to enable compare mode
20
+ * @returns {Promise<[import("@eox/chart").EOxChart["spec"] | null,Record<string,any>|null]>}
21
+ **/
22
+ async function processCharts({
23
+ links,
24
+ jsonformValue,
25
+ specUrl,
26
+ customEndpointsHandler,
27
+ jsonformSchema,
28
+ selectedStac,
29
+ isPolling,
30
+ jobs,
31
+ enableCompare = false,
32
+ }) {
33
+ if (!specUrl || !links) return [null, null];
34
+ /** @type {import("vega-lite").TopLevelSpec} **/
35
+ const spec = await axios.get(specUrl).then((resp) => {
36
+ return resp.data;
37
+ });
38
+
39
+ /** @type {Record<string,any>} */
40
+ const dataValues = {};
41
+
42
+ const [standardLinks, endpointLinks] = separateEndpointLinks(
43
+ links,
44
+ "service",
45
+ undefined,
46
+ );
47
+
48
+ const data =
49
+ customEndpointsHandler &&
50
+ jsonformValue &&
51
+ (await customEndpointsHandler({
52
+ jsonformSchema,
53
+ jsonformValue,
54
+ links: endpointLinks,
55
+ selectedStac,
56
+ isPolling,
57
+ jobs,
58
+ enableCompare,
59
+ }));
60
+
61
+ if (data && data.length) {
62
+ //@ts-expect-error we assume data to exist in spec
63
+ spec.data.values = data;
64
+ return [spec, dataValues];
65
+ }
66
+
67
+ const dataLinks = standardLinks.filter((link) => link.rel === "service");
68
+ try {
69
+ checkForData: for (const link of dataLinks ?? []) {
70
+ switch (link.type) {
71
+ case undefined:
72
+ continue;
73
+ case "application/json":
74
+ await injectVegaInlineData(spec, {
75
+ url: link.href,
76
+ jsonformValue: jsonformValue,
77
+ link: link,
78
+ flatstyleUrl: /** @type string */ (link["eox:flatstyle"]),
79
+ jsonformSchema,
80
+ });
81
+ break checkForData;
82
+ case "text/csv":
83
+ await injectVegaUrlData(spec, {
84
+ url: link.href,
85
+ jsonformValue: jsonformValue,
86
+ flatstyleUrl: /** @type string */ (link["eox:flatstyle"]),
87
+ });
88
+ break checkForData;
89
+ }
90
+ }
91
+ } catch (e) {
92
+ console.error("[eodash] Error while injecting Vega data", e);
93
+ }
94
+ return [spec, dataValues];
95
+ }
96
+
97
+ /**
98
+ *
99
+ * @param {import("vega-lite").TopLevelSpec} spec
100
+ * @param {object} injectables
101
+ * @param {string} injectables.url
102
+ * @param {Record<string,any>} [injectables.jsonformValue]
103
+ * @param {import("stac-ts").StacLink} injectables.link
104
+ * @param {url} [injectables.flatstyleUrl]
105
+ * @param {import("json-schema").JSONSchema7} [injectables.jsonformSchema]
106
+ */
107
+ async function injectVegaInlineData(
108
+ spec,
109
+ { url, jsonformValue, link, flatstyleUrl, jsonformSchema },
110
+ ) {
111
+ if (!spec.data) {
112
+ return;
113
+ }
114
+ if (link.method == "GET") {
115
+ // we see if any of the multiQuery values match an array in the jsonformValue
116
+ // and if so, we can do multiple requests and merge all data together.
117
+ //@ts-expect-error type jsonform Schema
118
+ const multiQuery = jsonformSchema?.options?.multiQuery;
119
+ const matches = Object.keys(jsonformValue ?? {}).filter((key) => {
120
+ return Array.isArray(multiQuery)
121
+ ? multiQuery.includes(key)
122
+ : multiQuery === key;
123
+ });
124
+ if (matches.length > 0 && jsonformValue) {
125
+ const dataValues = [];
126
+ for (const match of matches) {
127
+ if (Array.isArray(jsonformValue[match])) {
128
+ for (const value of jsonformValue[match]) {
129
+ const dataUrl = await renderDataUrl(
130
+ url,
131
+ { ...jsonformValue, [match]: value },
132
+ flatstyleUrl,
133
+ );
134
+ dataValues.push(await axios.get(dataUrl).then((resp) => resp.data));
135
+ }
136
+ } else {
137
+ const dataUrl = await renderDataUrl(url, jsonformValue, flatstyleUrl);
138
+ dataValues.push(await axios.get(dataUrl).then((resp) => resp.data));
139
+ }
140
+ }
141
+ /** @type {import("vega-lite/build/src/data").InlineData} */
142
+ (spec.data).values = dataValues.flat();
143
+ return spec;
144
+ }
145
+ // if no array matches, we can just do a single request
146
+ const dataUrl = await renderDataUrl(url, jsonformValue, flatstyleUrl);
147
+ /** @type {import("vega-lite/build/src/data").InlineData} */
148
+ (spec.data).values = await axios.get(dataUrl).then((resp) => {
149
+ return resp.data;
150
+ });
151
+ } else if (link.method == "POST") {
152
+ // get body template to be used in POST request, check first if available
153
+ if (!link.body) {
154
+ console.error(
155
+ "[eodash] Inline data POST request requires a body template",
156
+ );
157
+ return spec;
158
+ }
159
+ /** @type {string} */
160
+ const bodyTemplate = await axios
161
+ // @ts-expect-error we assume link.body to be a string, not defined in stac-ts
162
+ .get(link.body, { responseType: "text" })
163
+ .then((resp) => {
164
+ return resp.data;
165
+ });
166
+ const body = JSON.parse(
167
+ mustache.render(bodyTemplate, {
168
+ ...(jsonformValue ?? {}),
169
+ }),
170
+ );
171
+ /** @type {import("vega-lite/build/src/data").InlineData} */
172
+ (spec.data).values = await axios.post(url, body).then((resp) => {
173
+ return resp.data;
174
+ });
175
+ }
176
+ return spec;
177
+ }
178
+
179
+ /**
180
+ * @param {import("vega-lite").TopLevelSpec} spec
181
+ * @param {object} injectables
182
+ * @param {string} injectables.url
183
+ * @param {Record<string,any>} [injectables.jsonformValue]
184
+ * @param {url} [injectables.flatstyleUrl]
185
+ */
186
+ async function injectVegaUrlData(spec, { url, jsonformValue, flatstyleUrl }) {
187
+ if (!spec.data) {
188
+ return;
189
+ }
190
+ const dataUrl = await renderDataUrl(url, jsonformValue, flatstyleUrl);
191
+ /** @type {import("vega").UrlData} */
192
+ (spec.data).url = dataUrl;
193
+ return spec;
194
+ }
195
+ /**
196
+ *
197
+ * @param {string} url
198
+ * @param {Record<string,any>} [jsonformValue]
199
+ * @param {string} [flatstyleUrl]
200
+ */
201
+ async function renderDataUrl(url, jsonformValue, flatstyleUrl) {
202
+ let flatStyles = {};
203
+ if (flatstyleUrl) {
204
+ flatStyles = await axios.get(flatstyleUrl).then((resp) => resp.data);
205
+ }
206
+
207
+ return mustache.render(url, {
208
+ ...(jsonformValue ?? {}),
209
+ ...flatStyles,
210
+ });
211
+ }
212
+
213
+ /////// MAP LAYERS ///////
214
+
215
+ /**
216
+ * @param {object} options
217
+ * @param {import("stac-ts").StacLink[] | undefined} options.links
218
+ * @param {Record<string,any> | undefined} options.jsonformValue
219
+ * @param {string} options.layerId
220
+ * @param {string} [options.projection]
221
+ */
222
+ async function processGeoTiff({
223
+ links,
224
+ jsonformValue,
225
+ layerId,
226
+ projection,
227
+ }) {
228
+ if (!links) return;
229
+
230
+ const [geotiffLinks, _] = separateEndpointLinks(
231
+ links,
232
+ "service",
233
+ "image/tiff",
234
+ );
235
+
236
+ if (!geotiffLinks.length) {
237
+ return;
238
+ }
239
+ let urls = [];
240
+ let processId = "";
241
+ for (const link of geotiffLinks ?? []) {
242
+ urls.push(mustache.render(link.href, { ...(jsonformValue ?? {}) }));
243
+ }
244
+ const definitions = await Promise.all(
245
+ geotiffLinks.map((geotiffLink) =>
246
+ createTiffLayerDefinition(
247
+ geotiffLink,
248
+ layerId,
249
+ urls,
250
+ projection,
251
+ processId,
252
+ ),
253
+ ),
254
+ ).then((defs) => defs.filter((defs) => !!defs));
255
+ return definitions;
256
+ }
257
+
258
+ /**
259
+ * @param {import("stac-ts").StacLink[] | undefined} links
260
+ * @param {Record<string,any>|undefined} jsonformValue
261
+ * @param {number[]} origBbox
262
+ */
263
+ function processImage(links, jsonformValue, origBbox) {
264
+ if (!links) return;
265
+ const imageLinks = links.filter(
266
+ (link) => link.rel === "service" && link.type === "image/png",
267
+ );
268
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"Image","ImageStatic">[]} */
269
+ const layers = [];
270
+ for (const link of imageLinks) {
271
+ layers.push({
272
+ type: "Image",
273
+ properties: {
274
+ id: link.id + "_process",
275
+ title: "Results " + link.id,
276
+ },
277
+ source: {
278
+ type: "ImageStatic",
279
+ imageExtent: origBbox,
280
+ url: mustache.render(link.href, {
281
+ ...(jsonformValue ?? {}),
282
+ }),
283
+ },
284
+ });
285
+ }
286
+ return layers;
287
+ }
288
+
289
+ /**
290
+ * @param {import("stac-ts").StacLink[] | undefined} links
291
+ * @param {Record<string,any> | undefined} jsonformValue
292
+ * @param {string} layerId
293
+ */
294
+ async function processVector(links, jsonformValue, layerId) {
295
+ if (!links) return;
296
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"Vector",any>[]} */
297
+ const layers = [];
298
+ const vectorLinks = links.filter(
299
+ (link) => link.rel === "service" && link.type === "application/geo+json",
300
+ );
301
+ if (!vectorLinks.length) return layers;
302
+
303
+ let flatStyleJSON = null;
304
+
305
+ for (const link of vectorLinks) {
306
+ if ("eox:flatstyle" in (link ?? {})) {
307
+ flatStyleJSON = await axios
308
+ .get(/** @type {string} */ (link["eox:flatstyle"]))
309
+ .then((resp) => resp.data);
310
+ }
311
+
312
+ /** @type {Record<string,any>|undefined} */
313
+ let layerConfig;
314
+ /** @type {Record<string,any>|undefined} */
315
+ let style;
316
+ if (flatStyleJSON) {
317
+ const extracted = extractLayerConfig(layerId ?? "", flatStyleJSON);
318
+ layerConfig = extracted.layerConfig;
319
+ style = extracted.style;
320
+ }
321
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"Vector","Vector"|"FlatGeoBuf">} */
322
+ const layer = {
323
+ type: "Vector",
324
+ source: {
325
+ type: "Vector",
326
+ url: mustache.render(link.href, {
327
+ ...(jsonformValue ?? {}),
328
+ }),
329
+ format: "GeoJSON",
330
+ },
331
+ properties: {
332
+ id: link.id + "_process",
333
+ title: "Results " + layerId,
334
+ ...(layerConfig && { ...layerConfig }),
335
+ },
336
+ ...(style && { style }),
337
+ };
338
+ layers.push(layer);
339
+ }
340
+ return layers;
341
+ }
342
+
343
+ /**
344
+ * Unified wrapper for processing map layer types (Vector, Image, GeoTiff)
345
+ * @param {object} options
346
+ * @param {import("stac-ts").StacLink[] | undefined} options.links
347
+ * @param {Record<string,any> | undefined} options.jsonformValue
348
+ * @param {string} options.layerId
349
+ * @param {string} [options.projection] - Required for GeoTiff layers
350
+ * @param {number[]} options.origBbox - Required for Image layers
351
+ * @param {import("vue").Ref<boolean>} options.isPolling
352
+ * @param {import("stac-ts").StacCollection} options.selectedStac
353
+ * @param {import("json-schema").JSONSchema7} options.jsonformSchema
354
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} options.jobs
355
+ * @param {(input:import("../types").CustomEnpointInput)=>Promise<import("@eox/map").EoxLayer[]>} options.customLayersHandler
356
+ * @param {boolean} [options.enableCompare=false] - Whether to enable compare mode
357
+ */
358
+ async function processLayers({
359
+ links,
360
+ jsonformValue,
361
+ layerId,
362
+ projection,
363
+ origBbox,
364
+ isPolling,
365
+ selectedStac,
366
+ jsonformSchema,
367
+ jobs,
368
+ customLayersHandler,
369
+ enableCompare = false,
370
+ }) {
371
+ if (!links) return [];
372
+ /** @type {import("@eox/map").EoxLayer[]} */
373
+ const layers = [];
374
+
375
+ const [standardLinks, endpointLinks] = separateEndpointLinks(
376
+ links,
377
+ "service",
378
+ undefined,
379
+ );
380
+ // Handle custom endpoints first if handler is provided
381
+ if (customLayersHandler && jsonformValue && selectedStac && jsonformSchema) {
382
+ if (endpointLinks.length > 0) {
383
+ const customLayers = await customLayersHandler({
384
+ jsonformValue,
385
+ links: endpointLinks,
386
+ selectedStac,
387
+ isPolling,
388
+ jsonformSchema,
389
+ jobs,
390
+ enableCompare,
391
+ });
392
+
393
+ if (customLayers.length) {
394
+ layers.push(...customLayers);
395
+ }
396
+ }
397
+ }
398
+
399
+ const vectorlayers = await processVector(
400
+ standardLinks,
401
+ jsonformValue,
402
+ layerId,
403
+ );
404
+
405
+ const imagelayers = processImage(standardLinks, jsonformValue, origBbox);
406
+
407
+ const geotiffLayers = await processGeoTiff({
408
+ links: standardLinks,
409
+ jsonformValue,
410
+ layerId,
411
+ projection,
412
+ });
413
+
414
+ layers.push(
415
+ ...[
416
+ ...(vectorlayers ?? []),
417
+ ...(imagelayers ?? []),
418
+ ...(geotiffLayers ?? []),
419
+ ],
420
+ );
421
+
422
+ return layers;
423
+ }
424
+
425
+ ////// STAC PROCESSING /////
426
+ /**
427
+ * This function loads a STAC collection as a processing output.
428
+ * Currently, it only supports POI STAC collections
429
+ *
430
+ * @param {import("stac-ts").StacLink[]} links
431
+ * @param {Record<string,any>} jsonformValue
432
+ */
433
+ async function processSTAC(links, jsonformValue, enableCompare = false) {
434
+ const stacLink = links.find(
435
+ (link) =>
436
+ link.rel === "service" &&
437
+ link.type == "application/json; profile=collection" &&
438
+ link.endpoint === "STAC",
439
+ );
440
+
441
+ if (!stacLink) return;
442
+ let poiUrl = mustache.render(stacLink.href, {
443
+ ...(jsonformValue ?? {}),
444
+ });
445
+ if (isFirstLoad.value) {
446
+ // prevent the map from jumping to the initial position
447
+ isFirstLoad.value = false;
448
+ }
449
+ const store = useSTAcStore();
450
+ const loadPOI = enableCompare
451
+ ? store.loadSelectedCompareSTAC
452
+ : store.loadSelectedSTAC;
453
+ await loadPOI(poiUrl, true);
454
+ }
455
+
456
+ /**
457
+ *
458
+ * @param {import("^/EodashProcess/types").CustomEnpointInput} param0
459
+ */
460
+
461
+ async function handleEOxHubEndpoint({
462
+ links,
463
+ jsonformValue,
464
+ isPolling,
465
+ selectedStac,
466
+ jobs,
467
+ enableCompare = false,
468
+ }) {
469
+ if (!isPolling) {
470
+ return;
471
+ }
472
+ const eoxhubLinks = links.filter(
473
+ (link) => link.rel === "service" && link.endpoint === "eoxhub_workspaces",
474
+ );
475
+ const layers = [];
476
+ for (const link of eoxhubLinks) {
477
+ // TODO: prove of concept, needs to be reworked for sure
478
+ // Special handling for eoxhub workspace process endpoints
479
+ const postBody = await axios
480
+ .get(/** @type {string} */ (link["body"]), { responseType: "text" })
481
+ .then((resp) => resp.data);
482
+ const jsonData = JSON.parse(
483
+ mustache.render(postBody, { ...(jsonformValue ?? {}) }),
484
+ );
485
+ const currentIndicator = enableCompare ? compareIndicator : indicator;
486
+ try {
487
+ const responseProcess = await axios.post(link.href, jsonData, {
488
+ headers: {
489
+ "Content-Type": "application/json",
490
+ },
491
+ });
492
+
493
+ // We save the process status url into localstorage assigning it to the indicator id
494
+ const currentJobs = JSON.parse(
495
+ localStorage.getItem(currentIndicator.value) || "[]",
496
+ );
497
+ currentJobs.push(responseProcess.headers.location);
498
+ localStorage.setItem(currentIndicator.value, JSON.stringify(currentJobs));
499
+
500
+ const processResults = await pollProcessStatus({
501
+ jobs,
502
+ processUrl: responseProcess.headers.location,
503
+ isPolling,
504
+ enableCompare,
505
+ })
506
+ .then((resultItem) => {
507
+ return extractAsyncResults(resultItem);
508
+ })
509
+ .catch((error) => {
510
+ if (error instanceof Error) {
511
+ console.error("Polling failed:", error.message);
512
+ } else {
513
+ console.error("Unknown error occurred during polling:", error);
514
+ }
515
+ return [];
516
+ });
517
+ await updateJobsStatus(jobs, currentIndicator.value);
518
+
519
+ layers.push(
520
+ ...(await creatAsyncProcessLayerDefinitions(
521
+ processResults,
522
+ link,
523
+ selectedStac,
524
+ )),
525
+ );
526
+ } catch (error) {
527
+ await updateJobsStatus(jobs, currentIndicator.value);
528
+ if (error instanceof Error) {
529
+ console.error("Error sending POST request:", error.message);
530
+ } else {
531
+ console.error("Unknown error occurred:", error);
532
+ }
533
+ }
534
+ }
535
+
536
+ return layers;
537
+ }
538
+
539
+ const handleLayersCustomEndpoints = createCustomLayersEndpointsHandler([
540
+ handleEOxHubEndpoint,
541
+ ]);
542
+
543
+ /**
544
+ * @param {((input:import("^/EodashProcess/types").CustomEnpointInput)=> Promise<Record<string,any>[] | undefined | null>)[]} callbacks
545
+ * @returns {(input: import("^/EodashProcess/types").CustomEnpointInput) => Promise<import("@eox/map/.").EoxLayer[]>}
546
+ */
547
+ function createCustomLayersEndpointsHandler(callbacks) {
548
+ return async (inputs) => {
549
+ // this allows multiple endpoints links to exist
550
+ // and return multiple layers
551
+ return await Promise.all(
552
+ callbacks.map((callback) => callback(inputs)),
553
+ ).then((asyncProcessesLayers) => {
554
+ const layers = [];
555
+ for (const processLayers of asyncProcessesLayers) {
556
+ layers.push(...(processLayers?.filter(isValidEoxLayer) ?? []));
557
+ }
558
+ return layers;
559
+ });
560
+ };
561
+ }
562
+
563
+ /**
564
+ * @param {any} layer
565
+ * @returns {layer is import("@eox/map/.").EoxLayer}
566
+ */
567
+ function isValidEoxLayer(layer) {
568
+ return !!layer && !!layer.type && (!!layer.source || !!layer.layers);
569
+ }
570
+
571
+ async function handleSentinelHubProcess({
572
+ links,
573
+ jsonformValue,
574
+ jsonformSchema,
575
+ selectedStac,
576
+ enableCompare = false
577
+ }) {
578
+ const sentinelHubLink = links.find(
579
+ (link) => link.rel === "service" && link.endpoint === "sentinelhub"
580
+ );
581
+ if (!sentinelHubLink) {
582
+ return;
583
+ }
584
+ const evalScriptLink = await getEvalScriptLink(
585
+ selectedStac,
586
+ enableCompare ? currentCompareUrl.value : currentUrl.value
587
+ );
588
+ if (!evalScriptLink) {
589
+ console.error(
590
+ "[eodash] evalscript link for sentinel hub not found in indicator",
591
+ evalScriptLink
592
+ );
593
+ return;
594
+ }
595
+ if (!sentinelHubLink) {
596
+ return;
597
+ }
598
+ const endpoint = sentinelHubLink.href;
599
+ const bboxProperty = getBboxProperty(jsonformSchema);
600
+ const bbox = jsonformValue[bboxProperty];
601
+ const clientId = import.meta.env.EODASH_SENTINELHUB_CLIENT_ID;
602
+ const clientSecret = import.meta.env.EODASH_SENTINELHUB_CLIENT_SECRET;
603
+ const bearer = await sentinelHubAuth(clientId, clientSecret);
604
+ if (!bearer) {
605
+ console.error(
606
+ "[eodash] Error while fetching bearer token from sentinel hub"
607
+ );
608
+ return;
609
+ }
610
+ const timePairs = generateTimePairs(
611
+ selectedStac.extent.temporal.interval[0],
612
+ [jsonformValue["start_date"], jsonformValue["end_date"]],
613
+ jsonformValue["distribution"]
614
+ );
615
+ return await Promise.all(
616
+ timePairs.map(([to, from]) => {
617
+ return fetchSentinelHubData({
618
+ url: endpoint,
619
+ bearer,
620
+ bbox,
621
+ from,
622
+ to,
623
+ exampleLink: evalScriptLink
624
+ }).catch(
625
+ (err) => console.error(
626
+ "[eodash] Error while fetching data from sentinel hub endpoint:",
627
+ err
628
+ )
629
+ );
630
+ })
631
+ ).then((data) => data.flat().map((data2) => data2.outputs.data));
632
+ }
633
+ async function sentinelHubAuth(clientId, clientSecret) {
634
+ if (!clientId || !clientSecret) {
635
+ console.error(
636
+ "[eodash] Error (sentinelhub): client id or secret not found"
637
+ );
638
+ return;
639
+ }
640
+ const sessionToken = sessionStorage.getItem("sentinelhub_token");
641
+ const sessionTokenTime = (
642
+ /** @type {string} */
643
+ sessionStorage.getItem("sentinelhub_token_time")
644
+ );
645
+ const isValid = Date.now() - Number(sessionTokenTime) < 3600 * 1e3;
646
+ if (sessionToken && isValid) {
647
+ sessionStorage.setItem("sentinelhub_token_time", Date.now().toString());
648
+ return sessionToken;
649
+ }
650
+ const token = await retrieveSentinelHubToken(clientId, clientSecret);
651
+ if (!token) {
652
+ return;
653
+ }
654
+ sessionStorage.setItem("sentinelhub_token_time", Date.now().toString());
655
+ sessionStorage.setItem("sentinelhub_token", token);
656
+ return token;
657
+ }
658
+ async function retrieveSentinelHubToken(clientId, clientSecret) {
659
+ const url = "https://services.sentinel-hub.com/oauth/token";
660
+ const headers = {
661
+ "Content-Type": "application/x-www-form-urlencoded"
662
+ };
663
+ const body = new URLSearchParams({
664
+ client_id: clientId,
665
+ client_secret: clientSecret,
666
+ response_type: "token",
667
+ grant_type: "client_credentials"
668
+ });
669
+ return await axios.post(url, body, { headers }).then((resp) => resp.data.access_token);
670
+ }
671
+ async function fetchSentinelHubData({
672
+ url,
673
+ bearer,
674
+ bbox,
675
+ from,
676
+ to,
677
+ timeout = 2e4,
678
+ exampleLink
679
+ }) {
680
+ if (!exampleLink) {
681
+ console.error(
682
+ "[eodash] Error (sentinelhub): example link not found in indicator"
683
+ );
684
+ return;
685
+ }
686
+ if (!bbox) {
687
+ console.error("[eodash] Error (sentinelhub): bbox not found");
688
+ return;
689
+ }
690
+ if (!from || !to || new Date(from) > new Date(to)) {
691
+ console.error(
692
+ "[eodash] Error (sentinelhub): time range is faulty or not found",
693
+ from,
694
+ to
695
+ );
696
+ return;
697
+ }
698
+ const evalscript = await axios.get(exampleLink.href, { responseType: "text" }).then((resp) => resp.data);
699
+ if (!evalscript || evalscript.error) {
700
+ console.error(
701
+ "[eodash] Error (sentinelhub): evalscript not found",
702
+ evalscript
703
+ );
704
+ return;
705
+ }
706
+ const body = {
707
+ input: {
708
+ bounds: {
709
+ bbox
710
+ },
711
+ data: [
712
+ {
713
+ dataFilter: {},
714
+ type: exampleLink.dataId
715
+ }
716
+ ]
717
+ },
718
+ aggregation: {
719
+ evalscript,
720
+ timeRange: {
721
+ from,
722
+ to
723
+ },
724
+ aggregationInterval: {
725
+ of: "P1D"
726
+ },
727
+ width: 100,
728
+ height: 100
729
+ },
730
+ calculations: {
731
+ default: {}
732
+ }
733
+ };
734
+ const config = {
735
+ headers: {
736
+ Authorization: `Bearer ${bearer}`,
737
+ "Content-Type": "application/json"
738
+ },
739
+ params: {
740
+ credentials: "same-origin"
741
+ },
742
+ timeout
743
+ };
744
+ return await axios.post(url, body, config).then((resp) => {
745
+ const fetched = resp.data.data;
746
+ fetched.forEach((dataItem) => {
747
+ dataItem.outputs.data.date = from;
748
+ });
749
+ return fetched;
750
+ }).catch((err) => {
751
+ if (err.response?.status === 401) {
752
+ console.error(
753
+ "[eodash] Error (sentinelhub): bearer token expired, please try again"
754
+ );
755
+ sessionStorage.removeItem("sentinelhub_token");
756
+ sessionStorage.removeItem("sentinelhub_token_time");
757
+ return;
758
+ }
759
+ console.error(
760
+ "[eodash] Error (sentinelhub): error while fetching data from sentinel hub",
761
+ err.response?.data
762
+ );
763
+ return [];
764
+ });
765
+ }
766
+ async function getEvalScriptLink(selectedStac, absoluteUrl) {
767
+ const evalScriptLink = selectedStac.links.find(
768
+ (link) => link.rel === "example" && link.title === "evalscript"
769
+ );
770
+ if (evalScriptLink) {
771
+ return evalScriptLink;
772
+ }
773
+ for (const link of extractCollectionUrls(selectedStac, absoluteUrl)) {
774
+ const scriptLink = axios.get(link).then(
775
+ (resp) => (
776
+ /** @type {import("stac-ts").StacCollection} */
777
+ resp.data.links.find(
778
+ (link2) => link2.rel === "example" && link2.title === "evalscript"
779
+ )
780
+ )
781
+ );
782
+ if (scriptLink) {
783
+ return scriptLink;
784
+ }
785
+ }
786
+ }
787
+
788
+ /**
789
+ * @param {import("^/EodashProcess/types").CustomEnpointInput} inputs
790
+ */
791
+ async function handleVedaEndpoint({
792
+ links,
793
+ jsonformSchema,
794
+ jsonformValue,
795
+ selectedStac,
796
+ enableCompare = false,
797
+ }) {
798
+ const vedaLink = links.find(
799
+ (link) => link.rel === "service" && link.endpoint === "veda",
800
+ );
801
+ if (!vedaLink) {
802
+ return;
803
+ }
804
+ const vedaEndpoint = vedaLink?.href;
805
+ const bboxProperty = getBboxProperty(jsonformSchema);
806
+ // this should be type geojson
807
+ const bboxGeoJSON = JSON.parse(jsonformValue[bboxProperty]);
808
+
809
+ const configs = await fetchVedaCOGsConfig(
810
+ selectedStac,
811
+ enableCompare ? currentCompareUrl.value : currentUrl.value,
812
+ );
813
+ // TODO: convert jsonform bbox type to geojson in the schema to avoid the conversion here
814
+ return await Promise.all(
815
+ configs.map(({ endpoint, datetime }) => {
816
+ return axios
817
+ .post(vedaEndpoint + `?url=${endpoint}`, {
818
+ ...{
819
+ type: "Feature",
820
+ properties: {},
821
+ geometry: bboxGeoJSON,
822
+ },
823
+ })
824
+ .then((resp) => {
825
+ const fetchedSats = resp.data.properties.statistics;
826
+ fetchedSats.date = datetime;
827
+ return fetchedSats;
828
+ })
829
+ .catch((resp) =>
830
+ console.error(
831
+ "[eodash] Error while fetching data from veda endpoint:",
832
+ resp,
833
+ ),
834
+ );
835
+ }),
836
+ );
837
+ }
838
+
839
+ /**
840
+ * Fetches the COGs endpoints from the STAC collections
841
+ * @param {import("stac-ts").StacCollection} selectedStac
842
+ * @param {string} absoluteUrl
843
+ */
844
+ async function fetchVedaCOGsConfig(selectedStac, absoluteUrl) {
845
+ // retrieve the collections from the indicator
846
+ const collectionLinks = selectedStac.links.filter(
847
+ (link) => link.rel == "child",
848
+ );
849
+ /** @type {import("stac-ts").StacCollection[]} */
850
+ const collections = [];
851
+ if (!collectionLinks.length) {
852
+ collections.push(selectedStac);
853
+ } else {
854
+ collections.push(
855
+ ...(await Promise.all(
856
+ collectionLinks.map((link) =>
857
+ axios
858
+ .get(toAbsolute(link.href, absoluteUrl))
859
+ .then((resp) => resp.data)
860
+ .then(async (collection) => {
861
+ // items in geoparquet handling specially to get item links
862
+ const parquetAsset = Object.values(collection.assets ?? {}).find(
863
+ (asset) =>
864
+ asset.type === "application/vnd.apache.parquet" &&
865
+ asset.roles?.includes("collection-mirror"),
866
+ );
867
+ if (parquetAsset) {
868
+ const parquetAbsoluteUrl = toAbsolute(parquetAsset.href, toAbsolute(link.href, absoluteUrl));
869
+ await readParquetItems(parquetAbsoluteUrl).then((items) => {
870
+ collection.links.push(...generateLinksFromItems(items));
871
+ });
872
+ }
873
+ return collection;
874
+ }),
875
+ ),
876
+ )),
877
+ );
878
+ }
879
+ /** @type {{endpoint:string; datetime:string}[]} */
880
+ const configs = [];
881
+ for (const collection of collections) {
882
+ const datetimeProperty = /** @type string **/ (
883
+ getDatetimeProperty(collection.links)
884
+ );
885
+ const itemLinks = collection.links.filter((link) => link.rel == "item");
886
+ configs.push(
887
+ ...itemLinks.map((link) => ({
888
+ endpoint: /** @type {string} */ (link["cog_href"]),
889
+ datetime: /** @type string **/ (link[datetimeProperty]),
890
+ })),
891
+ );
892
+ }
893
+
894
+ // Sort by date ascending
895
+ configs.sort(
896
+ (a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime(),
897
+ );
898
+
899
+ const maxConfigs = 50;
900
+ if (configs.length <= maxConfigs) {
901
+ return configs;
902
+ }
903
+ // we need to sample if the number of configs are more than 50
904
+ const totalSize = configs.length;
905
+ const sampledConfigs = [];
906
+ for (let i = 0; i < maxConfigs; i++) {
907
+ // Calculate the index to pick, ensuring distribution and inclusion of first/last
908
+ const index = Math.floor((i * (totalSize - 1)) / (maxConfigs - 1));
909
+ sampledConfigs.push(configs[index]);
910
+ }
911
+ return sampledConfigs;
912
+ }
913
+
914
+ const handleChartCustomEndpoints = createCustomChartEndpointsHandler([
915
+ handleSentinelHubProcess,
916
+ handleVedaEndpoint,
917
+ ]);
918
+ /**
919
+ * @param {((input:import("^/EodashProcess/types").CustomEnpointInput)=> Promise<any[] | undefined | null>)[]} callbacks
920
+ */
921
+ function createCustomChartEndpointsHandler(callbacks) {
922
+ /**
923
+ * @param {import("^/EodashProcess/types").CustomEnpointInput} inputs
924
+ * */
925
+ return async (inputs) => {
926
+ for (const callback of callbacks) {
927
+ const data = await callback(inputs);
928
+ log.debug(
929
+ "Custom endpoint data:",
930
+ data,
931
+ "for callback:",
932
+ callback.name,
933
+ "inputs:",
934
+ inputs,
935
+ );
936
+ const isNotValid = !data || !data.length || data.some((item) => !item);
937
+ if (isNotValid) {
938
+ continue;
939
+ }
940
+ return data;
941
+ }
942
+ return null;
943
+ };
944
+ }
945
+
946
+ /**
947
+ * Fetch and set the jsonform schema to initialize the process
948
+ *
949
+ * @export
950
+ * @async
951
+ * @param {Object} params
952
+ * @param {import("vue").Ref<import("stac-ts").StacCollection | null>} params.selectedStac
953
+ * @param {import("vue").Ref<import("@eox/jsonform").EOxJSONForm | null>} params.jsonformEl
954
+ * @param {import("vue").Ref<Record<string,any> | null>} params.jsonformSchema
955
+ * @param {import("vue").Ref<import("@eox/chart").EOxChart["spec"] | null>} params.chartSpec
956
+ * @param {import("vue").Ref<any[]>} params.processResults
957
+ * @param {import("vue").Ref<boolean>} params.isProcessed
958
+ * @param {import("vue").Ref<boolean>} params.loading
959
+ * @param {import("vue").Ref<boolean>} params.isPolling
960
+ * @param {boolean} params.enableCompare
961
+ */
962
+ async function initProcess({
963
+ selectedStac,
964
+ jsonformEl,
965
+ jsonformSchema,
966
+ chartSpec,
967
+ isProcessed,
968
+ processResults,
969
+ loading,
970
+ isPolling,
971
+ enableCompare,
972
+ }) {
973
+ const isPoiAlive = enableCompare ? !!comparePoi.value : !!poi.value;
974
+ let updatedJsonform = null;
975
+ if (selectedStac.value?.["eodash:jsonform"]) {
976
+ updatedJsonform = await axios
977
+ //@ts-expect-error eodash extention
978
+ .get(selectedStac.value["eodash:jsonform"])
979
+ .then((resp) => resp.data);
980
+ }
981
+
982
+ if (!updatedJsonform && isPoiAlive) {
983
+ jsonformSchema.value = null;
984
+ return;
985
+ }
986
+ resetProcess({
987
+ loading,
988
+ isProcessed,
989
+ chartSpec,
990
+ jsonformSchema,
991
+ isPolling,
992
+ processResults,
993
+ });
994
+
995
+ await jsonformEl.value?.editor.destroy();
996
+ if (updatedJsonform) {
997
+ if (enableCompare) {
998
+ updatedJsonform = updateJsonformSchemaTarget(updatedJsonform);
999
+ }
1000
+ jsonformSchema.value = updatedJsonform;
1001
+ }
1002
+ }
1003
+
1004
+ /**
1005
+ *
1006
+ * @param {object} params
1007
+ * @param {import("vue").Ref<boolean>} params.loading
1008
+ * @param {import("vue").Ref<import("stac-ts").StacCollection | null>} params.selectedStac
1009
+ * @param {import("vue").Ref<import("@eox/jsonform").EOxJSONForm | null>} params.jsonformEl
1010
+ * @param {import("vue").Ref<Record<string,any>|null>} params.jsonformSchema
1011
+ * @param {import("vue").Ref<import("@eox/chart").EOxChart["spec"] | null>} params.chartSpec
1012
+ * @param {import("vue").Ref<Record<string, any> | null>} params.chartData
1013
+ * @param {import("vue").Ref<boolean>} params.isPolling
1014
+ * @param {import("vue").Ref<any[]>} params.processResults
1015
+ * @param {import("@eox/map").EOxMap | null} params.mapElement
1016
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} params.jobs
1017
+ */
1018
+ async function handleProcesses({
1019
+ loading,
1020
+ selectedStac,
1021
+ jsonformEl,
1022
+ jsonformSchema,
1023
+ chartSpec,
1024
+ chartData,
1025
+ isPolling,
1026
+ processResults,
1027
+ mapElement,
1028
+ jobs,
1029
+ }) {
1030
+ if (!jsonformEl.value || !jsonformSchema.value || !selectedStac.value) {
1031
+ return;
1032
+ }
1033
+
1034
+ log.debug("Processing...");
1035
+ loading.value = true;
1036
+ try {
1037
+ const serviceLinks = selectedStac.value?.links?.filter(
1038
+ (l) => l.rel === "service",
1039
+ );
1040
+
1041
+ const bboxProperty = getBboxProperty(jsonformSchema.value);
1042
+ const jsonformValue = /** @type {Record<string,any>} */ (
1043
+ jsonformEl.value?.value
1044
+ );
1045
+
1046
+ extractGeometries(jsonformValue, jsonformSchema.value);
1047
+
1048
+ const origBbox = jsonformValue[bboxProperty];
1049
+
1050
+ const specUrl = /** @type {string} */ (
1051
+ selectedStac.value?.["eodash:vegadefinition"]
1052
+ );
1053
+ const layerId = selectedStac.value?.id ?? "";
1054
+
1055
+ [chartSpec.value, chartData.value] = await processCharts({
1056
+ links: serviceLinks,
1057
+ jsonformValue: { ...(jsonformValue ?? {}) },
1058
+ jsonformSchema: jsonformSchema.value,
1059
+ enableCompare: mapElement?.id === "compare",
1060
+ selectedStac: selectedStac.value,
1061
+ specUrl,
1062
+ isPolling,
1063
+ jobs,
1064
+ customEndpointsHandler: handleChartCustomEndpoints,
1065
+ });
1066
+
1067
+ if (Object.keys(chartData.value ?? {}).length) {
1068
+ processResults.value.push(chartData.value);
1069
+ }
1070
+
1071
+ //@ts-expect-error we assume that the spec data is of type InlineData
1072
+ if (chartSpec.value?.data?.values?.length) {
1073
+ //@ts-expect-error we assume that the spec data is of type InlineData
1074
+ processResults.value.push(chartSpec.value?.data.values);
1075
+ }
1076
+
1077
+ if (chartSpec.value && !("background" in chartSpec.value)) {
1078
+ chartSpec.value["background"] = "transparent";
1079
+ }
1080
+
1081
+ await processSTAC(
1082
+ serviceLinks,
1083
+ jsonformValue,
1084
+ mapElement?.id === "compare",
1085
+ );
1086
+
1087
+ const newLayers = await processLayers({
1088
+ isPolling,
1089
+ links: serviceLinks,
1090
+ jsonformValue: { ...(jsonformValue ?? {}) },
1091
+ jsonformSchema: jsonformSchema.value,
1092
+ selectedStac: selectedStac.value,
1093
+ enableCompare: mapElement?.id === "compare",
1094
+ layerId,
1095
+ origBbox,
1096
+ jobs,
1097
+ customLayersHandler: handleLayersCustomEndpoints,
1098
+ projection: /** @type {{name?:string}} */ (
1099
+ selectedStac.value["eodash:mapProjection"]
1100
+ )?.["name"],
1101
+ });
1102
+
1103
+ // save layers results
1104
+ if (newLayers.length) {
1105
+ for (const layer of newLayers) {
1106
+ if (layer.type === "WebGLTile" && layer.source?.type === "GeoTIFF") {
1107
+ processResults.value.push(...(layer.source.sources ?? []));
1108
+ } else if (layer.source && "url" in layer.source) {
1109
+ processResults.value.push(layer.source.url);
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ applyProcessLayersToMap(mapElement, newLayers);
1115
+ loading.value = false;
1116
+ } catch (error) {
1117
+ console.error("[eodash] Error while running process:", error);
1118
+ loading.value = false;
1119
+ throw error;
1120
+ }
1121
+ }
1122
+
1123
+ /**
1124
+ * Reset the process state
1125
+ * @param {Object} params
1126
+ * @param {import("vue").Ref<boolean>} params.loading
1127
+ * @param {import("vue").Ref<boolean>} params.isProcessed
1128
+ * @param {import("vue").Ref<import("@eox/chart").EOxChart["spec"] | null>} params.chartSpec
1129
+ * @param {import("vue").Ref<boolean>} params.isPolling
1130
+ * @param {import("vue").Ref<any[]>} params.processResults
1131
+ * @param {import("vue").Ref<Record<string,any>|null>} params.jsonformSchema
1132
+ */
1133
+ function resetProcess({
1134
+ loading,
1135
+ isProcessed,
1136
+ chartSpec,
1137
+ jsonformSchema,
1138
+ processResults,
1139
+ isPolling,
1140
+ }) {
1141
+ loading.value = false;
1142
+ isProcessed.value = false;
1143
+ isPolling.value = false;
1144
+ chartSpec.value = null;
1145
+ processResults.value = [];
1146
+ jsonformSchema.value = null;
1147
+ }
1148
+
1149
+ /**
1150
+ * Handles the click event on a chart to extract temporal information and update the global datetime value.
1151
+ *
1152
+ * @param {object} evt - The click event object.
1153
+ * @param {object} evt.target - The target of the event, expected to have a Vega-Lite specification (`spec`).
1154
+ * @param {object} evt.target.spec - The Vega-Lite specification of the chart.
1155
+ * @param {Record<string,{type?:string;field?:string;}>} [evt.target.spec.encoding] - The encoding specification of the chart.
1156
+ * @param {object} evt.detail - The detail of the event, containing information about the clicked item.
1157
+ * @param {import("vega").Item} evt.detail.item - The Vega item that was clicked.
1158
+ */
1159
+ const onChartClick = (evt) => {
1160
+ const chartSpec = evt.target?.spec;
1161
+ if (
1162
+ !chartSpec ||
1163
+ !evt.detail?.item?.datum ||
1164
+ !evt.detail?.item?.datum.datum
1165
+ ) {
1166
+ return;
1167
+ }
1168
+ const encodingKey = Object.keys(chartSpec.encoding ?? {}).find(
1169
+ (key) => chartSpec.encoding?.[key].type === "temporal",
1170
+ );
1171
+ if (!encodingKey) {
1172
+ return;
1173
+ }
1174
+ const temporalKey = chartSpec.encoding?.[encodingKey].field;
1175
+ if (!temporalKey) {
1176
+ return;
1177
+ }
1178
+
1179
+ try {
1180
+ const vegaItem = evt.detail.item;
1181
+ let datestring = "";
1182
+ // It seems sometimes we have datum inside datum and sometimes not
1183
+ if (vegaItem.datum && vegaItem.datum.datum) {
1184
+ // If datum is nested, we use the nested datum
1185
+ datestring = vegaItem.datum.datum[temporalKey];
1186
+ } else {
1187
+ // Otherwise, we use the top-level datum
1188
+ datestring = vegaItem.datum[temporalKey];
1189
+ }
1190
+ const temporalValue = new Date(datestring);
1191
+ datetime.value = temporalValue.toISOString();
1192
+ } catch (error) {
1193
+ console.warn(
1194
+ "[eodash] Error while setting datetime from eox-chart:",
1195
+ error,
1196
+ );
1197
+ }
1198
+ };
1199
+
1200
+ /**
1201
+ * Loads the main indicator of a Point of Interest (POI)
1202
+ */
1203
+ const loadPOiIndicator = () => {
1204
+ if (!indicator.value) {
1205
+ indicator.value =
1206
+ new URLSearchParams(window.location.search).get("indicator") ?? "";
1207
+ }
1208
+ const stacStore = useSTAcStore();
1209
+ const link = stacStore.stac?.find(
1210
+ (link) => useGetSubCodeId(link) === indicator.value,
1211
+ );
1212
+ stacStore.loadSelectedSTAC(link?.href);
1213
+ if (comparePoi.value) {
1214
+ if (compareIndicator.value) {
1215
+ const comparelink = stacStore.stac?.find(
1216
+ (link) => useGetSubCodeId(link) === compareIndicator.value,
1217
+ );
1218
+ stacStore.loadSelectedCompareSTAC(comparelink?.href).catch((err) => {
1219
+ console.error("[eodash] Error loading compare STAC:", err);
1220
+ });
1221
+ } else {
1222
+ stacStore.resetSelectedCompareSTAC();
1223
+ }
1224
+ }
1225
+ };
1226
+
1227
+ export { handleProcesses as h, initProcess as i, loadPOiIndicator as l, onChartClick as o };