@eodash/eodash 5.0.0 → 5.1.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 (155) 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 +42 -25
  5. package/core/client/components/EodashOverlay.vue +1 -1
  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 +9 -10
  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 +80 -47
  15. package/core/client/eodashSTAC/helpers.js +136 -27
  16. package/core/client/eodashSTAC/parquet.js +145 -0
  17. package/core/client/eodashSTAC/triggers.js +6 -3
  18. package/core/client/plugins/index.js +4 -3
  19. package/core/client/plugins/vuetify.js +3 -0
  20. package/core/client/store/actions.js +21 -4
  21. package/core/client/store/stac.js +93 -56
  22. package/core/client/store/states.js +15 -5
  23. package/core/client/types.ts +59 -43
  24. package/core/client/utils/index.js +79 -0
  25. package/core/client/utils/keys.js +2 -2
  26. package/core/client/utils/states.js +30 -5
  27. package/core/client/views/Dashboard.vue +36 -32
  28. package/core/client/vite-env.d.ts +7 -0
  29. package/dist/client/{DashboardLayout-CkWvOMOW.js → DashboardLayout-ByVs1DrY.js} +23 -12
  30. package/dist/client/{DynamicWebComponent-DYBbpvUK.js → DynamicWebComponent-C3W7HSQm.js} +1 -1
  31. package/dist/client/{EodashDatePicker-CALmW3SI.js → EodashDatePicker-BIAf1sMT.js} +59 -32
  32. package/dist/client/{EodashItemFilter-DlQiE713.js → EodashItemFilter-DPznh8UB.js} +20 -10
  33. package/dist/client/{EodashLayerControl-DEzEbft7.js → EodashLayerControl-Bhxjw4V2.js} +29 -16
  34. package/dist/client/EodashLayoutSwitcher-C5qTEffW.js +61 -0
  35. package/dist/client/EodashMapBtns-WoGq8MuV.js +173 -0
  36. package/dist/client/{EodashStacInfo-DPPxDkF6.js → EodashStacInfo-CSvvF2jI.js} +3 -18
  37. package/dist/client/{EodashTools-CUaL9s4H.js → EodashTools-Cv1SXQ5y.js} +13 -13
  38. package/dist/client/{ExportState-DjyIZVhl.js → ExportState-D-iuwaad.js} +58 -52
  39. package/dist/client/{Footer-DyL0JoWt.js → Footer-CyF0zRAk.js} +15 -13
  40. package/dist/client/{Header-B5Dgty9l.js → Header-CgD8jDKU.js} +33 -28
  41. package/dist/client/{MobileLayout-CRsg_5Q4.js → MobileLayout-EKQ_kpSh.js} +69 -60
  42. package/dist/client/{PopUp-BfB8s_ki.js → PopUp-BsYLvWch.js} +19 -10
  43. package/dist/client/ProcessList-C2xsLU2_.js +191 -0
  44. package/dist/client/{VImg-FD1WVphJ.js → VImg-OHe8YTs2.js} +24 -24
  45. package/dist/client/{VMain-DJKG4SvM.js → VMain-PryTLU4a.js} +7 -7
  46. package/dist/client/{VOverlay-BzOdRu9h.js → VOverlay-yUn7p-Uf.js} +64 -27
  47. package/dist/client/{VTooltip-CfeefrXI.js → VTooltip-DZ0fjpB3.js} +13 -10
  48. package/dist/client/{WidgetsContainer-C2TaTdb6.js → WidgetsContainer-B9LBadcC.js} +1 -1
  49. package/dist/client/asWebComponent-By_7_JjS.js +19193 -0
  50. package/dist/client/async-DkSu_u2K.js +740 -0
  51. package/dist/client/eo-dash.js +1 -1
  52. package/dist/client/{forwardRefs-Bon_Kku1.js → forwardRefs-BXxrv98s.js} +31 -4
  53. package/dist/client/handling-CgmFXkW6.js +1212 -0
  54. package/dist/client/helpers-Dy0Q13tP.js +4534 -0
  55. package/dist/client/{index-Bm9cbtx5.js → index-BuhOHXKv.js} +2 -4
  56. package/dist/client/{index-CIHH_3dW.js → index-Ch_HchK3.js} +39 -32
  57. package/dist/client/{index-4CT7Tz83.js → index-Dqj4tbx2.js} +2 -2
  58. package/dist/client/index-skjhlH8u.js +376 -0
  59. package/dist/client/{ssrBoot-BP7SYRyC.js → ssrBoot-Zgc_Ttvi.js} +2 -2
  60. package/dist/client/templates.js +850 -0
  61. package/dist/client/transition-C98Yn4Vo.js +40 -0
  62. package/dist/node/cli.js +16 -6
  63. package/dist/node/types.d.ts +1 -1
  64. package/dist/types/core/client/App.vue.d.ts +2 -2
  65. package/dist/types/core/client/asWebComponent.d.ts +1 -1
  66. package/dist/types/core/client/components/DynamicWebComponent.vue.d.ts +1 -3
  67. package/dist/types/core/client/components/Footer.vue.d.ts +1 -105
  68. package/dist/types/core/client/components/IframeWrapper.vue.d.ts +1 -1
  69. package/dist/types/core/client/components/MobileLayout.vue.d.ts +1 -324
  70. package/dist/types/core/client/composables/DefineEodash.d.ts +2 -2
  71. package/dist/types/core/client/composables/DefineTemplate.d.ts +1 -1
  72. package/dist/types/core/client/composables/DefineWidgets.d.ts +4 -4
  73. package/dist/types/core/client/composables/index.d.ts +24 -2
  74. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +9 -6
  75. package/dist/types/core/client/eodashSTAC/helpers.d.ts +20 -5
  76. package/dist/types/core/client/eodashSTAC/parquet.d.ts +2 -0
  77. package/dist/types/core/client/plugins/vuetify.d.ts +7 -4
  78. package/dist/types/core/client/store/actions.d.ts +3 -2
  79. package/dist/types/core/client/store/stac.d.ts +16 -13
  80. package/dist/types/core/client/store/states.d.ts +14 -4
  81. package/dist/types/core/client/types.d.ts +45 -30
  82. package/dist/types/core/client/utils/index.d.ts +2 -0
  83. package/dist/types/core/client/utils/keys.d.ts +4 -4
  84. package/dist/types/core/client/utils/states.d.ts +59 -47
  85. package/dist/types/core/client/views/Dashboard.vue.d.ts +2 -2
  86. package/dist/types/templates/baseConfig.d.ts +4 -0
  87. package/dist/types/templates/compare.d.ts +210 -0
  88. package/dist/types/templates/expert.d.ts +151 -0
  89. package/dist/types/templates/index.d.ts +6 -0
  90. package/dist/types/templates/light.d.ts +145 -0
  91. package/dist/types/widgets/EodashDatePicker.vue.d.ts +1 -458
  92. package/dist/types/widgets/EodashItemFilter.vue.d.ts +3 -3
  93. package/dist/types/widgets/EodashLayerControl.vue.d.ts +14 -7
  94. package/dist/types/widgets/EodashLayoutSwitcher.vue.d.ts +1 -3
  95. package/dist/types/widgets/EodashMap/index.vue.d.ts +1 -4
  96. package/dist/types/widgets/EodashMapBtns.vue.d.ts +8 -8
  97. package/dist/types/widgets/EodashProcess/ProcessList.vue.d.ts +8 -1
  98. package/dist/types/widgets/EodashProcess/index.vue.d.ts +8 -4
  99. package/dist/types/widgets/EodashProcess/methods/async.d.ts +18 -18
  100. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +3 -2
  101. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/index.d.ts +1 -0
  102. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.d.ts +6 -0
  103. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.d.ts +4 -0
  104. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +5 -0
  105. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/index.d.ts +1 -0
  106. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +12 -5
  107. package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +72 -41
  108. package/dist/types/widgets/EodashProcess/methods/utils.d.ts +41 -21
  109. package/dist/types/widgets/EodashProcess/states.d.ts +11 -0
  110. package/dist/types/widgets/EodashProcess/types.d.ts +41 -0
  111. package/dist/types/widgets/EodashStacInfo.vue.d.ts +14 -14
  112. package/dist/types/widgets/EodashTools.vue.d.ts +3 -3
  113. package/dist/types/widgets/ExportState.vue.d.ts +1 -1
  114. package/dist/types/widgets/PopUp.vue.d.ts +11 -16
  115. package/dist/types/widgets/WidgetsContainer.vue.d.ts +3 -6
  116. package/package.json +53 -45
  117. package/templates/baseConfig.js +68 -0
  118. package/templates/compare.js +162 -0
  119. package/templates/expert.js +123 -0
  120. package/templates/index.js +8 -0
  121. package/templates/light.js +130 -0
  122. package/widgets/EodashDatePicker.vue +80 -31
  123. package/widgets/EodashItemFilter.vue +26 -11
  124. package/widgets/EodashLayerControl.vue +20 -11
  125. package/widgets/EodashLayoutSwitcher.vue +6 -3
  126. package/widgets/EodashMap/index.vue +3 -8
  127. package/widgets/EodashMap/methods/create-layers-config.js +4 -3
  128. package/widgets/EodashMap/methods/index.js +33 -23
  129. package/widgets/EodashMapBtns.vue +83 -41
  130. package/widgets/EodashProcess/ProcessList.vue +34 -10
  131. package/widgets/EodashProcess/index.vue +55 -20
  132. package/widgets/EodashProcess/methods/async.js +77 -59
  133. package/widgets/EodashProcess/methods/composables.js +21 -14
  134. package/widgets/EodashProcess/methods/custom-endpoints/chart/index.js +35 -0
  135. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +275 -0
  136. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +116 -0
  137. package/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.js +94 -0
  138. package/widgets/EodashProcess/methods/custom-endpoints/layers/index.js +33 -0
  139. package/widgets/EodashProcess/methods/handling.js +127 -80
  140. package/widgets/EodashProcess/methods/outputs.js +376 -125
  141. package/widgets/EodashProcess/methods/utils.js +398 -10
  142. package/widgets/EodashProcess/states.js +13 -0
  143. package/widgets/EodashProcess/types.ts +46 -0
  144. package/widgets/EodashStacInfo.vue +2 -17
  145. package/widgets/EodashTools.vue +13 -13
  146. package/widgets/WidgetsContainer.vue +1 -1
  147. package/core/client/eodash.js +0 -454
  148. package/dist/client/EodashLayoutSwitcher-CDeCV8F-.js +0 -52
  149. package/dist/client/EodashMapBtns-CktQCfa-.js +0 -131
  150. package/dist/client/ProcessList-DTefwQZx.js +0 -484
  151. package/dist/client/asWebComponent-CLhcT715.js +0 -12479
  152. package/dist/client/eo-dash.css +0 -5
  153. package/dist/client/index-DiGDvTQU.js +0 -780
  154. package/dist/client/transition-C5I57hn6.js +0 -37
  155. package/dist/types/core/client/eodash.d.ts +0 -8
