@eodash/eodash 5.0.0-alpha.1.13 → 5.0.0-alpha.1.15

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.
@@ -0,0 +1,164 @@
1
+ import { Collection, Item } from 'stac-js';
2
+ import { toAbsolute } from 'stac-js/src/http.js';
3
+ import { generateFeatures } from './helpers'
4
+
5
+ export class EodashCollection {
6
+ /** @type {string | undefined} */
7
+ #collectionUrl = undefined;
8
+ #collectionStac = undefined;
9
+ /** @type {*} */
10
+ selectedItem = null;
11
+
12
+ /**
13
+ * @param {string} collectionUrl
14
+ */
15
+ constructor(collectionUrl) {
16
+ this.#collectionUrl = collectionUrl;
17
+ }
18
+ /**
19
+ * @param {*} item
20
+ * @returns
21
+ * @async
22
+ */
23
+ createLayersJson = async (item = null) => {
24
+ let stacItem, stac;
25
+ // TODO get auxiliary layers from collection
26
+ let layersJson = [
27
+ {
28
+ type: 'Tile',
29
+ properties: {
30
+ id: 'OSM',
31
+ },
32
+ source: {
33
+ type: 'OSM',
34
+ },
35
+ },
36
+ ];
37
+ // Load collectionstac if not yet initialized
38
+ if (!this.#collectionStac) {
39
+ //@ts-expect-error
40
+ const response = await fetch(this.#collectionUrl);
41
+ stac = await response.json();
42
+ this.#collectionStac = new Collection(stac);
43
+ }
44
+
45
+ if(stac && stac.endpointtype === "GeoDB") {
46
+ // Special handling of point based data
47
+ const allFeatures = generateFeatures(stac.links);
48
+ layersJson.unshift({
49
+ type: "Vector",
50
+ properties: {
51
+ id: stac.id,
52
+ },
53
+ source: {
54
+ type: "Vector",
55
+ // @ts-ignore
56
+ url: "data:," + encodeURIComponent(JSON.stringify(allFeatures)),
57
+ format: "GeoJSON",
58
+ },
59
+ style: {
60
+ "circle-radius": 5,
61
+ "circle-fill-color": "#00417077",
62
+ "circle-stroke-color": "#004170",
63
+ "fill-color": "#00417077",
64
+ "stroke-color": "#004170",
65
+ }
66
+ });
67
+ return layersJson;
68
+ } else {
69
+ if (item instanceof Date) {
70
+ // if collectionStac not yet initialized we do it here
71
+ stacItem = this.getItems().sort((a, b) => {
72
+ //@ts-expect-error
73
+ const distanceA = Math.abs(new Date(a.datetime) - item);
74
+ //@ts-expect-error
75
+ const distanceB = Math.abs(new Date(b.datetime) - item);
76
+ return distanceA - distanceB;
77
+ })[0];
78
+ this.selectedItem = stacItem;
79
+ } else {
80
+ stacItem = item;
81
+ }
82
+ const response = await fetch(
83
+ stacItem
84
+ ? toAbsolute(stacItem.href, this.#collectionUrl)
85
+ : this.#collectionUrl
86
+ );
87
+ stac = await response.json();
88
+
89
+ if (!stacItem) {
90
+ // no specific item was requested; render last item
91
+ this.#collectionStac = new Collection(stac);
92
+ const items = this.getItems();
93
+ this.selectedItem = items[items.length - 1];
94
+ layersJson = await this.createLayersJson(this.selectedItem);
95
+ return layersJson;
96
+ } else {
97
+ // specific item was requested
98
+ const item = new Item(stac);
99
+ this.selectedItem = item;
100
+ //@ts-expect-error
101
+ layersJson.unshift(this.buildJson(item));
102
+ return layersJson;
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * @param {*} item
109
+ */
110
+ buildJson(item) {
111
+ let json;
112
+ // TODO implement other types, such as COG
113
+ if (/** @type {import('stac-ts').StacLink[]} */(item.links)
114
+ .find((l) => l.rel === 'wms' || l.rel === 'wmts' || l.rel === 'xyz')) {
115
+ json = {
116
+ type: 'STAC',
117
+ displayWebMapLink: true,
118
+ displayFootprint: false,
119
+ data: item,
120
+ properties: {
121
+ id: item.id,
122
+ },
123
+ };
124
+ } else {
125
+ // fall back to rendering the feature
126
+ json = {
127
+ type: 'Vector',
128
+ source: {
129
+ type: 'Vector',
130
+ url: 'data:,' + encodeURIComponent(JSON.stringify(item.geometry)),
131
+ format: 'GeoJSON',
132
+ },
133
+ properties: {
134
+ id: item.id,
135
+ },
136
+ };
137
+ }
138
+
139
+ return json;
140
+ }
141
+
142
+ getItems() {
143
+ return (
144
+ //@ts-expect-error
145
+ // eslint-disable-next-line
146
+ /** @type {import('stac-ts').StacLink[]}*/(this.#collectionStac?.links)
147
+ .filter((i) => i.rel === 'item')
148
+ // sort by `datetime`, where oldest is first in array
149
+ .sort((a, b) => ( /** @type {number} */(a.datetime) < /** @type {number} */(b.datetime) ? -1 : 1))
150
+ );
151
+ }
152
+
153
+ getDates() {
154
+ return (
155
+ //@ts-expect-error
156
+ // eslint-disable-next-line
157
+ /** @type {import('stac-ts').StacLink[]}*/ (this.#collectionStac?.links)
158
+ .filter((i) => i.rel === 'item')
159
+ // sort by `datetime`, where oldest is first in array
160
+ .sort((a, b) => ( /** @type {number} */(a.datetime) < /** @type {number} */(b.datetime) ? -1 : 1))
161
+ .map((i) => new Date( /** @type {number} */(i.datetime)))
162
+ );
163
+ }
164
+ }
@@ -0,0 +1,40 @@
1
+
2
+ /**
3
+ * @param {import("stac-ts").StacLink[]} links
4
+ */
5
+ export function generateFeatures(links) {
6
+ /**
7
+ * @type {{
8
+ * type:string;
9
+ * geometry:{
10
+ * type: string;
11
+ * coordinates: [number, number],
12
+ * }
13
+ * }[]}
14
+ */
15
+ const features = [];
16
+ links.forEach(element => {
17
+ if (element.rel === "item" && "latlng" in element) {
18
+ //@ts-expect-error
19
+ const [lat, lon] = element.latlng.split(",").map((/** @type {string} */it) => Number(it))
20
+ features.push({
21
+ type: 'Feature',
22
+ geometry: {
23
+ type: 'Point',
24
+ coordinates: [lon, lat],
25
+ },
26
+ })
27
+ }
28
+ });
29
+ const geojsonObject = {
30
+ 'type': 'FeatureCollection',
31
+ 'crs': {
32
+ 'type': 'name',
33
+ 'properties': {
34
+ 'name': 'EPSG:4326',
35
+ },
36
+ },
37
+ features,
38
+ };
39
+ return geojsonObject;
40
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * loads font in the app using `webfontloader`
3
+ * @param {string} [family="Roboto"]
4
+ * @param {string} [link]
5
+ * @returns {Promise<string>} - font family name
6
+ * @see {@link "https://github.com/typekit/webfontloader "}
7
+ */
8
+ export const loadFont = async (family = "Roboto", link) => {
9
+ const WebFontLoader = (await import('webfontloader')).default;
10
+ if (link) {
11
+ WebFontLoader.load({
12
+ custom: {
13
+ // Use FVD notation to include families https://github.com/typekit/fvd
14
+ families: [family],
15
+ // Path to stylesheet that defines font-face
16
+ urls: [link],
17
+ },
18
+ });
19
+ }
20
+ else {
21
+ WebFontLoader.load({
22
+ google: {
23
+ families: [family]
24
+ }
25
+ });
26
+ }
27
+ return family;
28
+ };
@@ -6,16 +6,18 @@
6
6
 
7
7
  <script setup>
8
8
  import { useEodashRuntime } from "@/composables/DefineEodash";
9
- import { useUpdateTheme } from "@/composables";
9
+ import { useRouteParams, useUpdateTheme } from "@/composables";
10
10
  import { useSTAcStore } from '@/store/stac';
11
11
  import { defineAsyncComponent } from "vue";
12
12
  import { useDisplay, useLayout } from "vuetify/lib/framework.mjs";
13
- import { loadFont } from '@/store/Actions'
13
+ import { loadFont } from '@/utils'
14
14
  import { useSeoMeta } from "@unhead/vue"
15
15
 
16
16
 
17
17
  const eodash = await useEodashRuntime()
18
18
 
19
+ useRouteParams()
20
+
19
21
  const theme = useUpdateTheme('dashboardTheme', eodash.brand?.theme)
20
22
  theme.global.name.value = 'dashboardTheme'
21
23
 
@@ -36,6 +38,10 @@ useSeoMeta(eodash.brand.meta ?? {})
36
38
  </script>
37
39
 
38
40
  <style scoped lang="scss">
41
+ html {
42
+ overflow: hidden;
43
+ }
44
+
39
45
  * {
40
46
  font-family: v-bind('fontFamily');
41
47
  }
@@ -11,19 +11,21 @@ declare interface Window {
11
11
  declare module '@eox/stacinfo' {
12
12
  export const EOxStacInfo: CustomElementConstructor
13
13
  }
14
+ declare module '@eox/map' {
15
+ export const EOxMap: CustomElementConstructor
16
+ }
17
+ declare module '@eox/itemfilter' {
18
+ export const EOxItemFilter: CustomElementConstructor
19
+ }
14
20
  declare module 'user:config' {
15
21
  const eodash: import("@/types").Eodash | Promise<import("@/types").Eodash>;
16
22
  export default eodash
17
23
  }
18
- declare module '@eox/chart' {
19
- export const EOxChart: CustomElementConstructor
20
- }
21
- declare module '@eox/layercontrol' {
22
- export const EOxLayerControl: CustomElementConstructor
23
- }
24
- declare module '@eox/timecontrol' {
25
- export const EOxTimeControl: CustomElementConstructor
24
+ declare module "stac-js" {
25
+ const STAC: any, Collection: any, Item: any
26
+ export { STAC, Collection, Item }
26
27
  }
27
- declare module '@eox/jsonform' {
28
- export const EOxJSONForm: CustomElementConstructor
28
+ declare module "stac-js/src/http.js" {
29
+ const toAbsolute: any
30
+ export { toAbsolute }
29
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eodash/eodash",
3
- "version": "5.0.0-alpha.1.13",
3
+ "version": "5.0.0-alpha.1.15",
4
4
  "type": "module",
5
5
  "types": "./core/types.d.ts",
6
6
  "files": [
@@ -24,22 +24,27 @@
24
24
  "preview": "vite preview",
25
25
  "lint": "eslint . --fix",
26
26
  "docs:generate": "typedoc --options typedoc.config.json",
27
- "docs:dev": "vitepress dev docs",
27
+ "docs:dev": "vitepress dev docs --port 3333",
28
28
  "docs:build": "npm run docs:generate && vitepress build docs",
29
29
  "docs:preview": "vitepress preview docs"
30
30
  },
31
31
  "dependencies": {
32
+ "@eox/itemfilter": "^0.14.0",
32
33
  "@eox/layout": "^0.1.0",
34
+ "@eox/map": "^1.4.0",
33
35
  "@eox/stacinfo": "^0.3.0",
34
36
  "@mdi/font": "7.0.96",
35
37
  "@unhead/vue": "^1.8.20",
36
38
  "@vitejs/plugin-vue": "^5.0.0",
39
+ "@web-comp/core": "^1.0.2",
40
+ "@web-comp/date-picker": "^1.0.8",
37
41
  "animated-details": "gist:2912bb049fa906671807415eb0e87188",
38
42
  "axios": "^1.6.2",
39
43
  "commander": "^12.0.0",
40
44
  "core-js": "^3.29.0",
41
45
  "pinia": "^2.0.0",
42
46
  "sass": "^1.60.0",
47
+ "stac-js": "^0.0.9",
43
48
  "stac-ts": "^1.0.3",
44
49
  "vite": "^5.1.5",
45
50
  "vite-plugin-vuetify": "^2.0.0",
@@ -68,4 +73,4 @@
68
73
  "bin": {
69
74
  "eodash": "./bin/app.js"
70
75
  }
71
- }
76
+ }
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <span class="fill-height fill-width align-center justify-center">
3
+ <div v-if="inline" class="fill-height fill-width">
4
+
5
+ <v-text-field base-color="primary" class="fill-height fill-width pa-2 align-center" type="date" bg-color="surface"
6
+ color="primary" density="comfortable" label="Select Date" v-model="currentDate" variant="plain" hide-details />
7
+ </div>
8
+ <v-date-picker v-else ref="datePicker" :width="width" :height="height" hide-header v-model="currentDate"
9
+ color="primary" bg-color="surface" location="center" class="overflow-auto fill-height fill-width"
10
+ position="relative" show-adjacent-months></v-date-picker>
11
+ </span>
12
+ </template>
13
+ <script setup>
14
+ import { computed, ref, onMounted } from "vue";
15
+ import { datetime } from "@/store/States"
16
+
17
+ const props = defineProps(["inline"])
18
+
19
+ const currentDate = computed({
20
+ get() {
21
+ return props.inline ? datetime.value.split("T")[0] : new Date(datetime.value) ?? new Date()
22
+ },
23
+ /** @param {Date | string} updatedDate */
24
+ set(updatedDate) {
25
+ if (props.inline) {
26
+ updatedDate = new Date(updatedDate);
27
+ }
28
+ //@ts-expect-error
29
+ if (updatedDate instanceof Date && !isNaN(updatedDate)) {
30
+ datetime.value = updatedDate.toISOString()
31
+ } else {
32
+ datetime.value = new Date().toISOString()
33
+ }
34
+ }
35
+ });
36
+
37
+ /**
38
+ * @type {import("vue").Ref<import("vuetify/components").VDatePicker | null>}
39
+ **/
40
+ const datePicker = ref(null)
41
+ /** @type {import("vue").Ref<string|undefined>} */
42
+ const width = ref()
43
+ /** @type {import("vue").Ref<string|undefined>} */
44
+ const height = ref()
45
+ onMounted(() => {
46
+ /** @type {HTMLElement} */
47
+ const parentEl = datePicker.value?.$el.parentElement?.parentElement
48
+ width.value = parentEl?.clientWidth ? parentEl.clientWidth + "px" : undefined
49
+ height.value = parentEl?.clientHeight ? parentEl.clientHeight + "px" : undefined
50
+ })
51
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <DynamicWebComponent :link="link" tag-name="eox-itemfilter" :properties="properties" :on-mounted="onMounted" />
3
+ </template>
4
+ <script setup>
5
+ import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
6
+
7
+ const link = () => import("@eox/itemfilter");
8
+
9
+ const properties = {
10
+ config: {
11
+ titleProperty: "title",
12
+ filterProperties: [
13
+ {
14
+ keys: ["title", "themes"],
15
+ title: "Search",
16
+ type: "text",
17
+ expanded: true,
18
+ },
19
+ {
20
+ key: "themes",
21
+ title: "Theme",
22
+ type: "multiselect",
23
+ featured: true,
24
+ },
25
+ ],
26
+ aggregateResults: "themes",
27
+ enableHighlighting: true,
28
+ },
29
+ };
30
+
31
+ /** @type {import("../core/types").WebComponentProps["onMounted"]}*/
32
+ const onMounted = (el, store, router) => {
33
+ /**
34
+ * @typedef {object} Item
35
+ * @property {string} href
36
+ * */
37
+ /** @type {any} */ (el).apply(
38
+ // Only list child elements in list
39
+ store.stac?.filter((item) => item.rel === "child")
40
+ );
41
+ // Check if indicator is selected
42
+ const { query } = router.currentRoute.value;
43
+ if ("indicator" in query) {
44
+ const match = store.stac?.find((item) => item.id === query.indicator);
45
+ if (match) {
46
+ //@ts-expect-error
47
+ (el).selectedResult = match;
48
+ store.loadSelectedSTAC(match.href);
49
+ }
50
+ }
51
+ /** @type {any} */ (el).config.onSelect =
52
+ /**
53
+ * @param {Item} item
54
+ * */
55
+ async (item) => {
56
+ console.log(item);
57
+ await store.loadSelectedSTAC(item.href);
58
+ };
59
+ };
60
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <DynamicWebComponent :link="link" tag-name="eox-map" :properties="properties" :on-mounted="onMounted"
3
+ :on-unmounted="onUnmounted" />
4
+ </template>
5
+ <script setup>
6
+ import { inject, watch } from "vue";
7
+ import { toAbsolute } from "stac-js/src/http.js";
8
+ import { EodashCollection } from "@/utils/eodashSTAC";
9
+ import { eodashKey } from "@/store/Keys";
10
+ import { mapInstance, datetime } from "@/store/States";
11
+ import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
12
+ import { storeToRefs } from "pinia";
13
+ import { useRouter } from "vue-router";
14
+ import "@eox/map/dist/eox-map-advanced-layers-and-sources.js";
15
+
16
+ const eodashConfig = /** @type {import("@/types").Eodash} */ inject(eodashKey);
17
+
18
+ const properties = {
19
+ class: "fill-height fill-width overflow-none",
20
+ center: [15, 48],
21
+ layers: [{ type: "Tile", source: { type: "OSM" } }],
22
+ };
23
+ const router = useRouter();
24
+ const { query } = router.currentRoute.value;
25
+ if ("x" in query && "y" in query) {
26
+ properties.center = [Number(query.x), Number(query.y)];
27
+ }
28
+ if ("z" in query) {
29
+ // @ts-ignore
30
+ properties.zoom = query.z;
31
+ }
32
+ const link = () => import("@eox/map");
33
+
34
+ /** @type {import("openlayers").EventsListenerFunctionType}*/
35
+ let handleMoveEnd;
36
+
37
+ /** @type {import("@/types").WebComponentProps["onMounted"]} */
38
+ const onMounted = (el, store, _router) => {
39
+ mapInstance.value = /** @type {any} */ (el).map;
40
+
41
+ const { selectedStac } = storeToRefs(store);
42
+
43
+ watch(
44
+ [selectedStac, datetime],
45
+ async ([updatedStac, updatedTime]) => {
46
+ if (updatedStac) {
47
+ const parentCollUrl = toAbsolute(
48
+ `./${updatedStac.id}/collection.json`,
49
+ eodashConfig.stacEndpoint
50
+ );
51
+ const childCollUrl = toAbsolute(
52
+ updatedStac.links[1].href,
53
+ parentCollUrl
54
+ );
55
+ const eodash = new EodashCollection(childCollUrl);
56
+ if (updatedTime) {
57
+ /** @type {any} */ (el).layers = await eodash.createLayersJson(
58
+ new Date(updatedTime)
59
+ );
60
+ } else {
61
+ /** @type {any} */ (el).layers = await eodash.createLayersJson();
62
+ }
63
+ }
64
+ },
65
+ { immediate: true }
66
+ );
67
+ };
68
+ /** @type {import("@/types").WebComponentProps["onUnmounted"]} */
69
+ const onUnmounted = (_el, _store, _router) => {
70
+ mapInstance.value?.un("moveend", handleMoveEnd);
71
+ };
72
+ </script>