@eodash/eodash 5.7.1 → 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 (66) hide show
  1. package/core/client/eodashSTAC/EodashCollection.js +14 -5
  2. package/core/client/eodashSTAC/createLayers.js +193 -80
  3. package/core/client/eodashSTAC/helpers.js +330 -39
  4. package/core/client/eodashSTAC/mosaic.js +10 -4
  5. package/core/client/store/stac.js +29 -0
  6. package/core/client/types.ts +5 -0
  7. package/dist/client/{DashboardLayout-ePbdvUTE.js → DashboardLayout-DEs4f_Vy.js} +2 -2
  8. package/dist/client/{DynamicWebComponent-g5SdRbjY.js → DynamicWebComponent-PvnV88BA.js} +1 -1
  9. package/dist/client/EodashChart-D-3AGKZY.js +260 -0
  10. package/dist/client/{EodashDatePicker-CY1TrsDo.js → EodashDatePicker-Dm_goZzs.js} +3 -3
  11. package/dist/client/{EodashItemFilter-DzshHDlS.js → EodashItemFilter-CJy3zYzD.js} +2 -2
  12. package/dist/client/{EodashLayerControl-DMCeMkSd.js → EodashLayerControl-rL8UnpkA.js} +11 -19
  13. package/dist/client/{EodashLayoutSwitcher-iJ8mX1rF.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
  14. package/dist/client/{EodashMapBtns-C7Cr7q5Y.js → EodashMapBtns-CG7u2Aq8.js} +34 -33
  15. package/dist/client/{EodashStacInfo-K7-iuXa7.js → EodashStacInfo-CJhhxthD.js} +2 -2
  16. package/dist/client/{EodashTools-BEK7e7JM.js → EodashTools-CWyK8QJT.js} +4 -4
  17. package/dist/client/{ExportState-CqIzfMYQ.js → ExportState-BXhy5TOU.js} +19 -11
  18. package/dist/client/{Footer-1P0cmBOZ.js → Footer-DZPvnhcT.js} +1 -1
  19. package/dist/client/{Header-CkkBaBNh.js → Header-CiGsDE_a.js} +2 -2
  20. package/dist/client/{MobileLayout-BPf49kDU.js → MobileLayout-BJHW7x5V.js} +2 -2
  21. package/dist/client/{PopUp-DPZq5SWI.js → PopUp-7_XnhvCc.js} +3 -3
  22. package/dist/client/{ProcessList-DuI6WpS1.js → ProcessList-LL_UCbvc.js} +4 -4
  23. package/dist/client/{VDivider-Bj9LG_JV.js → VDivider-DWYM0WUr.js} +2 -2
  24. package/dist/client/{VImg-Ddv5OyMz.js → VImg-B75dgw2K.js} +2 -2
  25. package/dist/client/{VMain-7yqk_qC3.js → VMain-DvMmOL4f.js} +1 -1
  26. package/dist/client/{VTooltip-2hAJyEoa.js → VTooltip-ClWHUmWL.js} +2 -2
  27. package/dist/client/{WidgetsContainer-2DOrlwCB.js → WidgetsContainer-BqaWs4ch.js} +1 -1
  28. package/dist/client/{asWebComponent-BlFN2lkB.js → asWebComponent-IZanwL-A.js} +239 -91
  29. package/dist/client/{async-B2H8ZwUK.js → async-CiNIOBVz.js} +31 -29
  30. package/dist/client/{create-layers-config-u_vn5hPk.js → create-layers-config-3S3y_kZg.js} +1 -1
  31. package/dist/client/{dialog-transition-BrMkMiNa.js → dialog-transition-c4ZSnzac.js} +2 -2
  32. package/dist/client/eo-dash.js +1 -1
  33. package/dist/client/{forwardRefs-DAlror6d.js → forwardRefs-B-c1_6yo.js} +2 -2
  34. package/dist/client/{handling-B6I1V5eG.js → handling-NvY8Csry.js} +3 -4
  35. package/dist/client/{helpers-CBqIUSXJ.js → helpers-BFuh19CP.js} +328 -40
  36. package/dist/client/{index-DhKU6KqM.js → index-1wMAyuNL.js} +17 -9
  37. package/dist/client/{index-DXLz4sFR.js → index-B8jGJSTA.js} +4 -4
  38. package/dist/client/{index-DjQlbWbJ.js → index-BKQQJkhy.js} +166 -61
  39. package/dist/client/{index-BWq1ZKaV.js → index-D0jUyol8.js} +10 -11
  40. package/dist/client/{index-CJ9UI3ln.js → index-KGxz5Egv.js} +2 -2
  41. package/dist/client/{mosaic-BFYLEwyx.js → mosaic-vHvSuih6.js} +10 -6
  42. package/dist/client/templates.js +6 -7
  43. package/dist/client/{transition-D_W6Wn9v.js → transition-BkyUsZh_.js} +1 -1
  44. package/dist/types/core/client/eodashSTAC/helpers.d.ts +47 -7
  45. package/dist/types/core/client/store/stac.d.ts +8 -2
  46. package/dist/types/core/client/types.d.ts +5 -0
  47. package/dist/types/templates/explore.d.ts +1 -0
  48. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  49. package/package.json +5 -5
  50. package/templates/baseConfig.js +2 -1
  51. package/templates/compare.js +0 -1
  52. package/templates/expert.js +2 -3
  53. package/templates/explore.js +1 -0
  54. package/templates/lite.js +0 -1
  55. package/widgets/EodashChart.vue +161 -21
  56. package/widgets/EodashItemCatalog/methods/map.js +0 -1
  57. package/widgets/EodashLayerControl.vue +10 -15
  58. package/widgets/EodashMap/index.vue +3 -1
  59. package/widgets/EodashMap/methods/btns.js +27 -26
  60. package/widgets/EodashMap/methods/use-map-loading.js +8 -2
  61. package/widgets/EodashProcess/index.vue +135 -16
  62. package/widgets/EodashProcess/methods/composables.js +13 -0
  63. package/widgets/EodashProcess/methods/outputs.js +0 -1
  64. package/widgets/EodashProcess/methods/utils.js +29 -27
  65. package/widgets/ExportState.vue +13 -4
  66. package/dist/client/EodashChart-ChrkA5oS.js +0 -145
@@ -19,7 +19,9 @@ export const useSTAcStore: import("pinia").StoreDefinition<"stac", Pick<{
19
19
  url: string;
20
20
  titilerVersion?: 1 | 2;
21
21
  })[]>;
