@eodash/eodash 5.7.0 → 5.8.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 (119) hide show
  1. package/core/client/components/EodashOverlay.vue +27 -4
  2. package/core/client/composables/DefineEodash.js +0 -1
  3. package/core/client/composables/DefineWidgets.js +3 -1
  4. package/core/client/eodashSTAC/EodashCollection.js +87 -24
  5. package/core/client/eodashSTAC/cql.js +64 -0
  6. package/core/client/eodashSTAC/createLayers.js +353 -187
  7. package/core/client/eodashSTAC/helpers.js +694 -110
  8. package/core/client/eodashSTAC/mosaic.js +422 -0
  9. package/core/client/plugins/axios.js +31 -1
  10. package/core/client/store/actions.js +1 -1
  11. package/core/client/store/stac.js +54 -4
  12. package/core/client/store/states.js +20 -2
  13. package/core/client/types.ts +152 -28
  14. package/core/client/utils/bands-editor/exampleSchema.json +50 -50
  15. package/core/client/utils/states.js +10 -0
  16. package/dist/client/{DashboardLayout-kRsmsNW5.js → DashboardLayout-DEs4f_Vy.js} +2 -2
  17. package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-PvnV88BA.js} +1 -1
  18. package/dist/client/EodashChart-D-3AGKZY.js +260 -0
  19. package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-Dm_goZzs.js} +3 -3
  20. package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-CJy3zYzD.js} +2 -1
  21. package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-rL8UnpkA.js} +55 -33
  22. package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
  23. package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-CG7u2Aq8.js} +227 -112
  24. package/dist/client/EodashStacInfo-CJhhxthD.js +169 -0
  25. package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-CWyK8QJT.js} +4 -4
  26. package/dist/client/{ExportState-DbOm8hBI.js → ExportState-BXhy5TOU.js} +20 -174
  27. package/dist/client/{Footer-BKg-VVkj.js → Footer-DZPvnhcT.js} +1 -1
  28. package/dist/client/{Header-DCrwCvmY.js → Header-CiGsDE_a.js} +2 -2
  29. package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BJHW7x5V.js} +2 -2
  30. package/dist/client/{PopUp-9uimfK49.js → PopUp-7_XnhvCc.js} +4 -168
  31. package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-LL_UCbvc.js} +5 -5
  32. package/dist/client/VDivider-DWYM0WUr.js +168 -0
  33. package/dist/client/{VImg-CrmB76ZY.js → VImg-B75dgw2K.js} +2 -2
  34. package/dist/client/{VMain-OKZNpcEG.js → VMain-DvMmOL4f.js} +1 -1
  35. package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-ClWHUmWL.js} +2 -2
  36. package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-BqaWs4ch.js} +1 -1
  37. package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-IZanwL-A.js} +677 -379
  38. package/dist/client/{async-DSKj6g1f.js → async-CiNIOBVz.js} +31 -29
  39. package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-3S3y_kZg.js} +17 -17
  40. package/dist/client/dialog-transition-c4ZSnzac.js +170 -0
  41. package/dist/client/eo-dash.js +1 -1
  42. package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-B-c1_6yo.js} +8 -87
  43. package/dist/client/{handling-BrcDDlgS.js → handling-NvY8Csry.js} +3 -4
  44. package/dist/client/{helpers-DjZVl_WF.js → helpers-BFuh19CP.js} +1094 -194
  45. package/dist/client/{index-1JKE68n3.js → index-1wMAyuNL.js} +219 -67
  46. package/dist/client/{index-CSG_JoOA.js → index-B8jGJSTA.js} +62 -56
  47. package/dist/client/{index-Cu3-13Xg.js → index-BKQQJkhy.js} +167 -61
  48. package/dist/client/index-D0jUyol8.js +3167 -0
  49. package/dist/client/{index-CUjl-ZsI.js → index-KGxz5Egv.js} +2 -2
  50. package/dist/client/mosaic-vHvSuih6.js +459 -0
  51. package/dist/client/templates.js +251 -121
  52. package/dist/client/{transition-OHcz5Qq1.js → transition-BkyUsZh_.js} +1 -1
  53. package/dist/node/cli.js +5 -5
  54. package/dist/node/types.d.ts +4 -0
  55. package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
  56. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +57 -25
  57. package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
  58. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +42 -23
  59. package/dist/types/core/client/eodashSTAC/helpers.d.ts +118 -19
  60. package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
  61. package/dist/types/core/client/store/stac.d.ts +38 -8
  62. package/dist/types/core/client/store/states.d.ts +24 -2
  63. package/dist/types/core/client/types.d.ts +128 -24
  64. package/dist/types/core/client/utils/states.d.ts +12 -0
  65. package/dist/types/templates/baseConfig.d.ts +1 -100
  66. package/dist/types/templates/compare.d.ts +32 -3
  67. package/dist/types/templates/expert.d.ts +18 -0
  68. package/dist/types/templates/explore.d.ts +10 -1
  69. package/dist/types/templates/lite.d.ts +11 -0
  70. package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
  71. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
  72. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
  73. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
  74. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
  75. package/dist/types/widgets/EodashLayerControl.vue.d.ts +35 -16
  76. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +14 -10
  77. package/dist/types/widgets/EodashMap/index.vue.d.ts +86 -37
  78. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  79. package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
  80. package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
  81. package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
  82. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  83. package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
  84. package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
  85. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
  86. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
  87. package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
  88. package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
  89. package/package.json +34 -34
  90. package/templates/baseConfig.js +37 -6
  91. package/templates/compare.js +61 -7
  92. package/templates/expert.js +24 -2
  93. package/templates/explore.js +12 -5
  94. package/templates/lite.js +13 -0
  95. package/widgets/EodashChart.vue +162 -21
  96. package/widgets/EodashItemCatalog/index.vue +363 -35
  97. package/widgets/EodashItemCatalog/methods/filters.js +134 -70
  98. package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
  99. package/widgets/EodashItemCatalog/methods/map.js +141 -28
  100. package/widgets/EodashLayerControl.vue +51 -25
  101. package/widgets/EodashMap/EodashMapBtns.vue +109 -48
  102. package/widgets/EodashMap/index.vue +91 -105
  103. package/widgets/EodashMap/methods/btns.js +64 -31
  104. package/widgets/EodashMap/methods/create-layers-config.js +16 -16
  105. package/widgets/EodashMap/methods/index.js +71 -8
  106. package/widgets/EodashMap/methods/use-map-loading.js +58 -0
  107. package/widgets/EodashProcess/index.vue +136 -16
  108. package/widgets/EodashProcess/methods/composables.js +13 -0
  109. package/widgets/EodashProcess/methods/outputs.js +0 -1
  110. package/widgets/EodashProcess/methods/utils.js +29 -27
  111. package/widgets/EodashProcess/states.js +1 -1
  112. package/widgets/EodashStacInfo.vue +64 -2
  113. package/widgets/EodashTimeSlider/index.vue +58 -2
  114. package/widgets/EodashTimeSlider/methods/index.js +5 -48
  115. package/widgets/EodashTimeSlider/types.ts +7 -17
  116. package/widgets/ExportState.vue +13 -4
  117. package/dist/client/EodashChart-CLDqE93C.js +0 -144
  118. package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
  119. package/dist/client/index-5Jhsa8Xe.js +0 -570
