@eodash/eodash 5.0.0-rc.2.4 → 5.0.0-rc.3
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.
- package/core/client/composables/DefineWidgets.js +8 -1
- package/core/client/eodash.js +9 -9
- package/core/client/eodashSTAC/createLayers.js +4 -17
- package/core/client/eodashSTAC/helpers.js +23 -0
- package/core/client/store/stac.js +3 -1
- package/core/client/types.ts +127 -21
- package/core/client/utils/states.js +9 -0
- package/core/client/vite-env.d.ts +0 -13
- package/dist/client/{DashboardLayout-D1UcB3RV.js → DashboardLayout-t_PavJPO.js} +2 -2
- package/dist/client/{DynamicWebComponent-DtZ_mHL9.js → DynamicWebComponent-y07rVJch.js} +1 -1
- package/dist/client/{EodashDatePicker-CYU0MZX5.js → EodashDatePicker-CcOfyzGD.js} +3 -83
- package/dist/client/{EodashItemFilter-SE9oW3oZ.js → EodashItemFilter-B9HCvIMi.js} +1 -1
- package/dist/client/{EodashLayerControl-BuGe29Nt.js → EodashLayerControl-KStn7Nb_.js} +8 -2
- package/dist/client/{EodashLayoutSwitcher-6wLl-Gtd.js → EodashLayoutSwitcher-DqeFO3RN.js} +2 -2
- package/dist/client/{EodashMapBtns-BWWu6eHG.js → EodashMapBtns-5BF27qJB.js} +36 -12
- package/dist/client/{EodashStacInfo-DjRSGLHM.js → EodashStacInfo-C_hDy6Pd.js} +7 -1
- package/dist/client/{EodashTools-CJ4hBH_X.js → EodashTools-BXflvRf8.js} +5 -4
- package/dist/client/{ExportState-BqnlEpzR.js → ExportState-C0QRemK1.js} +27 -12
- package/dist/client/{Footer-C_3WrfI4.js → Footer-7VGyGUAs.js} +1 -1
- package/dist/client/{Header-D_hcGpNG.js → Header-BQJnXHYq.js} +3 -3
- package/dist/client/{MobileLayout-CDbupC9v.js → MobileLayout-b8nQ-Vyl.js} +5 -5
- package/dist/client/{PopUp-Ba6mY2jQ.js → PopUp-DgNrh9Df.js} +3 -3
- package/dist/client/ProcessList-C62SOVO6.js +484 -0
- package/dist/client/{VImg-Yc9F9pYq.js → VImg-D4eT3IQ1.js} +2 -2
- package/dist/client/{VMain-BiS7HPEk.js → VMain-C3hN2-H3.js} +1 -1
- package/dist/client/{VOverlay-B9mxXaCv.js → VOverlay-tAeNygaA.js} +15 -6
- package/dist/client/{VTooltip-XJLaLrZQ.js → VTooltip-B0Q3iHMZ.js} +3 -3
- package/dist/client/{WidgetsContainer-DRVb_73N.js → WidgetsContainer-CtDHfCYf.js} +1 -1
- package/dist/client/{asWebComponent-DZpMGxEY.js → asWebComponent-BJ2NWunV.js} +100 -95
- package/dist/client/eo-dash.css +2 -2
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{forwardRefs-BtkfywIE.js → forwardRefs-CIFAqXaZ.js} +9 -9
- package/dist/client/{EodashMap-DhVCoYMi.js → index-BQ16n4Sk.js} +103 -78
- package/dist/client/index-Cv7HBz49.js +85 -0
- package/dist/client/{EodashProcess-GSj_LMsK.js → index-Da5xXX6Q.js} +349 -443
- package/dist/client/{index-f55xuyof.js → index-DvcUndod.js} +1 -1
- package/dist/client/{transition-CtL4BoVi.js → transition-Cdb4K27U.js} +1 -1
- package/dist/types/core/client/components/MobileLayout.vue.d.ts +9 -9
- package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +11 -6
- package/dist/types/core/client/eodashSTAC/createLayers.d.ts +6 -5
- package/dist/types/core/client/eodashSTAC/helpers.d.ts +383 -2
- package/dist/types/core/client/types.d.ts +85 -19
- package/dist/types/core/client/utils/states.d.ts +7 -0
- package/dist/types/widgets/EodashDatePicker.vue.d.ts +4 -4
- package/dist/types/widgets/EodashItemFilter.vue.d.ts +18 -18
- package/dist/types/widgets/EodashLayerControl.vue.d.ts +2 -2
- package/dist/types/widgets/EodashLayoutSwitcher.vue.d.ts +2 -2
- package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +9 -0
- package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -0
- package/dist/types/widgets/EodashMapBtns.vue.d.ts +8 -2
- package/dist/types/widgets/EodashProcess/ProcessList.vue.d.ts +2 -0
- package/dist/types/widgets/EodashProcess/methods/async.d.ts +45 -0
- package/dist/types/widgets/EodashProcess/methods/composables.d.ts +19 -0
- package/dist/types/widgets/EodashProcess/methods/handling.d.ts +78 -0
- package/dist/types/widgets/EodashProcess/methods/outputs.d.ts +54 -0
- package/dist/types/widgets/EodashProcess/methods/utils.d.ts +42 -0
- package/dist/types/widgets/EodashStacInfo.vue.d.ts +12 -12
- package/dist/types/widgets/EodashTools.vue.d.ts +44 -6
- package/dist/types/widgets/PopUp.vue.d.ts +4 -4
- package/package.json +30 -29
- package/widgets/EodashLayerControl.vue +8 -1
- package/widgets/{EodashMap.vue → EodashMap/index.vue} +53 -29
- package/widgets/EodashMap/methods/create-layers-config.js +151 -0
- package/{core/client/composables/EodashMap.js → widgets/EodashMap/methods/index.js} +4 -153
- package/widgets/EodashMapBtns.vue +33 -7
- package/widgets/EodashProcess/ProcessList.vue +82 -0
- package/widgets/EodashProcess/index.vue +186 -0
- package/widgets/EodashProcess/methods/async.js +209 -0
- package/widgets/EodashProcess/methods/composables.js +129 -0
- package/widgets/EodashProcess/methods/handling.js +254 -0
- package/widgets/EodashProcess/methods/outputs.js +216 -0
- package/widgets/EodashProcess/methods/utils.js +138 -0
- package/widgets/EodashStacInfo.vue +6 -0
- package/widgets/EodashTools.vue +1 -0
- package/core/client/composables/EodashProcess.js +0 -624
- package/dist/types/core/client/composables/EodashMap.d.ts +0 -6
- package/dist/types/core/client/composables/EodashProcess.d.ts +0 -162
- package/widgets/EodashProcess.vue +0 -208
- /package/dist/types/widgets/{EodashMap.vue.d.ts → EodashMap/index.vue.d.ts} +0 -0
- /package/dist/types/widgets/{EodashProcess.vue.d.ts → EodashProcess/index.vue.d.ts} +0 -0
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
/>
|
|
17
17
|
<v-btn
|
|
18
18
|
class="map-btn"
|
|
19
|
-
:icon="[
|
|
19
|
+
:icon="[compareIcon]"
|
|
20
20
|
v-if="compareIndicators"
|
|
21
|
-
@click="
|
|
21
|
+
@click="onCompareClick"
|
|
22
22
|
/>
|
|
23
23
|
<PopUp
|
|
24
24
|
v-model="showCompareIndicators"
|
|
@@ -39,14 +39,17 @@
|
|
|
39
39
|
import { makePanelTransparent } from "@/composables";
|
|
40
40
|
import { changeMapProjection, setActiveTemplate } from "@/store/actions";
|
|
41
41
|
import { availableMapProjection } from "@/store/states";
|
|
42
|
-
import { mdiCompare, mdiEarthBox, mdiMapPlus } from "@mdi/js";
|
|
42
|
+
import { mdiCompare, mdiCompareRemove, mdiEarthBox, mdiMapPlus } from "@mdi/js";
|
|
43
43
|
import ExportState from "^/ExportState.vue";
|
|
44
|
-
import { computed, ref } from "vue";
|
|
44
|
+
import { computed, ref, triggerRef } from "vue";
|
|
45
45
|
import PopUp from "./PopUp.vue";
|
|
46
46
|
import EodashItemFilter from "./EodashItemFilter.vue";
|
|
47
47
|
import { useDisplay } from "vuetify/lib/framework.mjs";
|
|
48
|
+
import { useSTAcStore } from "@/store/stac";
|
|
49
|
+
import { storeToRefs } from "pinia";
|
|
50
|
+
import { switchToCompare } from "@/utils/states";
|
|
48
51
|
|
|
49
|
-
defineProps({
|
|
52
|
+
const { compareIndicators, changeProjection, exportMap } = defineProps({
|
|
50
53
|
exportMap: {
|
|
51
54
|
type: Boolean,
|
|
52
55
|
default: true,
|
|
@@ -56,22 +59,45 @@ defineProps({
|
|
|
56
59
|
default: true,
|
|
57
60
|
},
|
|
58
61
|
compareIndicators: {
|
|
59
|
-
type
|
|
62
|
+
/** @type {import("vue").PropType<boolean | {compareTemplate?:string;fallbackTemplate?:string}> }*/
|
|
63
|
+
type: [Boolean, Object],
|
|
60
64
|
default: true,
|
|
61
65
|
},
|
|
62
66
|
});
|
|
67
|
+
const { selectedStac, selectedCompareStac } = storeToRefs(useSTAcStore());
|
|
63
68
|
const { smAndDown } = useDisplay();
|
|
64
69
|
const popupWidth = computed(() => (smAndDown ? "70%" : "500px"));
|
|
65
70
|
const popupHeight = computed(() => (smAndDown ? "90%" : "500px"));
|
|
66
71
|
|
|
67
72
|
const showMapState = ref(false);
|
|
68
73
|
const showCompareIndicators = ref(false);
|
|
74
|
+
const compareIcon = computed(() =>
|
|
75
|
+
switchToCompare.value ? mdiCompare : mdiCompareRemove,
|
|
76
|
+
);
|
|
77
|
+
const onCompareClick = () => {
|
|
78
|
+
if (switchToCompare.value) {
|
|
79
|
+
showCompareIndicators.value = !showCompareIndicators.value;
|
|
80
|
+
} else {
|
|
81
|
+
switchToCompare.value = true;
|
|
82
|
+
const fallbackTemplate =
|
|
83
|
+
(typeof compareIndicators === "object" &&
|
|
84
|
+
compareIndicators.fallbackTemplate) ||
|
|
85
|
+
"expert";
|
|
86
|
+
selectedCompareStac.value = null;
|
|
87
|
+
setActiveTemplate(fallbackTemplate);
|
|
88
|
+
triggerRef(selectedStac);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
69
91
|
|
|
70
92
|
/** @type {import("vue").Ref<HTMLDivElement|null>} */
|
|
71
93
|
const rootRef = ref(null);
|
|
72
94
|
|
|
73
95
|
const onSelectCompareIndicator = () => {
|
|
74
|
-
|
|
96
|
+
const compareTemplate =
|
|
97
|
+
(typeof compareIndicators === "object" &&
|
|
98
|
+
compareIndicators.compareTemplate) ||
|
|
99
|
+
"compare";
|
|
100
|
+
setActiveTemplate(compareTemplate);
|
|
75
101
|
showCompareIndicators.value = !showCompareIndicators.value;
|
|
76
102
|
};
|
|
77
103
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<v-table
|
|
4
|
+
v-if="jobs.length"
|
|
5
|
+
density="compact"
|
|
6
|
+
style="background-color: transparent"
|
|
7
|
+
>
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th class="text-left">Executed on</th>
|
|
11
|
+
<th class="text-left">Status</th>
|
|
12
|
+
<th class="text-left"></th>
|
|
13
|
+
<th class="text-left"></th>
|
|
14
|
+
<th class="text-left"></th>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody>
|
|
18
|
+
<tr v-for="item in jobs" :key="item.date">
|
|
19
|
+
<td>
|
|
20
|
+
{{ new Date(item.job_start_datetime).toISOString().slice(0, 16) }}
|
|
21
|
+
</td>
|
|
22
|
+
<td>{{ item.status }}</td>
|
|
23
|
+
<td style="padding: 0px">
|
|
24
|
+
<v-btn
|
|
25
|
+
:disabled="item.status !== 'successful'"
|
|
26
|
+
color="primary"
|
|
27
|
+
@click="loadProcess(item, selectedStac)"
|
|
28
|
+
:icon="[mdiUploadBox]"
|
|
29
|
+
variant="text"
|
|
30
|
+
v-tooltip="'Load results to map'"
|
|
31
|
+
>
|
|
32
|
+
</v-btn>
|
|
33
|
+
</td>
|
|
34
|
+
<td style="padding: 0px">
|
|
35
|
+
<v-btn
|
|
36
|
+
:disabled="item.status !== 'successful'"
|
|
37
|
+
color="primary"
|
|
38
|
+
@click="downloadPreviousResults(item, selectedStac)"
|
|
39
|
+
:icon="[mdiDownloadBox]"
|
|
40
|
+
variant="text"
|
|
41
|
+
v-tooltip="'Download results'"
|
|
42
|
+
>
|
|
43
|
+
</v-btn>
|
|
44
|
+
</td>
|
|
45
|
+
<td style="padding: 0px">
|
|
46
|
+
<v-btn
|
|
47
|
+
color="#ff5252"
|
|
48
|
+
@click="deleteJob(item)"
|
|
49
|
+
:icon="[mdiTrashCanOutline]"
|
|
50
|
+
variant="text"
|
|
51
|
+
v-tooltip="'Remove job'"
|
|
52
|
+
>
|
|
53
|
+
</v-btn>
|
|
54
|
+
</td>
|
|
55
|
+
</tr>
|
|
56
|
+
</tbody>
|
|
57
|
+
</v-table>
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
60
|
+
<script setup>
|
|
61
|
+
import { mdiUploadBox, mdiDownloadBox, mdiTrashCanOutline } from "@mdi/js";
|
|
62
|
+
import { onMounted, toRefs } from "vue";
|
|
63
|
+
import { useSTAcStore } from "@/store/stac";
|
|
64
|
+
import { indicator } from "@/store/states";
|
|
65
|
+
import {
|
|
66
|
+
deleteJob,
|
|
67
|
+
downloadPreviousResults,
|
|
68
|
+
jobs,
|
|
69
|
+
loadProcess,
|
|
70
|
+
updateJobsStatus,
|
|
71
|
+
} from "./methods/async";
|
|
72
|
+
|
|
73
|
+
const { selectedStac } = toRefs(useSTAcStore());
|
|
74
|
+
|
|
75
|
+
onMounted(() => updateJobsStatus(jobs, indicator));
|
|
76
|
+
</script>
|
|
77
|
+
<style lang="scss">
|
|
78
|
+
div.v-table__wrapper {
|
|
79
|
+
overflow: hidden !important;
|
|
80
|
+
height: max-content !important;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="container" class="process-container">
|
|
3
|
+
<ProcessList />
|
|
4
|
+
|
|
5
|
+
<eox-jsonform
|
|
6
|
+
v-if="jsonformSchema"
|
|
7
|
+
ref="jsonformEl"
|
|
8
|
+
.schema="jsonformSchema"
|
|
9
|
+
></eox-jsonform>
|
|
10
|
+
<eox-chart
|
|
11
|
+
class="chart"
|
|
12
|
+
v-if="isProcessed && chartSpec"
|
|
13
|
+
.spec="toRaw(chartSpec)"
|
|
14
|
+
.dataValues="toRaw(chartData)"
|
|
15
|
+
@click:item="onChartClick"
|
|
16
|
+
:style="chartStyles"
|
|
17
|
+
/>
|
|
18
|
+
<div style="text-align: right">
|
|
19
|
+
<v-btn
|
|
20
|
+
v-if="!autoExec"
|
|
21
|
+
:loading="loading"
|
|
22
|
+
style="margin-right: 20px"
|
|
23
|
+
@click="startProcess"
|
|
24
|
+
color="primary"
|
|
25
|
+
>
|
|
26
|
+
Execute
|
|
27
|
+
</v-btn>
|
|
28
|
+
<v-btn
|
|
29
|
+
v-if="processResults.length && isProcessed && !isAsync"
|
|
30
|
+
color="primary"
|
|
31
|
+
@click="downloadResults"
|
|
32
|
+
>
|
|
33
|
+
Download
|
|
34
|
+
</v-btn>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
<script setup>
|
|
39
|
+
import "@eox/chart";
|
|
40
|
+
import "@eox/drawtools";
|
|
41
|
+
import "@eox/jsonform";
|
|
42
|
+
import { useSTAcStore } from "@/store/stac";
|
|
43
|
+
import { storeToRefs } from "pinia";
|
|
44
|
+
import { computed, ref, toRaw, useTemplateRef } from "vue";
|
|
45
|
+
import ProcessList from "./ProcessList.vue";
|
|
46
|
+
import { handleProcesses, onChartClick } from "./methods/handling";
|
|
47
|
+
import { useInitProcess, useAutoExec } from "./methods/composables";
|
|
48
|
+
import { jobs, updateJobsStatus } from "./methods/async";
|
|
49
|
+
import { indicator } from "@/store/states";
|
|
50
|
+
import { download } from "./methods/utils";
|
|
51
|
+
|
|
52
|
+
/** @type {import("vue").Ref<import("vega").Spec|null>} */
|
|
53
|
+
const chartSpec = ref(null);
|
|
54
|
+
|
|
55
|
+
/** @type {import("vue").Ref<Record<string,any>|null>} */
|
|
56
|
+
const chartData = ref(null);
|
|
57
|
+
const isProcessed = ref(false);
|
|
58
|
+
|
|
59
|
+
/** @type {import("vue").Ref<Record<string,any>|null>} */
|
|
60
|
+
const jsonformSchema = ref(null);
|
|
61
|
+
|
|
62
|
+
const jsonformEl =
|
|
63
|
+
/** @type {Readonly<import("vue").ShallowRef<import("@eox/jsonform").EOxJSONForm | null>>} */ (
|
|
64
|
+
useTemplateRef("jsonformEl")
|
|
65
|
+
);
|
|
66
|
+
const isAsync = computed(
|
|
67
|
+
() =>
|
|
68
|
+
selectedStac.value?.links.filter((l) => l.endpoint === "eoxhub_workspaces")
|
|
69
|
+
.length,
|
|
70
|
+
);
|
|
71
|
+
const containerEl = useTemplateRef("container");
|
|
72
|
+
|
|
73
|
+
const loading = ref(false);
|
|
74
|
+
|
|
75
|
+
const autoExec = ref(false);
|
|
76
|
+
|
|
77
|
+
const isPolling = ref(false);
|
|
78
|
+
/** @type {import("vue").Ref<any[]>} */
|
|
79
|
+
const processResults = ref([]);
|
|
80
|
+
|
|
81
|
+
const { selectedStac } = storeToRefs(useSTAcStore());
|
|
82
|
+
|
|
83
|
+
useInitProcess({
|
|
84
|
+
//@ts-expect-error TODO
|
|
85
|
+
selectedStac,
|
|
86
|
+
jsonformEl,
|
|
87
|
+
jsonformSchema,
|
|
88
|
+
chartSpec,
|
|
89
|
+
isProcessed,
|
|
90
|
+
processResults,
|
|
91
|
+
loading,
|
|
92
|
+
isPolling,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const downloadResults = () => {
|
|
96
|
+
processResults.value.forEach((result) => {
|
|
97
|
+
if (!result) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let fileName = "";
|
|
101
|
+
if (typeof result === "string") {
|
|
102
|
+
fileName = result.includes("/")
|
|
103
|
+
? (result.split("/").pop() ?? "")
|
|
104
|
+
: result;
|
|
105
|
+
fileName = fileName.includes("?") ? fileName.split("?")[0] : fileName;
|
|
106
|
+
} else {
|
|
107
|
+
fileName = selectedStac.value?.id + "_process_results.json";
|
|
108
|
+
}
|
|
109
|
+
download(fileName, result);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const startProcess = async () => {
|
|
114
|
+
/** @param {*} jsonformSchema */
|
|
115
|
+
const getDrawToolsProperty = (jsonformSchema) => {
|
|
116
|
+
for (const property in jsonformSchema.properties) {
|
|
117
|
+
if (jsonformSchema.properties[property]?.options?.drawtools) {
|
|
118
|
+
return property;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const drawToolsProperty = getDrawToolsProperty(jsonformSchema.value);
|
|
123
|
+
const propertyIsEmpty =
|
|
124
|
+
drawToolsProperty &&
|
|
125
|
+
//@ts-expect-error jsonfrom.value is not typed
|
|
126
|
+
Array.isArray(jsonformEl.value?.value[drawToolsProperty]) &&
|
|
127
|
+
//@ts-expect-error jsonfrom.value is not typed
|
|
128
|
+
!jsonformEl.value?.value[drawToolsProperty].length;
|
|
129
|
+
|
|
130
|
+
if (propertyIsEmpty) {
|
|
131
|
+
isProcessed.value = false;
|
|
132
|
+
chartSpec.value = null;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const errors = jsonformEl.value?.editor.validate();
|
|
136
|
+
if (errors?.length) {
|
|
137
|
+
console.warn("[eodash] Form validation failed", errors);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
processResults.value = [];
|
|
142
|
+
|
|
143
|
+
await handleProcesses({
|
|
144
|
+
jsonformEl,
|
|
145
|
+
jsonformSchema,
|
|
146
|
+
chartSpec,
|
|
147
|
+
chartData,
|
|
148
|
+
loading,
|
|
149
|
+
//@ts-expect-error TODO
|
|
150
|
+
selectedStac,
|
|
151
|
+
isProcessed,
|
|
152
|
+
isPolling,
|
|
153
|
+
processResults,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
isProcessed.value = true;
|
|
157
|
+
if (isAsync.value) updateJobsStatus(jobs, indicator);
|
|
158
|
+
};
|
|
159
|
+
useAutoExec(autoExec, jsonformEl, jsonformSchema, startProcess);
|
|
160
|
+
|
|
161
|
+
const chartStyles = computed(() => {
|
|
162
|
+
/** @type {Record<string,string> }*/
|
|
163
|
+
const styles = {};
|
|
164
|
+
if (!chartSpec.value?.["height"]) {
|
|
165
|
+
styles["height"] =
|
|
166
|
+
Math.max(
|
|
167
|
+
(containerEl.value?.offsetHeight ?? 0) -
|
|
168
|
+
(jsonformEl.value?.offsetHeight ?? 0),
|
|
169
|
+
200,
|
|
170
|
+
) + "px";
|
|
171
|
+
}
|
|
172
|
+
return styles;
|
|
173
|
+
});
|
|
174
|
+
</script>
|
|
175
|
+
<style>
|
|
176
|
+
.process-container {
|
|
177
|
+
height: 100%;
|
|
178
|
+
overflow-y: auto;
|
|
179
|
+
}
|
|
180
|
+
eox-chart {
|
|
181
|
+
--background-color: transparent;
|
|
182
|
+
}
|
|
183
|
+
eox-jsonform {
|
|
184
|
+
padding: 0.7em;
|
|
185
|
+
}
|
|
186
|
+
</style>
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { indicator, mapEl } from "@/store/states";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { ref } from "vue";
|
|
4
|
+
import { createLayerDefinition, download } from "./utils";
|
|
5
|
+
import log from "loglevel";
|
|
6
|
+
import { getLayers } from "@/store/actions";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The list of job result from the server
|
|
10
|
+
* {job_start_datetime: string, job_end_datetime: string,status: string}
|
|
11
|
+
* @type {import("vue").Ref<any[]>}
|
|
12
|
+
**/
|
|
13
|
+
export const jobs = ref([]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Polls the process status and fetches a result item when the process is successful.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} params - Parameters for polling the process status.
|
|
19
|
+
* @param {string} params.processUrl - The URL of the process JSON report.
|
|
20
|
+
* @param {import("vue").Ref<boolean>} params.isPolling - checks wether the polling should continue
|
|
21
|
+
* @param {number} [params.pollInterval=5000] - The interval (in milliseconds) between polling attempts.
|
|
22
|
+
* @param {number} [params.maxRetries=60] - The maximum number of polling attempts.
|
|
23
|
+
* @returns {Promise<JSON>} The fetched results JSON.
|
|
24
|
+
* @throws {Error} If the process does not complete successfully within the maximum retries.
|
|
25
|
+
*/
|
|
26
|
+
export async function pollProcessStatus({
|
|
27
|
+
processUrl,
|
|
28
|
+
isPolling,
|
|
29
|
+
pollInterval = 10000,
|
|
30
|
+
maxRetries = 560,
|
|
31
|
+
}) {
|
|
32
|
+
let retries = 0;
|
|
33
|
+
isPolling.value = true;
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
updateJobsStatus(jobs, indicator);
|
|
36
|
+
}, 500);
|
|
37
|
+
while (retries < maxRetries && isPolling.value) {
|
|
38
|
+
try {
|
|
39
|
+
// Fetch the process JSON report
|
|
40
|
+
const cacheBuster = new Date().getTime(); // Add a timestamp for cache busting
|
|
41
|
+
const response = await axios.get(`${processUrl}?t=${cacheBuster}`);
|
|
42
|
+
const processReport = response.data;
|
|
43
|
+
|
|
44
|
+
// Check if the status is "successful"
|
|
45
|
+
if (processReport.status === "successful") {
|
|
46
|
+
console.log("Process completed successfully. Fetching result item...");
|
|
47
|
+
|
|
48
|
+
// Extract the result item URL
|
|
49
|
+
const resultsUrl = processReport.links[1].href;
|
|
50
|
+
if (!resultsUrl) {
|
|
51
|
+
throw new Error(`Result links not found in the process report.`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fetch the result item
|
|
55
|
+
const resultResponse = await axios.get(resultsUrl);
|
|
56
|
+
console.log("Result file fetched successfully:", resultResponse.data);
|
|
57
|
+
return resultResponse.data; // Return the json result list
|
|
58
|
+
}
|
|
59
|
+
if (processReport.status === "failed") {
|
|
60
|
+
isPolling.value = false;
|
|
61
|
+
throw new Error("Process failed.", processReport);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Log the current status if not successful
|
|
65
|
+
console.log(
|
|
66
|
+
`Status: ${processReport.status}. Retrying in ${pollInterval / 1000} seconds...`,
|
|
67
|
+
);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
console.error("Error while polling process status:", error.message);
|
|
71
|
+
} else {
|
|
72
|
+
console.error("Unknown error occurred:", error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Wait for the next poll
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
78
|
+
retries++;
|
|
79
|
+
}
|
|
80
|
+
if (!isPolling.value) {
|
|
81
|
+
console.warn("Polling was stopped before the process was completed.");
|
|
82
|
+
return JSON.parse("{}");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new Error(
|
|
86
|
+
"Max retries reached. Process did not complete successfully.",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
*
|
|
92
|
+
* @param {*} jobs
|
|
93
|
+
* @param {*} indicator
|
|
94
|
+
*/
|
|
95
|
+
export async function updateJobsStatus(jobs, indicator) {
|
|
96
|
+
/** @type {string[]} */
|
|
97
|
+
const jobsUrls = JSON.parse(localStorage.getItem(indicator.value) || "[]");
|
|
98
|
+
const jobResults = await Promise.all(
|
|
99
|
+
jobsUrls.map((url) => fetch(url).then((response) => response.json())),
|
|
100
|
+
);
|
|
101
|
+
jobResults.sort((a, b) => {
|
|
102
|
+
return (
|
|
103
|
+
new Date(b.job_start_datetime).getTime() -
|
|
104
|
+
new Date(a.job_start_datetime).getTime()
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
jobs.value = jobResults;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Removes a job from the local storage and updates the job status
|
|
112
|
+
* @param {*} jobObject
|
|
113
|
+
*/
|
|
114
|
+
export const deleteJob = async (jobObject) => {
|
|
115
|
+
/** @type {string[]} */
|
|
116
|
+
const jobsUrls = JSON.parse(localStorage.getItem(indicator.value) || "[]");
|
|
117
|
+
const newJobs = jobsUrls.filter((url) => !url.includes(jobObject.jobID));
|
|
118
|
+
localStorage.setItem(indicator.value, JSON.stringify(newJobs));
|
|
119
|
+
updateJobsStatus(jobs, indicator);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Downloads an existing process results
|
|
124
|
+
* @param {*} jobObject
|
|
125
|
+
* @param {*} selectedStac
|
|
126
|
+
*/
|
|
127
|
+
export const downloadPreviousResults = async (jobObject, selectedStac) => {
|
|
128
|
+
/** @type {any[]} */
|
|
129
|
+
const results = [];
|
|
130
|
+
await fetch(jobObject.links[1].href)
|
|
131
|
+
.then((response) => response.json())
|
|
132
|
+
.then((data) => {
|
|
133
|
+
results.push(...data.urls);
|
|
134
|
+
});
|
|
135
|
+
results.forEach((result) => {
|
|
136
|
+
if (!result) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
let fileName = "";
|
|
140
|
+
if (typeof result === "string") {
|
|
141
|
+
fileName = result.includes("/")
|
|
142
|
+
? (result.split("/").pop() ?? "")
|
|
143
|
+
: result;
|
|
144
|
+
fileName = fileName.includes("?") ? fileName.split("?")[0] : fileName;
|
|
145
|
+
} else {
|
|
146
|
+
fileName = selectedStac?.id + "_process_results.json";
|
|
147
|
+
}
|
|
148
|
+
download(fileName, result);
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Load the process results and update the map layers.
|
|
154
|
+
*
|
|
155
|
+
* @async
|
|
156
|
+
* @param {*} jobObject
|
|
157
|
+
* @param {*} selectedStac
|
|
158
|
+
*/
|
|
159
|
+
export const loadProcess = async (jobObject, selectedStac) => {
|
|
160
|
+
/** @type {any[]} */
|
|
161
|
+
const results = [];
|
|
162
|
+
await axios
|
|
163
|
+
.get(jobObject.links[1].href)
|
|
164
|
+
.then((response) => results.push(response.data));
|
|
165
|
+
|
|
166
|
+
await loadPreviousProcess({
|
|
167
|
+
selectedStac,
|
|
168
|
+
results,
|
|
169
|
+
jobId: jobObject.jobID,
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* load a geotiff to the map from an existing process
|
|
175
|
+
*
|
|
176
|
+
* @param {Object} params
|
|
177
|
+
* @param {import("stac-ts").StacCollection | null} params.selectedStac
|
|
178
|
+
* @param {any[]} params.results
|
|
179
|
+
* @param {string} params.jobId
|
|
180
|
+
*/
|
|
181
|
+
export async function loadPreviousProcess({ selectedStac, results, jobId }) {
|
|
182
|
+
const geotiffLinks = selectedStac?.links.filter(
|
|
183
|
+
(link) => link.rel === "service" && link.type === "image/tiff",
|
|
184
|
+
);
|
|
185
|
+
// const stacProjection = selectedStac
|
|
186
|
+
const geotiffLayer = await createLayerDefinition(
|
|
187
|
+
geotiffLinks?.[0],
|
|
188
|
+
selectedStac?.id ?? "",
|
|
189
|
+
results?.[0].urls,
|
|
190
|
+
//@ts-expect-error TODO
|
|
191
|
+
selectedStac?.["eodash:mapProjection"]?.["name"] ?? null,
|
|
192
|
+
jobId,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
log.debug("rendered layers after loading previous process:", geotiffLayer);
|
|
196
|
+
|
|
197
|
+
if (geotiffLayer) {
|
|
198
|
+
const layers = [...(geotiffLayer ? [geotiffLayer] : [])];
|
|
199
|
+
let currentLayers = [...getLayers()];
|
|
200
|
+
let analysisGroup = currentLayers.find((l) =>
|
|
201
|
+
l.properties.id.includes("AnalysisGroup"),
|
|
202
|
+
);
|
|
203
|
+
analysisGroup?.layers.push(...layers);
|
|
204
|
+
|
|
205
|
+
if (mapEl.value) {
|
|
206
|
+
mapEl.value.layers = [...currentLayers];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { mapEl } from "@/store/states";
|
|
2
|
+
import { initProcess } from "./handling";
|
|
3
|
+
import { useEventBus } from "@vueuse/core";
|
|
4
|
+
import { nextTick, onMounted, watch } from "vue";
|
|
5
|
+
import { eoxLayersKey } from "@/utils/keys";
|
|
6
|
+
import { useOnLayersUpdate } from "@/composables";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Composable resposible of timing the Initialization of the process
|
|
10
|
+
*
|
|
11
|
+
* @export
|
|
12
|
+
* @async
|
|
13
|
+
* @param {Object} params
|
|
14
|
+
* @param {import("vue").Ref<import("stac-ts").StacCollection>} params.selectedStac
|
|
15
|
+
* @param {import("vue").Ref<import("@eox/jsonform").EOxJSONForm | null>} params.jsonformEl
|
|
16
|
+
* @param {import("vue").Ref<Record<string,any> | null>} params.jsonformSchema
|
|
17
|
+
* @param {import("vue").Ref<import("@eox/chart").EOxChart["spec"] | null>} params.chartSpec
|
|
18
|
+
* @param {import("vue").Ref<any[]>} params.processResults
|
|
19
|
+
* @param {import("vue").Ref<boolean>} params.isProcessed
|
|
20
|
+
* @param {import("vue").Ref<boolean>} params.loading
|
|
21
|
+
* @param {import("vue").Ref<boolean>} params.isPolling
|
|
22
|
+
*/
|
|
23
|
+
export const useInitProcess = ({
|
|
24
|
+
selectedStac,
|
|
25
|
+
jsonformEl,
|
|
26
|
+
jsonformSchema,
|
|
27
|
+
chartSpec,
|
|
28
|
+
isProcessed,
|
|
29
|
+
processResults,
|
|
30
|
+
loading,
|
|
31
|
+
isPolling,
|
|
32
|
+
}) => {
|
|
33
|
+
const layersEvents = useEventBus(eoxLayersKey);
|
|
34
|
+
|
|
35
|
+
onMounted(async () => {
|
|
36
|
+
// wait for the layers to be rendered
|
|
37
|
+
if (mapEl.value?.layers.length > 1) {
|
|
38
|
+
await initProcess({
|
|
39
|
+
selectedStac,
|
|
40
|
+
jsonformEl,
|
|
41
|
+
jsonformSchema,
|
|
42
|
+
chartSpec,
|
|
43
|
+
isProcessed,
|
|
44
|
+
processResults,
|
|
45
|
+
loading,
|
|
46
|
+
isPolling,
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
layersEvents.once(async () => {
|
|
50
|
+
await initProcess({
|
|
51
|
+
selectedStac,
|
|
52
|
+
jsonformEl,
|
|
53
|
+
jsonformSchema,
|
|
54
|
+
chartSpec,
|
|
55
|
+
isProcessed,
|
|
56
|
+
loading,
|
|
57
|
+
processResults,
|
|
58
|
+
isPolling,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
useOnLayersUpdate(async (evt, _payload) => {
|
|
65
|
+
if (evt === "layers:updated") {
|
|
66
|
+
await initProcess({
|
|
67
|
+
selectedStac,
|
|
68
|
+
jsonformEl,
|
|
69
|
+
jsonformSchema,
|
|
70
|
+
chartSpec,
|
|
71
|
+
isProcessed,
|
|
72
|
+
processResults,
|
|
73
|
+
loading,
|
|
74
|
+
isPolling,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Auto execute the process when the jsonform has the execute option
|
|
82
|
+
*
|
|
83
|
+
* @param {import("vue").Ref<boolean>} autoExec
|
|
84
|
+
* @param {import("vue").Ref<import("@eox/jsonform").EOxJSONForm | null>} jsonformEl
|
|
85
|
+
* @param {import("vue").Ref<Record<string,any> | null>} jsonformSchema
|
|
86
|
+
* @param {() => Promise<void>} startProcess
|
|
87
|
+
**/
|
|
88
|
+
export function useAutoExec(
|
|
89
|
+
autoExec,
|
|
90
|
+
jsonformEl,
|
|
91
|
+
jsonformSchema,
|
|
92
|
+
startProcess,
|
|
93
|
+
) {
|
|
94
|
+
/**
|
|
95
|
+
* @param {CustomEvent} _e
|
|
96
|
+
**/
|
|
97
|
+
const onJsonFormChange = async (_e) => {
|
|
98
|
+
await startProcess();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const addEventListener = async () => {
|
|
102
|
+
await nextTick(() => {
|
|
103
|
+
//@ts-expect-error TODO
|
|
104
|
+
jsonformEl.value?.addEventListener("change", onJsonFormChange);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
const removeEventListener = () => {
|
|
108
|
+
//@ts-expect-error TODO
|
|
109
|
+
jsonformEl.value?.removeEventListener("change", onJsonFormChange);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
watch(jsonformSchema, (updatedSchema) => {
|
|
113
|
+
autoExec.value = updatedSchema?.options?.["execute"] || false;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
onMounted(() => {
|
|
117
|
+
watch(
|
|
118
|
+
autoExec,
|
|
119
|
+
async (exec) => {
|
|
120
|
+
if (exec) {
|
|
121
|
+
await addEventListener();
|
|
122
|
+
} else {
|
|
123
|
+
removeEventListener();
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{ immediate: true },
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
}
|