@eodash/eodash 5.1.0 → 5.3.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.
- package/core/client/components/DashboardLayout.vue +1 -2
- package/core/client/components/EodashOverlay.vue +4 -5
- package/core/client/components/MobileLayout.vue +42 -21
- package/core/client/composables/index.js +54 -60
- package/core/client/eodashSTAC/EodashCollection.js +199 -108
- package/core/client/eodashSTAC/auth.js +86 -0
- package/core/client/eodashSTAC/createLayers.js +234 -4
- package/core/client/eodashSTAC/helpers.js +281 -59
- package/core/client/eodashSTAC/parquet.js +0 -13
- package/core/client/eodashSTAC/triggers.js +1 -1
- package/core/client/store/actions.js +14 -0
- package/core/client/store/stac.js +46 -8
- package/core/client/store/states.js +6 -0
- package/core/client/types.ts +206 -3
- package/core/client/utils/bands-editor/arithmetic.js +144 -0
- package/core/client/utils/bands-editor/colors.js +36 -0
- package/core/client/utils/bands-editor/dom.js +196 -0
- package/core/client/utils/bands-editor/exampleSchema.json +1320 -0
- package/core/client/utils/bands-editor/index.js +68 -0
- package/core/client/utils/bands-editor/rgb.js +102 -0
- package/core/client/utils/index.js +5 -2
- package/core/client/views/Dashboard.vue +1 -1
- package/core/client/vite-env.d.ts +122 -0
- package/dist/client/{DashboardLayout-ByVs1DrY.js → DashboardLayout-Cq15p4TH.js} +5 -6
- package/dist/client/{DynamicWebComponent-C3W7HSQm.js → DynamicWebComponent-Cv-fPRG1.js} +1 -1
- package/dist/client/{EodashDatePicker-BIAf1sMT.js → EodashDatePicker-CPlJwEIO.js} +20 -22
- package/dist/client/{EodashItemFilter-DPznh8UB.js → EodashItemFilter-Ydebgbjj.js} +46 -31
- package/dist/client/EodashLayerControl-COhrkNEs.js +1517 -0
- package/dist/client/{EodashLayoutSwitcher-C5qTEffW.js → EodashLayoutSwitcher-pnKhTRZV.js} +4 -4
- package/dist/client/EodashMapBtns-Cj0Fx119.js +301 -0
- package/dist/client/{EodashStacInfo-CSvvF2jI.js → EodashStacInfo-Dadkg_Nj.js} +1 -1
- package/dist/client/EodashTimeSlider-CpoHX0S7.js +53 -0
- package/dist/client/{EodashTools-Cv1SXQ5y.js → EodashTools-UGBG7KC9.js} +10 -7
- package/dist/client/{ExportState-D-iuwaad.js → ExportState-GtJkAqeZ.js} +145 -121
- package/dist/client/{Footer-CyF0zRAk.js → Footer-D3ZPG5c4.js} +1 -1
- package/dist/client/{Header-CgD8jDKU.js → Header-z6AK-wpN.js} +2 -3
- package/dist/client/MobileLayout-BXNsNftb.js +118 -0
- package/dist/client/{PopUp-BsYLvWch.js → PopUp-BbQdjENV.js} +79 -44
- package/dist/client/{ProcessList-C2xsLU2_.js → ProcessList-C6VsdsYI.js} +18 -12
- package/dist/client/{VImg-OHe8YTs2.js → VImg-CxaMSB99.js} +203 -5
- package/dist/client/{VMain-PryTLU4a.js → VMain-Ds7yw0wj.js} +1 -1
- package/dist/client/{VTooltip-DZ0fjpB3.js → VTooltip-Cze6CEVh.js} +2 -3
- package/dist/client/{WidgetsContainer-B9LBadcC.js → WidgetsContainer-D66bj-JJ.js} +1 -1
- package/dist/client/asWebComponent-CWbNRdf9.js +8895 -0
- package/dist/client/{async-DkSu_u2K.js → async-BA7oWCMX.js} +69 -5
- package/dist/client/easing-CH0-9wR8.js +35 -0
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{VOverlay-yUn7p-Uf.js → forwardRefs-BUfxOIo-.js} +308 -28
- package/dist/client/{handling-CgmFXkW6.js → handling-DlNTtKB-.js} +27 -6
- package/dist/client/{helpers-Dy0Q13tP.js → helpers-CtE0W7iu.js} +595 -278
- package/dist/client/{index-skjhlH8u.js → index-CeEZIjO6.js} +26 -13
- package/dist/client/{index-Ch_HchK3.js → index-CsKbRDeN.js} +238 -77
- package/dist/client/{index-Dqj4tbx2.js → index-D4_NRKrf.js} +2 -2
- package/dist/client/index-DeECc3lV.js +571 -0
- package/dist/client/material-symbols-outlined.woff2 +0 -0
- package/dist/client/material-symbols-rounded.woff2 +0 -0
- package/dist/client/material-symbols-sharp.woff2 +0 -0
- package/dist/client/material-symbols-subset.woff2 +0 -0
- package/dist/client/templates.js +106 -49
- package/dist/client/{transition-C98Yn4Vo.js → transition-Byvp3L6Y.js} +1 -1
- package/dist/node/cli.js +6 -6
- package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +24 -10
- package/dist/types/core/client/eodashSTAC/auth.d.ts +7 -0
- package/dist/types/core/client/eodashSTAC/createLayers.d.ts +15 -3
- package/dist/types/core/client/eodashSTAC/helpers.d.ts +51 -15
- package/dist/types/core/client/plugins/vuetify.d.ts +14 -14
- package/dist/types/core/client/store/actions.d.ts +2 -0
- package/dist/types/core/client/store/stac.d.ts +16 -7
- package/dist/types/core/client/store/states.d.ts +4 -0
- package/dist/types/core/client/types.d.ts +171 -3
- package/dist/types/core/client/utils/bands-editor/arithmetic.d.ts +8 -0
- package/dist/types/core/client/utils/bands-editor/colors.d.ts +15 -0
- package/dist/types/core/client/utils/bands-editor/dom.d.ts +42 -0
- package/dist/types/core/client/utils/bands-editor/index.d.ts +20 -0
- package/dist/types/core/client/utils/bands-editor/rgb.d.ts +15 -0
- package/dist/types/core/client/utils/index.d.ts +1 -1
- package/dist/types/templates/baseConfig.d.ts +87 -1
- package/dist/types/templates/compare.d.ts +0 -25
- package/dist/types/templates/expert.d.ts +17 -21
- package/dist/types/templates/explore.d.ts +67 -0
- package/dist/types/templates/index.d.ts +1 -1
- package/dist/types/templates/{light.d.ts → lite.d.ts} +9 -0
- package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +21 -0
- package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +49 -0
- package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +4 -0
- package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +12 -0
- package/dist/types/widgets/EodashItemCatalog/types.d.ts +14 -0
- package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +6 -0
- package/dist/types/widgets/EodashMap/index.vue.d.ts +114 -0
- package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
- package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
- package/dist/types/widgets/EodashProcess/methods/async.d.ts +1 -0
- package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
- package/dist/types/widgets/EodashTimeSlider.vue.d.ts +7 -0
- package/dist/types/widgets/EodashTools.vue.d.ts +10 -10
- package/dist/types/widgets/ExportState.vue.d.ts +2 -0
- package/package.json +31 -28
- package/templates/baseConfig.js +10 -5
- package/templates/compare.js +2 -22
- package/templates/expert.js +19 -18
- package/templates/explore.js +62 -0
- package/templates/index.js +1 -1
- package/templates/{light.js → lite.js} +11 -2
- package/widgets/EodashDatePicker.vue +15 -18
- package/widgets/EodashItemCatalog/index.vue +161 -0
- package/widgets/EodashItemCatalog/methods/filters.js +216 -0
- package/widgets/EodashItemCatalog/methods/handlers.js +50 -0
- package/widgets/EodashItemCatalog/methods/map.js +144 -0
- package/widgets/EodashItemCatalog/types.ts +15 -0
- package/widgets/EodashItemFilter.vue +35 -28
- package/widgets/EodashLayerControl.vue +10 -6
- package/widgets/EodashLayoutSwitcher.vue +1 -1
- package/widgets/EodashMap/EodashMapBtns.vue +278 -0
- package/widgets/EodashMap/index.vue +263 -38
- package/widgets/EodashMap/methods/create-layers-config.js +9 -6
- package/widgets/EodashMap/methods/index.js +27 -13
- package/widgets/EodashProcess/ProcessList.vue +13 -1
- package/widgets/EodashProcess/index.vue +17 -1
- package/widgets/EodashProcess/methods/async.js +22 -1
- package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +25 -3
- package/widgets/EodashProcess/methods/handling.js +2 -0
- package/widgets/EodashProcess/methods/outputs.js +1 -0
- package/widgets/EodashProcess/methods/utils.js +45 -1
- package/widgets/EodashTimeSlider.vue +40 -0
- package/widgets/EodashTools.vue +7 -3
- package/widgets/ExportState.vue +53 -22
- package/dist/client/EodashLayerControl-Bhxjw4V2.js +0 -154
- package/dist/client/EodashMapBtns-WoGq8MuV.js +0 -173
- package/dist/client/MobileLayout-EKQ_kpSh.js +0 -1226
- package/dist/client/asWebComponent-By_7_JjS.js +0 -19193
- package/dist/client/forwardRefs-BXxrv98s.js +0 -272
- package/dist/client/index-BuhOHXKv.js +0 -199
- package/widgets/EodashMapBtns.vue +0 -155
|
@@ -46,14 +46,28 @@ export function generateFeatures(links, extraProperties = {}, rel = "item") {
|
|
|
46
46
|
*
|
|
47
47
|
* @param {string} collectionId
|
|
48
48
|
* @param { import("@/types").EodashStyleJson} [style]
|
|
49
|
+
* @param {Record<string,any>} [rasterJsonform]
|
|
49
50
|
* */
|
|
50
|
-
export function extractLayerConfig(collectionId, style) {
|
|
51
|
-
if (!style) {
|
|
51
|
+
export function extractLayerConfig(collectionId, style, rasterJsonform) {
|
|
52
|
+
if (!style && !rasterJsonform) {
|
|
52
53
|
return { layerConfig: undefined, style: undefined };
|
|
53
54
|
}
|
|
54
|
-
style
|
|
55
|
+
if (style) {
|
|
56
|
+
style = { ...style };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (rasterJsonform) {
|
|
60
|
+
return {
|
|
61
|
+
layerConfig: {
|
|
62
|
+
schema: rasterJsonform.jsonform,
|
|
63
|
+
legend: rasterJsonform.legend,
|
|
64
|
+
type: "tileUrl",
|
|
65
|
+
},
|
|
66
|
+
style,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
55
69
|
|
|
56
|
-
if (Object.keys(style.variables ?? {}).length) {
|
|
70
|
+
if (style?.variables && Object.keys(style.variables ?? {}).length) {
|
|
57
71
|
style.variables = getStyleVariablesState(collectionId, style.variables);
|
|
58
72
|
}
|
|
59
73
|
|
|
@@ -61,6 +75,7 @@ export function extractLayerConfig(collectionId, style) {
|
|
|
61
75
|
let layerConfig = undefined;
|
|
62
76
|
|
|
63
77
|
if (style?.jsonform) {
|
|
78
|
+
// this explicitly sets legend only if jsonform is configured
|
|
64
79
|
layerConfig = { schema: style.jsonform, type: "style" };
|
|
65
80
|
delete style.jsonform;
|
|
66
81
|
if (style?.legend) {
|
|
@@ -75,7 +90,25 @@ export function extractLayerConfig(collectionId, style) {
|
|
|
75
90
|
|
|
76
91
|
return { layerConfig, style };
|
|
77
92
|
}
|
|
78
|
-
|
|
93
|
+
/**
|
|
94
|
+
*
|
|
95
|
+
* @param {number[]} bbox
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
98
|
+
export const sanitizeBbox = (bbox) => {
|
|
99
|
+
if (!bbox || !bbox.length || bbox.length !== 4) {
|
|
100
|
+
return [0, 0, 0, 0];
|
|
101
|
+
}
|
|
102
|
+
let [minX, minY, maxX, maxY] = bbox;
|
|
103
|
+
// Normalize longitudes to be within -180 to 180
|
|
104
|
+
minX = Math.max(((minX + 180) % 360) - 180, -180);
|
|
105
|
+
maxX = Math.min(((maxX - 180) % 360) + 180, 180);
|
|
106
|
+
// Normalize latitudes to be within -90 to 90
|
|
107
|
+
minY = Math.max(((minY + 90) % 180) - 90, -90);
|
|
108
|
+
maxY = Math.min(((maxY - 90) % 180) + 90, 90);
|
|
109
|
+
|
|
110
|
+
return [minX, minY, maxX, maxY];
|
|
111
|
+
};
|
|
79
112
|
/**
|
|
80
113
|
* Function to extract collection urls from an indicator
|
|
81
114
|
* @param {import("stac-ts").StacCatalog
|
|
@@ -100,6 +133,10 @@ export function extractCollectionUrls(stacObject, basepath) {
|
|
|
100
133
|
return collectionUrls;
|
|
101
134
|
}
|
|
102
135
|
children.forEach((link) => {
|
|
136
|
+
if (link.href.startsWith("http")) {
|
|
137
|
+
collectionUrls.push(link.href);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
103
140
|
collectionUrls.push(toAbsolute(link.href, basepath));
|
|
104
141
|
});
|
|
105
142
|
return collectionUrls;
|
|
@@ -115,6 +152,8 @@ export const extractRoles = (properties, linkOrAsset) => {
|
|
|
115
152
|
roles?.forEach((role) => {
|
|
116
153
|
if (role === "visible") {
|
|
117
154
|
properties.visible = true;
|
|
155
|
+
} else if (role === "invisible") {
|
|
156
|
+
properties.visible = false;
|
|
118
157
|
}
|
|
119
158
|
if (role === "overlay" || role === "baselayer") {
|
|
120
159
|
properties.group = role;
|
|
@@ -124,13 +163,19 @@ export const extractRoles = (properties, linkOrAsset) => {
|
|
|
124
163
|
};
|
|
125
164
|
|
|
126
165
|
/**
|
|
127
|
-
* Extracts style JSON from a STAC Item
|
|
166
|
+
* Extracts a single non-link style JSON from a STAC Item optionally for a selected key mapping
|
|
128
167
|
* @param {import("stac-ts").StacItem} item
|
|
129
168
|
* @param {string} itemUrl
|
|
130
|
-
* @
|
|
169
|
+
* @param {string | undefined} key
|
|
170
|
+
* @returns
|
|
131
171
|
**/
|
|
132
|
-
export const fetchStyle = async (item, itemUrl) => {
|
|
133
|
-
|
|
172
|
+
export const fetchStyle = async (item, itemUrl, key=undefined) => {
|
|
173
|
+
let styleLink = null;
|
|
174
|
+
if (key) {
|
|
175
|
+
styleLink = item.links.find((link) => link.rel.includes("style") && link["links:keys"] && /** @type {Array<string>} */ (link["links:keys"]).includes(key) );
|
|
176
|
+
} else {
|
|
177
|
+
styleLink = item.links.find((link) => link.rel.includes("style") && !link["links:keys"]);
|
|
178
|
+
}
|
|
134
179
|
if (styleLink) {
|
|
135
180
|
let url = "";
|
|
136
181
|
if (styleLink.href.startsWith("http")) {
|
|
@@ -147,6 +192,27 @@ export const fetchStyle = async (item, itemUrl) => {
|
|
|
147
192
|
}
|
|
148
193
|
};
|
|
149
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Fetches all style JSONs from a STAC Item and returns an array with style objects
|
|
197
|
+
* @param {import("stac-ts").StacItem} item
|
|
198
|
+
* @param {string} itemUrl
|
|
199
|
+
* @returns { Promise <Array<import("@/types").EodashStyleJson>>}
|
|
200
|
+
**/
|
|
201
|
+
export const fetchAllStyles = async (item, itemUrl) => {
|
|
202
|
+
const styleLinks = item.links.filter((link) => link.rel.includes("style"));
|
|
203
|
+
const fetchPromises = styleLinks.map(async (link) => {
|
|
204
|
+
let url = link.href.startsWith("http")
|
|
205
|
+
? link.href
|
|
206
|
+
: toAbsolute(link.href, itemUrl);
|
|
207
|
+
|
|
208
|
+
const styleJson = await axios.get(url).then((resp) => resp.data);
|
|
209
|
+
log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
|
|
210
|
+
return styleJson;
|
|
211
|
+
});
|
|
212
|
+
const results = await Promise.all(fetchPromises);
|
|
213
|
+
return results;
|
|
214
|
+
};
|
|
215
|
+
|
|
150
216
|
/**
|
|
151
217
|
* Return projection code which is to be registered in `eox-map`
|
|
152
218
|
* @param {string|number|{name: string, def: string}} [projection]
|
|
@@ -169,59 +235,89 @@ export const getProjectionCode = (projection) => {
|
|
|
169
235
|
|
|
170
236
|
/**
|
|
171
237
|
* Extracts layercontrol LayerDatetime property from STAC Links
|
|
172
|
-
* @param {import("stac-ts").StacLink[]} [
|
|
238
|
+
* @param {import("stac-ts").StacLink[] | import("stac-ts").StacItem[] | undefined} [items]
|
|
173
239
|
* @param {string|null} [currentStep]
|
|
174
240
|
**/
|
|
175
|
-
export const
|
|
176
|
-
if (!currentStep || !
|
|
177
|
-
return undefined;
|
|
241
|
+
export const extractLayerTimeValues = (items, currentStep) => {
|
|
242
|
+
if (!currentStep || !items?.length) {
|
|
243
|
+
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
178
244
|
}
|
|
179
245
|
|
|
180
|
-
// check if
|
|
181
|
-
const dateProperty = getDatetimeProperty(
|
|
246
|
+
// check if items has a datetime value
|
|
247
|
+
const dateProperty = getDatetimeProperty(items);
|
|
182
248
|
|
|
183
249
|
if (!dateProperty) {
|
|
184
|
-
return undefined;
|
|
250
|
+
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
185
251
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const controlValues = [];
|
|
252
|
+
/** @type {{date:string;itemId:string}[]} */
|
|
253
|
+
const timeValues = [];
|
|
189
254
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
/**
|
|
256
|
+
* @param {typeof timeValues} vals
|
|
257
|
+
* @param {import("stac-ts").StacLink} link
|
|
258
|
+
*/
|
|
259
|
+
const reduceLinks = (vals, link) => {
|
|
192
260
|
if (link[dateProperty] && link.rel === "item") {
|
|
193
|
-
vals.push(
|
|
194
|
-
|
|
195
|
-
|
|
261
|
+
vals.push({
|
|
262
|
+
itemId: /** @type {string} */ (link.id),
|
|
263
|
+
date: new Date(
|
|
264
|
+
/** @type {string} */ (link[dateProperty]),
|
|
265
|
+
).toISOString(),
|
|
266
|
+
});
|
|
196
267
|
}
|
|
197
268
|
return vals;
|
|
198
|
-
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
*
|
|
273
|
+
* @param {typeof timeValues} vals
|
|
274
|
+
* @param {import("stac-ts").StacItem} item
|
|
275
|
+
*/
|
|
276
|
+
const reduceItems = (vals, item) => {
|
|
277
|
+
const date = item.properties?.[dateProperty];
|
|
278
|
+
if (date) {
|
|
279
|
+
vals.push({
|
|
280
|
+
itemId: /** @type {string} */ (item.id),
|
|
281
|
+
date: new Date(/** @type {string} */ (date)).toISOString(),
|
|
282
|
+
...item.properties,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return vals;
|
|
286
|
+
};
|
|
287
|
+
currentStep = new Date(currentStep).toISOString();
|
|
288
|
+
//@ts-expect-error TODO
|
|
289
|
+
items.reduce(isSTACItem(items[0]) ? reduceItems : reduceLinks, timeValues);
|
|
199
290
|
} catch (e) {
|
|
200
291
|
console.warn("[eodash] not supported datetime format was provided", e);
|
|
201
|
-
return undefined;
|
|
292
|
+
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
202
293
|
}
|
|
203
|
-
// not enough
|
|
204
|
-
if (
|
|
205
|
-
return undefined;
|
|
294
|
+
// not enough timeValues
|
|
295
|
+
if (timeValues.length <= 1) {
|
|
296
|
+
return { layerDatetime: undefined, timeControlValues: undefined };
|
|
206
297
|
}
|
|
207
298
|
|
|
208
299
|
// item datetime is not included in the item links datetime
|
|
209
|
-
if (!
|
|
300
|
+
if (!timeValues.some((val) => val.date === currentStep)) {
|
|
210
301
|
const currentStepTime = new Date(currentStep).getTime();
|
|
211
|
-
currentStep =
|
|
212
|
-
const aDiff = Math.abs(new Date(
|
|
213
|
-
const bDiff = Math.abs(new Date(
|
|
214
|
-
return bDiff < aDiff ?
|
|
215
|
-
});
|
|
302
|
+
currentStep = timeValues.reduce((time, step) => {
|
|
303
|
+
const aDiff = Math.abs(new Date(time).getTime() - currentStepTime);
|
|
304
|
+
const bDiff = Math.abs(new Date(step.date).getTime() - currentStepTime);
|
|
305
|
+
return bDiff < aDiff ? step.date : time;
|
|
306
|
+
}, timeValues[0].date);
|
|
216
307
|
}
|
|
217
308
|
|
|
218
|
-
|
|
219
|
-
controlValues,
|
|
309
|
+
const layerDatetime = {
|
|
310
|
+
controlValues: timeValues.map((d) => d.date).sort(),
|
|
220
311
|
currentStep,
|
|
221
312
|
slider: true,
|
|
222
313
|
navigation: true,
|
|
223
314
|
play: false,
|
|
224
|
-
displayFormat: "DD.MM.YYYY HH:
|
|
315
|
+
displayFormat: "DD.MM.YYYY HH:mm",
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
layerDatetime,
|
|
320
|
+
timeControlValues: timeValues,
|
|
225
321
|
};
|
|
226
322
|
};
|
|
227
323
|
|
|
@@ -287,23 +383,23 @@ export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
|
|
|
287
383
|
*/
|
|
288
384
|
export const getColFromLayer = (indicators, layer) => {
|
|
289
385
|
// init cols
|
|
290
|
-
const collections = indicators.map((ind) => ind.collectionStac);
|
|
386
|
+
// const collections = indicators.map((ind) => ind.collectionStac);
|
|
291
387
|
const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
|
|
292
388
|
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
389
|
+
return indicators.find(async (ind) => {
|
|
390
|
+
const isCollection = ind.collectionStac?.id === collectionId;
|
|
391
|
+
if (!isCollection) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
/** @type {string[]} */
|
|
395
|
+
const itemIds = [];
|
|
396
|
+
await ind.getItems().then((items) => {
|
|
397
|
+
itemIds.push(
|
|
398
|
+
...(items?.map((item) => /** @type {string} */ (item.id)) ?? []),
|
|
303
399
|
);
|
|
304
|
-
|
|
400
|
+
});
|
|
401
|
+
return itemIds.includes(itemId);
|
|
305
402
|
});
|
|
306
|
-
return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
|
|
307
403
|
};
|
|
308
404
|
|
|
309
405
|
/**
|
|
@@ -413,12 +509,20 @@ export async function mergeGeojsons(geojsonUrls) {
|
|
|
413
509
|
features: [],
|
|
414
510
|
};
|
|
415
511
|
await Promise.all(
|
|
416
|
-
geojsonUrls.map((url) =>
|
|
417
|
-
axios
|
|
512
|
+
geojsonUrls.map((url) => {
|
|
513
|
+
// Use native fetch for blob URLs to avoid axios/cache interceptor issues
|
|
514
|
+
if (url.startsWith("blob:")) {
|
|
515
|
+
return fetch(url)
|
|
516
|
+
.then(async (resp) => await resp.json())
|
|
517
|
+
.then((geojson) => {
|
|
518
|
+
merged.features.push(...(geojson.features ?? []));
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
return axios.get(url).then((resp) => {
|
|
418
522
|
const geojson = resp.data;
|
|
419
523
|
merged.features.push(...(geojson.features ?? []));
|
|
420
|
-
})
|
|
421
|
-
),
|
|
524
|
+
});
|
|
525
|
+
}),
|
|
422
526
|
);
|
|
423
527
|
|
|
424
528
|
return encodeURI(
|
|
@@ -532,16 +636,37 @@ export const addTooltipInteraction = (layer, style) => {
|
|
|
532
636
|
|
|
533
637
|
/**
|
|
534
638
|
*
|
|
535
|
-
* @param {import("stac-ts").StacLink[]} [
|
|
639
|
+
* @param {import("stac-ts").StacLink[] | import("stac-ts").StacItem[] |undefined |null} [linksOrItems]
|
|
536
640
|
*/
|
|
537
|
-
export function getDatetimeProperty(
|
|
538
|
-
if (!
|
|
641
|
+
export function getDatetimeProperty(linksOrItems) {
|
|
642
|
+
if (!linksOrItems?.length) {
|
|
539
643
|
return undefined;
|
|
540
644
|
}
|
|
645
|
+
const first = linksOrItems[0];
|
|
646
|
+
let checkProperties = false;
|
|
647
|
+
if (isSTACItem(first)) {
|
|
648
|
+
checkProperties = true;
|
|
649
|
+
}
|
|
650
|
+
|
|
541
651
|
// TODO: consider other properties for datetime ranges
|
|
542
652
|
const datetimeProperties = ["datetime", "start_datetime", "end_datetime"];
|
|
653
|
+
if (checkProperties) {
|
|
654
|
+
for (const prop of datetimeProperties) {
|
|
655
|
+
const propExists = linksOrItems.some(
|
|
656
|
+
(l) =>
|
|
657
|
+
//@ts-expect-error TODO
|
|
658
|
+
l["properties"]?.[prop] &&
|
|
659
|
+
//@ts-expect-error TODO
|
|
660
|
+
typeof l["properties"]?.[prop] === "string",
|
|
661
|
+
);
|
|
662
|
+
if (!propExists) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
return prop;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
543
668
|
for (const prop of datetimeProperties) {
|
|
544
|
-
const propExists =
|
|
669
|
+
const propExists = linksOrItems.some(
|
|
545
670
|
(l) => l[prop] && typeof l[prop] === "string",
|
|
546
671
|
);
|
|
547
672
|
if (!propExists) {
|
|
@@ -550,3 +675,100 @@ export function getDatetimeProperty(links) {
|
|
|
550
675
|
return prop;
|
|
551
676
|
}
|
|
552
677
|
}
|
|
678
|
+
/**
|
|
679
|
+
*
|
|
680
|
+
* @param {*} stacObject
|
|
681
|
+
* @returns {stacObject is import("stac-ts").StacItem}
|
|
682
|
+
*/
|
|
683
|
+
export function isSTACItem(stacObject) {
|
|
684
|
+
return (
|
|
685
|
+
stacObject &&
|
|
686
|
+
typeof stacObject === "object" &&
|
|
687
|
+
stacObject.collection &&
|
|
688
|
+
stacObject.id &&
|
|
689
|
+
stacObject.properties &&
|
|
690
|
+
typeof stacObject.properties === "object"
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Fetch all STAC items from a STAC API endpoint.
|
|
696
|
+
* @param {string} itemsUrl
|
|
697
|
+
* @param {string} [query]
|
|
698
|
+
* @param {number} [limit=100] - The maximum number of items to fetch per request.
|
|
699
|
+
* @param {boolean} [returnFirst] - If true, only the first page of results will be returned.
|
|
700
|
+
* @param {number} [maxNumber=1000] - if the matched number of items exceed this, only the first page will be returned.
|
|
701
|
+
*/
|
|
702
|
+
export async function fetchApiItems(
|
|
703
|
+
itemsUrl,
|
|
704
|
+
query,
|
|
705
|
+
limit = 100,
|
|
706
|
+
returnFirst = false,
|
|
707
|
+
maxNumber = 1000,
|
|
708
|
+
) {
|
|
709
|
+
itemsUrl = itemsUrl.includes("?") ? itemsUrl.split("?")[0] : itemsUrl;
|
|
710
|
+
itemsUrl += query ? `?limit=${limit}&${query}` : `?limit=${limit}`;
|
|
711
|
+
|
|
712
|
+
const itemsFeatureCollection = await axios
|
|
713
|
+
.get(itemsUrl)
|
|
714
|
+
.then((resp) => resp.data);
|
|
715
|
+
/** @type {import("stac-ts").StacItem[]} */
|
|
716
|
+
const items = itemsFeatureCollection.features;
|
|
717
|
+
const nextLink = itemsFeatureCollection.links?.find(
|
|
718
|
+
//@ts-expect-error TODO: itemsFeatureCollection is not typed
|
|
719
|
+
(link) => link.rel === "next",
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
if (!nextLink || returnFirst) {
|
|
723
|
+
return items;
|
|
724
|
+
}
|
|
725
|
+
/** @type {number} */
|
|
726
|
+
const matchedItems = itemsFeatureCollection.numberMatched;
|
|
727
|
+
// Avoid fetching too many items
|
|
728
|
+
if (matchedItems >= maxNumber) {
|
|
729
|
+
console.warn(
|
|
730
|
+
`[eodash] The number of items matched (${matchedItems}) exceeds the maximum allowed (${maxNumber})`,
|
|
731
|
+
);
|
|
732
|
+
return items;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
let [nextLinkURL, nextLinkQuery] = nextLink.href.split("?");
|
|
736
|
+
nextLinkQuery = nextLinkQuery.replace(/limit=\d+/, "");
|
|
737
|
+
if (query) {
|
|
738
|
+
const queryParams = new URLSearchParams(query);
|
|
739
|
+
const nextLinkParams = new URLSearchParams(nextLinkQuery);
|
|
740
|
+
|
|
741
|
+
for (const key of nextLinkParams.keys()) {
|
|
742
|
+
queryParams.delete(key);
|
|
743
|
+
}
|
|
744
|
+
const remainingQuery = queryParams.toString();
|
|
745
|
+
if (remainingQuery) {
|
|
746
|
+
nextLinkQuery += `&${remainingQuery}`;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const nextPage = await fetchApiItems(nextLinkURL, nextLinkQuery);
|
|
751
|
+
items.push(...nextPage);
|
|
752
|
+
return items;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* @param {import ("stac-ts").StacCollection | undefined | null} collection
|
|
756
|
+
* @returns {object}
|
|
757
|
+
*/
|
|
758
|
+
export function extractLayerLegend(collection) {
|
|
759
|
+
let extraProperties = {};
|
|
760
|
+
if (collection?.assets?.legend?.href) {
|
|
761
|
+
extraProperties = {
|
|
762
|
+
description: `<div style="width: 100%">
|
|
763
|
+
<img src="${collection.assets.legend.href}" style="max-height:70px; margin-top:-15px; margin-bottom:-20px;" />
|
|
764
|
+
</div>`,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
// Check if collection has eox:colorlegend definition, if yes overwrite legend description
|
|
768
|
+
if (collection && collection["eox:colorlegend"]) {
|
|
769
|
+
extraProperties = {
|
|
770
|
+
layerLegend: collection["eox:colorlegend"],
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
return extraProperties;
|
|
774
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { parquetRead } from "hyparquet";
|
|
2
|
-
import WKB from "ol/format/WKB.js";
|
|
3
|
-
import GeoJSON from "ol/format/GeoJSON";
|
|
4
2
|
import log from "loglevel";
|
|
5
3
|
|
|
6
4
|
/**
|
|
@@ -62,8 +60,6 @@ export const adjustParquetItems = (items) => {
|
|
|
62
60
|
|
|
63
61
|
return /** @type {import("stac-ts").StacItem} */ ({
|
|
64
62
|
...item,
|
|
65
|
-
//@ts-expect-error geometry wkb conversion by stac-geoparquet
|
|
66
|
-
geometry: wkbToGeometry(item.geometry),
|
|
67
63
|
|
|
68
64
|
assets: ((assets) => {
|
|
69
65
|
for (const [key, value] of Object.entries(assets)) {
|
|
@@ -82,15 +78,6 @@ export const adjustParquetItems = (items) => {
|
|
|
82
78
|
});
|
|
83
79
|
});
|
|
84
80
|
};
|
|
85
|
-
/**
|
|
86
|
-
* @param {Uint8Array} wkb - Well Known Binary
|
|
87
|
-
*/
|
|
88
|
-
function wkbToGeometry(wkb) {
|
|
89
|
-
const geoJsonFormatter = new GeoJSON();
|
|
90
|
-
const wkbReader = new WKB();
|
|
91
|
-
const olGeometry = wkbReader.readGeometry(wkb);
|
|
92
|
-
return geoJsonFormatter.writeGeometryObject(olGeometry);
|
|
93
|
-
}
|
|
94
81
|
|
|
95
82
|
/**
|
|
96
83
|
*
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
activeTemplate,
|
|
6
6
|
poi,
|
|
7
7
|
comparePoi,
|
|
8
|
+
chartEl,
|
|
9
|
+
compareChartEl,
|
|
8
10
|
} from "@/store/states";
|
|
9
11
|
import { getProjectionCode } from "@/eodashSTAC/helpers";
|
|
10
12
|
import log from "loglevel";
|
|
@@ -22,6 +24,18 @@ export const getLayers = () => mapEl.value?.layers.toReversed() ?? [];
|
|
|
22
24
|
export const getCompareLayers = () =>
|
|
23
25
|
mapCompareEl.value?.layers.toReversed() ?? [];
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Returns the current chart spec from {@link chartEl}
|
|
29
|
+
* @returns {import("vega-embed").VisualizationSpec | null}
|
|
30
|
+
*/
|
|
31
|
+
export const getChartSpec = () => chartEl.value?.spec ?? null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns the current chart spec from {@link compareChartEl}
|
|
35
|
+
* @returns {import("vega-embed").VisualizationSpec | null}
|
|
36
|
+
*/
|
|
37
|
+
export const getCompareChartSpec = () => compareChartEl.value?.spec ?? null;
|
|
38
|
+
|
|
25
39
|
/**
|
|
26
40
|
* Register EPSG projection in `eox-map`
|
|
27
41
|
* @param {string|number|{name: string, def: string, extent?:number[]}} [projection]*/
|
|
@@ -19,9 +19,15 @@ import { updateEodashCollections } from "@/utils";
|
|
|
19
19
|
export const useSTAcStore = defineStore("stac", () => {
|
|
20
20
|
/**
|
|
21
21
|
* STAC catalog endpoint URL
|
|
22
|
-
* @type {import("vue").Ref<
|
|
22
|
+
* @type {import("vue").Ref<string| null>}
|
|
23
23
|
*/
|
|
24
24
|
const stacEndpoint = ref(null);
|
|
25
|
+
/**
|
|
26
|
+
* Raster endpoint URL
|
|
27
|
+
* @type {import("vue").Ref<string | null>}
|
|
28
|
+
*/
|
|
29
|
+
const rasterEndpoint = ref(null);
|
|
30
|
+
const isApi = ref(false);
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
33
|
* Links of the root STAC catalog
|
|
@@ -49,20 +55,35 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
49
55
|
* >}
|
|
50
56
|
*/
|
|
51
57
|
const selectedCompareStac = ref(null);
|
|
58
|
+
/**
|
|
59
|
+
* Currently selected item
|
|
60
|
+
* @type {import("vue").Ref<import("stac-ts").StacLink | import("stac-ts").StacItem | null>}
|
|
61
|
+
*/
|
|
62
|
+
const selectedItem = ref(null);
|
|
52
63
|
|
|
53
64
|
/**
|
|
54
65
|
* Initializes the store by assigning the STAC endpoint.
|
|
55
66
|
* @param {import("@/types").StacEndpoint} endpoint
|
|
56
67
|
*/
|
|
57
68
|
function init(endpoint) {
|
|
58
|
-
|
|
69
|
+
if (!endpoint) {
|
|
70
|
+
throw new Error("STAC endpoint is not defined");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof endpoint === "string") {
|
|
74
|
+
stacEndpoint.value = endpoint;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
stacEndpoint.value = endpoint.endpoint;
|
|
78
|
+
isApi.value = endpoint.api ?? false;
|
|
79
|
+
rasterEndpoint.value = endpoint.rasterEndpoint ?? null;
|
|
59
80
|
}
|
|
60
81
|
|
|
61
82
|
/**
|
|
62
83
|
* Fetches root stac catalog and assign it to `stac`
|
|
63
84
|
*
|
|
64
|
-
* @param {
|
|
65
|
-
* is `
|
|
85
|
+
* @param {string} [url=stacEndpoint] Default
|
|
86
|
+
* is the configured `stacEndpoint` url
|
|
66
87
|
* @returns {Promise<void>}
|
|
67
88
|
* @see {@link stac}
|
|
68
89
|
*/
|
|
@@ -78,14 +99,17 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
78
99
|
stac.value = null;
|
|
79
100
|
return;
|
|
80
101
|
}
|
|
102
|
+
if (isApi.value) {
|
|
103
|
+
url = url + "/collections?limit=1000"; // to get all collections
|
|
104
|
+
}
|
|
105
|
+
const property = isApi.value ? "collections" : "links";
|
|
81
106
|
|
|
82
107
|
log.debug("Loading STAC endpoint", url);
|
|
83
108
|
await axios
|
|
84
109
|
.get(url)
|
|
85
110
|
.then((resp) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
).links.map((link) => {
|
|
111
|
+
//@ts-expect-error TODO
|
|
112
|
+
const links = resp.data[property].map((link) => {
|
|
89
113
|
if (!link.title) {
|
|
90
114
|
link.title = `${link.rel} ${link.href}`;
|
|
91
115
|
}
|
|
@@ -104,10 +128,11 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
104
128
|
*
|
|
105
129
|
* @param {string} relativePath - Stac link href
|
|
106
130
|
* @param {boolean} [isPoi=false] - If true, the STAC is loaded for a point of interest
|
|
131
|
+
* @param {Object} [stacItem] - The STAC item to load
|
|
107
132
|
* @returns {Promise<void>}
|
|
108
133
|
* @see {@link selectedStac}
|
|
109
134
|
*/
|
|
110
|
-
async function loadSelectedSTAC(relativePath = "", isPoi = false) {
|
|
135
|
+
async function loadSelectedSTAC(relativePath = "", isPoi = false, stacItem) {
|
|
111
136
|
if (!stacEndpoint.value) {
|
|
112
137
|
return Promise.reject(new Error("STAC endpoint is not defined"));
|
|
113
138
|
}
|
|
@@ -116,6 +141,11 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
116
141
|
// construct absolute URL of a poi
|
|
117
142
|
absoluteUrl.value = constructPoiUrl(relativePath, indicator.value);
|
|
118
143
|
}
|
|
144
|
+
//@ts-expect-error "this" type is not exported by pinia
|
|
145
|
+
const patch = this?.$patch;
|
|
146
|
+
if (stacItem && patch) {
|
|
147
|
+
patch({ selectedItem: stacItem });
|
|
148
|
+
}
|
|
119
149
|
|
|
120
150
|
await axios
|
|
121
151
|
.get(absoluteUrl.value)
|
|
@@ -125,6 +155,8 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
125
155
|
resp.data,
|
|
126
156
|
absoluteUrl.value,
|
|
127
157
|
collectionsPalette,
|
|
158
|
+
isApi.value,
|
|
159
|
+
rasterEndpoint.value,
|
|
128
160
|
);
|
|
129
161
|
selectedStac.value = resp.data;
|
|
130
162
|
// set indicator and poi
|
|
@@ -141,6 +173,7 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
141
173
|
* Fetches selected stac object and assign it to `selectedCompareStac`
|
|
142
174
|
*
|
|
143
175
|
* @param {string} relativePath - Stac link href
|
|
176
|
+
* @param {boolean} [isPOI=false] - If true, the STAC is loaded for a point of interest
|
|
144
177
|
* @returns {Promise<void>}
|
|
145
178
|
* @see {@link selectedCompareStac}
|
|
146
179
|
*/
|
|
@@ -163,6 +196,8 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
163
196
|
resp.data,
|
|
164
197
|
absoluteUrl.value,
|
|
165
198
|
[...collectionsPalette].reverse(),
|
|
199
|
+
isApi.value,
|
|
200
|
+
rasterEndpoint.value,
|
|
166
201
|
);
|
|
167
202
|
selectedCompareStac.value = resp.data;
|
|
168
203
|
compareIndicator.value = isPOI
|
|
@@ -203,6 +238,8 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
203
238
|
}
|
|
204
239
|
|
|
205
240
|
return {
|
|
241
|
+
stacEndpoint,
|
|
242
|
+
isApi,
|
|
206
243
|
stac,
|
|
207
244
|
init,
|
|
208
245
|
loadSTAC,
|
|
@@ -211,5 +248,6 @@ export const useSTAcStore = defineStore("stac", () => {
|
|
|
211
248
|
resetSelectedCompareSTAC,
|
|
212
249
|
selectedStac,
|
|
213
250
|
selectedCompareStac,
|
|
251
|
+
selectedItem,
|
|
214
252
|
};
|
|
215
253
|
});
|
|
@@ -45,3 +45,9 @@ export const poi = ref("");
|
|
|
45
45
|
* Selected point of interest for comparison, used for location collections
|
|
46
46
|
*/
|
|
47
47
|
export const comparePoi = ref("");
|
|
48
|
+
|
|
49
|
+
/** @type {import("vue").Ref<import("@eox/chart").EOxChart | null>} */
|
|
50
|
+
export const chartEl = shallowRef(null);
|
|
51
|
+
|
|
52
|
+
/** @type {import("vue").Ref<import("@eox/chart").EOxChart | null>} */
|
|
53
|
+
export const compareChartEl = shallowRef(null);
|