@@ -0,0 +1,740 @@
1
+ import { n as getCompareLayers, l as getLayers, p as replaceLayer, h as extractLayerConfig, m as mergeGeojsons, f as axios, N as axios$1, C as compareIndicator, B as indicator } from './helpers-Dy0Q13tP.js';
2
+ import { a4 as useEmitLayersUpdate } from './asWebComponent-By_7_JjS.js';
3
+ import { isMulti } from '@eox/jsonform/src/custom-inputs/spatial/utils';
4
+ import log from 'loglevel';
5
+
6
+ /**
7
+ * @param {Record<string,any> |null} [jsonformSchema]
8
+ **/
9
+ function getBboxProperty(jsonformSchema) {
10
+ return /** @type {string} */ (
11
+ Object.keys(jsonformSchema?.properties ?? {}).find(
12
+ (key) => jsonformSchema?.properties[key].format === "bounding-box",
13
+ )
14
+ );
15
+ }
16
+
17
+ /**
18
+ * Extracts the keys of type "geojson" from the jsonform schema
19
+ * @param {Record<string,any> |null} [jsonformSchema]
20
+ **/
21
+ function getGeoJsonProperties(jsonformSchema) {
22
+ return /** @type {string[]} */ (
23
+ Object.keys(jsonformSchema?.properties ?? {}).filter(
24
+ (key) => jsonformSchema?.properties[key].type === "geojson",
25
+ )
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Converts jsonform geojson values to stringified geometries
31
+ * @param {Record<string,any> |null} [jsonformSchema]
32
+ * @param {Record<string,any>} jsonformValue
33
+ **/
34
+ function extractGeometries(jsonformValue, jsonformSchema) {
35
+ const geojsonKeys = getGeoJsonProperties(jsonformSchema);
36
+
37
+ for (const key of geojsonKeys) {
38
+ if (!jsonformValue[key]) {
39
+ continue;
40
+ }
41
+
42
+ if (isMulti(jsonformSchema?.properties[key])) {
43
+ // jsonformValue[key] is a feature collection
44
+ jsonformValue[key] =
45
+ /** @type {import("ol/format/GeoJSON").GeoJSONFeatureCollection} */ (
46
+ jsonformValue[key]
47
+ ).features.map((feature) => JSON.stringify(feature.geometry));
48
+ } else {
49
+ // jsonformValue[key] is a single feature
50
+ jsonformValue[key] = JSON.stringify(jsonformValue[key].geometry);
51
+ }
52
+ }
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {import("stac-ts").StacLink} link
58
+ * @param {string} layerId
59
+ * @param {string[]} urls
60
+ * @param {import("openlayers").ProjectionLike} projection
61
+ * @param {string} processId
62
+ */
63
+ async function createTiffLayerDefinition(
64
+ link,
65
+ layerId,
66
+ urls,
67
+ projection,
68
+ processId,
69
+ ) {
70
+ let flatStyleJSON = null;
71
+ if ("eox:flatstyle" in (link ?? {})) {
72
+ flatStyleJSON = await axios
73
+ .get(/** @type {string} */ (link["eox:flatstyle"]))
74
+ .then((resp) => resp.data);
75
+ }
76
+
77
+ let layerConfig;
78
+ let style;
79
+ if (flatStyleJSON) {
80
+ const extracted = extractLayerConfig(layerId ?? "", flatStyleJSON);
81
+ layerConfig = extracted.layerConfig;
82
+ style = extracted.style;
83
+ }
84
+ // We want to make sure the urls are alphabetically sorted
85
+ urls = urls.sort();
86
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"WebGLTile","GeoTIFF"> | undefined} */
87
+ const layerdef =
88
+ urls.length > 0
89
+ ? {
90
+ type: "WebGLTile",
91
+ source: {
92
+ type: "GeoTIFF",
93
+ normalize: !style,
94
+ sources: urls.map((url) => ({ url })),
95
+ },
96
+ properties: {
97
+ id: link.id + "_process" + processId,
98
+ title: "Results " + layerId,
99
+ ...(layerConfig && { layerConfig: layerConfig }),
100
+ layerControlToolsExpand: true,
101
+ },
102
+ ...(style && { style: style }),
103
+ }
104
+ : undefined;
105
+
106
+ // We want to see if the currently selected indicator uses a
107
+ // specific map projection if it does we want to apply it
108
+ if (projection && layerdef) {
109
+ //@ts-expect-error TODO
110
+ layerdef.source.projection = projection;
111
+ }
112
+ return layerdef;
113
+ }
114
+ /**
115
+ * @param {string} fileName
116
+ * @param {string|Record<string,any>} content
117
+ * @returns
118
+ */
119
+ const download = (fileName, content) => {
120
+ if (!content) {
121
+ return;
122
+ }
123
+ let url = /** @type string */ (content);
124
+ if (typeof content === "object") {
125
+ content = JSON.stringify(content);
126
+ const blob = new Blob([content], { type: "text" });
127
+ url = URL.createObjectURL(blob);
128
+ }
129
+ const link = document.createElement("a");
130
+ if (confirm(`Would you like to download ${fileName}?`)) {
131
+ link.href = url;
132
+ link.download = fileName;
133
+ link.click();
134
+ }
135
+ URL.revokeObjectURL(url);
136
+ link.remove();
137
+ };
138
+
139
+ /**
140
+ * Generate time pairs from a temporal extent
141
+ * @param {import("stac-ts").TemporalExtent} stacExtent - [start, end]
142
+ * @param {import("stac-ts").TemporalExtent} [userExtent] -[start, end]
143
+ * @param {string} [distribution] - daily, weekly, monthly, or yearly
144
+ */
145
+ function generateTimePairs(stacExtent, userExtent, distribution) {
146
+ // check whether the userExtent is provided
147
+ // if it is check that it doesn't exceed the stacExtent
148
+ // and clamp it otherwise
149
+
150
+ /** @type {string|Date} */
151
+ let from = "";
152
+
153
+ /** @type {string|Date} */
154
+ let to = "";
155
+ [from, to] = /** @type {[string, string]} */ (userExtent ?? ["", ""]);
156
+
157
+ const [stacFrom, stacTo] = /** @type {[string, string]} */ (
158
+ stacExtent ?? ["", ""]
159
+ );
160
+
161
+ try {
162
+ if (from && to) {
163
+ from = new Date(from);
164
+ to = new Date(to);
165
+ } else {
166
+ from = new Date(stacFrom);
167
+ to = new Date(stacTo);
168
+ }
169
+
170
+ if (from < new Date(stacFrom) || from > new Date(stacTo)) {
171
+ console.warn(
172
+ "[eodash] warn: start date is outside of the collection temporal extent and will be clamped",
173
+ `\nprovided start date:${from.toISOString()}`,
174
+ `\ncollection start date:${stacFrom}`,
175
+ );
176
+ from = new Date(stacFrom);
177
+ }
178
+
179
+ if (to > new Date(stacTo) || to < new Date(stacFrom)) {
180
+ console.warn(
181
+ "[eodash] warn: end date is outside of the collection temporal extent and will be clamped",
182
+ `\nprovided end date:${to.toISOString()}`,
183
+ `\ncollection end date:${stacTo}`,
184
+ );
185
+ to = new Date(stacTo);
186
+ }
187
+
188
+ if (from > to) {
189
+ console.error(
190
+ "[eodash] Error: start date is greater than end date",
191
+ from,
192
+ to,
193
+ );
194
+ return [];
195
+ }
196
+ } catch (e) {
197
+ //@ts-expect-error e should be an error
198
+ console.error("[eodash] Invalid date:", e.message);
199
+ return [];
200
+ }
201
+
202
+ const startDate = /** @type {Date} */ (from).toISOString();
203
+ const endDate = /** @type {Date} */ (to).toISOString();
204
+
205
+ if (!startDate || !endDate) {
206
+ return [];
207
+ }
208
+ const times = [];
209
+ let latest = new Date(endDate);
210
+ const start = new Date(startDate);
211
+ const oneDay = 24 * 60 * 60 * 1000;
212
+ // Use fixed step of 1 day (in milliseconds)
213
+ const step =
214
+ distribution === "daily"
215
+ ? oneDay
216
+ : distribution === "weekly"
217
+ ? oneDay * 7
218
+ : distribution === "monthly"
219
+ ? oneDay * 30
220
+ : distribution === "yearly"
221
+ ? oneDay * 365
222
+ : oneDay;
223
+
224
+ // Add dates, limiting to 31 dates (30 pairs maximum)
225
+ while (latest >= start && times.length < 31) {
226
+ times.push(new Date(latest));
227
+ latest.setTime(latest.getTime() - step);
228
+ }
229
+
230
+ const timePairs = [];
231
+ for (let i = 0; i < times.length - 1; i++) {
232
+ timePairs.push([times[i].toISOString(), times[i + 1].toISOString()]);
233
+ }
234
+
235
+ return timePairs;
236
+ }
237
+
238
+ /**
239
+ * Filter links to separate those with and without endpoint property
240
+ * @param {import("stac-ts").StacLink[] | undefined} links
241
+ * @param {string} [relType] - Optional relationship type
242
+ * @param {string} [contentType] - Optional content type
243
+ * @returns {[import("stac-ts").StacLink[], import("stac-ts").StacLink[]]}
244
+ */
245
+ function separateEndpointLinks(links, relType, contentType) {
246
+ if (!links) return [[], []];
247
+ const standardLinks = [];
248
+ const endpointLinks = [];
249
+
250
+ for (const link of links) {
251
+ // Check if the link matches the specified relType and contentType (if provided)
252
+ const relTypeMatch = link.rel === relType ;
253
+ const contentTypeMatch = contentType ? link.type === contentType : true;
254
+
255
+ if (relTypeMatch && contentTypeMatch) {
256
+ if (link.endpoint) {
257
+ endpointLinks.push(link);
258
+ } else {
259
+ standardLinks.push(link);
260
+ }
261
+ }
262
+ }
263
+
264
+ return [standardLinks, endpointLinks];
265
+ }
266
+
267
+ /**
268
+ * Generates layer definitions for asynchronous process results.
269
+ * using AsyncProcessResults data structure.
270
+ * @param {import("^/EodashProcess/types").AsyncProcessResults} processResults
271
+ * @param {import("stac-ts").StacLink} endpointLink
272
+ * @param {import("stac-ts").StacCollection|null} selectedStac
273
+ * @param {string} [postfixId=""] - Optional layers id postfix
274
+ * @returns
275
+ */
276
+ async function creatAsyncProcessLayerDefinitions(
277
+ processResults,
278
+ endpointLink,
279
+ selectedStac,
280
+ postfixId = "",
281
+ ) {
282
+ /** @type {import("@eox/map").EoxLayer[]} */
283
+ const layers = [];
284
+ const flatStyles = await fetchProcessStyles(endpointLink);
285
+
286
+ for (const resultItem of processResults) {
287
+ const flatStyleJSON = extractStyleFromResult(resultItem, flatStyles);
288
+ let style, layerConfig;
289
+ if (flatStyleJSON) {
290
+ const extracted = extractLayerConfig(
291
+ selectedStac?.id ?? "",
292
+ flatStyleJSON,
293
+ );
294
+ layerConfig = extracted.layerConfig;
295
+ style = extracted.style;
296
+ }
297
+
298
+ switch (resultItem.type) {
299
+ case "image/tiff": {
300
+ layers.push({
301
+ type: "WebGLTile",
302
+ properties: {
303
+ id: endpointLink.id + "_process" + resultItem.id + postfixId,
304
+ title:
305
+ "Results " +
306
+ (selectedStac?.id ?? "") +
307
+ " " +
308
+ (resultItem.id ?? ""),
309
+ layerControlToolsExpand: true,
310
+ ...(layerConfig && { layerConfig }),
311
+ },
312
+ source: {
313
+ type: "GeoTIFF",
314
+ normalize: !style,
315
+ sources: resultItem.urls.map((url) => ({ url })),
316
+ //@ts-expect-error TODO
317
+ ...(selectedStac["eodash:mapProjection"]?.["name"] && {
318
+ //@ts-expect-error TODO
319
+ projection: selectedStac["eodash:mapProjection"]["name"],
320
+ }),
321
+ },
322
+ ...(style && { style }),
323
+ });
324
+ break;
325
+ }
326
+ case "application/geo+json": {
327
+ const mergedUrl = await mergeGeojsons(resultItem.urls);
328
+ layers.push({
329
+ type: "Vector",
330
+ source: {
331
+ type: "Vector",
332
+ format: "GeoJSON",
333
+ ...(mergedUrl && { url: mergedUrl }),
334
+ },
335
+ properties: {
336
+ id: endpointLink.id + "_process_" + resultItem.id + postfixId,
337
+ title:
338
+ "Results " +
339
+ (selectedStac?.id ?? "") +
340
+ " " +
341
+ (resultItem.id ?? ""),
342
+ ...(layerConfig && {
343
+ layerConfig: {
344
+ ...layerConfig,
345
+ style,
346
+ },
347
+ }),
348
+ },
349
+ ...(!style?.variables && { style }),
350
+ interactions: [],
351
+ });
352
+ break;
353
+ }
354
+ default:
355
+ console.warn(
356
+ `[eodash] Unsupported result type "${resultItem.type}" for ${resultItem.id} layer creation.`,
357
+ );
358
+ break;
359
+ }
360
+ }
361
+ return layers;
362
+ }
363
+
364
+ /**
365
+ * @param {import("stac-ts").StacLink} endpointLink
366
+ * @returns
367
+ */
368
+ async function fetchProcessStyles(endpointLink) {
369
+ /** @type {import("@/types").EodashStyleJson | (Record<string,import("@/types").EodashStyleJson> & {multipleStyles:true}) | null} */
370
+ let flatStyles = null;
371
+ if (endpointLink["eox:flatstyle"]) {
372
+ if (typeof endpointLink["eox:flatstyle"] === "string") {
373
+ flatStyles = await axios
374
+ .get(/** @type {string} */ (endpointLink["eox:flatstyle"]))
375
+ .then((resp) => /** @type {} */ resp.data);
376
+ } else if (
377
+ Array.isArray(endpointLink["eox:flatstyle"]) &&
378
+ endpointLink["eox:flatstyle"].length
379
+ ) {
380
+ // multipleStyles as a flag to indicate it
381
+ flatStyles = { multipleStyles: true };
382
+
383
+ await Promise.all(
384
+ /** @type {{id:string;url:string}[]} */
385
+ (endpointLink["eox:flatstyle"]).map(async (styleDict) => {
386
+ //@ts-expect-error TODO
387
+ flatStyles[styleDict.id] = await axios
388
+ .get(styleDict.url)
389
+ .then(
390
+ (resp) =>
391
+ /** @type {import("@/types").EodashStyleJson} */ (resp.data),
392
+ );
393
+ }),
394
+ );
395
+ } else {
396
+ // multipleStyles as a flag to indicate it
397
+ flatStyles = { multipleStyles: true };
398
+ await Promise.all(
399
+ Object.keys(endpointLink["eox:flatstyle"] ?? {}).map((key) => {
400
+ //@ts-expect-error TODO
401
+ flatStyles[key] = axios
402
+ //@ts-expect-error TODO
403
+ .get(endpointLink["eox:flatstyle"][key])
404
+ .then((resp) => resp.data);
405
+ }),
406
+ );
407
+ }
408
+ }
409
+ return flatStyles;
410
+ }
411
+
412
+ /**
413
+ *
414
+ * @param {import("^/EodashProcess/types").AsyncProcessResults[number]} processResult
415
+ * @param {null| import("@/types").EodashStyleJson | (Record<string,import("@/types").EodashStyleJson> & {multipleStyles:true})} flatStyles
416
+ */
417
+ function extractStyleFromResult(processResult, flatStyles) {
418
+ if (!flatStyles) {
419
+ return undefined;
420
+ }
421
+ if (!("multipleStyles" in flatStyles)) {
422
+ return flatStyles;
423
+ }
424
+
425
+ const outputKey = processResult.id;
426
+ if (!outputKey || !(outputKey in flatStyles)) {
427
+ return undefined;
428
+ }
429
+ return flatStyles[outputKey];
430
+ }
431
+
432
+ /**
433
+ *
434
+ * @param {import("^/EodashProcess/types").EOxHubProcessResults} resultItem
435
+ * @returns {import("^/EodashProcess/types").AsyncProcessResults}
436
+ */
437
+ function extractAsyncResults(resultItem) {
438
+ if (!resultItem) {
439
+ return [];
440
+ }
441
+ // if no type specified we assume the results are geotiff sources
442
+ if ("urls" in resultItem && Array.isArray(resultItem.urls)) {
443
+ return [{ id: "", urls: resultItem.urls, type: "image/tiff" }];
444
+ }
445
+
446
+ const extracted = [];
447
+ for (const key in resultItem) {
448
+ if (key === "id") {
449
+ continue;
450
+ }
451
+ extracted.push({
452
+ // used as a key to identify the corresponding style
453
+ id: key,
454
+ //@ts-expect-error TODO
455
+ urls: /** @type {string[]} */ (resultItem[key].urls),
456
+ //@ts-expect-error TODO
457
+ type: /** @type {string} */ (resultItem[key].mimetype),
458
+ });
459
+ }
460
+ return extracted;
461
+ }
462
+ /**
463
+ * @param {import("@eox/map").EOxMap | null} mapElement
464
+ * @param {import("@eox/map").EoxLayer[]} processLayers
465
+ */
466
+ const applyProcessLayersToMap = (mapElement, processLayers) => {
467
+ if (!processLayers.length || !mapElement) {
468
+ return;
469
+ }
470
+ const getMapLayers =
471
+ mapElement.id === "compare" ? getCompareLayers : getLayers;
472
+ const currentLayers = [...getMapLayers()];
473
+
474
+ let analysisGroup =
475
+ /*** @type {import("@eox/map/src/layers").EOxLayerTypeGroup | undefined} */ (
476
+ currentLayers.find((l) => l.properties?.id.includes("AnalysisGroup"))
477
+ );
478
+ if (!analysisGroup) {
479
+ return;
480
+ }
481
+
482
+ for (const layer of processLayers) {
483
+ const exists = analysisGroup.layers.find(
484
+ (l) => l.properties?.id === layer.properties?.id,
485
+ );
486
+ if (!exists) {
487
+ analysisGroup.layers.unshift(layer);
488
+ } else {
489
+ analysisGroup.layers = replaceLayer(
490
+ analysisGroup.layers,
491
+ layer.properties?.id ?? "",
492
+ [layer],
493
+ );
494
+ }
495
+ }
496
+ if (mapElement) {
497
+ const layers = [...currentLayers];
498
+ const evtKey =
499
+ mapElement.id === "compare"
500
+ ? "compareProcess:updated"
501
+ : "process:updated";
502
+ useEmitLayersUpdate(evtKey, mapElement, layers);
503
+ mapElement.layers = layers;
504
+ }
505
+ };
506
+ /**
507
+ * Updates the jsonform schema to target the compare map
508
+ * @param {import("json-schema").JSONSchema7 | null | undefined} jsonformSchema
509
+ */
510
+ function updateJsonformSchemaTarget(jsonformSchema) {
511
+ if (!jsonformSchema) {
512
+ return jsonformSchema;
513
+ }
514
+ const stringified = JSON.stringify(jsonformSchema).replaceAll(
515
+ "eox-map#main",
516
+ "eox-map#compare",
517
+ );
518
+ return /** @type {import("json-schema").JSONSchema7} */ (
519
+ JSON.parse(stringified)
520
+ );
521
+ }
522
+
523
+ /**
524
+ * Polls the process status and fetches a result item when the process is successful.
525
+ *
526
+ * @param {Object} params - Parameters for polling the process status.
527
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} params.jobs - The list of jobs to update.
528
+ * @param {string} params.processUrl - The URL of the process JSON report.
529
+ * @param {import("vue").Ref<boolean>} params.isPolling - checks wether the polling should continue
530
+ * @param {number} [params.pollInterval=5000] - The interval (in milliseconds) between polling attempts.
531
+ * @param {number} [params.maxRetries=60] - The maximum number of polling attempts.
532
+ * @param {boolean} [params.enableCompare=false] - Whether to enable comparison mode, affecting the indicator used.
533
+ * @returns {Promise<import("../types").EOxHubProcessResults>} The fetched results JSON.
534
+ * @throws {Error} If the process does not complete successfully within the maximum retries.
535
+ */
536
+ async function pollProcessStatus({
537
+ jobs,
538
+ processUrl,
539
+ isPolling,
540
+ pollInterval = 10000,
541
+ maxRetries = 560,
542
+ enableCompare = false,
543
+ }) {
544
+ let retries = 0;
545
+ isPolling.value = true;
546
+ // Ensure the jobs status is updated after the job has started and before polling
547
+ setTimeout(() => {
548
+ updateJobsStatus(
549
+ jobs,
550
+ enableCompare ? compareIndicator.value : indicator.value,
551
+ );
552
+ }, 500);
553
+
554
+ while (retries < maxRetries && isPolling.value) {
555
+ try {
556
+ // Fetch the process JSON report
557
+ const cacheBuster = new Date().getTime(); // Add a timestamp for cache busting
558
+ const response = await axios$1.get(`${processUrl}?t=${cacheBuster}`);
559
+ const processReport = response.data;
560
+
561
+ // Check if the status is "successful"
562
+ if (processReport.status === "successful") {
563
+ console.log("Process completed successfully. Fetching result item...");
564
+
565
+ // Extract the result item URL
566
+ const resultsUrl = processReport.links[1].href;
567
+ if (!resultsUrl) {
568
+ throw new Error(`Result links not found in the process report.`);
569
+ }
570
+
571
+ // Fetch the result item
572
+ const resultResponse = await axios$1.get(resultsUrl);
573
+ console.log("Result file fetched successfully:", resultResponse.data);
574
+ return resultResponse.data; // Return the json result list
575
+ }
576
+ if (processReport.status === "failed") {
577
+ isPolling.value = false;
578
+ throw new Error("Process failed.", processReport);
579
+ }
580
+
581
+ // Log the current status if not successful
582
+ console.log(
583
+ `Status: ${processReport.status}. Retrying in ${pollInterval / 1000} seconds...`,
584
+ );
585
+ } catch (error) {
586
+ if (error instanceof Error) {
587
+ console.error("Error while polling process status:", error.message);
588
+ } else {
589
+ console.error("Unknown error occurred:", error);
590
+ }
591
+ }
592
+
593
+ // Wait for the next poll
594
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
595
+ retries++;
596
+ }
597
+ if (!isPolling.value) {
598
+ console.warn("Polling was stopped before the process was completed.");
599
+ return JSON.parse("{}");
600
+ }
601
+
602
+ throw new Error(
603
+ "Max retries reached. Process did not complete successfully.",
604
+ );
605
+ }
606
+
607
+ /**
608
+ *
609
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} jobs
610
+ * @param {string} indicator
611
+ */
612
+ async function updateJobsStatus(jobs, indicator) {
613
+ /** @type {string[]} */
614
+ const jobsUrls = JSON.parse(localStorage.getItem(indicator) || "[]");
615
+ /** @type {import("../types").AsyncJob[]} */
616
+ const jobResults = await Promise.all(
617
+ jobsUrls.map((url) =>
618
+ axios$1
619
+ .get(url, { params: { t: Date.now() } })
620
+ .then((response) => response.data),
621
+ ),
622
+ );
623
+ jobResults.sort((a, b) => {
624
+ return (
625
+ new Date(b.job_start_datetime).getTime() -
626
+ new Date(a.job_start_datetime).getTime()
627
+ );
628
+ });
629
+ jobs.value = jobResults;
630
+ }
631
+
632
+ /**
633
+ * Removes a job from the local storage and updates the job status
634
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} jobs
635
+ * @param {import("../types").AsyncJob} jobObject
636
+ * @param {string} indicator
637
+ */
638
+ const deleteJob = async (jobs, jobObject, indicator) => {
639
+ /** @type {string[]} */
640
+ const jobsUrls = JSON.parse(localStorage.getItem(indicator) || "[]");
641
+ const newJobs = jobsUrls.filter((url) => !url.includes(jobObject.jobID));
642
+ localStorage.setItem(indicator, JSON.stringify(newJobs));
643
+ await updateJobsStatus(jobs, indicator);
644
+ };
645
+
646
+ /**
647
+ * Downloads an existing process results
648
+ * @param {import("../types").AsyncJob} jobObject
649
+ * @param {import("stac-ts").StacCollection | null} selectedStac
650
+ */
651
+ const downloadPreviousResults = async (jobObject, selectedStac) => {
652
+ /** @type {string[]} */
653
+ const results = [];
654
+ const link = jobObject.links.find(
655
+ (link) => link.rel.includes("results") && link.type == "application/json",
656
+ );
657
+ if (!link) {
658
+ return;
659
+ }
660
+ await axios$1
661
+ .get(link.href)
662
+ .then((response) => response.data)
663
+ .then((data) => {
664
+ results.push(...data.urls);
665
+ });
666
+ results.forEach((result) => {
667
+ if (!result) {
668
+ return;
669
+ }
670
+ let fileName = "";
671
+ if (typeof result === "string") {
672
+ fileName = result.includes("/")
673
+ ? (result.split("/").pop() ?? "")
674
+ : result;
675
+ fileName = fileName.includes("?") ? fileName.split("?")[0] : fileName;
676
+ } else {
677
+ fileName = selectedStac?.id + "_process_results.json";
678
+ }
679
+ download(fileName, result);
680
+ });
681
+ };
682
+
683
+ /**
684
+ * Load the process results and update the map layers.
685
+ *
686
+ * @async
687
+ * @param {import("../types").AsyncJob} jobObject
688
+ * @param {import("stac-ts").StacCollection | null} selectedStac
689
+ * @param {import("@eox/map").EOxMap | null} mapElement
690
+ */
691
+ const loadProcess = async (jobObject, selectedStac, mapElement) => {
692
+ /** @type {import("../types").EOxHubProcessResults} */
693
+ const results = await axios$1
694
+ .get(jobObject.links[1].href)
695
+ .then((response) => response.data);
696
+
697
+ await loadPreviousProcess({
698
+ selectedStac,
699
+ results,
700
+ jobId: jobObject.jobID,
701
+ mapElement,
702
+ });
703
+ };
704
+
705
+ /**
706
+ * load a geotiff to the map from an existing process
707
+ *
708
+ * @param {Object} params
709
+ * @param {import("stac-ts").StacCollection | null} params.selectedStac
710
+ * @param {string} params.jobId
711
+ * @param {import("../types").EOxHubProcessResults} params.results
712
+ * @param {import("@eox/map").EOxMap | null} params.mapElement
713
+ */
714
+ async function loadPreviousProcess({
715
+ selectedStac,
716
+ results,
717
+ jobId,
718
+ mapElement,
719
+ }) {
720
+ const asyncLink = selectedStac?.links.find(
721
+ (link) => link.rel === "service" && link.endpoint == "eoxhub_workspaces",
722
+ );
723
+ if (!asyncLink) {
724
+ return;
725
+ }
726
+
727
+ const unifiedResult = extractAsyncResults(results);
728
+
729
+ const layers = await creatAsyncProcessLayerDefinitions(
730
+ unifiedResult,
731
+ asyncLink,
732
+ selectedStac,
733
+ jobId,
734
+ );
735
+
736
+ log.debug("rendered layers after loading previous process:", layers);
737
+ applyProcessLayersToMap(mapElement, layers);
738
+ }
739
+
740
+ export { creatAsyncProcessLayerDefinitions as a, generateTimePairs as b, createTiffLayerDefinition as c, download as d, extractAsyncResults as e, updateJsonformSchemaTarget as f, getBboxProperty as g, extractGeometries as h, applyProcessLayersToMap as i, downloadPreviousResults as j, deleteJob as k, loadProcess as l, pollProcessStatus as p, separateEndpointLinks as s, updateJobsStatus as u };