@eodash/eodash 5.6.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/core/client/eodashSTAC/EodashCollection.js +5 -1
  2. package/core/client/eodashSTAC/helpers.js +4 -4
  3. package/dist/client/{DashboardLayout-DkKDhfgH.js → DashboardLayout-CqMb7OXc.js} +2 -2
  4. package/dist/client/{DynamicWebComponent-BrpkA-e3.js → DynamicWebComponent-DqKqTf7l.js} +1 -1
  5. package/dist/client/{EodashChart-p7_1FRlF.js → EodashChart-PkUQfH0k.js} +4 -4
  6. package/dist/client/{EodashDatePicker-HK0Jl7wQ.js → EodashDatePicker-DLJGdTtX.js} +3 -3
  7. package/dist/client/{EodashItemFilter-BYQDA78B.js → EodashItemFilter-Dk8KjFcX.js} +1 -1
  8. package/dist/client/{EodashLayerControl-NcrNncGn.js → EodashLayerControl-CC1Cogrn.js} +2 -2
  9. package/dist/client/{EodashLayoutSwitcher-CC2Znd0I.js → EodashLayoutSwitcher-5WZYd6rr.js} +3 -3
  10. package/dist/client/{EodashMapBtns-BJSh7qT-.js → EodashMapBtns-BHrSDNoC.js} +6 -6
  11. package/dist/client/{EodashStacInfo-BA-0UjvT.js → EodashStacInfo-B90wobPc.js} +31 -31
  12. package/dist/client/{EodashTimeSlider-GDv1hbw7.js → EodashTimeSlider-5vm7F3ju.js} +2 -2
  13. package/dist/client/{EodashTools-CWmoH0g_.js → EodashTools-CGZtTh45.js} +4 -4
  14. package/dist/client/{ExportState-CfAfX4RN.js → ExportState-Ce0ZnrX_.js} +4 -4
  15. package/dist/client/{Footer-DLGxQTZt.js → Footer-CnZqEiop.js} +1 -1
  16. package/dist/client/{Header-eyEyyzD6.js → Header-Dr5PBIm8.js} +2 -2
  17. package/dist/client/{MobileLayout-RzLOyXwL.js → MobileLayout-DyMjaeif.js} +2 -2
  18. package/dist/client/{PopUp-WNs6Io2N.js → PopUp-DjvE_71e.js} +2 -2
  19. package/dist/client/{ProcessList-RzVqbd5E.js → ProcessList-kf3olI7W.js} +4 -4
  20. package/dist/client/{VImg-GKUTFoV-.js → VImg-Bj6uWt6j.js} +2 -2
  21. package/dist/client/{VMain-D7_72mHr.js → VMain-Cm0UmKGi.js} +1 -1
  22. package/dist/client/{VTooltip-BbmvtvYw.js → VTooltip-DbBu-ky3.js} +2 -2
  23. package/dist/client/{WidgetsContainer-Dw5d7SOW.js → WidgetsContainer-CfLnd44U.js} +1 -1
  24. package/dist/client/{asWebComponent-DDt7V3Bl.js → asWebComponent-DfULAbpq.js} +13 -9
  25. package/dist/client/{async-CT3KEV52.js → async-CIbKXEEe.js} +10 -20
  26. package/dist/client/eo-dash.js +1 -1
  27. package/dist/client/{forwardRefs-CVzxyoSq.js → forwardRefs-yj3VWOK5.js} +2 -2
  28. package/dist/client/{handling-C285F_Mq.js → handling-s8xt2JwF.js} +390 -90
  29. package/dist/client/{helpers-CZFH0EVN.js → helpers-DAVGYl_f.js} +4 -4
  30. package/dist/client/{index-B-m7V3Kb.js → index-4TrjfJ32.js} +3 -3
  31. package/dist/client/{index-B_6H-N14.js → index-Bu8BMA9q.js} +39 -31
  32. package/dist/client/{index-DgOXD7wd.js → index-CYz6dT7K.js} +2 -2
  33. package/dist/client/{index-CF3ZMzTz.js → index-CugS__t5.js} +3 -3
  34. package/dist/client/templates.js +1 -1
  35. package/dist/client/{transition-C2gnrWg0.js → transition-DaOsiD4u.js} +1 -1
  36. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +1 -2
  37. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +6 -6
  38. package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +3 -1
  39. package/dist/types/widgets/EodashProcess/methods/template-helpers.d.ts +99 -0
  40. package/dist/types/widgets/EodashProcess/methods/utils.d.ts +1 -6
  41. package/dist/types/widgets/EodashProcess/types.d.ts +1 -0
  42. package/dist/types/widgets/EodashStacInfo.vue.d.ts +10 -10
  43. package/package.json +1 -1
  44. package/widgets/EodashProcess/index.vue +16 -12
  45. package/widgets/EodashProcess/methods/composables.js +18 -15
  46. package/widgets/EodashProcess/methods/handling.js +78 -62
  47. package/widgets/EodashProcess/methods/outputs.js +74 -29
  48. package/widgets/EodashProcess/methods/template-helpers.js +246 -0
  49. package/widgets/EodashProcess/methods/utils.js +7 -17
  50. package/widgets/EodashProcess/types.ts +1 -0
  51. package/widgets/EodashStacInfo.vue +30 -30
