@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,804 @@
1
+ import { n as getCompareLayers, l as getLayers, p as replaceLayer, h as extractLayerConfig, s as extractLayerLegend, m as mergeGeojsons, f as axios, O as axios$1, D as compareIndicator, C as indicator } from './helpers-wXK7Ywio.js';
2
+ import { N as useEmitLayersUpdate } from './asWebComponent-ZyEzWOOf.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
+ /** @type {import("@/types").EodashStyleJson | undefined} */
289
+ let style;
290
+ /** @type {Record<string, unknown> | undefined} */
291
+ let layerConfig;
292
+ if (flatStyleJSON) {
293
+ const extracted = extractLayerConfig(
294
+ selectedStac?.id ?? "",
295
+ flatStyleJSON,
296
+ );
297
+ layerConfig = extracted.layerConfig;
298
+ style = extracted.style;
299
+ }
300
+
301
+ // Check if collection has eox:colorlegend definition, if yes overwrite legend description
302
+ let extraProperties = extractLayerLegend(selectedStac);
303
+
304
+ switch (resultItem.type) {
305
+ case "image/tiff": {
306
+ layers.push({
307
+ type: "WebGLTile",
308
+ properties: {
309
+ id: endpointLink.id + "_process" + resultItem.id + postfixId,
310
+ title:
311
+ "Results " +
312
+ (selectedStac?.id ?? "") +
313
+ " " +
314
+ (resultItem.id ?? ""),
315
+ layerControlToolsExpand: true,
316
+ ...(layerConfig && { layerConfig }),
317
+ ...extraProperties,
318
+ },
319
+ source: {
320
+ type: "GeoTIFF",
321
+ normalize: !style,
322
+ sources: resultItem.urls.map((url) => ({ url })),
323
+ //@ts-expect-error TODO
324
+ ...(selectedStac["eodash:mapProjection"]?.["name"] && {
325
+ //@ts-expect-error TODO
326
+ projection: selectedStac["eodash:mapProjection"]["name"],
327
+ }),
328
+ },
329
+ ...(style && { style }),
330
+ });
331
+ break;
332
+ }
333
+ case "application/geo+json": {
334
+ const mergedUrl = await mergeGeojsons(resultItem.urls);
335
+ layers.push({
336
+ type: "Vector",
337
+ source: {
338
+ type: "Vector",
339
+ format: "GeoJSON",
340
+ ...(mergedUrl && { url: mergedUrl }),
341
+ },
342
+ properties: {
343
+ id: endpointLink.id + "_process_" + resultItem.id + postfixId,
344
+ title:
345
+ "Results " +
346
+ (selectedStac?.id ?? "") +
347
+ " " +
348
+ (resultItem.id ?? ""),
349
+ ...(layerConfig && {
350
+ layerConfig: {
351
+ ...layerConfig,
352
+ style,
353
+ },
354
+ }),
355
+ ...extraProperties,
356
+ },
357
+ ...(!style?.variables && { style }),
358
+ interactions: [],
359
+ });
360
+ break;
361
+ }
362
+ case "application/vnd.flatgeobuf": {
363
+ // TODO after more flatgeobuf urls are possible in EOxMap https://github.com/EOX-A/EOxElements/issues/1789
364
+ // we should change this handler to only create one layer instead of many
365
+ resultItem.urls.forEach((url, i) => {
366
+ layers.push({
367
+ type: "Vector",
368
+ source: {
369
+ type: "FlatGeoBuf",
370
+ url,
371
+ },
372
+ properties: {
373
+ id:
374
+ endpointLink.id +
375
+ "_process_" +
376
+ resultItem.id +
377
+ postfixId +
378
+ `_${i}`,
379
+ title:
380
+ "Results " +
381
+ (selectedStac?.id ?? "") +
382
+ " " +
383
+ (resultItem.id ?? ""),
384
+ layerControlToolsExpand: true,
385
+ ...(layerConfig && {
386
+ layerConfig: {
387
+ ...layerConfig,
388
+ style,
389
+ },
390
+ }),
391
+ ...extraProperties,
392
+ },
393
+ });
394
+ });
395
+ break;
396
+ }
397
+ default:
398
+ console.warn(
399
+ `[eodash] Unsupported result type "${resultItem.type}" for ${resultItem.id} layer creation.`,
400
+ );
401
+ break;
402
+ }
403
+ }
404
+ return layers;
405
+ }
406
+
407
+ /**
408
+ * @param {import("stac-ts").StacLink} endpointLink
409
+ * @returns
410
+ */
411
+ async function fetchProcessStyles(endpointLink) {
412
+ /** @type {import("@/types").EodashStyleJson | (Record<string,import("@/types").EodashStyleJson> & {multipleStyles:true}) | null} */
413
+ let flatStyles = null;
414
+ if (endpointLink["eox:flatstyle"]) {
415
+ if (typeof endpointLink["eox:flatstyle"] === "string") {
416
+ flatStyles = await axios
417
+ .get(/** @type {string} */ (endpointLink["eox:flatstyle"]))
418
+ .then((resp) => /** @type {} */ resp.data);
419
+ } else if (
420
+ Array.isArray(endpointLink["eox:flatstyle"]) &&
421
+ endpointLink["eox:flatstyle"].length
422
+ ) {
423
+ // multipleStyles as a flag to indicate it
424
+ flatStyles = { multipleStyles: true };
425
+
426
+ await Promise.all(
427
+ /** @type {{id:string;url:string}[]} */
428
+ (endpointLink["eox:flatstyle"]).map(async (styleDict) => {
429
+ //@ts-expect-error TODO
430
+ flatStyles[styleDict.id] = await axios
431
+ .get(styleDict.url)
432
+ .then(
433
+ (resp) =>
434
+ /** @type {import("@/types").EodashStyleJson} */ (resp.data),
435
+ );
436
+ }),
437
+ );
438
+ } else {
439
+ // multipleStyles as a flag to indicate it
440
+ flatStyles = { multipleStyles: true };
441
+ await Promise.all(
442
+ Object.keys(endpointLink["eox:flatstyle"] ?? {}).map((key) => {
443
+ //@ts-expect-error TODO
444
+ flatStyles[key] = axios
445
+ //@ts-expect-error TODO
446
+ .get(endpointLink["eox:flatstyle"][key])
447
+ .then((resp) => resp.data);
448
+ }),
449
+ );
450
+ }
451
+ }
452
+ return flatStyles;
453
+ }
454
+
455
+ /**
456
+ *
457
+ * @param {import("^/EodashProcess/types").AsyncProcessResults[number]} processResult
458
+ * @param {null| import("@/types").EodashStyleJson | (Record<string,import("@/types").EodashStyleJson> & {multipleStyles:true})} flatStyles
459
+ */
460
+ function extractStyleFromResult(processResult, flatStyles) {
461
+ if (!flatStyles) {
462
+ return undefined;
463
+ }
464
+ if (!("multipleStyles" in flatStyles)) {
465
+ return flatStyles;
466
+ }
467
+
468
+ const outputKey = processResult.id;
469
+ if (!outputKey || !(outputKey in flatStyles)) {
470
+ return undefined;
471
+ }
472
+ return flatStyles[outputKey];
473
+ }
474
+
475
+ /**
476
+ *
477
+ * @param {import("^/EodashProcess/types").EOxHubProcessResults} resultItem
478
+ * @returns {import("^/EodashProcess/types").AsyncProcessResults}
479
+ */
480
+ function extractAsyncResults(resultItem) {
481
+ if (!resultItem) {
482
+ return [];
483
+ }
484
+ // if no type specified we assume the results are geotiff sources
485
+ if ("urls" in resultItem && Array.isArray(resultItem.urls)) {
486
+ return [{ id: "", urls: resultItem.urls, type: "image/tiff" }];
487
+ }
488
+
489
+ const extracted = [];
490
+ for (const key in resultItem) {
491
+ if (key === "id") {
492
+ continue;
493
+ }
494
+ extracted.push({
495
+ // used as a key to identify the corresponding style
496
+ id: key,
497
+ //@ts-expect-error TODO
498
+ urls: /** @type {string[]} */ (resultItem[key].urls),
499
+ //@ts-expect-error TODO
500
+ type: /** @type {string} */ (resultItem[key].mimetype),
501
+ });
502
+ }
503
+ return extracted;
504
+ }
505
+ /**
506
+ * @param {import("@eox/map").EOxMap | null} mapElement
507
+ * @param {import("@eox/map").EoxLayer[]} processLayers
508
+ */
509
+ const applyProcessLayersToMap = (mapElement, processLayers) => {
510
+ if (!processLayers.length || !mapElement) {
511
+ return;
512
+ }
513
+ const getMapLayers =
514
+ mapElement.id === "compare" ? getCompareLayers : getLayers;
515
+ const currentLayers = [...getMapLayers()];
516
+
517
+ let analysisGroup =
518
+ /*** @type {import("@eox/map/src/layers").EOxLayerTypeGroup | undefined} */ (
519
+ currentLayers.find((l) => l.properties?.id.includes("AnalysisGroup"))
520
+ );
521
+ if (!analysisGroup) {
522
+ return;
523
+ }
524
+
525
+ for (const layer of processLayers) {
526
+ const exists = analysisGroup.layers.find(
527
+ (l) => l.properties?.id === layer.properties?.id,
528
+ );
529
+ if (!exists) {
530
+ analysisGroup.layers.unshift(layer);
531
+ } else {
532
+ analysisGroup.layers = replaceLayer(
533
+ analysisGroup.layers,
534
+ layer.properties?.id ?? "",
535
+ [layer],
536
+ );
537
+ }
538
+ }
539
+ if (mapElement) {
540
+ const layers = [...currentLayers];
541
+ const evtKey =
542
+ mapElement.id === "compare"
543
+ ? "compareProcess:updated"
544
+ : "process:updated";
545
+ useEmitLayersUpdate(evtKey, mapElement, layers);
546
+ mapElement.layers = layers;
547
+ }
548
+ };
549
+ /**
550
+ * Updates the jsonform schema to target the compare map
551
+ * @param {import("json-schema").JSONSchema7 | null | undefined} jsonformSchema
552
+ */
553
+ function updateJsonformSchemaTarget(jsonformSchema) {
554
+ if (!jsonformSchema) {
555
+ return jsonformSchema;
556
+ }
557
+ const stringified = JSON.stringify(jsonformSchema).replaceAll(
558
+ "eox-map#main",
559
+ "eox-map#compare",
560
+ );
561
+ return /** @type {import("json-schema").JSONSchema7} */ (
562
+ JSON.parse(stringified)
563
+ );
564
+ }
565
+
566
+ /**
567
+ * Polls the process status and fetches a result item when the process is successful.
568
+ *
569
+ * @param {Object} params - Parameters for polling the process status.
570
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} params.jobs - The list of jobs to update.
571
+ * @param {string} params.processUrl - The URL of the process JSON report.
572
+ * @param {import("vue").Ref<boolean>} params.isPolling - checks wether the polling should continue
573
+ * @param {number} [params.pollInterval=5000] - The interval (in milliseconds) between polling attempts.
574
+ * @param {number} [params.maxRetries=60] - The maximum number of polling attempts.
575
+ * @param {boolean} [params.enableCompare=false] - Whether to enable comparison mode, affecting the indicator used.
576
+ * @returns {Promise<import("../types").EOxHubProcessResults>} The fetched results JSON.
577
+ * @throws {Error} If the process does not complete successfully within the maximum retries.
578
+ */
579
+ async function pollProcessStatus({
580
+ jobs,
581
+ processUrl,
582
+ isPolling,
583
+ pollInterval = 10000,
584
+ maxRetries = 560,
585
+ enableCompare = false,
586
+ }) {
587
+ let retries = 0;
588
+ isPolling.value = true;
589
+ // Ensure the jobs status is updated after the job has started and before polling
590
+ setTimeout(() => {
591
+ updateJobsStatus(
592
+ jobs,
593
+ enableCompare ? compareIndicator.value : indicator.value,
594
+ );
595
+ }, 500);
596
+
597
+ while (retries < maxRetries && isPolling.value) {
598
+ try {
599
+ // Fetch the process JSON report
600
+ const cacheBuster = new Date().getTime(); // Add a timestamp for cache busting
601
+ const response = await axios$1.get(`${processUrl}?t=${cacheBuster}`);
602
+ const processReport = response.data;
603
+
604
+ // Check if the status is "successful"
605
+ if (processReport.status === "successful") {
606
+ console.log("Process completed successfully. Fetching result item...");
607
+
608
+ // Extract the result item URL
609
+ const resultsUrl = processReport.links[1].href;
610
+ if (!resultsUrl) {
611
+ throw new Error(`Result links not found in the process report.`);
612
+ }
613
+
614
+ // Fetch the result item
615
+ const resultResponse = await axios$1.get(resultsUrl);
616
+ console.log("Result file fetched successfully:", resultResponse.data);
617
+ return resultResponse.data; // Return the json result list
618
+ }
619
+ if (processReport.status === "failed") {
620
+ isPolling.value = false;
621
+ throw new Error("Process failed.", processReport);
622
+ }
623
+
624
+ // Log the current status if not successful
625
+ console.log(
626
+ `Status: ${processReport.status}. Retrying in ${pollInterval / 1000} seconds...`,
627
+ );
628
+ } catch (error) {
629
+ if (error instanceof Error) {
630
+ console.error("Error while polling process status:", error.message);
631
+ } else {
632
+ console.error("Unknown error occurred:", error);
633
+ }
634
+ }
635
+
636
+ // Wait for the next poll
637
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
638
+ retries++;
639
+ }
640
+ if (!isPolling.value) {
641
+ console.warn("Polling was stopped before the process was completed.");
642
+ return JSON.parse("{}");
643
+ }
644
+
645
+ throw new Error(
646
+ "Max retries reached. Process did not complete successfully.",
647
+ );
648
+ }
649
+
650
+ /**
651
+ *
652
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} jobs
653
+ * @param {string} indicator
654
+ */
655
+ async function updateJobsStatus(jobs, indicator) {
656
+ /** @type {string[]} */
657
+ const jobsUrls = JSON.parse(localStorage.getItem(indicator) || "[]");
658
+ /** @type {import("../types").AsyncJob[]} */
659
+ const jobResults = await Promise.all(
660
+ jobsUrls.map((url) =>
661
+ axios$1
662
+ .get(url, { params: { t: Date.now() } })
663
+ .then((response) => response.data),
664
+ ),
665
+ );
666
+ jobResults.sort((a, b) => {
667
+ return (
668
+ new Date(b.job_start_datetime).getTime() -
669
+ new Date(a.job_start_datetime).getTime()
670
+ );
671
+ });
672
+ jobs.value = jobResults;
673
+ }
674
+
675
+ /**
676
+ * Removes a job from the local storage and updates the job status
677
+ * @param {import("vue").Ref<import("../types").AsyncJob[]>} jobs
678
+ * @param {import("../types").AsyncJob} jobObject
679
+ * @param {string} indicator
680
+ */
681
+ const deleteJob = async (jobs, jobObject, indicator) => {
682
+ /** @type {string[]} */
683
+ const jobsUrls = JSON.parse(localStorage.getItem(indicator) || "[]");
684
+ const newJobs = jobsUrls.filter((url) => !url.includes(jobObject.jobID));
685
+ localStorage.setItem(indicator, JSON.stringify(newJobs));
686
+ await updateJobsStatus(jobs, indicator);
687
+ };
688
+
689
+ /**
690
+ * Downloads an existing process results
691
+ * @param {import("../types").AsyncJob} jobObject
692
+ * @param {import("stac-ts").StacCollection | null} selectedStac
693
+ */
694
+ const downloadPreviousResults = async (jobObject, selectedStac) => {
695
+ /** @type {string[]} */
696
+ const results = [];
697
+ const link = jobObject.links.find(
698
+ (link) => link.rel.includes("results") && link.type == "application/json",
699
+ );
700
+ if (!link) {
701
+ return;
702
+ }
703
+ await axios$1
704
+ .get(link.href)
705
+ .then((response) => response.data)
706
+ .then((data) => {
707
+ // either urls is an Array
708
+ if (data.urls) {
709
+ results.push(...data.urls);
710
+ } else {
711
+ // or urls need to be aggregated from mapping objects
712
+ for (const outputMappingObject of Object.values(data)) {
713
+ results.push(...outputMappingObject.urls);
714
+ }
715
+ }
716
+ });
717
+ results.forEach((result) => {
718
+ if (!result) {
719
+ return;
720
+ }
721
+ let fileName = "";
722
+ if (typeof result === "string") {
723
+ fileName = result.includes("/")
724
+ ? (result.split("/").pop() ?? "")
725
+ : result;
726
+ fileName = fileName.includes("?") ? fileName.split("?")[0] : fileName;
727
+ } else {
728
+ fileName = selectedStac?.id + "_process_results.json";
729
+ }
730
+ download(fileName, result);
731
+ });
732
+ };
733
+
734
+ /**
735
+ * Load the process results and update the map layers.
736
+ *
737
+ * @async
738
+ * @param {import("../types").AsyncJob} jobObject
739
+ * @param {import("stac-ts").StacCollection | null} selectedStac
740
+ * @param {import("@eox/map").EOxMap | null} mapElement
741
+ */
742
+ const loadProcess = async (jobObject, selectedStac, mapElement) => {
743
+ /** @type {import("../types").EOxHubProcessResults} */
744
+ const results = await axios$1
745
+ .get(jobObject.links[1].href)
746
+ .then((response) => response.data);
747
+
748
+ await loadPreviousProcess({
749
+ selectedStac,
750
+ results,
751
+ jobId: jobObject.jobID,
752
+ mapElement,
753
+ });
754
+ };
755
+
756
+ /**
757
+ * load a geotiff to the map from an existing process
758
+ *
759
+ * @param {Object} params
760
+ * @param {import("stac-ts").StacCollection | null} params.selectedStac
761
+ * @param {string} params.jobId
762
+ * @param {import("../types").EOxHubProcessResults} params.results
763
+ * @param {import("@eox/map").EOxMap | null} params.mapElement
764
+ */
765
+ async function loadPreviousProcess({
766
+ selectedStac,
767
+ results,
768
+ jobId,
769
+ mapElement,
770
+ }) {
771
+ const asyncLink = selectedStac?.links.find(
772
+ (link) => link.rel === "service" && link.endpoint == "eoxhub_workspaces",
773
+ );
774
+ if (!asyncLink) {
775
+ return;
776
+ }
777
+
778
+ const unifiedResult = extractAsyncResults(results);
779
+
780
+ const layers = await creatAsyncProcessLayerDefinitions(
781
+ unifiedResult,
782
+ asyncLink,
783
+ selectedStac,
784
+ jobId,
785
+ );
786
+
787
+ log.debug("rendered layers after loading previous process:", layers);
788
+ applyProcessLayersToMap(mapElement, layers);
789
+ }
790
+
791
+ /**
792
+ * extracts job status url from local storage based on identifier
793
+ *
794
+ * @param {string} jobID
795
+ * @param {string} indicator
796
+ */
797
+ const getJobStatusUrl = (jobID, indicator) => {
798
+ /** @type {string[]} */
799
+ const jobsUrls = JSON.parse(localStorage.getItem(indicator) || "[]");
800
+ const jobUrl = jobsUrls.find((url) => url.includes(jobID));
801
+ return jobUrl;
802
+ };
803
+
804
+ 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, getJobStatusUrl as j, downloadPreviousResults as k, loadProcess as l, deleteJob as m, pollProcessStatus as p, separateEndpointLinks as s, updateJobsStatus as u };