@eodash/eodash 5.6.0 → 5.7.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 (91) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -1
  2. package/core/client/composables/index.js +6 -2
  3. package/core/client/eodashSTAC/EodashCollection.js +5 -21
  4. package/core/client/eodashSTAC/createLayers.js +23 -9
  5. package/core/client/eodashSTAC/helpers.js +37 -4
  6. package/core/client/types.ts +8 -1
  7. package/core/client/utils/index.js +4 -0
  8. package/dist/client/{DashboardLayout-DkKDhfgH.js → DashboardLayout-kRsmsNW5.js} +5 -5
  9. package/dist/client/{DynamicWebComponent-BrpkA-e3.js → DynamicWebComponent-CnKhYo0K.js} +1 -1
  10. package/dist/client/{EodashChart-p7_1FRlF.js → EodashChart-CLDqE93C.js} +4 -4
  11. package/dist/client/{EodashDatePicker-HK0Jl7wQ.js → EodashDatePicker-CMPDT-ce.js} +3 -3
  12. package/dist/client/{EodashItemFilter-BYQDA78B.js → EodashItemFilter-BpdA8QQa.js} +1 -1
  13. package/dist/client/{EodashLayerControl-NcrNncGn.js → EodashLayerControl-BHpDA4_0.js} +25 -7
  14. package/dist/client/{EodashLayoutSwitcher-CC2Znd0I.js → EodashLayoutSwitcher-B1fRUt7G.js} +3 -3
  15. package/dist/client/EodashMapBtns-Cpned0tc.js +478 -0
  16. package/dist/client/{EodashStacInfo-BA-0UjvT.js → EodashStacInfo-BQspAHKH.js} +31 -31
  17. package/dist/client/{EodashTools-CWmoH0g_.js → EodashTools-1zHz26gv.js} +4 -4
  18. package/dist/client/{ExportState-CfAfX4RN.js → ExportState-DbOm8hBI.js} +4 -4
  19. package/dist/client/{Footer-DLGxQTZt.js → Footer-BKg-VVkj.js} +1 -1
  20. package/dist/client/{Header-eyEyyzD6.js → Header-DCrwCvmY.js} +2 -2
  21. package/dist/client/MobileLayout-vWSHpluo.js +118 -0
  22. package/dist/client/{PopUp-WNs6Io2N.js → PopUp-9uimfK49.js} +2 -2
  23. package/dist/client/{ProcessList-RzVqbd5E.js → ProcessList-CTTIZa06.js} +4 -4
  24. package/dist/client/{VImg-GKUTFoV-.js → VImg-CrmB76ZY.js} +2 -2
  25. package/dist/client/{VMain-D7_72mHr.js → VMain-OKZNpcEG.js} +1 -1
  26. package/dist/client/{VTooltip-BbmvtvYw.js → VTooltip-CJw2FyWk.js} +2 -2
  27. package/dist/client/{WidgetsContainer-Dw5d7SOW.js → WidgetsContainer-BaWOnE5R.js} +1 -1
  28. package/dist/client/{asWebComponent-DDt7V3Bl.js → asWebComponent-BHWpMuE3.js} +47 -35
  29. package/dist/client/{async-CT3KEV52.js → async-DSKj6g1f.js} +29 -28
  30. package/dist/client/create-layers-config-CBoYedqB.js +182 -0
  31. package/dist/client/eo-dash.js +1 -1
  32. package/dist/client/{forwardRefs-CVzxyoSq.js → forwardRefs-DH51zPUr.js} +2 -2
  33. package/dist/client/{handling-C285F_Mq.js → handling-BrcDDlgS.js} +452 -114
  34. package/dist/client/{helpers-CZFH0EVN.js → helpers-DjZVl_WF.js} +2277 -1504
  35. package/dist/client/{index-B-m7V3Kb.js → index-1JKE68n3.js} +49 -194
  36. package/dist/client/{index-CF3ZMzTz.js → index-5Jhsa8Xe.js} +3 -3
  37. package/dist/client/index-CSG_JoOA.js +436 -0
  38. package/dist/client/{index-DgOXD7wd.js → index-CUjl-ZsI.js} +2 -2
  39. package/dist/client/{index-B_6H-N14.js → index-Cu3-13Xg.js} +84 -90
  40. package/dist/client/material-symbols-outlined.woff2 +0 -0
  41. package/dist/client/material-symbols-rounded.woff2 +0 -0
  42. package/dist/client/material-symbols-sharp.woff2 +0 -0
  43. package/dist/client/material-symbols-subset.woff2 +0 -0
  44. package/dist/client/templates.js +1 -1
  45. package/dist/client/{transition-C2gnrWg0.js → transition-OHcz5Qq1.js} +1 -1
  46. package/dist/node/cli.js +1 -1
  47. package/dist/types/core/client/composables/index.d.ts +1 -3
  48. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +6 -7
  49. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +3 -3
  50. package/dist/types/core/client/eodashSTAC/helpers.d.ts +1 -0
  51. package/dist/types/core/client/types.d.ts +8 -1
  52. package/dist/types/templates/baseConfig.d.ts +12 -0
  53. package/dist/types/widgets/EodashLayerControl.vue.d.ts +1 -1
  54. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +9 -0
  55. package/dist/types/widgets/EodashMap/index.vue.d.ts +7 -0
  56. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -8
  57. package/dist/types/widgets/EodashProcess/methods/composables.d.ts +2 -3
  58. package/dist/types/widgets/EodashProcess/methods/handling.d.ts +15 -10
  59. package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +3 -1
  60. package/dist/types/widgets/EodashProcess/methods/template-helpers.d.ts +99 -0
  61. package/dist/types/widgets/EodashProcess/methods/utils.d.ts +1 -6
  62. package/dist/types/widgets/EodashProcess/types.d.ts +1 -0
  63. package/dist/types/widgets/EodashStacInfo.vue.d.ts +10 -10
  64. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +34 -0
  65. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +39 -0
  66. package/dist/types/widgets/EodashTimeSlider/types.d.ts +62 -0
  67. package/package.json +32 -32
  68. package/widgets/EodashLayerControl.vue +22 -3
  69. package/widgets/EodashMap/EodashMapBtns.vue +46 -1
  70. package/widgets/EodashMap/index.vue +17 -5
  71. package/widgets/EodashMap/methods/create-layers-config.js +2 -0
  72. package/widgets/EodashMap/methods/index.js +23 -5
  73. package/widgets/EodashProcess/index.vue +17 -13
  74. package/widgets/EodashProcess/methods/async.js +19 -8
  75. package/widgets/EodashProcess/methods/composables.js +63 -74
  76. package/widgets/EodashProcess/methods/custom-endpoints/chart/sentinelhub-endpoint.js +9 -5
  77. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +7 -4
  78. package/widgets/EodashProcess/methods/handling.js +125 -76
  79. package/widgets/EodashProcess/methods/outputs.js +74 -29
  80. package/widgets/EodashProcess/methods/template-helpers.js +246 -0
  81. package/widgets/EodashProcess/methods/utils.js +7 -17
  82. package/widgets/EodashProcess/types.ts +1 -0
  83. package/widgets/EodashStacInfo.vue +30 -30
  84. package/widgets/EodashTimeSlider/index.vue +152 -0
  85. package/widgets/EodashTimeSlider/methods/index.js +279 -0
  86. package/widgets/EodashTimeSlider/types.ts +64 -0
  87. package/dist/client/EodashMapBtns-BJSh7qT-.js +0 -435
  88. package/dist/client/EodashTimeSlider-GDv1hbw7.js +0 -53
  89. package/dist/client/MobileLayout-RzLOyXwL.js +0 -118
  90. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +0 -15
  91. package/widgets/EodashTimeSlider.vue +0 -40