@@ -1,15 +1,261 @@
1
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, b as getBboxProperty, f as generateTimePairs, h as updateJsonformSchemaTarget, i as extractGeometries, j as applyProcessLayersToMap, g as getDrawToolsProperty } from './async-CT3KEV52.js';
3
- import { d as axios, b as extractLayerConfig, I as compareIndicator, F as indicator, y as extractCollectionUrls, H as currentCompareUrl, G as currentUrl, z as generateLinksFromItems, o as getDatetimeProperty, J as comparePoi, B as datetime, Z as compareChartData, _ as chartData, O as compareChartSpec, N as chartSpec, C as poi, v as getLayers } from './helpers-CZFH0EVN.js';
2
+ import { s as separateEndpointLinks, c as createTiffLayerDefinition, p as pollProcessStatus, e as extractAsyncResults, u as updateJobsStatus, a as creatAsyncProcessLayerDefinitions, b as getBboxProperty, f as generateTimePairs, h as extractGeometries, i as applyProcessLayersToMap, g as getDrawToolsProperties } from './async-CIbKXEEe.js';
3
+ import { d as axios, b as extractLayerConfig, I as compareIndicator, F as indicator, y as extractCollectionUrls, H as currentCompareUrl, G as currentUrl, z as generateLinksFromItems, o as getDatetimeProperty, J as comparePoi, B as datetime, Z as compareChartData, _ as chartData, O as compareChartSpec, N as chartSpec, C as poi, w as getCompareLayers, v as getLayers } from './helpers-DAVGYl_f.js';
4
4
  import mustache from 'mustache';
5
- import { T as isFirstLoad, H as useSTAcStore, X as readParquetItems, Y as useGetSubCodeId } from './asWebComponent-DDt7V3Bl.js';
5
+ import { T as isFirstLoad, H as useSTAcStore, X as readParquetItems, Y as useGetSubCodeId } from './asWebComponent-DfULAbpq.js';
6
6
  import { toAbsolute } from 'stac-js/src/http.js';
7
7
 