@@ -6,9 +6,12 @@ import { useSTAcStore } from "@/store/stac";
6
6
  import { storeToRefs } from "pinia";
7
7
  import { isFirstLoad } from "@/utils/states";
8
8
  import { useEmitLayersUpdate, useOnLayersUpdate } from "@/composables";
9
- import { mapPosition } from "@/store/states";
9
+ export { useEmitLayersUpdate, useOnLayersUpdate };
10
+ import { isGlobe, mapPosition } from "@/store/states";
10
11
  import { sanitizeBbox } from "@/eodashSTAC/helpers";
11
12
  import { transformExtent } from "@eox/map";
13
+
14
+ export { useMapLoading } from "./use-map-loading";
12
15
  /**
13
16
  * Holder for previous compare map view as it is overwritten by sync
14
17
  * @type { import("ol").View | null} mapElement
@@ -39,14 +42,52 @@ export const useHandleMapMoveEnd = (mapElement, mapPosition) => {
39
42
  }
40
43
  };
41
44
 
45
+ const handleGlobeMoveEnd = () => {
46
+ const camera = mapElement.value?.globe?.planet?.camera;
47
+ const lonLat = camera?.getLonLat();
48
+ if (!lonLat) return;
49
+ const { lon, lat, height } = lonLat;
50
+ if (![lon, lat, height].some(Number.isNaN)) {
51
+ mapPosition.value = [lon, lat, height];
52
+ }
53
+ };
54
+
55
+ /** @type {{ events: { off: Function } } | null} */
56
+ let subscribedCamera = null;
57
+
58
+ const subscribeGlobe = (retries = 3) => {
59
+ if (!isGlobe.value) return; // toggled back out during retry
60
+ const camera = mapElement.value?.globe?.planet?.camera;
61
+ if (camera) {
62
+ if (subscribedCamera === camera) return;
63
+ camera.events.on("moveend", handleGlobeMoveEnd);
64
+ subscribedCamera = camera;
65
+ handleGlobeMoveEnd();
66
+ return;
67
+ }
68
+ if (retries > 0) setTimeout(() => subscribeGlobe(retries - 1), 50);
69
+ };
70
+ const unsubscribeGlobe = () => {
71
+ subscribedCamera?.events.off("moveend", handleGlobeMoveEnd);
72
+ subscribedCamera = null;
73
+ };
74
+
75
+ const stopGlobeWatch = watch(isGlobe, (globe) =>
76
+ globe ? subscribeGlobe() : unsubscribeGlobe(),
77
+ );
78
+
42
79
  onMounted(() => {
43
- /** @type {import('ol/Map').default} */
44
- (mapElement.value?.map)?.on("moveend", handleMoveEnd);
80
+ const map = mapElement.value?.map;
81
+ map?.on("moveend", handleMoveEnd);
82
+ // Seed mapPosition from the initial view
83
+ handleMoveEnd(/** @type {any} */ ({ map }));
45
84
  });