22
- }, "stac" | "stacEndpoint" | "rasterEndpoint" | "isApi" | "selectedStac" | "selectedCompareStac" | "selectedItem" | "selectedCompareItem" | "supportedUpscalingEndpoints">, Pick<{
22
+ colormapRegistry: import("vue").Ref<Record<string, string[]> | null, Record<string, string[]> | null>;
23
+ loadColormapRegistry: (registry: string | Record<string, string[]>) => Promise<void>;
24
+ }, "stac" | "stacEndpoint" | "rasterEndpoint" | "isApi" | "selectedStac" | "selectedCompareStac" | "selectedItem" | "selectedCompareItem" | "supportedUpscalingEndpoints" | "colormapRegistry">, Pick<{
23
25
  stacEndpoint: import("vue").Ref<string | null, string | null>;
24
26
  rasterEndpoint: import("vue").Ref<string | null, string | null>;
25
27
  isApi: import("vue").Ref<boolean, boolean>;
@@ -40,6 +42,8 @@ export const useSTAcStore: import("pinia").StoreDefinition<"stac", Pick<{
40
42
  url: string;
41
43
  titilerVersion?: 1 | 2;
42
44
  })[]>;
45
+ colormapRegistry: import("vue").Ref<Record<string, string[]> | null, Record<string, string[]> | null>;
46
+ loadColormapRegistry: (registry: string | Record<string, string[]>) => Promise<void>;
43
47
  }, never>, Pick<{
44
48
  stacEndpoint: import("vue").Ref<string | null, string | null>;
45
49
  rasterEndpoint: import("vue").Ref<string | null, string | null>;
@@ -61,4 +65,6 @@ export const useSTAcStore: import("pinia").StoreDefinition<"stac", Pick<{
61
65
  url: string;
62
66
  titilerVersion?: 1 | 2;
63
67
  })[]>;
64
- }, "init" | "loadSTAC" | "loadSelectedSTAC" | "loadSelectedCompareSTAC" | "resetSelectedCompareSTAC">>;
68
+ colormapRegistry: import("vue").Ref<Record<string, string[]> | null, Record<string, string[]> | null>;
69
+ loadColormapRegistry: (registry: string | Record<string, string[]>) => Promise<void>;
70
+ }, "init" | "loadSTAC" | "loadSelectedSTAC" | "loadSelectedCompareSTAC" | "resetSelectedCompareSTAC" | "loadColormapRegistry">>;
@@ -259,6 +259,7 @@ export type StacEndpoint = string | {
259
259
  url: string;
260
260
  titilerVersion?: 1 | 2;
261
261
  }>;