8
+ /**
9
+ * Returns form value keys that match the multiQuery configuration.
10
+ * @param {Record<string, any> | undefined} jsonformValue
11
+ * @param {Record<string, any>} jsonformSchema
12
+ * @returns {string[]}
13
+ */
14
+ function getMultiQueryMatches(jsonformValue, jsonformSchema) {
15
+ const multiQuery = jsonformSchema?.options?.multiQuery;
16
+ return Object.keys(jsonformValue ?? {}).filter((key) => {
17
+ return Array.isArray(multiQuery)
18
+ ? multiQuery.includes(key)
19
+ : multiQuery === key;
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Extracts iterable scalar values for a multiQuery field.
25
+ * Wraps single values in an array; returns empty array for undefined.
26
+ * @param {string} match - The multiQuery field name
27
+ * @param {Record<string, any>} jsonformValue
28
+ * @returns {any[]}
29
+ */
30
+ function getScalarMultiQueryValues(match, jsonformValue) {
31
+ const value = jsonformValue?.[match];
32
+ if (Array.isArray(value)) {
33
+ return value;
34
+ }
35
+ return value === undefined ? [] : [value];
36
+ }
37
+
38
+ /**
39
+ * Extracts iterable values for a POST multiQuery field.
40
+ * For GeoJSON fields, uses rawJsonformValue to get original Feature objects
41
+ * (before extractGeometries stringifies them). For scalar fields, delegates
42
+ * to getScalarMultiQueryValues.
43
+ * @param {string} match - The multiQuery field name
44
+ * @param {Record<string, any>} jsonformValue - Form values after geometry extraction
45
+ * @param {Record<string, any> | undefined} rawJsonformValue - Form values before geometry extraction
46
+ * @param {Record<string, any>} jsonformSchema
47
+ * @returns {any[]}
48
+ */
49
+ function getPostMultiQueryValues(
50
+ match,
51
+ jsonformValue,
52
+ rawJsonformValue,
53
+ jsonformSchema,
54
+ ) {
55
+ if (jsonformSchema?.properties?.[match]?.type === "geojson") {
56
+ return getGeoJsonMultiQueryValues(rawJsonformValue?.[match]);
57
+ }
58
+ return getScalarMultiQueryValues(match, jsonformValue);
59
+ }
60
+
61
+ /**
62
+ * Extracts individual GeoJSON Feature objects from a value that may be
63
+ * a FeatureCollection, a single Feature, or an array of Features.
64
+ * Filters for valid Features with geometry and deduplicates.
65
+ * @param {any} value
66
+ * @returns {Record<string, any>[]}
67
+ */
68
+ function getGeoJsonMultiQueryValues(value) {
69
+ if (value?.type === "FeatureCollection") {
70
+ return dedupeMultiQueryValues(
71
+ (value.features ?? []).filter(
72
+ (/** @type {any} */ feature) =>
73
+ feature?.type === "Feature" && feature.geometry,
74
+ ),
75
+ );
76
+ }
77
+ if (value?.type === "Feature") {
78
+ return [value];
79
+ }
80
+ if (Array.isArray(value)) {
81
+ return dedupeMultiQueryValues(
82
+ value.filter(
83
+ (/** @type {any} */ feature) =>
84
+ feature?.type === "Feature" && feature.geometry,
85
+ ),
86
+ );
87
+ }
88
+ return [];
89
+ }
90
+
91
+ /**
92
+ * Removes duplicate values using JSON serialization as the identity key.
93
+ * @param {any[]} values
94
+ * @returns {any[]}
95
+ */
96
+ function dedupeMultiQueryValues(values) {
97
+ const seen = new Set();
98
+ const deduped = [];
99
+ for (const value of values ?? []) {
100
+ const key =
101
+ value && typeof value === "object"
102
+ ? JSON.stringify(value)
103
+ : String(value);
104
+ if (seen.has(key)) {
105
+ continue;
106
+ }
107
+ seen.add(key);
108
+ deduped.push(value);
109
+ }
110
+ return deduped;
111
+ }
112
+
113
+ /**
114
+ * Builds the Mustache rendering context for one multiQuery iteration.
115
+ * Spreads the full form value, overrides the matched field with the current
116
+ * iteration value, and adds a 1-based `multiQueryIndex`.
117
+ * @param {Record<string, any>} jsonformValue
118
+ * @param {string} match - The multiQuery field name being iterated
119
+ * @param {any} value - The current iteration value
120
+ * @param {number} index - Zero-based iteration index
121
+ * @returns {Record<string, any>}
122
+ */
123
+ function createMultiQueryContext(jsonformValue, match, value, index) {
124
+ return {
125
+ ...(jsonformValue ?? {}),
126
+ [match]: value,
127
+ multiQueryIndex: index + 1,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Flattens an array of POST responses into a single values array.
133
+ * Each response that is itself an array is spread; scalars are wrapped.
134
+ * @param {any[]} values
135
+ * @returns {any[]}
136
+ */
137
+ function aggregateInlineResponses(values) {
138
+ return values.flatMap((value) => (Array.isArray(value) ? value : [value]));
139
+ }
140
+
141
+ /**
142
+ * Renders a JSON body template string with the given context.
143
+ * Primary path: parses the template as JSON, then recursively renders each
144
+ * node (preserving object-valued placeholders as raw JSON).
145
+ * Fallback: treats the template as a Mustache string, renders, then parses.
146
+ * @param {string} bodyTemplate - JSON template string (may contain Mustache placeholders)
147
+ * @param {Record<string, any>} context - Values to substitute into the template
148
+ * @returns {any} Parsed JSON body ready for POST
149
+ */
150
+ function renderJsonBodyTemplate(bodyTemplate, context) {
151
+ try {
152
+ const parsedTemplate = JSON.parse(bodyTemplate);
153
+ return renderTemplateNode(parsedTemplate, context);
154
+ } catch {
155
+ try {
156
+ return JSON.parse(
157
+ mustache.render(bodyTemplate, {
158
+ ...(context ?? {}),
159
+ }),
160
+ );
161
+ } catch (e) {
162
+ throw new Error(
163
+ `[eodash] Failed to render POST body template: ${e instanceof Error ? e.message : String(e)}`,
164
+ );
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Recursively renders a parsed JSON template node with context values.
171
+ * - Arrays and objects are traversed recursively.
172
+ * - Exact placeholders (where the entire string value is `"{{path}}"` or
173
+ * `"{{{path}}}"`) are resolved to their original typed value (object, array,
174
+ * number, etc.) instead of being stringified. This is critical for embedding
175
+ * GeoJSON geometry objects in POST bodies.
176
+ * - Partial placeholders and mixed strings use standard Mustache interpolation.
177
+ * @param {any} templateNode
178
+ * @param {Record<string, any>} context
179
+ * @returns {any}
180
+ */
181
+ function renderTemplateNode(templateNode, context) {
182
+ if (Array.isArray(templateNode)) {
183
+ return templateNode.map((value) => renderTemplateNode(value, context));
184
+ }
185
+ if (templateNode && typeof templateNode === "object") {
186
+ return Object.fromEntries(
187
+ Object.entries(templateNode).map(([key, value]) => [
188
+ mustache.render(key, context),
189
+ renderTemplateNode(value, context),
190
+ ]),
191
+ );
192
+ }
193
+ if (typeof templateNode !== "string") {
194
+ return templateNode;
195
+ }
196
+
197
+ // Detect exact placeholder: entire value is "{{path}}" or "{{{path}}}".
198
+ // Matches both double-brace {{...}} and triple-brace {{{...}}} syntax.
199
+ // When matched, the resolved value is returned as its original type (e.g. an
200
+ // object), rather than being coerced to a string by Mustache.
201
+ const exactPlaceholder = templateNode.match(
202
+ /^\{\{\{?\s*([^{}]+?)\s*\}?\}\}$/,
203
+ );
204
+ if (exactPlaceholder) {
205
+ const resolvedValue = resolveTemplatePath(context, exactPlaceholder[1]);
206
+ if (resolvedValue !== undefined) {
207
+ return cloneTemplateValue(resolvedValue);
208
+ }
209
+ }
210
+
211
+ return mustache.render(templateNode, context);
212
+ }
213
+
214
+ /**
215
+ * Resolves a dot/bracket-notation path against a context object.
216
+ * Supports paths like `"feature.geometry"` or `"list[0].name"`.
217
+ * @param {Record<string, any>} context
218
+ * @param {string} path
219
+ * @returns {any}
220
+ */
221
+ function resolveTemplatePath(context, path) {
222
+ const segments = path
223
+ .replace(/\[(\d+)\]/g, ".$1")
224
+ .split(".")
225
+ .map((segment) => segment.trim())
226
+ .filter(Boolean);
227
+
228
+ let currentValue = context;
229
+ for (const segment of segments) {
230
+ if (currentValue === null || currentValue === undefined) {
231
+ return undefined;
232
+ }
233
+ currentValue = currentValue[segment];
234
+ }
235
+ return currentValue;
236
+ }
237
+
238
+ /**
239
+ * Deep-clones objects via JSON round-trip to prevent template rendering
240
+ * from mutating the original context values. This also strips non-JSON-
241
+ * serializable properties (e.g. OL Feature methods from drawtools),
242
+ * producing clean plain objects. Primitives pass through.
243
+ * @param {any} value
244
+ * @returns {any}
245
+ */
246
+ function cloneTemplateValue(value) {
247
+ if (value && typeof value === "object") {
248
+ return JSON.parse(JSON.stringify(value));
249
+ }
250
+ return value;
251
+ }
252
+
8
253
  ////// --- CHARTS --- //////
9
254
  /**
10
255
  * @param {object} options
11
256
  * @param {import("stac-ts").StacLink[] | undefined} options.links
12
257
  * @param {Record<string,any> | undefined} options.jsonformValue
258
+ * @param {Record<string,any> | undefined} [options.rawJsonformValue]
13
259
  * @param {string} options.specUrl
14
260
  * @param {(input:import("^/EodashProcess/types").CustomEnpointInput)=>Promise<Record<string,any>[] | undefined | null>} [options.customEndpointsHandler]
15
261
  * @param {import("vue").Ref<boolean>} options.isPolling
@@ -22,6 +268,7 @@ import { toAbsolute } from 'stac-js/src/http.js';
22
268
  async function processCharts({
23
269
  links,
24
270
  jsonformValue,
271
+ rawJsonformValue,
25
272
  specUrl,
26
273
  customEndpointsHandler,
27
274
  jsonformSchema,
@@ -51,6 +298,7 @@ async function processCharts({
51
298
  (await customEndpointsHandler({
52
299
  jsonformSchema,
53
300
  jsonformValue,
301
+ rawJsonformValue,
54
302
  links: endpointLinks,
55
303
  selectedStac,
56
304
  isPolling,
@@ -112,6 +360,7 @@ async function processCharts({
112
360
  await injectVegaInlineData(spec, {
113
361
  url: link.href,
114
362
  jsonformValue: jsonformValue,
363
+ rawJsonformValue,
115
364
  link: link,
116
365
  flatstyleUrl: /** @type string */ (link["eox:flatstyle"]),
117
366
  jsonformSchema,
@@ -138,46 +387,39 @@ async function processCharts({
138
387
  * @param {object} injectables
139
388
  * @param {string} injectables.url
140
389
  * @param {Record<string,any>} [injectables.jsonformValue]
390
+ * @param {Record<string,any>} [injectables.rawJsonformValue]
141
391
  * @param {import("stac-ts").StacLink} injectables.link
142
392
  * @param {url} [injectables.flatstyleUrl]
143
393
  * @param {import("json-schema").JSONSchema7} [injectables.jsonformSchema]
144
394
  */
145
395
  async function injectVegaInlineData(
146
396
  spec,
147
- { url, jsonformValue, link, flatstyleUrl, jsonformSchema },
397
+ { url, jsonformValue, rawJsonformValue, link, flatstyleUrl, jsonformSchema },
148
398
  ) {
149
399
  if (!spec.data) {
150
400
  return;
151
401
  }
402
+ // @ts-expect-error JSONSchema7 is structurally compatible with Record<string, any>
403
+ const multiQueryMatches = getMultiQueryMatches(jsonformValue, jsonformSchema);
152
404
  if (link.method == "GET") {
153
- // we see if any of the multiQuery values match an array in the jsonformValue
154
- // and if so, we can do multiple requests and merge all data together.
155
- //@ts-expect-error type jsonform Schema
156
- const multiQuery = jsonformSchema?.options?.multiQuery;
157
- const matches = Object.keys(jsonformValue ?? {}).filter((key) => {
158
- return Array.isArray(multiQuery)
159
- ? multiQuery.includes(key)
160
- : multiQuery === key;
161
- });
162
- if (matches.length > 0 && jsonformValue) {
405
+ if (multiQueryMatches.length > 0 && jsonformValue) {
163
406
  const dataValues = [];
164
- for (const match of matches) {
165
- if (Array.isArray(jsonformValue[match])) {
166
- for (const value of jsonformValue[match]) {
167
- const dataUrl = await renderDataUrl(
168
- url,
169
- { ...jsonformValue, [match]: value },
170
- flatstyleUrl,
171
- );
172
- dataValues.push(await axios.get(dataUrl).then((resp) => resp.data));
173
- }
174
- } else {
175
- const dataUrl = await renderDataUrl(url, jsonformValue, flatstyleUrl);
407
+ for (const match of multiQueryMatches) {
408
+ const queryValues = getScalarMultiQueryValues(match, jsonformValue);
409
+ if (!queryValues.length) {
410
+ continue;
411
+ }
412
+ for (const value of queryValues) {
413
+ const dataUrl = await renderDataUrl(
414
+ url,
415
+ { ...jsonformValue, [match]: value },
416
+ flatstyleUrl,
417
+ );
176
418
  dataValues.push(await axios.get(dataUrl).then((resp) => resp.data));
177
419
  }
178
420
  }
179
421
  /** @type {import("vega-lite/build/src/data").InlineData} */
180
- (spec.data).values = dataValues.flat();
422
+ (spec.data).values = aggregateInlineResponses(dataValues);
181
423
  return spec;
182
424
  }
183
425
  // if no array matches, we can just do a single request
@@ -201,11 +443,49 @@ async function injectVegaInlineData(
201
443
  .then((resp) => {
202
444
  return resp.data;
203
445
  });
204
- const body = JSON.parse(
205
- mustache.render(bodyTemplate, {
206
- ...(jsonformValue ?? {}),
207
- }),
208
- );
446
+ if (
447
+ // @ts-expect-error jsonform options extension not in JSONSchema7
448
+ jsonformSchema?.options?.multiQuery &&
449
+ multiQueryMatches.length === 0
450
+ ) {
451
+ /** @type {import("vega-lite/build/src/data").InlineData} */
452
+ (spec.data).values = [];
453
+ return spec;
454
+ }
455
+ if (multiQueryMatches.length > 0 && jsonformValue) {
456
+ const responses = [];
457
+ for (const match of multiQueryMatches) {
458
+ const queryValues = getPostMultiQueryValues(
459
+ match,
460
+ jsonformValue,
461
+ rawJsonformValue,
462
+ /** @type {Record<string, any>} */ (jsonformSchema),
463
+ );
464
+ if (!queryValues.length) {
465
+ continue;
466
+ }
467
+ // Sequential POSTs (not parallel) to avoid rate-limiting on endpoints
468
+ // like TiTiler-PgSTAC. For small selection counts this is fine; if
469
+ // parallelism is needed later, switch to Promise.all.
470
+ for (const [index, value] of queryValues.entries()) {
471
+ const requestContext = createMultiQueryContext(
472
+ jsonformValue,
473
+ match,
474
+ value,
475
+ index,
476
+ );
477
+ const body = renderJsonBodyTemplate(bodyTemplate, requestContext);
478
+ responses.push(await axios.post(url, body).then((resp) => resp.data));
479
+ }
480
+ }
481
+ /** @type {import("vega-lite/build/src/data").InlineData} */
482
+ (spec.data).values = aggregateInlineResponses(responses);
483
+ return spec;
484
+ }
485
+
486
+ const body = renderJsonBodyTemplate(bodyTemplate, {
487
+ ...(jsonformValue ?? {}),
488
+ });
209
489
  /** @type {import("vega-lite/build/src/data").InlineData} */
210
490
  (spec.data).values = await axios.post(url, body).then((resp) => {
211
491
  return resp.data;
@@ -214,6 +494,8 @@ async function injectVegaInlineData(
214
494
  return spec;
215
495
  }
216
496
 
497
+ // MultiQuery helpers and template rendering are in ./template-helpers.js
498
+
217
499
  /**
218
500
  * @param {import("vega-lite").TopLevelSpec} spec
219
501
  * @param {object} injectables
@@ -1005,7 +1287,6 @@ function createCustomChartEndpointsHandler(callbacks) {
1005
1287
  * @async
1006
1288
  * @param {Object} params
1007
1289
  * @param {import("vue").Ref<import("stac-ts").StacCollection | null>} params.selectedStac
1008
- * @param {import("vue").Ref<import("@eox/jsonform").EOxJSONForm | null>} params.jsonformEl
1009
1290
  * @param {import("vue").Ref<Record<string,any> | null>} params.jsonformSchema
1010
1291
  * @param {import("vue").Ref<any[]>} params.processResults
1011
1292
  * @param {import("vue").Ref<boolean>} params.isProcessed
@@ -1015,7 +1296,6 @@ function createCustomChartEndpointsHandler(callbacks) {
1015
1296
  */
1016
1297
  async function initProcess({
1017
1298
  selectedStac,
1018
- jsonformEl,
1019
1299
  jsonformSchema,
1020
1300
  isProcessed,
1021
1301
  processResults,
@@ -1026,10 +1306,10 @@ async function initProcess({
1026
1306
  const isPoiAlive = enableCompare ? !!comparePoi.value : !!poi.value;
1027
1307
  let updatedJsonform = null;
1028
1308
  if (selectedStac.value?.["eodash:jsonform"]) {
1029
- updatedJsonform = await axios
1309
+ updatedJsonform = await fetch(
1030
1310
  //@ts-expect-error eodash extention
1031
- .get(selectedStac.value["eodash:jsonform"])
1032
- .then((resp) => resp.data);
1311
+ selectedStac.value["eodash:jsonform"],
1312
+ ).then((resp) => resp.json());
1033
1313
  }
1034
1314
 
1035
1315
  if (!updatedJsonform && isPoiAlive) {
@@ -1045,17 +1325,13 @@ async function initProcess({
1045
1325
  enableCompare,
1046
1326
  });
1047
1327
 
1048
- await jsonformEl.value?.editor.destroy();
1049
1328
  if (updatedJsonform) {
1050
1329
  // make sure correct target layer id is used in jsonform
1051
- let newJsonForm = null;
1052
- newJsonForm = await updateJsonformIdentifier({
1330
+ const newJsonForm = await updateJsonformIdentifier({
1053
1331
  jsonformSchema: updatedJsonform,
1054
- newLayers: getLayers(),
1332
+ newLayers: enableCompare ? getCompareLayers() : getLayers(),
1333
+ enableCompare,
1055
1334
  });
1056
- if (enableCompare) {
1057
- newJsonForm = updateJsonformSchemaTarget(newJsonForm);
1058
- }
1059
1335
  // trigger jsonform update in next tick
1060
1336
  jsonformSchema.value = null;
1061
1337
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -1071,62 +1347,76 @@ async function initProcess({
1071
1347
  * @param {Object} params
1072
1348
  * @param {Record<string,any> | null} params.jsonformSchema params.jsonformSchema
1073
1349
  * @param {Record<string, any>[] | undefined} params.newLayers params.newLayers
1074
- * @returns {Promise<Record<string,any> | null | undefined>} updated jsonform schema
1350
+ * @param { boolean } params.enableCompare params.enableCompare
1351
+ * @returns {Promise<Record<string,any> | null>} updated jsonform schema
1075
1352
  */
1076
- async function updateJsonformIdentifier({ jsonformSchema, newLayers }) {
1353
+ async function updateJsonformIdentifier({
1354
+ jsonformSchema,
1355
+ newLayers,
1356
+ enableCompare,
1357
+ }) {
1077
1358
  const form = jsonformSchema;
1078
1359
  if (!form) {
1079
- return;
1360
+ return null;
1080
1361
  }
1081
- const drawToolsProperty = getDrawToolsProperty(form);
1082
- if (
1083
- drawToolsProperty &&
1084
- newLayers &&
1085
- form?.properties[drawToolsProperty]?.options?.drawtools?.layerId
1086
- ) {
1087
- // get partial or full id and try to match with correct eoxmap layer
1088
- // check if newLayers is an array or an object with layers property
1089
- let layers = newLayers;
1090
- // @ts-expect-error TODO payload coming from time update sometimes is not an object with layers property
1091
- if (newLayers.layers && Array.isArray(newLayers.layers)) {
1092
- // @ts-expect-error TODO payload coming from time update sometimes is not an object with layers property
1093
- layers = newLayers.layers;
1362
+ const drawToolsProperties = getDrawToolsProperties(form);
1363
+ drawToolsProperties.forEach((drawToolsProperty) => {
1364
+ if (
1365
+ drawToolsProperty &&
1366
+ form?.properties[drawToolsProperty]?.options?.drawtools?.for &&
1367
+ enableCompare
1368
+ ) {
1369
+ form.properties[drawToolsProperty].options.drawtools.for =
1370
+ "eox-map#compare";
1094
1371
  }
1095
-
1096
- const layerId =
1097
- form.properties[drawToolsProperty].options.drawtools.layerId.split(
1098
- ";:;",
1099
- )[0];
1100
- let matchedLayerId = null;
1101
- // layers are not flat can be grouped, we need to recursively search
1102
- const traverseLayers = (
1103
- /** @type {Record<string, any>[] | undefined} */ layersArray,
1104
- ) => {
1105
- if (!layersArray) {
1106
- return;
1372
+ if (
1373
+ drawToolsProperty &&
1374
+ newLayers &&
1375
+ form?.properties[drawToolsProperty]?.options?.drawtools?.layerId
1376
+ ) {
1377
+ // get partial or full id and try to match with correct eoxmap layer
1378
+ // check if newLayers is an array or an object with layers property
1379
+ let layers = newLayers;
1380
+ // @ts-expect-error TODO payload coming from time update sometimes is not an object with layers property
1381
+ if (newLayers.layers && Array.isArray(newLayers.layers)) {
1382
+ // @ts-expect-error TODO payload coming from time update sometimes is not an object with layers property
1383
+ layers = newLayers.layers;
1107
1384
  }
1108
- for (const layer of layersArray) {
1109
- if (layer.type === "Group" && Array.isArray(layer.layers)) {
1110
- traverseLayers(layer.layers);
1111
- } else {
1112
- if (layer.properties?.id?.startsWith(layerId)) {
1113
- matchedLayerId = layer.properties.id;
1114
- break;
1385
+
1386
+ const layerId =
1387
+ form.properties[drawToolsProperty].options.drawtools.layerId.split(
1388
+ ";:;",
1389
+ )[0];
1390
+ let matchedLayerId = null;
1391
+ // layers are not flat can be grouped, we need to recursively search
1392
+ const traverseLayers = (
1393
+ /** @type {Record<string, any>[] | undefined} */ layersArray,
1394
+ ) => {
1395
+ if (!layersArray) {
1396
+ return;
1397
+ }
1398
+ for (const layer of layersArray) {
1399
+ if (layer.type === "Group" && Array.isArray(layer.layers)) {
1400
+ traverseLayers(layer.layers);
1401
+ } else {
1402
+ if (layer.properties?.id?.startsWith(layerId)) {
1403
+ matchedLayerId = layer.properties.id;
1404
+ break;
1405
+ }
1115
1406
  }
1116
1407
  }
1408
+ };
1409
+ traverseLayers(layers);
1410
+ if (matchedLayerId) {
1411
+ form.properties[drawToolsProperty].options.drawtools.layerId =
1412
+ matchedLayerId;
1413
+ } else {
1414
+ console.warn(
1415
+ `Could not find matching layer for processing form with id: ${layerId}`,
1416
+ );
1117
1417
  }
1118
- };
1119
- traverseLayers(layers);
1120
- if (matchedLayerId) {
1121
- form.properties.feature.options.drawtools.layerId = matchedLayerId;
1122
- return form;
1123
- } else {
1124
- console.warn(
1125
- `Could not find matching layer for processing form with id: ${layerId}`,
1126
- );
1127
- return null;
1128
1418
  }
1129
- }
1419
+ });
1130
1420
  return form;
1131
1421
  }
1132
1422
 
@@ -1165,6 +1455,15 @@ async function handleProcesses({
1165
1455
  );
1166
1456
 
1167
1457
  const bboxProperty = getBboxProperty(jsonformSchema.value);
1458
+ // Preserve raw form value before extractGeometries mutates it.
1459
+ // Needed so POST multiQuery can iterate original GeoJSON Feature objects.
1460
+ // Uses JSON round-trip instead of structuredClone to handle OL Feature
1461
+ // objects from drawtools (which contain non-cloneable methods).
1462
+ const rawJsonformValue = JSON.parse(
1463
+ JSON.stringify(
1464
+ /** @type {Record<string, any>} */ (jsonformEl.value?.value ?? {}),
1465
+ ),
1466
+ );
1168
1467
  const jsonformValue = /** @type {Record<string,any>} */ (
1169
1468
  jsonformEl.value?.value
1170
1469
  );
@@ -1183,6 +1482,7 @@ async function handleProcesses({
1183
1482
  [tempChartSpec, usedChartData.value] = await processCharts({
1184
1483
  links: serviceLinks,
1185
1484
  jsonformValue: { ...(jsonformValue ?? {}) },
1485
+ rawJsonformValue,
1186
1486
  jsonformSchema: jsonformSchema.value,
1187
1487
  enableCompare,
1188
1488
  selectedStac: selectedStac.value,
@@ -4611,11 +4611,11 @@ const extractLayerTimeValues = (items, currentStep) => {
4611
4611
  const findLayersByLayerPrefix = (layers, referenceLayer) => {
4612
4612
  const refId = referenceLayer?.properties?.id;
4613
4613
 
4614
- if (typeof refId !== "string" || !refId.includes(";")) {
4615
- throw new Error("Reference layer ID must contain a ';' separator.");
4614
+ if (typeof refId !== "string" || !refId.includes(";:;")) {
4615
+ throw new Error("Reference layer ID must contain a ';:;' separator.");
4616
4616
  }
4617
4617
 
4618
- const prefix = refId.split(";")[0];
4618
+ const prefix = refId.split(";:;")[0];
4619
4619
  const matches = [];
4620
4620
 
4621
4621
  for (const layer of layers) {
@@ -4623,7 +4623,7 @@ const findLayersByLayerPrefix = (layers, referenceLayer) => {
4623
4623
  matches.push(...findLayersByLayerPrefix(layer.layers, referenceLayer));
4624
4624
  } else {
4625
4625
  const id = layer?.properties?.id;
4626
- if (typeof id === "string" && id.startsWith(prefix)) {
4626
+ if (typeof id === "string" && id.split(";:;")[0] === prefix) {
4627
4627
  matches.push(layer);
4628
4628
  }
4629
4629
  }
@@ -1,13 +1,13 @@
1
1
  import { onMounted, onUnmounted, watch, computed, withAsyncContext, useTemplateRef, ref, toRaw, openBlock, createElementBlock, createElementVNode, unref, normalizeStyle, createCommentVNode, createVNode } from 'vue';
2
2
  import { transformExtent } from '@eox/map';
3
3
  import '@eox/map/src/plugins/advancedLayersAndSources';
4
- import { U as setMapProjFromCol, E as mapPosition, R as sanitizeBbox, i as mapEl, S as mapCompareEl, F as indicator, I as compareIndicator, C as poi, V as isGlobe, B as datetime } from './helpers-CZFH0EVN.js';
4
+ import { U as setMapProjFromCol, E as mapPosition, R as sanitizeBbox, i as mapEl, S as mapCompareEl, F as indicator, I as compareIndicator, C as poi, V as isGlobe, B as datetime } from './helpers-DAVGYl_f.js';
5
5
  import { storeToRefs } from 'pinia';
6
- import { R as EodashCollection, H as useSTAcStore, S as useEmitLayersUpdate, T as isFirstLoad, N as useOnLayersUpdate, _ as _export_sfc, G as useDisplay, a as useLayout, Q as layerControlFormValue, P as layerControlFormValueCompare, M as eodashCompareCollections, L as eodashCollections } from './asWebComponent-DDt7V3Bl.js';
6
+ import { R as EodashCollection, H as useSTAcStore, S as useEmitLayersUpdate, T as isFirstLoad, N as useOnLayersUpdate, _ as _export_sfc, G as useDisplay, a as useLayout, Q as layerControlFormValue, P as layerControlFormValueCompare, M as eodashCompareCollections, L as eodashCollections } from './asWebComponent-DfULAbpq.js';
7
7
  import log from 'loglevel';
8
8
  import { i as inAndOut } from './easing-CH0-9wR8.js';
9
9
  import mustache from 'mustache';
10
- import EodashMapBtns from './EodashMapBtns-BJSh7qT-.js';
10
+ import EodashMapBtns from './EodashMapBtns-BHrSDNoC.js';
11
11
 
12
12
  /**
13
13
  * Creates full layer configuration from indicator and time information