46
85
 
47
86
  onUnmounted(() => {
48
87
  /** @type {import('ol/Map').default} */
49
88
  (mapElement.value?.map)?.un("moveend", handleMoveEnd);
89
+ unsubscribeGlobe();
90
+ stopGlobeWatch();
50
91
  });
51
92
  };
52
93
 
@@ -61,6 +102,7 @@ export const useHandleMapMoveEnd = (mapElement, mapPosition) => {
61
102
  * @param {import("vue").Ref<import("@eox/map").EOxMap| null>} partnerMap
62
103
  * @param {boolean} zoomToExtent
63
104
  * @param {import("vue").Ref<import("stac-ts").StacItem | import("stac-ts").StacLink | null>} [selectedItem]
105
+ * @param {Record<string, any>[]} [defaultBaseLayers]
64
106
  */
65
107
  export const useInitMap = (
66
108
  mapElement,
@@ -71,6 +113,7 @@ export const useInitMap = (
71
113
  partnerMap,
72
114
  zoomToExtent,
73
115
  selectedItem,
116
+ defaultBaseLayers,
74
117
  ) => {
75
118
  log.debug(
76
119
  "InitMap",
@@ -100,12 +143,24 @@ export const useInitMap = (
100
143
  );
101
144
 
102
145
  if (updatedStac) {
103
- if (
146
+ const isSameStac = updatedStac?.id === previousStac?.id;
147
+ const isSameItem = updatedItem?.id === previousItem?.id;
148
+
149
+ // Item deselect: overview render is owned by the item catalog widget.
150
+ const isItemDeselect =
151
+ previousItem &&
152
+ !updatedItem &&
153
+ isSameStac &&
154
+ updatedTime === previousTime;
155
+ if (isItemDeselect) return;
156
+
157
+ // Re-fire from our own datetime.value write, skip and clear
158
+ const isOwnDatetimeUpdate =
104
159
  internalDatetime !== null &&
105
160
  updatedTime === internalDatetime &&
106
- updatedStac?.id === previousStac?.id &&
107
- updatedItem?.id === previousItem?.id
108
- ) {
161
+ isSameStac &&
162
+ isSameItem;
163
+ if (isOwnDatetimeUpdate) {
109
164
  internalDatetime = null;
110
165
  return;
111
166
  }
@@ -153,7 +208,9 @@ export const useInitMap = (
153
208
  updatedStac,
154
209
  eodashCols,
155
210
  updatedItem ?? updatedTime,
211
+ defaultBaseLayers,
156
212
  );
213
+
157
214
  log.debug(
158
215
  "Assigned layers after changing time only",
159
216
  JSON.parse(JSON.stringify(layersCollection)),
@@ -175,6 +232,11 @@ export const useInitMap = (
175
232
  if (interval && interval.length > 0 && interval[0].length > 1) {
176
233
  // @ts-expect-error this is the defined STAC structure
177
234
  endInterval = new Date(interval[0][1]);
235
+ // If end interval is in the future, set to now,
236
+ // for item fetching based on search endpoint
237
+ if (endInterval.getTime() > Date.now()) {
238
+ endInterval = new Date();
239
+ }
178
240
  log.debug(
179
241
  "Indicator load: found stac extent, setting time to latest value",
180
242
  endInterval,
@@ -200,7 +262,8 @@ export const useInitMap = (
200
262
  layersCollection = await createLayersConfig(
201
263
  updatedStac,
202
264
  eodashCols,
203
- resolvedTime,
265
+ updatedItem ?? resolvedTime,
266
+ defaultBaseLayers,
204
267
  );
205
268
 
206
269
  if (zoomToExtent) {
@@ -0,0 +1,58 @@
1
+ import { onUnmounted, watch } from "vue";
2
+ import { loading } from "@/store/states";
3
+
4
+ /**
5
+ * Tracks map loading state at the source level via `loading.activeLoads`,
6
+ * the same counter used by the axios plugin. Re-attaches source listeners
7
+ * whenever eox-map fires `layerschanged`, using `getFlatLayersArray`.
8
+ *
9
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
10
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} compareMapElement
11
+ */
12
+ export const useMapLoading = (mapElement, compareMapElement) => {
13
+ // This instance's outstanding loads, so unmount subtracts only its share.
14
+ let localLoads = 0;
15
+ const startHandler = () => {
16
+ localLoads++;
17
+ loading.activeLoads++;
18
+ };
19
+ const endHandler = () => {
20
+ localLoads = Math.max(0, localLoads - 1);
21
+ loading.activeLoads = Math.max(0, loading.activeLoads - 1);
22
+ };
23
+
24
+ /** @param {import("@eox/map").EOxMap | null} newMapEl */
25
+ const watcherHandler = (newMapEl) => {
26
+ if (!newMapEl) return;
27
+
28
+ const hasStartListener = newMapEl.map
29
+ .getListeners("loadstart")
30
+ ?.some((listner) => listner === startHandler);
31
+ const hasEndListener = newMapEl.map
32
+ .getListeners("loadend")
33
+ ?.some((listner) => listner === endHandler);
34
+
35
+ if (!hasStartListener) {
36
+ newMapEl.map.addEventListener("loadstart", startHandler);
37
+ }
38
+ if (!hasEndListener) {
39
+ newMapEl.map.addEventListener("loadend", endHandler);
40
+ }
41
+ };
42
+ const stopMainWatcher = watch(mapElement, watcherHandler, {
43
+ immediate: true,
44
+ });
45
+ const stopCompareWatcher = watch(compareMapElement, watcherHandler, {
46
+ immediate: true,
47
+ });
48
+
49
+ onUnmounted(() => {
50
+ mapElement.value?.map.removeEventListener("loadstart", startHandler);
51
+ mapElement.value?.map.removeEventListener("loadend", endHandler);
52
+ compareMapElement.value?.map.removeEventListener("loadstart", startHandler);
53
+ compareMapElement.value?.map.removeEventListener("loadend", endHandler);
54
+ stopMainWatcher();
55
+ stopCompareWatcher();
56
+ loading.activeLoads = Math.max(0, loading.activeLoads - localLoads);
57
+ });
58
+ };
@@ -1,19 +1,24 @@
1
1
  <template>
2
- <div ref="container" class="py-1">
3
- <ProcessList :map-element="mapElement" :enable-compare="enableCompare" />
4
- <eox-jsonform
5
- v-if="jsonformSchema"
6
- :key="jsonformKey"
7
- ref="jsonformEl"
8
- .schema="jsonformSchema"
9
- ></eox-jsonform>
10
- <EodashChart
11
- v-if="!areChartsSeparateLayout"
12
- :vega-embed-options="vegaEmbedOptions"
13
- :enable-compare="enableCompare"
2
+ <div ref="container" class="eodash-process-container">
3
+ <div class="eodash-process-content">
4
+ <ProcessList :map-element="mapElement" :enable-compare="enableCompare" />
5
+ <eox-jsonform
6
+ v-if="jsonformSchema"
7
+ :key="jsonformKey"
8
+ ref="jsonformEl"
9
+ .schema="jsonformSchema"
10
+ ></eox-jsonform>
11
+ <EodashChart
12
+ v-if="!areChartsSeparateLayout"
13
+ :vega-embed-options="vegaEmbedOptions"
14
+ :enable-compare="enableCompare"
15
+ >
16
+ </EodashChart>
17
+ </div>
18
+ <div
19
+ class="eodash-process-actions"
20
+ v-if="showExecBtn || (processResults.length && isProcessed && !isAsync)"
14
21
  >
15
- </EodashChart>
16
- <div class="text-right">
17
22
  <v-btn
18
23
  v-if="showExecBtn"
19
24
  :loading="loading"
@@ -45,7 +50,7 @@ import "@eox/drawtools";
45
50
  import "@eox/jsonform";
46
51
  import { useSTAcStore } from "@/store/stac";
47
52
  import { storeToRefs } from "pinia";
48
- import { computed, ref, useTemplateRef } from "vue";
53
+ import { computed, ref, useTemplateRef, watch } from "vue";
49
54
  import ProcessList from "./ProcessList.vue";
50
55
  import EodashChart from "../EodashChart.vue";
51
56
  import { handleProcesses } from "./methods/handling";
@@ -70,6 +75,7 @@ const { enableCompare, vegaEmbedOptions } = defineProps({
70
75
  default: false,
71
76
  },
72
77
  vegaEmbedOptions: {
78
+ /** @type {import("vue").PropType<import("vega-embed").EmbedOptions>} */
73
79
  type: Object,
74
80
  default() {
75
81
  return { actions: true };
@@ -87,6 +93,90 @@ const jsonformEl =
87
93
  useTemplateRef("jsonformEl")
88
94
  );
89
95
 
96
+ // Inject custom styles into the eox-jsonform shadow DOM to make eox-drawtools inline
97
+ watch(jsonformEl, (el) => {
98
+ if (el && el.shadowRoot) {
99
+ const styleId = "eodash-drawtools-inline-style";
100
+ if (!el.shadowRoot.getElementById(styleId)) {
101
+ const style = document.createElement("style");
102
+ style.id = styleId;
103
+ style.textContent = `
104
+ /* Compact standard form elements */
105
+ .form-control, .form-group {
106
+ margin-bottom: 8px !important;
107
+ }
108
+ .form-control > label, .form-group > label {
109
+ margin-bottom: 2px !important;
110
+ font-size: 0.9em;
111
+ }
112
+
113
+ /* Specific layout for drawtools */
114
+ .form-control:has(eox-drawtools) {
115
+ position: relative;
116
+ padding: 8px 12px !important;
117
+ border: none !important;
118
+ background: transparent !important;
119
+ margin-bottom: 8px !important;
120
+ }
121
+ .form-control:has(eox-drawtools) > label {
122
+ position: absolute;
123
+ left: 12px;
124
+ top: 8px;
125
+ margin: 0 !important;
126
+ width: calc(100% - 180px); /* Give label maximum available width */
127
+ line-height: 1.2;
128
+ display: flex;
129
+ align-items: flex-start;
130
+ padding-top: 8px;
131
+ pointer-events: none; /* Let clicks pass through to buttons if they overlap slightly */
132
+ }
133
+ .form-control:has(eox-drawtools) > eox-drawtools {
134
+ display: block;
135
+ width: 100%;
136
+ }
137
+ `;
138
+ el.shadowRoot.appendChild(style);
139
+ }
140
+
141
+ const injectDrawtoolsStyle = () => {
142
+ const drawtools = el?.shadowRoot?.querySelector("eox-drawtools");
143
+ if (drawtools && drawtools.shadowRoot) {
144
+ if (
145
+ !drawtools.shadowRoot.getElementById("eodash-drawtools-indent-style")
146
+ ) {
147
+ const dtStyle = document.createElement("style");
148
+ dtStyle.id = "eodash-drawtools-indent-style";
149
+ dtStyle.textContent = `
150
+ eox-drawtools-controller {
151
+ display: flex;
152
+ justify-content: flex-end; /* Push buttons to the right */
153
+ min-height: 40px;
154
+ width: 100%;
155
+ }
156
+ eox-drawtools-list {
157
+ display: block;
158
+ margin-top: 10px;
159
+ width: 100%;
160
+ }
161
+ `;
162
+ drawtools.shadowRoot.appendChild(dtStyle);
163
+ return true;
164
+ }
165
+ }
166
+ return false;
167
+ };
168
+
169
+ if (!injectDrawtoolsStyle()) {
170
+ const observer = new MutationObserver(() => {
171
+ if (injectDrawtoolsStyle()) {
172
+ observer.disconnect();
173
+ }
174
+ });
175
+ observer.observe(el.shadowRoot, { childList: true, subtree: true });
176
+ }
177
+ }
178
+ });
179
+
90
180
  const isAsync = computed(
91
181
  () =>
92
182
  selectedStac.value?.links.filter((l) => l.endpoint === "eoxhub_workspaces")
@@ -188,7 +278,37 @@ useAutoExec(autoExec, jsonformEl, jsonformSchema, startProcess);
188
278
  </script>
189
279
  <style>
190
280
  eox-jsonform {
191
- padding: 0.7em;
281
+ padding: 0 12px;
192
282
  min-height: 0px;
283
+ flex-shrink: 0;
284
+ }
285
+
286
+ /* Force the specific panel wrapping this component to utilize 100% height */
287
+ .bg-surface:has(.eodash-process-container) {
288
+ height: calc(100% - 30px);
289
+ overflow: hidden;
290
+ }
291
+
292
+ /* Make sure this container takes up full height and acts as a flex column */
293
+ .eodash-process-container {
294
+ height: 100%;
295
+ display: flex;
296
+ flex-direction: column;
297
+ overflow: hidden;
298
+ }
299
+
300
+ .eodash-process-content {
301
+ flex-grow: 1;
302
+ overflow-y: auto;
303
+ display: flex;
304
+ flex-direction: column;
305
+ }
306
+
307
+ .eodash-process-actions {
308
+ text-align: right;
309
+ padding: 4px 12px;
310
+ flex-shrink: 0;
311
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
312
+ background: inherit;
193
313
  }
194
314
  </style>
@@ -59,6 +59,19 @@ export const useInitProcess = ({
59
59
  });
60
60
 
61
61
  if (newJsonForm) {
62
+ // Layer rebuilds strip drawtools' selection interactions; remount
63
+ // the form so initSelection re-attaches them.
64
+ const selectionStripped =
65
+ Object.values(newJsonForm.properties ?? {}).some(
66
+ (p) => p?.options?.drawtools?.layerId,
67
+ ) &&
68
+ !mapElement.value?.selectInteractions?.[
69
+ "SelectLayerClickInteraction"
70
+ ];
71
+ if (selectionStripped) {
72
+ jsonformSchema.value = null;
73
+ await nextTick();
74
+ }
62
75
  jsonformSchema.value = newJsonForm;
63
76
  }
64
77
  }
@@ -421,7 +421,6 @@ export async function processVector(links, jsonformValue, layerId) {
421
421
  type: "Vector",
422
422
  source: {
423
423
  type: "Vector",
424
- //@ts-expect-error TODO
425
424
  url: mustache.render(link.href, {
426
425
  ...(jsonformValue ?? {}),
427
426
  }),
@@ -89,10 +89,10 @@ export async function createTiffLayerDefinition(
89
89
  }
90
90
  // We want to make sure the urls are alphabetically sorted
91
91
  urls = urls.sort();
92
- /** @type {import("@eox/map/src/layers").EOxLayerType<"WebGLTile","GeoTIFF"> | undefined} */
92
+
93
93
  const layerdef =
94
94
  urls.length > 0
95
- ? {
95
+ ? /** @type {import("@eox/map/src/layers").EOxLayerType<"WebGLTile","GeoTIFF">} */ ({
96
96
  type: "WebGLTile",
97
97
  source: {
98
98
  type: "GeoTIFF",
@@ -106,7 +106,7 @@ export async function createTiffLayerDefinition(
106
106
  layerControlToolsExpand: true,
107
107
  },
108
108
  ...(style && { style: style }),
109
- }
109
+ })
110
110
  : undefined;
111
111
 
112
112
  // We want to see if the currently selected indicator uses a
@@ -309,31 +309,33 @@ export async function creatAsyncProcessLayerDefinitions(
309
309
 
310
310
  switch (resultItem.type) {
311
311
  case "image/tiff": {
312
- layers.push({
313
- type: "WebGLTile",
314
- properties: {
315
- id: endpointLink.id + "_process" + resultItem.id + postfixId,
316
- title:
317
- "Results " +
318
- (selectedStac?.id ?? "") +
319
- " " +
320
- (resultItem.id ?? ""),
321
- layerControlToolsExpand: true,
322
- ...(layerConfig && { layerConfig }),
323
- ...extraProperties,
324
- },
325
- source: {
326
- type: "GeoTIFF",
327
- normalize: !style,
328
- sources: resultItem.urls.map((url) => ({ url })),
329
- //@ts-expect-error TODO
330
- ...(selectedStac["eodash:mapProjection"]?.["name"] && {
312
+ layers.push(
313
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"WebGLTile","GeoTIFF">} */ ({
314
+ type: "WebGLTile",
315
+ properties: {
316
+ id: endpointLink.id + "_process" + resultItem.id + postfixId,
317
+ title:
318
+ "Results " +
319
+ (selectedStac?.id ?? "") +
320
+ " " +
321
+ (resultItem.id ?? ""),
322
+ layerControlToolsExpand: true,
323
+ ...(layerConfig && { layerConfig }),
324
+ ...extraProperties,
325
+ },
326
+ source: {
327
+ type: "GeoTIFF",
328
+ normalize: !style,
329
+ sources: resultItem.urls.map((url) => ({ url })),
331
330
  //@ts-expect-error TODO
332
- projection: selectedStac["eodash:mapProjection"]["name"],
333
- }),
334
- },
335
- ...(style && { style }),
336
- });
331
+ ...(selectedStac["eodash:mapProjection"]?.["name"] && {
332
+ //@ts-expect-error TODO
333
+ projection: selectedStac["eodash:mapProjection"]["name"],
334
+ }),
335
+ },
336
+ ...(style && { style }),
337
+ }),
338
+ );
337
339
  break;
338
340
  }
339
341
  case "application/geo+json": {
@@ -4,7 +4,7 @@ import { ref } from "vue";
4
4
  * The list of job result from the server
5
5
  * {job_start_datetime: string, job_end_datetime: string,status: string}
6
6
  * @type {import("vue").Ref<import("./types").AsyncJob[]>}
7
- **/
7
+ */
8
8
  export const jobs = ref([]);
9
9
  /**
10
10
  * The list of jobs results from the server for the compare map
@@ -1,7 +1,8 @@
1
1
  <template>
2
2
  <div class="flex-grow-1">
3
3
  <eox-stacinfo
4
- .for="currentUrl"
4
+ v-if="stacInfoURL"
5
+ .for="stacInfoURL"
5
6
  .allowHtml="allowHtml"
6
7
  .body="body"
7
8
  .featured="featured"
@@ -9,6 +10,7 @@
9
10
  .header="header"
10
11
  .tags="tags"
11
12
  style="--color-primary-lighter: none"
13
+ .styleOverride="styleOverride"
12
14
  >
13
15
  </eox-stacinfo>
14
16
  </div>
@@ -17,8 +19,21 @@
17
19
  <script setup>
18
20
  import "@eox/stacinfo";
19
21
  import { currentUrl } from "@/store/states";
22
+ import { storeToRefs } from "pinia";
23
+ import { useSTAcStore } from "@/store/stac";
24
+ import { computed, onUnmounted, ref, watch } from "vue";
25
+ import { isSTACItem } from "@/eodashSTAC/helpers";
20
26
 
21
- const { allowHtml, featured, footer, header, body, tags } = defineProps({
27
+ const { level, allowHtml, featured, footer, header, body, tags } = defineProps({
28
+ level: {
29
+ type: /** @type {import("vue").PropType<"item" | "collection">} */ (String),
30
+ default: "collection",
31
+ validator: (/** @type {string} */ v) => ["collection", "item"].includes(v),
32
+ },
33
+ styleOverride: {
34
+ type: String,
35
+ default: "",
36
+ },
22
37
  allowHtml: {
23
38
  type: Boolean,
24
39
  default: true,
@@ -92,4 +107,51 @@ const { allowHtml, featured, footer, header, body, tags } = defineProps({
92
107
  default: () => ["sci:citation", "sci:doi", "sci:publication"],
93
108
  },
94
109
  });
110
+
111
+ const { selectedItem } = storeToRefs(useSTAcStore());
112
+
113
+ /** @type {import("vue").Ref<string | null>} */
114
+ const itemUrl = ref(null);
115
+ /**
116
+ * Active object URL (only set for the blob fallback)
117
+ * @type {string | null}
118
+ */
119
+ let activeItemUrl = null;
120
+
121
+ const revokeItem = () => {
122
+ if (activeItemUrl) {
123
+ URL.revokeObjectURL(activeItemUrl);
124
+ activeItemUrl = null;
125
+ }
126
+ };
127
+
128
+ watch(
129
+ selectedItem,
130
+ (item) => {
131
+ if (level !== "item" || !item) return;
132
+ revokeItem();
133
+ if (!isSTACItem(item)) {
134
+ itemUrl.value = item.href;
135
+ return;
136
+ }
137
+ const selfHref = item?.links?.find((l) => l.rel === "self")?.href;
138
+ if (selfHref) {
139
+ itemUrl.value = selfHref;
140
+ } else if (item) {
141
+ const blob = new Blob([JSON.stringify(item)], {
142
+ type: "application/json",
143
+ });
144
+ activeItemUrl = URL.createObjectURL(blob);
145
+ itemUrl.value = activeItemUrl;
146
+ } else {
147
+ itemUrl.value = null;
148
+ }
149
+ },
150
+ { immediate: true },
151
+ );
152
+ onUnmounted(revokeItem);
153
+
154
+ const stacInfoURL = computed(() =>
155
+ level === "item" ? itemUrl.value : currentUrl.value,
156
+ );
95
157
  </script>