262
+ colormapRegistry?: string | Record<string, string[]>;
262
263
  };
263
264
  /** @group Eodash */
264
265
  export interface EodashFont {
@@ -518,6 +519,10 @@ export interface Render {
518
519
  expression?: string;
519
520
  /** Zoom levels range applicable for the visualization */
520
521
  minmax_zoom?: number[];
522
+ /** Band indexes to apply the rendering to. */
523
+ bidx?: number[];
524
+ /** Tile size to request from the tile server. */
525
+ tilesize?: number;
521
526
  }
522
527
  /** @ignore */
523
528
  export interface TitilerSTACParameters {
@@ -26,6 +26,7 @@ declare const _default: {
26
26
  enableZoom: true;
27
27
  enableExportMap: true;
28
28
  enableChangeProjection: true;
29
+ enableMosaic: false;
29
30
  enableCompareIndicators: {
30
31
  fallbackTemplate: string;
31
32
  compareTemplate: string;
@@ -2,4 +2,4 @@
2
2
  *
3
3
  * @param {import("../../../../EodashProcess/types").CustomEnpointInput} param0
4
4
  */
5
- export function handleEOxHubEndpoint({ links, jsonformValue, isPolling, selectedStac, jobs, enableCompare, }: import("../../../../EodashProcess/types").CustomEnpointInput): Promise<(import("@eox/map/src/layers").EOxLayerType<"Vector", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"Vector", "FlatGeoBuf"> | import("@eox/map/src/layers").EOxLayerType<"Vector", "Cluster"> | import("@eox/map/src/layers").EOxLayerType<"VectorTile", "VectorTile"> | import("@eox/map/src/layers").EOxLayerType<"WebGLTile", "GeoTIFF"> | import("@eox/map/src/layers").EOxLayerType<"WebGLTile", "GeoZarr"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "OSM"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "WMTSCapabilities"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "StadiaMaps"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "WMTS"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "XYZ"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileJSON"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileArcGISRest"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileDebug"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileImage"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileWMS"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "UrlTile"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "BingMaps"> | import("@eox/map/src/layers").EOxLayerType<"Image", "Image"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageCanvas"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageStatic"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageWMS"> | import("@eox/map/src/layers").EOxLayerType<"Image", "Raster"> | import("@eox/map/src/layers").EOxLayerType<"Image", "IIIF"> | import("@eox/map/src/layers").EOxLayerTypeMapboxStyle | import("@eox/map/src/layers").EOxLayerTypeGroup)[] | undefined>;
5
+ export function handleEOxHubEndpoint({ links, jsonformValue, isPolling, selectedStac, jobs, enableCompare, }: import("../../../../EodashProcess/types").CustomEnpointInput): Promise<(import("@eox/map/src/layers").EOxLayerType<"Vector", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"Vector", "FlatGeoBuf"> | import("@eox/map/src/layers").EOxLayerType<"Vector", "Cluster"> | import("@eox/map/src/layers").EOxLayerType<"VectorImage", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"Heatmap", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"WebGLVector", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"WebGLPoints", "Vector"> | import("@eox/map/src/layers").EOxLayerType<"Graticule", never> | import("@eox/map/src/layers").EOxLayerType<"VectorTile", "VectorTile"> | import("@eox/map/src/layers").EOxLayerType<"WebGLTile", "GeoTIFF"> | import("@eox/map/src/layers").EOxLayerType<"WebGLTile", "GeoZarr"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "OSM"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "WMTSCapabilities"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "StadiaMaps"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "WMTS"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "XYZ"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileJSON"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileArcGISRest"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileDebug"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileImage"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "TileWMS"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "UrlTile"> | import("@eox/map/src/layers").EOxLayerType<"Tile", "BingMaps"> | import("@eox/map/src/layers").EOxLayerType<"Image", "Image"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageCanvas"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageStatic"> | import("@eox/map/src/layers").EOxLayerType<"Image", "ImageWMS"> | import("@eox/map/src/layers").EOxLayerType<"Image", "Raster"> | import("@eox/map/src/layers").EOxLayerType<"Image", "IIIF"> | import("@eox/map/src/layers").EOxLayerType<"STAC", never> | import("@eox/map/src/layers").EOxLayerTypeMapboxStyle | import("@eox/map/src/layers").EOxLayerTypeGroup)[] | undefined>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eodash/eodash",
3
- "version": "5.7.1",
3
+ "version": "5.8.0",
4
4
  "type": "module",
5
5
  "types": "./dist/types/core/client/types.d.ts",
6
6
  "files": [
@@ -76,11 +76,11 @@
76
76
  "@eox/geosearch": "^1.2.0",
77
77
  "@eox/itemfilter": "^1.17.3",
78
78
  "@eox/jsonform": "^1.12.1",
79
- "@eox/layercontrol": "^1.7.0",
79
+ "@eox/layercontrol": "^1.8.0",
80
80
  "@eox/layout": "^1.0.0",
81
- "@eox/map": "^2.6.1",
81
+ "@eox/map": "^2.7.0",
82
82
  "@eox/stacinfo": "^1.2.0",
83
- "@eox/timecontrol": "https://pkg.pr.new/EOX-A/EOxElements/@eox/timecontrol@476eb30",
83
+ "@eox/timecontrol": "^2.6.0",
84
84
  "@eox/ui": "^1.1.0",
85
85
  "@mdi/js": "^7.4.47",
86
86
  "@vitejs/plugin-vue": "^6.0.7",
@@ -112,7 +112,7 @@
112
112
  "devDependencies": {
113
113
  "@babel/types": "^7.29.7",
114
114
  "@eox/eslint-config": "^2.1.0",
115
- "@eox/pages-theme-eox": "^1.7.0",
115
+ "@eox/pages-theme-eox": "^1.9.0",
116
116
  "@pinia/testing": "^1.0.3",
117
117
  "@types/json-schema": "^7.0.15",
118
118
  "@types/mustache": "^4.2.6",
@@ -20,7 +20,8 @@ const baseConfig = {
20
20
  { url: "openveda.cloud", titilerVersion: 1 },
21
21
  { url: "api.explorer.eopf.copernicus.eu", titilerVersion: 2 },
22
22
  ],
23
- rasterEndpoint: "https://api.explorer.eopf.copernicus.eu/rstaging",
23
+ colormapRegistry:
24
+ "https://raw.githubusercontent.com/eurodatacube/eodash-assets/refs/heads/main/defaults/colormaps.json",
24
25
  },
25
26
  brand: {
26
27
  noLayout: true,
@@ -41,7 +41,6 @@ export default {
41
41
  },
42
42
  source: {
43
43
  type: "XYZ",
44
- //@ts-expect-error todo
45
44
  url: "https://s2maps-tiles.eu/wmts/1.0.0/terrain-light_3857/default/g/{z}/{y}/{x}.jpeg",
46
45
  },
47
46
  },
@@ -40,7 +40,6 @@ export default {
40
40
  },
41
41
  source: {
42
42
  type: "XYZ",
43
- //@ts-expect-error todo
44
43
  url: "https://s2maps-tiles.eu/wmts/1.0.0/terrain-light_3857/default/g/{z}/{y}/{x}.jpeg",
45
44
  },
46
45
  },
@@ -96,7 +95,7 @@ export default {
96
95
  id: "Layercontrol",
97
96
  type: "internal",
98
97
  title: "Layers",
99
- layout: { x: 0, y: 1, w: "3/3/2", h: 11 },
98
+ layout: { x: 0, y: 1, w: "3/3/2", h: 10 },
100
99
  widget: {
101
100
  name: "EodashLayerControl",
102
101
  properties: {
@@ -147,7 +146,7 @@ export default {
147
146
  id: "Processes",
148
147
  type: "internal",
149
148
  title: "Processes",
150
- layout: { x: "9/9/10", y: 6, w: "3/3/2", h: 5 },
149
+ layout: { x: "9/9/10", y: 6, w: "3/3/2", h: 6 },
151
150
  widget: {
152
151
  name: "EodashProcess",
153
152
  },
@@ -28,6 +28,7 @@ export default {
28
28
  enableZoom: true,
29
29
  enableExportMap: true,
30
30
  enableChangeProjection: true,
31
+ enableMosaic: false,
31
32
  enableCompareIndicators: {
32
33
  fallbackTemplate: "explore",
33
34
  compareTemplate: "compare",
package/templates/lite.js CHANGED
@@ -33,7 +33,6 @@ export default {
33
33
  },
34
34
  source: {
35
35
  type: "XYZ",
36
- //@ts-expect-error todo
37
36
  url: "https://s2maps-tiles.eu/wmts/1.0.0/terrain-light_3857/default/g/{z}/{y}/{x}.jpeg",
38
37
  },
39
38
  },
@@ -1,6 +1,9 @@
1
1
  <template>
2
- <div ref="container">
3
- <div class="chart-frame">
2
+ <div ref="container" class="eodash-chart-wrapper">
3
+ <div
4
+ class="chart-frame"
5
+ :style="{ paddingBottom: hasBindings ? '25px' : '0px' }"
6
+ >
4
7
  <button
5
8
  v-if="usedChartData && usedChartSpec"
6
9
  class="chart-toggle"
@@ -12,9 +15,8 @@
12
15
  </svg>
13
16
  </button>
14
17
  <eox-chart
15
- class="pa-2"
16
- v-if="usedChartData && usedChartSpec"
17
- .spec="toRaw(usedChartSpec)"
18
+ v-if="usedChartData && renderedChartSpec"
19
+ .spec="toRaw(renderedChartSpec)"
18
20
  :key="chartRenderKey"
19
21
  .dataValues="toRaw(usedChartData)"
20
22
  @click:item="onChartClick"
@@ -33,6 +35,8 @@ import {
33
35
  ref,
34
36
  onMounted,
35
37
  onBeforeUnmount,
38
+ watch,
39
+ nextTick,
36
40
  } from "vue";
37
41
  import { onChartClick } from "./EodashProcess/methods/handling";
38
42
  import {
@@ -67,27 +71,136 @@ const usedChartSpec = computed(() => {
67
71
  return enableCompare ? compareChartSpec.value : chartSpec.value;
68
72
  });
69
73
 
74
+ const hasBindings = computed(() => {
75
+ const spec = usedChartSpec.value;
76
+ if (!spec) return false;
77
+
78
+ // Recursively search for any object with a 'bind' key that represents a physical UI input
79
+ let found = false;
80
+ /** @param {any} obj */
81
+ const searchBindings = (obj) => {
82
+ if (found || !obj || typeof obj !== "object") return;
83
+
84
+ // UI bindings that take up physical DOM space are objects with an 'input' property.
85
+ if (
86
+ "bind" in obj &&
87
+ typeof obj.bind === "object" &&
88
+ obj.bind !== null &&
89
+ "input" in obj.bind
90
+ ) {
91
+ found = true;
92
+ return;
93
+ }
94
+ Object.values(obj).forEach(searchBindings);
95
+ };
96
+ searchBindings(spec);
97
+ return found;
98
+ });
99
+
100
+ const renderedChartSpec = ref(null);
101
+
102
+ watch(
103
+ usedChartSpec,
104
+ (newSpec) => {
105
+ if (!newSpec) {
106
+ renderedChartSpec.value = null;
107
+ return;
108
+ }
109
+
110
+ // Create a deep copy so we can safely mutate it
111
+ const adjustedSpec = JSON.parse(JSON.stringify(newSpec));
112
+
113
+ // Force the chart to be fully responsive to its CSS container
114
+ adjustedSpec.height = "container";
115
+ adjustedSpec.width = "container";
116
+
117
+ // Delay passing the spec to eox-chart until the next DOM update cycle.
118
+ // This ensures the dynamic chartStyles are physically applied
119
+ // to the container BEFORE Vega calculates its canvas size.
120
+ nextTick(() => {
121
+ renderedChartSpec.value = adjustedSpec;
122
+ chartRenderKey.value = Math.random(); // Force eox-chart to completely remount
123
+
124
+ // Force a browser-level resize event after the chart mounts.
125
+ // This tells Vega to re-read the container dimensions once the CSS has finished painting.
126
+ setTimeout(() => {
127
+ window.dispatchEvent(new Event("resize"));
128
+ }, 150);
129
+ });
130
+ },
131
+ { immediate: true },
132
+ );
133
+
70
134
  const chartRenderKey = ref(0);
71
- const frameHeight = ref(225);
72
135
  const containerEl = useTemplateRef("container");
73
136
 
74
- /**
75
- @type { MutationObserver | null}
76
- */
137
+ /** @type {MutationObserver | null} */
77
138
  let observer = null;
139
+ /** @type {number | null} */
140
+ let styleInterval = null;
78
141
 
79
142
  onMounted(() => {
80
143
  const el = containerEl.value;
81
144
  if (!el) return;
82
145
 
83
- const parent = el.parentElement?.parentElement;
84
- if (parent) {
85
- const parentHeight = parent.getBoundingClientRect().height;
86
- frameHeight.value = Math.max(225, Math.floor(parentHeight));
87
- }
146
+ // Continuously inject basic styling for the bindings form to make it look decent
147
+ styleInterval = window.setInterval(() => {
148
+ if (el) {
149
+ const eoxChart = el.querySelector("eox-chart");
150
+ if (eoxChart && eoxChart.shadowRoot) {
151
+ if (!eoxChart.shadowRoot.querySelector("#eodash-chart-styles")) {
152
+ const style = document.createElement("style");
153
+ style.id = "eodash-chart-styles";
154
+ style.innerHTML = `
155
+ * {
156
+ box-sizing: border-box !important;
157
+ }
158
+ #vis {
159
+ min-height: 100px !important;
160
+ flex: 1 1 auto !important;
161
+ }
162
+ :host, .vega-embed {
163
+ display: flex !important;
164
+ flex-direction: column !important;
165
+ height: 100% !important;
166
+ padding: 0 !important;
167
+ margin: 0 !important;
168
+ }
169
+ .vega-bindings {
170
+ flex: 0 0 auto !important;
171
+ display: flex !important;
172
+ flex-wrap: wrap;
173
+ gap: 2px !important;
174
+ background: rgba(255, 255, 255, 0.85);
175
+ padding: 6px 12px !important;
176
+ border-radius: 6px;
177
+ box-shadow: 0 2px 5px rgba(0,0,0,0.15);
178
+ margin: 0 !important;
179
+ margin-top: -10px !important;
180
+ z-index: 10;
181
+ }
182
+ .vega-bindings:empty {
183
+ display: none !important;
184
+ }
185
+ .vega-embed > canvas, .vega-embed > svg {
186
+ height: 100% !important;
187
+ max-width: 100% !important;
188
+ object-fit: contain;
189
+ }
190
+ .vega-bind {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 6px;
194
+ margin-bottom: 0 !important;
195
+ }
196
+ `;
197
+ eoxChart.shadowRoot.appendChild(style);
198
+ }
199
+ }
200
+ }
201
+ }, 200);
88
202
 
89
- // for mobile view, the overlay panel containing chart is initially hidden
90
- // we create an observer when display of overlay is not none anymore
203
+ // For mobile view, handle overlay display changes
91
204
  const overlay = getOverlayParent(el);
92
205
  if (!overlay) return;
93
206
 
@@ -95,7 +208,6 @@ onMounted(() => {
95
208
  const style = getComputedStyle(overlay);
96
209
  const visible = style.display !== "none";
97
210
  if (visible) {
98
- // forcibly rerender chart, otherwise size of canvas is 0
99
211
  chartRenderKey.value = Math.random();
100
212
  }
101
213
  });
@@ -108,13 +220,14 @@ onMounted(() => {
108
220
 
109
221
  onBeforeUnmount(() => {
110
222
  observer?.disconnect();
223
+ if (styleInterval) window.clearInterval(styleInterval);
111
224
  });
112
225
 
113
226
  const chartStyles = computed(() => {
114
- const styles = {
115
- height: `${frameHeight.value}px`,
227
+ return {
228
+ height: "100%",
229
+ width: "100%",
116
230
  };
117
- return styles;
118
231
  });
119
232
 
120
233
  const toggleIcon = computed(() =>
@@ -125,14 +238,41 @@ function toggleLayout() {
125
238
  areChartsSeparateLayout.value = !areChartsSeparateLayout.value;
126
239
  }
127
240
  </script>
241
+
242
+ <style>
243
+ /* Force the outer dashboard panel wrapping this component to utilize 100% height in fullscreen mode */
244
+ .bg-surface:has(.eodash-chart-wrapper) {
245
+ height: 100%;
246
+ display: flex;
247
+ flex-direction: column;
248
+ }
249
+ </style>
250
+
128
251
  <style scoped>
252
+ .eodash-chart-wrapper {
253
+ height: 100%; /* Force full height in fullscreen layout */
254
+ flex-grow: 1;
255
+ min-height: 180px; /* Prevent chart from becoming unusably small */
256
+ display: flex;
257
+ flex-direction: column;
258
+ }
259
+
129
260
  .chart-frame {
130
261
  position: relative;
262
+ flex-grow: 1;
263
+ min-height: 180px; /* Prevent chart from becoming unusably small */
264
+ display: flex;
265
+ flex-direction: column;
266
+ }
267
+
268
+ eox-chart {
269
+ flex-grow: 1;
270
+ min-height: 0;
131
271
  }
132
272
 
133
273
  .chart-toggle {
134
274
  position: absolute;
135
- top: 18px;
275
+ top: 8px;
136
276
  right: 46px;
137
277
  z-index: 2;
138
278
  cursor: pointer;
@@ -46,7 +46,6 @@ export function renderItemsFeatures(
46
46
  },
47
47
  source: {
48
48
  type: "Vector",
49
- //@ts-expect-error todo
50
49
  url:
51
50
  "data:application/geo+json," +
52
51
  encodeURIComponent(
@@ -5,6 +5,7 @@
5
5
  :key="mapElement"
6
6
  v-bind="config"
7
7
  :for="mapElement"
8
+ .colormapRegistry="colormapRegistry"
8
9
  .showLayerZoomState="true"
9
10
  .customEditorInterfaces="bandsEditorInterface"
10
11
  @datetime:updated="debouncedHandleDateTime"
@@ -29,16 +30,19 @@
29
30
  <script setup>
30
31
  import "color-legend-element";
31
32
  import "@eox/timecontrol";
32
- import { computed, ref, watch } from "vue";
33
+ import { computed, ref } from "vue";
33
34
  import { mapEl, mapCompareEl } from "@/store/states";
34
- import { getColFromLayer } from "@/eodashSTAC/helpers";
35
35
  import {
36
36
  eodashCollections,
37
37
  eodashCompareCollections,
38
38
  layerControlFormValue,
39
39
  layerControlFormValueCompare,
40
40
  } from "@/utils/states";
41
- import { updateGeoZarrBands } from "@/eodashSTAC/helpers";
41
+ import {
42
+ getColFromLayer,
43
+ updateGeoZarrBands,
44
+ updateLayerUrl,
45
+ } from "@/eodashSTAC/helpers";
42
46
  import { storeToRefs } from "pinia";
43
47
  import { useSTAcStore } from "@/store/stac";
44
48
  import { bandsEditorInterface } from "@/utils/bands-editor";
@@ -99,7 +103,8 @@ const enableLayoutSwitcher = computed(
99
103
  () => !!props.layoutTarget && !!props.layoutIcon,
100
104
  );
101
105
 
102
- const { selectedCompareStac, selectedStac } = storeToRefs(useSTAcStore());
106
+ const { selectedCompareStac, selectedStac, colormapRegistry } =
107
+ storeToRefs(useSTAcStore());
103
108
 
104
109
  const showControls = computed(() => {
105
110
  if (props.map === "second") {
@@ -115,20 +120,9 @@ const mapElement = props.map === "second" ? mapCompareEl : mapEl;
115
120
  /** @type { import("vue").Ref<import("@eox/layercontrol").EOxLayerControl | null>} */
116
121
  const eoxLayercontrol = ref(null);
117
122
 
118
- // eox-timecontrol re-fires datetime:updated after layer reassignment;
119
- // dedupe by (collectionId, datetime)
120
- const processedDatetimes = new Map();
121
- watch([selectedStac, selectedCompareStac], () => processedDatetimes.clear());
122
-
123
123
  /** @param {CustomEvent<{layer:import('ol/layer').Layer; datetime:string;}>} evt */
124
124
  const handleDatetimeUpdate = async (evt) => {
125
125
  const { layer, datetime } = evt.detail;
126
- const collectionId = layer.get("id")?.split(";:;")[0] ?? layer.get("id");
127
- if (processedDatetimes.get(collectionId) === datetime) return;
128
- // First event per collection is eox-timecontrol's mount echo.
129
- const isFirstEvent = !processedDatetimes.has(collectionId);
130
- processedDatetimes.set(collectionId, datetime);
131
- if (isFirstEvent) return;
132
126
 
133
127
  const ec = await getColFromLayer(eodashCols, layer);
134
128
 
@@ -185,6 +179,7 @@ const debouncedHandleDateTime = (evt) => {
185
179
  */
186
180
  const onLayerConfigChange = (evt) => {
187
181
  updateGeoZarrBands(evt.detail.layer, evt.detail.jsonformValue);
182
+ updateLayerUrl(evt.detail.layer, evt.detail.jsonformValue);
188
183
 
189
184
  if (props.map === "second") {
190
185
  layerControlFormValueCompare.value = evt.detail.jsonformValue;
@@ -271,7 +271,9 @@ const controls = computed(() => {
271
271
  controlsObj.MousePosition = {
272
272
  projection: "EPSG:4326",
273
273
  coordinateFormat: (c) => {
274
- return `${c?.[1].toFixed(3)} °N, ${c?.[0].toFixed(3)} °E`;
274
+ if (!c) return "";
275
+ const [lon, lat] = c;
276
+ return `${Math.abs(lat).toFixed(3)} °${lat >= 0 ? "N" : "S"}, ${Math.abs(lon).toFixed(3)} °${lon >= 0 ? "E" : "W"}`;
275
277
  },
276
278
  target: cursorCoordsRef.value,
277
279
  };
@@ -76,37 +76,38 @@ function showAllPanels() {
76
76
  }
77
77
  /**
78
78
  *
79
- * @param {import("@eox/map").EoxLayer[]} layers
80
- * @return {import("@eox/map").EoxLayer[]}
79
+ * @param {import("@eox/map").EoxLayer[]} layers - eox map layers
80
+ * @returns {import("@eox/map").EoxLayer[]} anonymized Cors
81
81
  */
82
82
  function addCorsAnonym(layers) {
83
- //@ts-expect-error todo
84
- return layers.map((layer) => {
85
- if (layer.type === "Group") {
86
- layer.layers = addCorsAnonym([...(layer.layers ?? [])]);
87
- return layer;
88
- }
89
- // check if not mapbox style as a fix for ts error
90
- if (layer.type === "MapboxStyle") {
91
- return layer;
92
- }
83
+ return /** @type {import("@eox/map").EoxLayer[]} */ (
84
+ layers.map((layer) => {
85
+ if (layer.type === "Group") {
86
+ layer.layers = addCorsAnonym([...(layer.layers ?? [])]);
87
+ return layer;
88
+ }
89
+ // check if not mapbox style as a fix for ts error
90
+ if (layer.type === "MapboxStyle") {
91
+ return layer;
92
+ }
93
93
 
94
- return {
95
- ...layer,
96
- ...(layer.source && {
97
- source: {
98
- ...layer.source,
99
- crossOrigin: "anonymous",
100
- },
101
- ...(layer.sources && {
102
- sources: layer.sources.map((source) => ({
103
- ...source,
94
+ return {
95
+ ...layer,
96
+ ...("source" in layer && {
97
+ source: {
98
+ ...layer.source,
104
99
  crossOrigin: "anonymous",
105
- })),
100
+ },
101
+ ...(layer.sources && {
102
+ sources: layer.sources.map((source) => ({
103
+ ...source,
104
+ crossOrigin: "anonymous",
105
+ })),
106
+ }),
106
107
  }),
107
- }),
108
- };
109
- });
108
+ };
109
+ })
110
+ );
110
111
  }
111
112
 
112
113
  export const onMapZoomOut = () => {
@@ -10,11 +10,15 @@ import { loading } from "@/store/states";
10
10
  * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} compareMapElement
11
11
  */
12
12
  export const useMapLoading = (mapElement, compareMapElement) => {
13
+ // This instance's outstanding loads, so unmount subtracts only its share.
14
+ let localLoads = 0;
13
15
  const startHandler = () => {
16
+ localLoads++;
14
17
  loading.activeLoads++;
15
18
  };
16
19
  const endHandler = () => {
17
- loading.activeLoads--;
20
+ localLoads = Math.max(0, localLoads - 1);
21
+ loading.activeLoads = Math.max(0, loading.activeLoads - 1);
18
22
  };
19
23
 
20
24
  /** @param {import("@eox/map").EOxMap | null} newMapEl */
@@ -45,8 +49,10 @@ export const useMapLoading = (mapElement, compareMapElement) => {
45
49
  onUnmounted(() => {
46
50
  mapElement.value?.map.removeEventListener("loadstart", startHandler);
47
51
  mapElement.value?.map.removeEventListener("loadend", endHandler);
52
+ compareMapElement.value?.map.removeEventListener("loadstart", startHandler);
53
+ compareMapElement.value?.map.removeEventListener("loadend", endHandler);
48
54
  stopMainWatcher();
49
55
  stopCompareWatcher();
50
- loading.activeLoads = 0;
56
+ loading.activeLoads = Math.max(0, loading.activeLoads - localLoads);
51
57
  });
52
58
  };