@eodash/eodash 5.0.0-alpha.2.1 → 5.0.0-alpha.2.10
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/README.md +36 -3
- package/core/client/App.vue +26 -0
- package/core/client/SuspensedDashboard.ce.vue +105 -0
- package/core/client/asWebComponent.d.ts +23 -0
- package/core/client/asWebComponent.js +12 -0
- package/core/client/components/DashboardLayout.vue +77 -0
- package/core/client/components/DynamicWebComponent.vue +68 -0
- package/core/client/components/ErrorAlert.vue +36 -0
- package/core/{components → client/components}/Footer.vue +34 -15
- package/core/client/components/Header.vue +19 -0
- package/core/client/components/IframeWrapper.vue +11 -0
- package/core/client/components/Loading.vue +37 -0
- package/core/client/components/MobileLayout.vue +109 -0
- package/core/client/composables/DefineEodash.js +67 -0
- package/core/client/composables/DefineWidgets.js +161 -0
- package/core/client/composables/index.js +136 -0
- package/core/client/eodash.js +136 -0
- package/core/client/main.js +2 -0
- package/core/client/plugins/index.js +20 -0
- package/core/client/plugins/vuetify.js +35 -0
- package/core/client/render.js +12 -0
- package/core/client/store/Actions.js +62 -0
- package/core/client/store/States.js +21 -0
- package/core/client/store/index.js +20 -0
- package/core/client/store/stac.js +81 -0
- package/core/client/types.d.ts +287 -0
- package/core/client/utils/eodashSTAC.js +337 -0
- package/core/client/utils/helpers.js +172 -0
- package/core/client/utils/index.js +51 -0
- package/core/{utils → client/utils}/keys.js +2 -2
- package/core/client/views/Dashboard.vue +116 -0
- package/core/client/vite-env.d.ts +39 -0
- package/dist/client/DashboardLayout-C_3q8Y10.js +156 -0
- package/dist/{DynamicWebComponent-BrHVTesn.js → client/DynamicWebComponent-IiTTLBt-.js} +11 -11
- package/dist/client/EodashDatePicker-DgC1lccp.js +252 -0
- package/dist/{eox-itemfilter-DcQkRD2l.js → client/EodashItemFilter-DoWZGe6r.js} +1515 -1381
- package/dist/client/EodashMap-C96D3LLv.js +114352 -0
- package/dist/client/EodashMapBtns-DDtzV94u.js +50 -0
- package/dist/client/ExportState-CduS2mFs.js +558 -0
- package/dist/client/Footer-DHQcutCv.js +115 -0
- package/dist/client/Header-DC0y7rLK.js +350 -0
- package/dist/{IframeWrapper-Dd9zrX9s.js → client/IframeWrapper-BK-10FbS.js} +4 -4
- package/dist/client/MobileLayout-BpLoMY4i.js +945 -0
- package/dist/client/PopUp-CNMCWL0z.js +300 -0
- package/dist/client/VImg-DT7eb8V-.js +291 -0
- package/dist/client/VMain-OenWyy46.js +39 -0
- package/dist/client/VOverlay-x3UXpKhr.js +972 -0
- package/dist/{WidgetsContainer-DxfCu0I3.js → client/WidgetsContainer-qMUwtN0W.js} +24 -17
- package/dist/client/asWebComponent-DQrnLsI2.js +20404 -0
- package/dist/{basedecoder-Qm25PwVp-CHo5Pomv.js → client/basedecoder-DHcBySSe-BmCFNFnw.js} +5 -6
- package/dist/{decoder-kAoyGIq9-BBR5CgzS.js → client/decoder-Cth6J7EK-CtUoHA6r.js} +1 -1
- package/dist/client/deflate-BXt-9JA_-CWfClgpK.js +10 -0
- package/dist/client/eo-dash.js +6 -0
- package/dist/client/eox-jsonform-DCErgjhw.js +17515 -0
- package/dist/client/eox-layercontrol-D0Ht7Mag.js +21298 -0
- package/dist/client/eox-stacinfo-l7ALSV90.js +13969 -0
- package/dist/client/forwardRefs-CpNjL95t.js +185 -0
- package/dist/client/helpers-KCSgmfim.js +1417 -0
- package/dist/{index-DTkOfh2g.js → client/index-DopVrUzM.js} +22 -21
- package/dist/{jpeg-DNfUpLwy-Fjan-04T.js → client/jpeg-BAgeD1d3-oeHbFPUL.js} +5 -6
- package/dist/{lerc-C9VL9kri-cIdbW0sg.js → client/lerc-BgbQqdFI-D986ErVw.js} +37 -39
- package/dist/{lzw-BOMhmEDy-Dboc93VO.js → client/lzw-LAGDNbSC-DkP96qO9.js} +1 -1
- package/dist/{packbits-DaUD6MLm-Bu1PoTGa.js → client/packbits-BlDR4Kj5-C66n1-zr.js} +1 -1
- package/dist/{pako.esm-C3kYPGGQ-BMki8cQY.js → client/pako.esm-CB1uQYY0-DB0PYm1P.js} +6 -12
- package/dist/{raw-CcGKjn8q-DFOt-i8n.js → client/raw-CMGvRjfu-BRi6E4i1.js} +1 -1
- package/dist/{ssrBoot-D-b4-M19.js → client/ssrBoot-DkjdOWvj.js} +1 -1
- package/dist/client/style.css +5 -0
- package/dist/client/transition-BtJHh7JK.js +34 -0
- package/dist/{webfontloader-CyOFAuFB.js → client/webfontloader-qotgY98I.js} +56 -92
- package/dist/{webimage-D2c098k3-DLj1LQxB.js → client/webimage-BM_pbLN3-L2cGWK5l.js} +1 -1
- package/dist/node/cli.js +20 -0
- package/dist/node/main.js +2 -0
- package/dist/node/types.d.ts +47 -0
- package/package.json +54 -51
- package/widgets/EodashDatePicker.vue +145 -40
- package/widgets/EodashItemFilter.vue +73 -38
- package/widgets/EodashMap.vue +210 -37
- package/widgets/EodashMapBtns.vue +50 -0
- package/widgets/ExportState.vue +112 -0
- package/widgets/PopUp.vue +40 -0
- package/widgets/WidgetsContainer.vue +47 -27
- package/bin/app.js +0 -22
- package/bin/cli.js +0 -62
- package/bin/main.js +0 -9
- package/bin/serverConfig.js +0 -165
- package/bin/types.d.ts +0 -52
- package/bin/utils.js +0 -103
- package/core/App.vue +0 -19
- package/core/SuspensedDashboard.ce.vue +0 -65
- package/core/asWebComponent.d.ts +0 -18
- package/core/asWebComponent.js +0 -12
- package/core/components/DashboardLayout.vue +0 -44
- package/core/components/DynamicWebComponent.vue +0 -68
- package/core/components/Header.vue +0 -14
- package/core/components/IframeWrapper.vue +0 -11
- package/core/components/Loading.vue +0 -27
- package/core/components/MobileLayout.vue +0 -49
- package/core/composables/DefineEodash.js +0 -57
- package/core/composables/DefineWidgets.js +0 -129
- package/core/composables/index.js +0 -219
- package/core/eodash.js +0 -100
- package/core/main.js +0 -2
- package/core/plugins/index.js +0 -18
- package/core/plugins/vuetify.js +0 -30
- package/core/render.js +0 -13
- package/core/store/Actions.js +0 -0
- package/core/store/States.js +0 -21
- package/core/store/index.js +0 -17
- package/core/store/stac.js +0 -63
- package/core/types.d.ts +0 -356
- package/core/utils/eodashSTAC.js +0 -164
- package/core/utils/helpers.js +0 -40
- package/core/utils/index.js +0 -28
- package/core/views/Dashboard.vue +0 -59
- package/core/vite-env.d.ts +0 -31
- package/dist/DashboardLayout-Dp8AnYD9.js +0 -141
- package/dist/EodashDatePicker-D_udZ26j.js +0 -1645
- package/dist/EodashItemFilter-BLAGx2UD.js +0 -51
- package/dist/EodashMap-Bxl_aqY2.js +0 -53405
- package/dist/Footer-AohCH8U7.js +0 -118
- package/dist/Header-DC50S6GR.js +0 -609
- package/dist/MobileLayout-CgToA7Gp.js +0 -523
- package/dist/VBtn-Bz7ruRUg.js +0 -1106
- package/dist/VMain-BHfWJU2j.js +0 -35
- package/dist/asWebComponent-C8rb3b1D.js +0 -11323
- package/dist/color-DpYEub1f.js +0 -115
- package/dist/deflate-Be2Arps5-hDqMz3RA.js +0 -10
- package/dist/dimensions-CJaGeSrj.js +0 -53
- package/dist/eo-dash.js +0 -6
- package/dist/eox-map-BJ9SIixs.js +0 -36205
- package/dist/eox-stacinfo-B-YrT7Ug.js +0 -13698
- package/dist/forwardRefs-Bxeu9Obx.js +0 -142
- package/dist/index-Sa2Vg_gx.js +0 -65
- package/dist/style.css +0 -5
- /package/dist/{.gitkeep → client/.gitkeep} +0 -0
- /package/dist/{_commonjsHelpers-DaMA6jEr.js → client/_commonjsHelpers-DaMA6jEr.js} +0 -0
|
@@ -1,56 +1,161 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<VCDatePicker v-model="currentDate" :masks="masks" :attributes="attributes">
|
|
3
|
+
<template #default="{ inputValue, inputEvents }">
|
|
4
|
+
<div
|
|
5
|
+
class="flex rounded-lg border border-gray-300 dark:border-gray-600"
|
|
6
|
+
style="margin: 2px"
|
|
7
|
+
>
|
|
8
|
+
<input
|
|
9
|
+
:value="inputValue"
|
|
10
|
+
v-on="inputEvents"
|
|
11
|
+
style="margin: 1px"
|
|
12
|
+
class="flex-grow px-1 py-1 bg-white dark:bg-gray-700"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
</VCDatePicker>
|
|
17
|
+
<v-row align="center" justify="center" style="margin-top: 6px">
|
|
18
|
+
<v-btn
|
|
19
|
+
style="padding: 0px; margin-right: 4px"
|
|
20
|
+
density="compact"
|
|
21
|
+
v-tooltip:bottom="'Set date to oldest available dataset'"
|
|
22
|
+
@click="jumpDate(true)"
|
|
23
|
+
>
|
|
24
|
+
<v-icon :icon="[mdiRayEndArrow]" />
|
|
25
|
+
</v-btn>
|
|
26
|
+
<v-btn
|
|
27
|
+
style="padding: 0px; margin-left: 4px"
|
|
28
|
+
density="compact"
|
|
29
|
+
v-tooltip:bottom="'Set date to latest available dataset'"
|
|
30
|
+
@click="jumpDate(false)"
|
|
31
|
+
>
|
|
32
|
+
<v-icon :icon="[mdiRayStartArrow]" />
|
|
33
|
+
</v-btn>
|
|
34
|
+
</v-row>
|
|
13
35
|
</template>
|
|
36
|
+
|
|
14
37
|
<script setup>
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
38
|
+
import { DatePicker as VCDatePicker } from "v-calendar";
|
|
39
|
+
import "v-calendar/style.css";
|
|
40
|
+
import { computed, ref, onMounted, watch, inject } from "vue";
|
|
41
|
+
import { eodashKey } from "@/utils/keys";
|
|
42
|
+
import { toAbsolute } from "stac-js/src/http.js";
|
|
43
|
+
import { storeToRefs } from "pinia";
|
|
44
|
+
import { useSTAcStore } from "@/store/stac";
|
|
45
|
+
import { datetime } from "@/store/States";
|
|
46
|
+
import { mdiRayStartArrow, mdiRayEndArrow } from "@mdi/js";
|
|
47
|
+
import { extractCollectionUrls } from "@/utils/helpers";
|
|
17
48
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
49
|
+
/**
|
|
50
|
+
* @param {boolean} reverse
|
|
51
|
+
*/
|
|
52
|
+
function jumpDate(reverse) {
|
|
53
|
+
if (attributes.value && attributes.value.length > 0) {
|
|
54
|
+
// We have potentially multiple collections we need to iterate (currently only one)
|
|
55
|
+
let latestDateMS = reverse ? Infinity : -Infinity;
|
|
56
|
+
attributes.value.forEach((coll) => {
|
|
57
|
+
if (coll?.dates) {
|
|
58
|
+
coll.dates.forEach((d) => {
|
|
59
|
+
// TODO: we need to handle time ranges and other options here
|
|
60
|
+
if (d instanceof Date) {
|
|
61
|
+
if (
|
|
62
|
+
(!reverse && d.getTime() > latestDateMS) ||
|
|
63
|
+
(reverse && d.getTime() < latestDateMS)
|
|
64
|
+
) {
|
|
65
|
+
latestDateMS = d.getTime();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (latestDateMS !== 0) {
|
|
72
|
+
currentDate.value = new Date(latestDateMS);
|
|
73
|
+
}
|
|
21
74
|
}
|
|
22
|
-
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const eodashConfig = /** @type {import("@/types").Eodash} */ inject(eodashKey);
|
|
78
|
+
|
|
79
|
+
const masks = ref({
|
|
80
|
+
input: "YYYY-MM-DD",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Attributes displayed on datepicker
|
|
85
|
+
*
|
|
86
|
+
* @type {import("vue").Ref<
|
|
87
|
+
* (
|
|
88
|
+
* | import("v-calendar/dist/types/src/utils/attribute").AttributeConfig
|
|
89
|
+
* | undefined
|
|
90
|
+
* )[]
|
|
91
|
+
* >}
|
|
92
|
+
*/
|
|
93
|
+
const attributes = ref([]);
|
|
23
94
|
|
|
24
95
|
const currentDate = computed({
|
|
25
96
|
get() {
|
|
26
|
-
return
|
|
97
|
+
return datetime.value ? new Date(datetime.value) : new Date();
|
|
27
98
|
},
|
|
28
99
|
/** @param {Date | string} updatedDate */
|
|
29
100
|
set(updatedDate) {
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (updatedDate instanceof Date && !isNaN(updatedDate)) {
|
|
35
|
-
datetime.value = new Date(updatedDate.getTime() - updatedDate.getTimezoneOffset() * 60000).toISOString()
|
|
101
|
+
if (updatedDate instanceof Date && !isNaN(updatedDate.getTime())) {
|
|
102
|
+
datetime.value = new Date(
|
|
103
|
+
updatedDate.getTime() - updatedDate.getTimezoneOffset() * 60000,
|
|
104
|
+
).toISOString();
|
|
36
105
|
} else {
|
|
37
|
-
datetime.value = new Date().toISOString()
|
|
106
|
+
datetime.value = new Date().toISOString();
|
|
38
107
|
}
|
|
39
|
-
}
|
|
108
|
+
},
|
|
40
109
|
});
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @type {import("vue").Ref<import("vuetify/components").VDatePicker | null>}
|
|
44
|
-
**/
|
|
45
|
-
const datePicker = ref(null)
|
|
46
|
-
/** @type {import("vue").Ref<string|undefined>} */
|
|
47
|
-
const width = ref()
|
|
48
|
-
/** @type {import("vue").Ref<string|undefined>} */
|
|
49
|
-
const height = ref()
|
|
110
|
+
/** @type {import("@/types").WebComponentProps["onMounted"]} */
|
|
50
111
|
onMounted(() => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
112
|
+
const { selectedStac } = storeToRefs(useSTAcStore());
|
|
113
|
+
watch(
|
|
114
|
+
[selectedStac],
|
|
115
|
+
async ([updatedStac]) => {
|
|
116
|
+
if (updatedStac) {
|
|
117
|
+
const parentCollUrl = toAbsolute(
|
|
118
|
+
`./${updatedStac.id}/collection.json`,
|
|
119
|
+
eodashConfig.stacEndpoint,
|
|
120
|
+
);
|
|
121
|
+
const collectionUrls = extractCollectionUrls(
|
|
122
|
+
selectedStac.value,
|
|
123
|
+
parentCollUrl,
|
|
124
|
+
);
|
|
125
|
+
const wongPalette = [
|
|
126
|
+
"#009E73",
|
|
127
|
+
"#0072B2",
|
|
128
|
+
"#E69F00",
|
|
129
|
+
"#CC79A7",
|
|
130
|
+
"#56B4E9",
|
|
131
|
+
"#D55E00",
|
|
132
|
+
];
|
|
133
|
+
for (let idx = 0; idx < collectionUrls.length; idx++) {
|
|
134
|
+
const stacCollection = await (
|
|
135
|
+
await fetch(collectionUrls[idx])
|
|
136
|
+
).json();
|
|
137
|
+
const dates = stacCollection.links
|
|
138
|
+
.filter(
|
|
139
|
+
(/** @type {{ rel: string; datetime: string }} */ item) =>
|
|
140
|
+
item.rel === "item" && "datetime" in item,
|
|
141
|
+
)
|
|
142
|
+
.map(
|
|
143
|
+
(/** @type {{ datetime: string }} */ it) => new Date(it.datetime),
|
|
144
|
+
);
|
|
145
|
+
attributes.value = [
|
|
146
|
+
{
|
|
147
|
+
bar: {
|
|
148
|
+
style: {
|
|
149
|
+
backgroundColor: wongPalette[idx % wongPalette.length],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
dates,
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{ immediate: true },
|
|
159
|
+
);
|
|
160
|
+
});
|
|
56
161
|
</script>
|
|
@@ -1,60 +1,95 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<eox-itemfilter class="fill-height" :config="config" ref="eoxItemFilter">
|
|
3
|
+
<h4 slot="filterstitle" style="margin: 14px 8px">{{ filtersTitle }}</h4>
|
|
4
|
+
|
|
5
|
+
<h4 slot="resultstitle" style="margin: 14px 8px">{{ resultsTitle }}</h4>
|
|
6
|
+
</eox-itemfilter>
|
|
3
7
|
</template>
|
|
4
8
|
<script setup>
|
|
5
|
-
import
|
|
6
|
-
import
|
|
9
|
+
import { useSTAcStore } from "@/store/stac";
|
|
10
|
+
import "@eox/itemfilter";
|
|
11
|
+
import { onMounted, ref } from "vue";
|
|
7
12
|
|
|
8
|
-
const
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
filtersTitle: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: "Indicators",
|
|
17
|
+
},
|
|
18
|
+
resultsTitle: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: "",
|
|
21
|
+
},
|
|
22
|
+
titleProperty: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: "title",
|
|
25
|
+
},
|
|
9
26
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
aggregateResults: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: "themes",
|
|
30
|
+
},
|
|
31
|
+
enableHighlighting: { type: Boolean, default: true },
|
|
32
|
+
expandMultipleFilters: { type: Boolean, default: false },
|
|
33
|
+
expandMultipleResults: { type: Boolean, default: false },
|
|
34
|
+
filterProperties: {
|
|
35
|
+
/** @type {import("vue").PropType<{
|
|
36
|
+
* keys:string[];
|
|
37
|
+
* title:string;
|
|
38
|
+
* type:string;
|
|
39
|
+
* expanded?:boolean
|
|
40
|
+
* }[]> }*/
|
|
41
|
+
type: Array,
|
|
42
|
+
default: () => [
|
|
14
43
|
{
|
|
15
|
-
keys: ["title", "themes"],
|
|
44
|
+
keys: ["title", "themes", "description"],
|
|
16
45
|
title: "Search",
|
|
17
46
|
type: "text",
|
|
18
|
-
expanded: true,
|
|
19
47
|
},
|
|
20
48
|
{
|
|
21
49
|
key: "themes",
|
|
22
|
-
title: "Theme",
|
|
50
|
+
title: "Theme Filter",
|
|
23
51
|
type: "multiselect",
|
|
24
|
-
featured: true,
|
|
25
52
|
},
|
|
26
53
|
],
|
|
27
|
-
aggregateResults: "themes",
|
|
28
|
-
enableHighlighting: true,
|
|
29
54
|
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const config = {
|
|
58
|
+
titleProperty: props.titleProperty,
|
|
59
|
+
filterProperties: props.filterProperties,
|
|
60
|
+
aggregateResults: props.aggregateResults,
|
|
61
|
+
enableHighlighting: props.enableHighlighting,
|
|
62
|
+
expandMultipleFilters: props.expandMultipleFilters,
|
|
63
|
+
expandMultipleResults: props.expandMultipleResults,
|
|
30
64
|
};
|
|
65
|
+
/** @type {import("vue").Ref<HTMLElement & Record<string,any> | null>} */
|
|
66
|
+
const eoxItemFilter = ref(null);
|
|
67
|
+
|
|
68
|
+
const store = useSTAcStore();
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Check if selected indicator was already set in store
|
|
43
|
-
if (indicator && indicator.value !== "") {
|
|
44
|
-
const match = store.stac?.find((item) => item.id === indicator.value);
|
|
45
|
-
if (match) {
|
|
46
|
-
//@ts-expect-error
|
|
47
|
-
(el).selectedResult = match;
|
|
48
|
-
store.loadSelectedSTAC(match.href);
|
|
70
|
+
onMounted(() => {
|
|
71
|
+
const style = document.createElement("style");
|
|
72
|
+
style.innerHTML = `
|
|
73
|
+
section {
|
|
74
|
+
margin: 0 !important;
|
|
75
|
+
}
|
|
76
|
+
section button#filter-reset {
|
|
77
|
+
padding: 0 8px;
|
|
78
|
+
top: 8px;
|
|
79
|
+
right: 8px;
|
|
49
80
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
`;
|
|
82
|
+
eoxItemFilter.value?.shadowRoot?.appendChild(style);
|
|
83
|
+
|
|
84
|
+
eoxItemFilter.value?.apply(
|
|
85
|
+
// Only list child elements in list
|
|
86
|
+
store.stac?.filter((item) => item.rel === "child"),
|
|
87
|
+
);
|
|
88
|
+
/** @type {any} */ (eoxItemFilter.value).config.onSelect =
|
|
89
|
+
/** @param {import('stac-ts').StacLink} item */
|
|
55
90
|
async (item) => {
|
|
56
|
-
console.log(item);
|
|
57
91
|
await store.loadSelectedSTAC(item.href);
|
|
92
|
+
console.log(item, store.selectedStac);
|
|
58
93
|
};
|
|
59
|
-
};
|
|
94
|
+
});
|
|
60
95
|
</script>
|
package/widgets/EodashMap.vue
CHANGED
|
@@ -1,39 +1,80 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<eox-map
|
|
3
|
+
class="fill-height fill-width overflow-none"
|
|
4
|
+
ref="eoxMap"
|
|
5
|
+
:config="eoxMapConfig"
|
|
6
|
+
/>
|
|
4
7
|
</template>
|
|
5
8
|
<script setup>
|
|
6
|
-
import {
|
|
9
|
+
import { transformExtent } from "ol/proj";
|
|
10
|
+
import { inject, onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
|
7
11
|
import { toAbsolute } from "stac-js/src/http.js";
|
|
8
12
|
import { EodashCollection } from "@/utils/eodashSTAC";
|
|
13
|
+
import { extractCollectionUrls } from "@/utils/helpers";
|
|
9
14
|
import { eodashKey } from "@/utils/keys";
|
|
10
15
|
import { datetime, mapPosition } from "@/store/States";
|
|
11
|
-
import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
|
|
12
16
|
import { storeToRefs } from "pinia";
|
|
17
|
+
import { useSTAcStore } from "@/store/stac";
|
|
18
|
+
import "@eox/map";
|
|
13
19
|
import "@eox/map/dist/eox-map-advanced-layers-and-sources.js";
|
|
14
20
|
|
|
15
|
-
const eodashConfig = /** @type {import("@/types").Eodash} */
|
|
21
|
+
const eodashConfig = /** @type {import("@/types").Eodash} */ (
|
|
22
|
+
inject(eodashKey)
|
|
23
|
+
);
|
|
16
24
|
|
|
17
|
-
/** @type {Record<string,unknown>} */
|
|
18
|
-
const
|
|
19
|
-
|
|
25
|
+
/** @type {import("vue").Ref<(HTMLElement & Record<string,unknown>) | null>} */
|
|
26
|
+
const eoxMap = ref(null);
|
|
27
|
+
|
|
28
|
+
const eoxMapConfig = reactive({
|
|
29
|
+
/** @type {(number|undefined)[] | undefined} */
|
|
20
30
|
center: [15, 48],
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
/** @type {number | undefined} */
|
|
32
|
+
zoom: 4,
|
|
33
|
+
// TODO: we should probably introduce some way of defining
|
|
34
|
+
layers: [
|
|
35
|
+
{
|
|
36
|
+
type: "Vector",
|
|
37
|
+
source: {
|
|
38
|
+
type: "Vector",
|
|
39
|
+
url: "https://openlayers.org/data/vector/ecoregions.json",
|
|
40
|
+
format: "GeoJSON",
|
|
41
|
+
},
|
|
42
|
+
properties: {
|
|
43
|
+
id: "Regions",
|
|
44
|
+
title: "Regions",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "Tile",
|
|
49
|
+
properties: {
|
|
50
|
+
id: "osm",
|
|
51
|
+
title: "Background",
|
|
52
|
+
},
|
|
53
|
+
source: {
|
|
54
|
+
type: "OSM",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
|
|
23
60
|
// Check if selected indicator was already set in store
|
|
24
61
|
if (mapPosition && mapPosition.value && mapPosition.value.length === 3) {
|
|
25
62
|
// TODO: do further checks for invalid values?
|
|
26
|
-
|
|
27
|
-
|
|
63
|
+
// TODO: can we expect the values to be in a specific projection
|
|
64
|
+
eoxMapConfig.center = [mapPosition.value?.[0], mapPosition.value[1]];
|
|
65
|
+
eoxMapConfig.zoom = mapPosition.value[2];
|
|
28
66
|
}
|
|
29
67
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** @type {import("openlayers").EventsListenerFunctionType}*/
|
|
68
|
+
/** @type {import("openlayers").EventsListenerFunctionType} */
|
|
33
69
|
const handleMoveEnd = (evt) => {
|
|
34
70
|
const map = /** @type {import("openlayers").Map | undefined} */ (
|
|
35
|
-
/** @type {
|
|
71
|
+
/** @type {any} */ (evt).map
|
|
36
72
|
);
|
|
73
|
+
/*
|
|
74
|
+
const currentProj = map?.getView().getProjection();
|
|
75
|
+
const transFunc = getTransform(currentProj?.getCode(), 'EPSG:4326');
|
|
76
|
+
const [x, y] = transFunc(map?.getView().getCenter() ?? [0, 0], undefined, undefined);
|
|
77
|
+
*/
|
|
37
78
|
const [x, y] = map?.getView().getCenter() ?? [0, 0];
|
|
38
79
|
const z = map?.getView().getZoom();
|
|
39
80
|
if (!Number.isNaN(x) && !Number.isNaN(y) && !Number.isNaN(z)) {
|
|
@@ -41,42 +82,174 @@ const handleMoveEnd = (evt) => {
|
|
|
41
82
|
}
|
|
42
83
|
};
|
|
43
84
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
(
|
|
85
|
+
const store = useSTAcStore();
|
|
86
|
+
|
|
87
|
+
onMounted(() => {
|
|
88
|
+
/** @type {import('ol/Map').default} */
|
|
89
|
+
(eoxMap.value?.map)?.on("moveend", handleMoveEnd);
|
|
48
90
|
|
|
49
91
|
const { selectedStac } = storeToRefs(store);
|
|
50
92
|
|
|
51
93
|
watch(
|
|
52
94
|
[selectedStac, datetime],
|
|
53
|
-
async ([updatedStac, updatedTime]) => {
|
|
95
|
+
async ([updatedStac, updatedTime], [previousSTAC, _previousTime]) => {
|
|
54
96
|
if (updatedStac) {
|
|
55
97
|
const parentCollUrl = toAbsolute(
|
|
56
98
|
`./${updatedStac.id}/collection.json`,
|
|
57
|
-
eodashConfig.stacEndpoint
|
|
99
|
+
eodashConfig.stacEndpoint,
|
|
58
100
|
);
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
parentCollUrl
|
|
101
|
+
const collectionUrls = extractCollectionUrls(
|
|
102
|
+
selectedStac.value,
|
|
103
|
+
parentCollUrl,
|
|
62
104
|
);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
new
|
|
105
|
+
/** @type {import("@/utils/eodashSTAC").EodashCollection[]} */
|
|
106
|
+
const eodashCollections = [];
|
|
107
|
+
collectionUrls.forEach((cu) => {
|
|
108
|
+
eodashCollections.push(new EodashCollection(cu));
|
|
109
|
+
});
|
|
110
|
+
const uid = function () {
|
|
111
|
+
return (
|
|
112
|
+
Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
const layersCollection = [];
|
|
116
|
+
const dataLayers = {
|
|
117
|
+
type: "Group",
|
|
118
|
+
properties: {
|
|
119
|
+
id: uid(),
|
|
120
|
+
title: "Analysis Layers",
|
|
121
|
+
layerControlExpand: true,
|
|
122
|
+
},
|
|
123
|
+
layers: /** @type {object[]}*/ ([]),
|
|
124
|
+
};
|
|
125
|
+
for (let idx = 0; idx < eodashCollections.length; idx++) {
|
|
126
|
+
const ec = eodashCollections[idx];
|
|
127
|
+
let layers;
|
|
128
|
+
if (updatedTime) {
|
|
129
|
+
layers = await ec.createLayersJson(new Date(updatedTime));
|
|
130
|
+
} else {
|
|
131
|
+
layers = await ec.createLayersJson();
|
|
132
|
+
}
|
|
133
|
+
if (layers) {
|
|
134
|
+
dataLayers.layers.push(...layers);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Add expand to all analysis layers
|
|
138
|
+
dataLayers.layers.forEach((dl) => {
|
|
139
|
+
// @ts-expect-error no type definition for eox-map config
|
|
140
|
+
dl.properties.layerControlExpand = true;
|
|
141
|
+
// @ts-expect-error no type definition for eox-map config
|
|
142
|
+
dl.properties.layerControlToolsExpand = true;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
layersCollection.push(dataLayers);
|
|
146
|
+
// TODO: add base layers and overlays as defined in the top collection / indicator
|
|
147
|
+
// Probably best also to introduce background and overlay groups
|
|
148
|
+
// For now adding OSM as background
|
|
149
|
+
const indicator = new EodashCollection(parentCollUrl);
|
|
150
|
+
const indicatorLayers = await indicator.buildJsonArray(
|
|
151
|
+
// @ts-expect-error we pass a collection instead of an item, we want to reuse the layer extraction logic
|
|
152
|
+
selectedStac.value,
|
|
153
|
+
false,
|
|
154
|
+
false,
|
|
67
155
|
);
|
|
156
|
+
const baseLayers = {
|
|
157
|
+
type: "Group",
|
|
158
|
+
properties: {
|
|
159
|
+
id: uid(),
|
|
160
|
+
title: "Base Layers",
|
|
161
|
+
},
|
|
162
|
+
layers: /** @type {object[]}*/ ([]),
|
|
163
|
+
};
|
|
164
|
+
const overlayLayers = {
|
|
165
|
+
type: "Group",
|
|
166
|
+
properties: {
|
|
167
|
+
id: uid(),
|
|
168
|
+
title: "Overlay Layers",
|
|
169
|
+
},
|
|
170
|
+
layers: /** @type {object[]}*/ ([]),
|
|
171
|
+
};
|
|
172
|
+
if (indicatorLayers && indicatorLayers.length > 0) {
|
|
173
|
+
baseLayers.layers.push(
|
|
174
|
+
...indicatorLayers.filter(
|
|
175
|
+
// @ts-expect-error group is added by the buildJsonArray
|
|
176
|
+
(l) => l.properties.group === "baselayer",
|
|
177
|
+
),
|
|
178
|
+
);
|
|
179
|
+
// Add exclusive to baselayers and make sure only one is selected
|
|
180
|
+
baseLayers.layers.forEach((bl) => {
|
|
181
|
+
// @ts-expect-error no type definition for eox-map config
|
|
182
|
+
bl.properties.layerControlExclusive = true;
|
|
183
|
+
});
|
|
184
|
+
overlayLayers.layers.push(
|
|
185
|
+
// @ts-expect-error group is added by the buildJsonArray
|
|
186
|
+
...indicatorLayers.filter((l) => l.properties.group === "overlay"),
|
|
187
|
+
);
|
|
68
188
|
} else {
|
|
69
|
-
|
|
189
|
+
// Default to some baselayer
|
|
190
|
+
baseLayers.layers.push({
|
|
191
|
+
type: "Tile",
|
|
192
|
+
properties: {
|
|
193
|
+
id: "osm",
|
|
194
|
+
title: "Background",
|
|
195
|
+
},
|
|
196
|
+
source: {
|
|
197
|
+
type: "OSM",
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (baseLayers.layers.length > 0) {
|
|
202
|
+
layersCollection.push(baseLayers);
|
|
203
|
+
}
|
|
204
|
+
if (overlayLayers.layers.length > 0) {
|
|
205
|
+
layersCollection.unshift(overlayLayers);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// TODO: we can check if the collection / indicator has a specific
|
|
209
|
+
// projection it wants to be displayed in the map we can register
|
|
210
|
+
// and set the attribute here, e.g. like following
|
|
211
|
+
/*
|
|
212
|
+
(el)?.registerProjection(
|
|
213
|
+
'EPSG:3031','+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs'
|
|
214
|
+
);
|
|
215
|
+
(el)?.projection = "EPSG:3031";
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/** @type {any} */
|
|
219
|
+
(eoxMap.value).layers = layersCollection;
|
|
220
|
+
|
|
221
|
+
// only on different indicator selection and not on time change
|
|
222
|
+
if (previousSTAC?.id !== updatedStac.id) {
|
|
223
|
+
// Try to move map view to extent
|
|
224
|
+
const extent = await indicator.getExtent();
|
|
225
|
+
// Make sure for now we are always converting from 4326
|
|
226
|
+
// of stac items into current map projection
|
|
227
|
+
// TODO: This might change if we decide to use 4326 as default for zoom and extent
|
|
228
|
+
// Sanitize extent
|
|
229
|
+
const b = extent.spatial.bbox[0];
|
|
230
|
+
const sanitizedExtent = [
|
|
231
|
+
b[0] > -180 ? b[0] : -180,
|
|
232
|
+
b[1] > -90 ? b[1] : -90,
|
|
233
|
+
b[2] < 180 ? b[2] : 180,
|
|
234
|
+
b[3] < 90 ? b[3] : 90,
|
|
235
|
+
];
|
|
236
|
+
const reprojExtent = transformExtent(
|
|
237
|
+
sanitizedExtent,
|
|
238
|
+
"EPSG:4326",
|
|
239
|
+
// @ts-expect-error we should expect to have the view here
|
|
240
|
+
eoxMap.value?.map?.getView().getProjection(),
|
|
241
|
+
);
|
|
242
|
+
/** @type {any} */
|
|
243
|
+
(eoxMap.value).zoomExtent = reprojExtent;
|
|
70
244
|
}
|
|
71
245
|
}
|
|
72
246
|
},
|
|
73
|
-
{ immediate: true }
|
|
247
|
+
{ immediate: true },
|
|
74
248
|
);
|
|
75
|
-
};
|
|
249
|
+
});
|
|
76
250
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
251
|
+
onUnmounted(() => {
|
|
252
|
+
/** @type {import('ol/Map').default} */
|
|
253
|
+
(eoxMap.value?.map)?.un("moveend", handleMoveEnd);
|
|
254
|
+
});
|
|
82
255
|
</script>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="rootRef" class="d-flex flex-column align-end justify-end my-3 pa-2">
|
|
3
|
+
<v-btn
|
|
4
|
+
v-if="exportMap"
|
|
5
|
+
class="map-btn"
|
|
6
|
+
:icon="[mdiMapPlus]"
|
|
7
|
+
@click="showMapState = !showMapState"
|
|
8
|
+
/>
|
|
9
|
+
<ExportState v-if="exportMap" v-model="showMapState" />
|
|
10
|
+
<v-btn
|
|
11
|
+
class="map-btn"
|
|
12
|
+
:icon="[mdiEarthBox]"
|
|
13
|
+
v-if="changeProjection && !!availableMapProjection"
|
|
14
|
+
@click="changeMapProjection(availableMapProjection)"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
<script setup>
|
|
19
|
+
import { makePanelTransparent } from "@/composables";
|
|
20
|
+
import { changeMapProjection } from "@/store/Actions";
|
|
21
|
+
import { availableMapProjection } from "@/store/States";
|
|
22
|
+
import { mdiEarthBox, mdiMapPlus } from "@mdi/js";
|
|
23
|
+
import ExportState from "^/ExportState.vue";
|
|
24
|
+
import { ref } from "vue";
|
|
25
|
+
|
|
26
|
+
defineProps({
|
|
27
|
+
exportMap: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: true,
|
|
30
|
+
},
|
|
31
|
+
changeProjection: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: true,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const showMapState = ref(false);
|
|
38
|
+
|
|
39
|
+
/** @type {import("vue").Ref<HTMLDivElement|null>} */
|
|
40
|
+
const rootRef = ref(null);
|
|
41
|
+
makePanelTransparent(rootRef);
|
|
42
|
+
</script>
|
|
43
|
+
<style scoped>
|
|
44
|
+
.map-btn {
|
|
45
|
+
width: 36px;
|
|
46
|
+
height: 36px;
|
|
47
|
+
border-radius: 25%;
|
|
48
|
+
margin: 4px;
|
|
49
|
+
}
|
|
50
|
+
</style>
|