@@ -0,0 +1,34 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ filters: {
5
+ /** @type {import("vue").PropType<import("@eox/itemfilter").EOxItemFilter["filterProperties"]>} */
6
+ type: import("vue").PropType<import("@eox/itemfilter").EOxItemFilter["filterProperties"]>;
7
+ default: () => never[];
8
+ };
9
+ animate: {
10
+ type: BooleanConstructor;
11
+ default: boolean;
12
+ };
13
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
14
+ filters: {
15
+ /** @type {import("vue").PropType<import("@eox/itemfilter").EOxItemFilter["filterProperties"]>} */
16
+ type: import("vue").PropType<import("@eox/itemfilter").EOxItemFilter["filterProperties"]>;
17
+ default: () => never[];
18
+ };
19
+ animate: {
20
+ type: BooleanConstructor;
21
+ default: boolean;
22
+ };
23
+ }>> & Readonly<{}>, {
24
+ animate: boolean;
25
+ filters: {
26
+ key?: string;
27
+ keys?: Array<string>;
28
+ title: string;
29
+ type: string;
30
+ placeholder?: string;
31
+ format?: string;
32
+ state?: any;
33
+ }[];
34
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @param {string} stacEndpoint
3
+ * @param {[string, string]} selectedRange
4
+ * @param {import("../types").TimelineExportEventDetail["selectedRangeItems"]} selectedRangeItems
5
+ * @param {import("vue").Ref<import("stac-ts").StacCollection|null>} selectedStac
6
+ * @param {Record<string, import("../types").Filter>} filters
7
+ */
8
+ export function createAnimationLayers(stacEndpoint: string, selectedRange: [string, string], selectedRangeItems: import("../types").TimelineExportEventDetail["selectedRangeItems"], selectedStac: import("vue").Ref<import("stac-ts").StacCollection | null>, filters: Record<string, import("../types").Filter>): Promise<{
9
+ layers: Record<string, any>[];
10
+ date: string;
11
+ }[]>;
12
+ /**
13
+ *
14
+ * @param {Record<string, any>[]} layers
15
+ * @returns {Record<string, any>[]}
16
+ */
17
+ export function anonimizeLayersCORS(layers: Record<string, any>[]): Record<string, any>[];
18
+ /**
19
+ * returns the list of layers that has visibility hidden
20
+ * @param {import("@eox/map").EoxLayer[]} layers
21
+ * @returns {{ collections: string[]; layers: string[] }}
22
+ */
23
+ export function getHiddenLayers(layers: import("@eox/map").EoxLayer[]): {
24
+ collections: string[];
25
+ layers: string[];
26
+ };
27
+ /**
28
+ * Iterates through a list of layers and updates layer.properties.visible to true
29
+ * if it is set to false
30
+ * @param {import("@eox/map").EoxLayer[]} layers
31
+ * @returns {import("@eox/map").EoxLayer[]}
32
+ */
33
+ export function restoreLayersVisibility(layers: import("@eox/map").EoxLayer[]): import("@eox/map").EoxLayer[];
34
+ /**
35
+ * Build STAC API filter string from TimeSlider filters
36
+ * @param {Record<string, import("../types").Filter>} filters
37
+ * @returns {string}
38
+ */
39
+ export function buildStacFilters(filters: Record<string, import("../types").Filter>): string;
@@ -0,0 +1,62 @@
1
+ export interface TimelineSelectionEventDetail {
2
+ date: [string, string];
3
+ instances: {
4
+ layer: import("ol/layer/Layer").default;
5
+ source: import("ol/source/Source").default;
6
+ };
7
+ filters: Record<string, Filter>;
8
+ selectedItems: {
9
+ [layerId: string]: SelectedItem[];
10
+ };
11
+ }
12
+ export interface SelectedItem {
13
+ className: string;
14
+ date: string;
15
+ group: string;
16
+ id: string;
17
+ itemId: string;
18
+ local: string;
19
+ originalDate: string;
20
+ property: string;
21
+ start: string;
22
+ type: string;
23
+ utc: string;
24
+ }
25
+ export interface TimelineExportEventDetail {
26
+ eoxMapConfig: {
27
+ layers: Record<string, any>[];
28
+ zoom: number;
29
+ center: [number, number];
30
+ };
31
+ filters: Record<string, Filter>;
32
+ instances: {
33
+ layer: import("ol/layer/Layer").default;
34
+ source: import("ol/source/Source").default;
35
+ };
36
+ selectedRangeItems: {
37
+ [layerId: string]: SelectedItem[];
38
+ };
39
+ generate: (args: {
40
+ mapLayers: Array<{
41
+ layers: Record<string, any>[];
42
+ date: string;
43
+ }>;
44
+ center?: [number, number];
45
+ zoom?: number;
46
+ }) => Promise<void>;
47
+ }
48
+ export interface Filter {
49
+ key: string;
50
+ title: string;
51
+ type: "range" | "multiselect" | "select";
52
+ expanded: boolean;
53
+ min: number;
54
+ max: number;
55
+ step: number;
56
+ state: {
57
+ min: number;
58
+ max: number;
59
+ };
60
+ dirty: boolean;
61
+ stringifiedState: string;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eodash/eodash",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "type": "module",
5
5
  "types": "./dist/types/core/client/types.d.ts",
6
6
  "files": [
@@ -69,42 +69,42 @@
69
69
  "docs:generate": "npm run build:cli && npm run build:types && typedoc --options typedoc.config.json"
70
70
  },
71
71
  "dependencies": {
72
- "@eox/chart": "^1.1.0",
73
- "@eox/drawtools": "1.1.5",
74
- "@eox/geosearch": "^1.1.0",
75
- "@eox/itemfilter": "^1.14.0",
76
- "@eox/jsonform": "^1.9.0",
77
- "@eox/layercontrol": "^1.4.0",
72
+ "@eox/chart": "^1.2.0",
73
+ "@eox/drawtools": "^1.5.0",
74
+ "@eox/feedback": "^1.2.0",
75
+ "@eox/geosearch": "^1.2.0",
76
+ "@eox/itemfilter": "^1.16.0",
77
+ "@eox/jsonform": "^1.12.0",
78
+ "@eox/layercontrol": "^1.7.0",
78
79
  "@eox/layout": "^1.0.0",
79
- "@eox/map": "^2.2.0",
80
- "@eox/stacinfo": "^1.1.0",
81
- "@eox/timecontrol": "^2.1.2",
82
- "@eox/timeslider": "https://pkg.pr.new/EOX-A/EOxElements/@eox/timeslider@9873028",
83
- "@eox/ui": "^0.6.0",
80
+ "@eox/map": "^2.5.1",
81
+ "@eox/stacinfo": "^1.2.0",
82
+ "@eox/timecontrol": "^2.4.0",
83
+ "@eox/ui": "^1.1.0",
84
84
  "@mdi/js": "^7.4.47",
85
- "@vitejs/plugin-vue": "^6.0.4",
85
+ "@vitejs/plugin-vue": "^6.0.6",
86
86
  "@vueuse/core": "^14.2.1",
87
87
  "animated-details": "gist:2912bb049fa906671807415eb0e87188",
88
- "axios": "^1.13.5",
89
- "axios-cache-interceptor": "^1.11.4",
88
+ "axios": "^1.15.2",
89
+ "axios-cache-interceptor": "^1.12.0",
90
90
  "color-legend-element": "^1.3.0",
91
91
  "commander": "^14.0.3",
92
- "core-js": "^3.48.0",
93
- "dotenv": "^17.3.1",
94
- "hyparquet": "^1.25.0",
92
+ "core-js": "^3.49.0",
93
+ "dotenv": "^17.4.2",
94
+ "hyparquet": "^1.25.6",
95
95
  "loglevel": "^1.9.2",
96
96
  "mustache": "^4.2.0",
97
97
  "pinia": "^3.0.4",
98
- "proj4": "^2.20.2",
99
- "sass": "^1.97.3",
100
- "stac-js": "^0.2.1",
101
- "stac-ts": "^1.0.4",
98
+ "proj4": "^2.20.8",
99
+ "sass": "^1.99.0",
100
+ "stac-js": "^0.4.2",
101
+ "stac-ts": "^1.0.5",
102
102
  "v-calendar": "3.0.0",
103
103
  "vega-embed": "^6.29.0",
104
104
  "vega-lite": "^5.23.0",
105
105
  "vite": "^7.3.1",
106
106
  "vite-plugin-vuetify": "^2.1.3",
107
- "vue": "^3.5.29",
107
+ "vue": "^3.5.33",
108
108
  "vuetify": "^3.11.4",
109
109
  "webfontloader": "^1.6.28"
110
110
  },
@@ -114,25 +114,25 @@
114
114
  "@pinia/testing": "^1.0.3",
115
115
  "@types/json-schema": "^7.0.15",
116
116
  "@types/mustache": "^4.2.6",
117
- "@types/node": "^25.3.1",
117
+ "@types/node": "^25.6.0",
118
118
  "@types/openlayers": "^4.6.23",
119
119
  "@types/webfontloader": "^1.6.38",
120
- "cypress": "^15.11.0",
120
+ "cypress": "^15.14.1",
121
121
  "eslint": "^9.39.2",
122
122
  "eslint-plugin-vue": "^9.33.0",
123
- "pkg-pr-new": "^0.0.63",
124
- "prettier": "^3.8.1",
125
- "rollup": "^4.59.0",
123
+ "pkg-pr-new": "^0.0.66",
124
+ "prettier": "^3.8.3",
125
+ "rollup": "^4.60.2",
126
126
  "terminate": "^2.8.0",
127
127
  "tsc-alias": "^1.8.16",
128
- "typedoc": "^0.28.17",
129
- "typedoc-plugin-markdown": "^4.10.0",
128
+ "typedoc": "^0.28.19",
129
+ "typedoc-plugin-markdown": "^4.11.0",
130
130
  "typedoc-plugin-vue": "^1.5.1",
131
131
  "typedoc-vitepress-theme": "^1.1.2",
132
132
  "typescript": "^5.9.3",
133
133
  "vitepress": "^1.6.4",
134
- "vitest": "^4.0.18",
135
- "vue-tsc": "3.2.5"
134
+ "vitest": "^4.1.5",
135
+ "vue-tsc": "3.2.7"
136
136
  },
137
137
  "engines": {
138
138
  "node": ">=20.15.1"
@@ -24,7 +24,7 @@
24
24
  <script setup>
25
25
  import "color-legend-element";
26
26
  import "@eox/timecontrol";
27
- import { computed, ref } from "vue";
27
+ import { computed, ref, watch } from "vue";
28
28
  import { mapEl, mapCompareEl } from "@/store/states";
29
29
  import { getColFromLayer } from "@/eodashSTAC/helpers";
30
30
  import {
@@ -36,6 +36,7 @@ import {
36
36
  import { storeToRefs } from "pinia";
37
37
  import { useSTAcStore } from "@/store/stac";
38
38
  import { bandsEditorInterface } from "@/utils/bands-editor";
39
+ import { useEmitLayersUpdate } from "@/composables";
39
40
 
40
41
  if (!customElements.get("eox-layercontrol")) {
41
42
  await import("@eox/layercontrol");
@@ -70,6 +71,7 @@ const config = {
70
71
  };
71
72
 
72
73
  const { selectedCompareStac, selectedStac } = storeToRefs(useSTAcStore());
74
+
73
75
  const showControls = computed(() => {
74
76
  if (props.map === "second") {
75
77
  return mapCompareEl.value !== null && selectedCompareStac.value !== null;
@@ -84,9 +86,20 @@ const mapElement = props.map === "second" ? mapCompareEl : mapEl;
84
86
  /** @type { import("vue").Ref<HTMLElement & Record<string,any> | null>} */
85
87
  const eoxLayercontrol = ref(null);
86
88
 
89
+ // eox-timecontrol re-fires datetime:updated after layer reassignment;
90
+ // dedupe by (collectionId, datetime)
91
+ const processedDatetimes = new Map();
92
+ watch([selectedStac, selectedCompareStac], () => processedDatetimes.clear());
93
+
87
94
  /** @param {CustomEvent<{layer:import('ol/layer').Layer; datetime:string;}>} evt */
88
95
  const handleDatetimeUpdate = async (evt) => {
89
96
  const { layer, datetime } = evt.detail;
97
+ const collectionId = layer.get("id")?.split(";:;")[0] ?? layer.get("id");
98
+ if (processedDatetimes.get(collectionId) === datetime) return;
99
+ // First event per collection is eox-timecontrol's mount echo.
100
+ const isFirstEvent = !processedDatetimes.has(collectionId);
101
+ processedDatetimes.set(collectionId, datetime);
102
+ if (isFirstEvent) return;
90
103
 
91
104
  const ec = await getColFromLayer(eodashCols, layer);
92
105
 
@@ -97,9 +110,10 @@ const handleDatetimeUpdate = async (evt) => {
97
110
  updatedLayers = await ec.updateLayerJson(
98
111
  datetime,
99
112
  layer.get("id"),
100
- props.map,
113
+ mapElement.value?.layers ?? [],
101
114
  );
102
115
  }
116
+ if (!updatedLayers?.length) return;
103
117
  /** @type {Record<String,any>[] | undefined} */
104
118
  const dataLayers = updatedLayers?.find(
105
119
  (l) => l?.properties?.id === "AnalysisGroup",
@@ -111,9 +125,14 @@ const handleDatetimeUpdate = async (evt) => {
111
125
  dl.properties.layerControlExpand = true;
112
126
  dl.properties.layerControlToolsExpand = true;
113
127
  });
114
- // assign layers to the map
115
128
  /** @type {HTMLElement & Record<string,any>} */
116
129
  (mapElement.value).layers = updatedLayers;
130
+ // Emit after layer assignment so listeners resolve against the new layer.
131
+ await useEmitLayersUpdate(
132
+ props.map === "second" ? "compareLayertime:updated" : "layertime:updated",
133
+ mapElement.value,
134
+ updatedLayers,
135
+ );
117
136
  }
118
137
  };
119
138
 
@@ -80,6 +80,28 @@
80
80
  {{ isGlobe ? "switch to 2D" : "switch to 3D" }}
81
81
  </div>
82
82
  </button>
83
+ <button
84
+ v-if="enableFeedback && eodash?.brand?.feedback"
85
+ class="primary small circle small-elevate"
86
+ @click="showFeedback = !showFeedback"
87
+ >
88
+ <i class="small"
89
+ ><svg viewBox="0 0 24 24">
90
+ <path :d="mdiMessageQuestion" />
91
+ </svg>
92
+ </i>
93
+ <div class="tooltip left">Provide Feedback</div>
94
+ </button>
95
+ <div
96
+ v-if="showFeedback && enableFeedback && eodash?.brand?.feedback"
97
+ class="feedback-container small-elevate"
98
+ >
99
+ <eox-feedback
100
+ :endpoint="eodash?.brand?.feedback?.endpoint"
101
+ .schema="eodash?.brand?.feedback?.schema"
102
+ @close="showFeedback = false"
103
+ ></eox-feedback>
104
+ </div>
83
105
  <eox-geosearch
84
106
  v-if="mapEl && !isGlobe && enableSearch"
85
107
  :for="mapEl"
@@ -110,7 +132,7 @@
110
132
  </div>
111
133
  </template>
112
134
  <script setup>
113
- import { useTransparentPanel } from "@/composables";
135
+ import { useTransparentPanel, useEodash } from "@/composables";
114
136
  import { changeMapProjection } from "@/store/actions";
115
137
  import {
116
138
  activeTemplate,
@@ -129,6 +151,7 @@ import {
129
151
  mdiPlus,
130
152
  mdiStarFourPointsCircleOutline,
131
153
  mdiEarth,
154
+ mdiMessageQuestion,
132
155
  } from "@mdi/js";
133
156
  import ExportState from "^/ExportState.vue";
134
157
  import { computed, ref } from "vue";
@@ -146,6 +169,12 @@ import {
146
169
  } from "./methods/btns";
147
170
  import "@eox/geosearch";
148
171
 
172
+ if (!customElements.get("eox-feedback")) {
173
+ //@ts-expect-error will be fixed in https://github.com/EOX-A/EOxElements/pull/2238
174
+ await import("@eox/feedback");
175
+ }
176
+ const eodash = useEodash();
177
+
149
178
  const {
150
179
  compareIndicators,
151
180
  changeProjection,
@@ -155,6 +184,7 @@ const {
155
184
  enableZoom,
156
185
  searchParams,
157
186
  enableGlobe,
187
+ enableFeedback,
158
188
  } = defineProps({
159
189
  exportMap: {
160
190
  type: Boolean,
@@ -193,6 +223,10 @@ const {
193
223
  type: Boolean,
194
224
  default: true,
195
225
  },
226
+ enableFeedback: {
227
+ type: Boolean,
228
+ default: true,
229
+ },
196
230
  });
197
231
 
198
232
  const { smAndDown } = useDisplay();
@@ -200,6 +234,7 @@ const popupWidth = computed(() => (smAndDown.value ? "80%" : "70%"));
200
234
  const popupHeight = computed(() => (smAndDown.value ? "90%" : "70%"));
201
235
 
202
236
  const showMapState = ref(false);
237
+ const showFeedback = ref(false);
203
238
  const isInCompareMode = computed(
204
239
  () =>
205
240
  activeTemplate.value ===
@@ -246,6 +281,16 @@ const opencageUrl = `https://api.opencagedata.com/geocode/v1/json?key=${opencage
246
281
  margin-bottom: 5px;
247
282
  background-color: var(--primary);
248
283
  }
284
+
285
+ .feedback-container {
286
+ position: absolute;
287
+ right: 70px;
288
+ pointer-events: auto;
289
+ z-index: 20;
290
+ border-radius: 8px;
291
+ min-width: 300px;
292
+ }
293
+
249
294
  /* Make sure buttons have pointer event */
250
295
  .geosearch-detached {
251
296
  pointer-events: auto !important;
@@ -61,6 +61,7 @@
61
61
  :enableZoom="(indicator || compareIndicator || poi) ? btnsProps.enableZoom : false
62
62
  "
63
63
  :enableGlobe="(indicator || compareIndicator || poi) ? btnsProps.enableGlobe : false"
64
+ :enableFeedback="(indicator || compareIndicator || poi) ? btnsProps.enableFeedback : false"
64
65
  :searchParams="btnsProps.searchParams"
65
66
  />
66
67
  </div>
@@ -69,7 +70,7 @@
69
70
  <script setup>
70
71
  import "@eox/map";
71
72
  import "@eox/map/src/plugins/advancedLayersAndSources";
72
- import { computed, onMounted, ref, toRaw, useTemplateRef } from "vue";
73
+ import { nextTick, computed, onMounted, ref, toRaw, useTemplateRef } from "vue";
73
74
  import {
74
75
  datetime,
75
76
  mapEl,
@@ -142,6 +143,7 @@ const props = defineProps({
142
143
  * searchParams?: object;
143
144
  * enableZoom?: boolean;
144
145
  * enableGlobe?: boolean;
146
+ * enableFeedback?: boolean;
145
147
  * enableCompareIndicators?: boolean | {
146
148
  * compareTemplate?:string;
147
149
  * fallbackTemplate?:string;
@@ -157,6 +159,7 @@ const props = defineProps({
157
159
  enableSearch: true,
158
160
  enableZoom: true,
159
161
  enableGlobe: true,
162
+ enableFeedback: true,
160
163
  searchParams: {},
161
164
  }),
162
165
  },
@@ -200,6 +203,7 @@ const btnsProps = computed(() => ({
200
203
  enableSearch: props.btns.enableSearch ?? true,
201
204
  enableZoom: props.btns.enableZoom ?? true,
202
205
  enableGlobe: props.btns.enableGlobe ?? true,
206
+ enableFeedback: props.btns.enableFeedback ?? true,
203
207
  searchParams: props.btns.searchParams,
204
208
  }));
205
209
 
@@ -241,7 +245,11 @@ const controls = computed(() => {
241
245
  controlsObj.MousePosition = {
242
246
  projection: "EPSG:4326",
243
247
  coordinateFormat: (/** @type {[number, number]} */ c) => {
244
- return `${c[1].toFixed(3)} °N, ${c[0].toFixed(3)} °E`;
248
+ const lat = c[1];
249
+ const lng = c[0];
250
+ const latStr = `${Math.abs(lat).toFixed(3)}°${lat >= 0 ? "N" : "S"}`;
251
+ const lngStr = `${Math.abs(lng).toFixed(3)}°${lng >= 0 ? "E" : "W"}`;
252
+ return `${latStr}, ${lngStr}`;
245
253
  },
246
254
  target: cursorCoordsRef.value,
247
255
  };
@@ -276,10 +284,10 @@ const eoxMapCompareLayers = ref([
276
284
  },
277
285
  ]);
278
286
 
279
- const animationOptions = {
280
- duration: 1200,
287
+ const animationOptions = ref({
288
+ duration: 0, // Initially set to 0 for an instant "jump"
281
289
  easing: inAndOut,
282
- };
290
+ });
283
291
 
284
292
  /** @type {import("vue").Ref<import("@eox/map").EOxMap | null>} */
285
293
  const eoxMap = ref(null);
@@ -330,6 +338,10 @@ onMounted(() => {
330
338
  props.zoomToExtent,
331
339
  selectedItem,
332
340
  );
341
+ // After the initial mount and "jump", set the animation duration for subsequent flyTo calls
342
+ nextTick(() => {
343
+ animationOptions.value.duration = 1200;
344
+ });
333
345
  });
334
346
 
335
347
  useUpdateTooltipProperties(eodashCollections, tooltipProperties);
@@ -10,6 +10,7 @@ import log from "loglevel";
10
10
  * } selectedIndicator
11
11
  * @param {EodashCollection[]} eodashCols
12
12
  * @param {string | import("stac-ts").StacItem | null} [timeOrItem] - time as a string, or a stac item
13
+ * @returns {Promise<Record<string, any>[]>}
13
14
  */
14
15
 
15
16
  export const createLayersConfig = async (
@@ -68,6 +69,7 @@ export const createLayersConfig = async (
68
69
  if (targetLayer) {
69
70
  if (Array.isArray(vl.roles) && vl.roles.includes("disable")) {
70
71
  targetLayer.properties.visible = false;
72
+ targetLayer.properties.layerControlExpand = false;
71
73
  } else if (Array.isArray(vl.roles) && vl.roles.includes("hidden")) {
72
74
  targetLayer.properties.layerControlHide = true;
73
75
  }
@@ -83,6 +83,10 @@ export const useInitMap = (
83
83
  const watching = selectedItem
84
84
  ? [selectedIndicator, datetime, selectedItem]
85
85
  : [selectedIndicator, datetime];
86
+ // Tags datetime values we set ourselves so the watcher skips its own echo.
87
+ /** @type {string | null} */
88
+ let internalDatetime = null;
89
+
86
90
  const stopIndicatorWatcher = watch(
87
91
  watching,
88
92
  async (updated, previous) => {
@@ -96,6 +100,16 @@ export const useInitMap = (
96
100
  );
97
101
 
98
102
  if (updatedStac) {
103
+ if (
104
+ internalDatetime !== null &&
105
+ updatedTime === internalDatetime &&
106
+ updatedStac?.id === previousStac?.id &&
107
+ updatedItem?.id === previousItem?.id
108
+ ) {
109
+ internalDatetime = null;
110
+ return;
111
+ }
112
+
99
113
  log.debug(
100
114
  "Selected Indicator watch triggered",
101
115
  updatedStac,
@@ -145,7 +159,6 @@ export const useInitMap = (
145
159
  JSON.parse(JSON.stringify(layersCollection)),
146
160
  );
147
161
  mapLayers.value = layersCollection;
148
-
149
162
  useEmitLayersUpdate(
150
163
  mapElement.value?.id === "compare"
151
164
  ? "compareTime:updated"
@@ -167,22 +180,27 @@ export const useInitMap = (
167
180
  endInterval,
168
181
  );
169
182
  }
183
+ let resolvedTime = updatedItem ?? updatedTime;
170
184
  if (
171
185
  !updatedItem &&
172
186
  endInterval !== null &&
173
187
  endInterval.toISOString() !== datetime.value &&
174
188
  !isFirstLoad.value
175
189
  ) {
176
- datetime.value = endInterval.toISOString();
190
+ resolvedTime = endInterval.toISOString();
191
+ internalDatetime = resolvedTime;
192
+ datetime.value = resolvedTime;
177
193
  } else if (isFirstLoad.value && !datetime.value && endInterval) {
178
- datetime.value = endInterval.toISOString();
194
+ resolvedTime = endInterval.toISOString();
195
+ internalDatetime = resolvedTime;
196
+ datetime.value = resolvedTime;
179
197
  }
180
198
 
181
199
  /** @type {Record<string,any>[]} */
182
200
  layersCollection = await createLayersConfig(
183
201
  updatedStac,
184
202
  eodashCols,
185
- updatedItem ?? updatedTime,
203
+ resolvedTime,
186
204
  );
187
205
 
188
206
  if (zoomToExtent) {
@@ -251,7 +269,7 @@ export const useUpdateTooltipProperties = (
251
269
  * @param {string} evt */
252
270
  const listenTo = (evt) =>
253
271
  enableCompare ? evt.includes("compare") : !evt.includes("compare");
254
- useOnLayersUpdate(async (evt, _payload) => {
272
+ useOnLayersUpdate(async (evt) => {
255
273
  if (!listenTo(evt)) {
256
274
  return;
257
275
  }
@@ -60,7 +60,7 @@ import {
60
60
  compareChartSpec,
61
61
  areChartsSeparateLayout,
62
62
  } from "@/store/states";
63
- import { download, getDrawToolsProperty } from "./methods/utils";
63
+ import { download, getDrawToolsProperties } from "./methods/utils";
64
64
  import { compareJobs, jobs } from "./states";
65
65
  import { mdiCogPlayOutline, mdiDownloadCircleOutline } from "@mdi/js";
66
66
 
@@ -111,13 +111,15 @@ const currentIndicator = enableCompare ? compareIndicator : indicator;
111
111
  const currentJobs = enableCompare ? compareJobs : jobs;
112
112
 
113
113
  const jsonformKey = computed(
114
- () => currentIndicator.value + mapElement.value?.id,
114
+ () =>
115
+ currentIndicator.value +
116
+ mapElement.value?.id +
117
+ JSON.stringify(jsonformSchema.value),
115
118
  );
116
119
 
117
120
  useInitProcess({
118
121
  selectedStac: currentSelectedStac,
119
- mapElement: mapElement.value,
120
- jsonformEl,
122
+ mapElement,
121
123
  jsonformSchema,
122
124
  isProcessed,
123
125
  processResults,
@@ -144,15 +146,17 @@ const downloadResults = () => {
144
146
  };
145
147
 
146
148
  const startProcess = async () => {
147
- const drawToolsProperty = getDrawToolsProperty(jsonformSchema.value);
148
- const propertyIsEmpty =
149
- drawToolsProperty &&
150
- //@ts-expect-error jsonfrom.value is not typed
151
- Array.isArray(jsonformEl.value?.value[drawToolsProperty]) &&
152
- //@ts-expect-error jsonfrom.value is not typed
153
- !jsonformEl.value?.value[drawToolsProperty].length;
154
-
155
- if (propertyIsEmpty) {
149
+ const drawToolsProperties = getDrawToolsProperties(jsonformSchema.value);
150
+
151
+ const anyDrawtoolPropertyEmpty = drawToolsProperties.some(
152
+ (drawToolsProperty) =>
153
+ //@ts-expect-error jsonfrom.value is not typed
154
+ Array.isArray(jsonformEl.value?.value[drawToolsProperty]) &&
155
+ //@ts-expect-error jsonfrom.value is not typed
156
+ !jsonformEl.value?.value[drawToolsProperty].length,
157
+ );
158
+
159
+ if (anyDrawtoolPropertyEmpty) {
156
160
  isProcessed.value = false;
157
161
  const usedChartSpec = enableCompare ? compareChartSpec : chartSpec;
158
162
  usedChartSpec.value = null;
@@ -106,16 +106,27 @@ export async function updateJobsStatus(jobs, indicator) {
106
106
  jobsUrls.map((url) =>
107
107
  axios
108
108
  .get(url, { params: { t: Date.now() } })
109
- .then((response) => response.data),
109
+ .then((response) => response.data)
110
+ .catch((error) => {
111
+ if (axios.isAxiosError(error)) {
112
+ console.warn(
113
+ `Job URL not accessible: ${url}. Maybe was deleted from the processing endpoint or endpoint is unavailable. Skipping...`,
114
+ );
115
+ return null;
116
+ }
117
+ throw error;
118
+ }),
110
119
  ),
111
120
  );
112
- jobResults.sort((a, b) => {
113
- return (
114
- new Date(b.job_start_datetime).getTime() -
115
- new Date(a.job_start_datetime).getTime()
116
- );
117
- });
118
- jobs.value.splice(0, jobs.value.length, ...jobResults);
121
+ const validResults = jobResults
122
+ .filter((job) => job !== null)
123
+ .sort((a, b) => {
124
+ return (
125
+ new Date(b.job_start_datetime).getTime() -
126
+ new Date(a.job_start_datetime).getTime()
127
+ );
128
+ });
129
+ jobs.value.splice(0, jobs.value.length, ...validResults);
119
130
  }
120
131
 
121
132
  /**