@eodash/eodash 5.2.0 → 5.3.1
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 +0 -1
- package/core/client/composables/index.js +53 -59
- package/core/client/eodashSTAC/EodashCollection.js +196 -94
- package/core/client/eodashSTAC/auth.js +86 -0
- package/core/client/eodashSTAC/createLayers.js +204 -4
- package/core/client/eodashSTAC/helpers.js +254 -62
- 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-Dq9Kfe6O.js → DashboardLayout-BAstYnhU.js} +4 -5
- package/dist/client/{DynamicWebComponent-DCBMXskE.js → DynamicWebComponent-7v4_DFqP.js} +1 -1
- package/dist/client/{EodashDatePicker-DtngxU6s.js → EodashDatePicker-IVHLv9UN.js} +20 -22
- package/dist/client/{EodashItemFilter-ClQebJQt.js → EodashItemFilter-BPMpnXjo.js} +46 -31
- package/dist/client/EodashLayerControl-CSnQh2tb.js +1517 -0
- package/dist/client/{EodashLayoutSwitcher-DQ8SfVDd.js → EodashLayoutSwitcher-CPpGM8Pb.js} +4 -4
- package/dist/client/EodashMapBtns-C_jyUJ2x.js +301 -0
- package/dist/client/{EodashStacInfo-Dt1nF06x.js → EodashStacInfo-DjuWc0Iz.js} +1 -1
- package/dist/client/EodashTimeSlider-CDh9Lf02.js +53 -0
- package/dist/client/{EodashTools-DV5ykmWc.js → EodashTools-DSvDUUlL.js} +10 -7
- package/dist/client/{ExportState-B6zZQUmE.js → ExportState-BhjxS0jG.js} +145 -120
- package/dist/client/{Footer-DNhXs8k6.js → Footer-C3PPcdjv.js} +1 -1
- package/dist/client/{Header-BjhN5JY4.js → Header-E5NbT7HE.js} +2 -2
- package/dist/client/MobileLayout-DY7OHr1k.js +118 -0
- package/dist/client/{PopUp-CgpvNr3o.js → PopUp-CSPXdqKI.js} +79 -43
- package/dist/client/{ProcessList-vecpxThi.js → ProcessList-C3HV7G0b.js} +5 -6
- package/dist/client/{VImg-CETuikH2.js → VImg-FoXcOnWF.js} +6 -3
- package/dist/client/{VMain-Ci9DyaGU.js → VMain-Ck2g1QOG.js} +1 -1
- package/dist/client/{VTooltip-J4ac48X7.js → VTooltip-F_1Zcvhp.js} +2 -2
- package/dist/client/{WidgetsContainer-CCML4TyV.js → WidgetsContainer-Cq9uZEuN.js} +1 -1
- package/dist/client/asWebComponent-DZeEbWG0.js +8895 -0
- package/dist/client/{async-B7jIrM53.js → async-Dk79llLt.js} +2 -2
- package/dist/client/easing-CH0-9wR8.js +35 -0
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{forwardRefs-BQclvjMq.js → forwardRefs-BbvoXHtj.js} +58 -45
- package/dist/client/{handling-BS24aG1q.js → handling-DxucYlYh.js} +12 -6
- package/dist/client/{helpers-wXK7Ywio.js → helpers-CI_7CUmn.js} +568 -281
- package/dist/client/index-BO5uGfUe.js +571 -0
- package/dist/client/{index-9KR-G20t.js → index-C13BiO9C.js} +2 -2
- package/dist/client/{index-4UCzZi8B.js → index-DcCcdbgR.js} +26 -13
- package/dist/client/{index-B2XpdgR6.js → index-KrGHjH-_.js} +63 -36
- package/dist/client/templates.js +82 -15
- package/dist/client/{transition-yBii4fu6.js → transition-Ctkv90El.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 +47 -16
- 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 +170 -2
- 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/expert.d.ts +6 -6
- 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} +5 -5
- 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/EodashMap/EodashMapBtns.vue.d.ts +2 -0
- package/dist/types/widgets/EodashMap/index.vue.d.ts +108 -2
- 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/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 +28 -27
- package/templates/baseConfig.js +10 -5
- package/templates/compare.js +2 -2
- package/templates/expert.js +5 -5
- package/templates/explore.js +62 -0
- package/templates/index.js +1 -1
- package/templates/{light.js → lite.js} +1 -1
- 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 +18 -9
- package/widgets/EodashMap/index.vue +22 -12
- package/widgets/EodashMap/methods/create-layers-config.js +9 -6
- package/widgets/EodashMap/methods/index.js +27 -13
- package/widgets/EodashProcess/index.vue +17 -1
- package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +9 -3
- package/widgets/EodashProcess/methods/handling.js +2 -0
- package/widgets/EodashProcess/methods/outputs.js +1 -0
- package/widgets/EodashTimeSlider.vue +40 -0
- package/widgets/EodashTools.vue +7 -3
- package/widgets/ExportState.vue +53 -22
- package/dist/client/EodashLayerControl-BLBds28C.js +0 -154
- package/dist/client/EodashMapBtns-B89_YBDw.js +0 -326
- package/dist/client/MobileLayout-JelB6w1G.js +0 -118
- package/dist/client/asWebComponent-ZyEzWOOf.js +0 -19092
|
@@ -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,60 +235,90 @@ 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
315
|
displayFormat: "DD.MM.YYYY HH:mm",
|
|
225
316
|
};
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
layerDatetime,
|
|
320
|
+
timeControlValues: timeValues,
|
|
321
|
+
};
|
|
226
322
|
};
|
|
227
323
|
|
|
228
324
|
/**
|
|
@@ -285,25 +381,17 @@ export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
|
|
|
285
381
|
* @param {import('../eodashSTAC/EodashCollection.js').EodashCollection[]} indicators
|
|
286
382
|
* @param {import('ol/layer').Layer} layer
|
|
287
383
|
*/
|
|
288
|
-
export const getColFromLayer = (indicators, layer) => {
|
|
289
|
-
// init cols
|
|
290
|
-
const collections = indicators.map((ind) => ind.collectionStac);
|
|
384
|
+
export const getColFromLayer = async (indicators, layer) => {
|
|
291
385
|
const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
|
|
292
386
|
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
//@ts-expect-error attaching the item in link when parsing .parquet items, see @/eodashSTAC/parquet.js
|
|
302
|
-
(link["item"] && link["item"].id === itemId)),
|
|
303
|
-
);
|
|
304
|
-
return isInd;
|
|
305
|
-
});
|
|
306
|
-
return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
|
|
387
|
+
for (const ind of indicators) {
|
|
388
|
+
if (ind.collectionStac?.id !== collectionId) continue;
|
|
389
|
+
const items = await ind.getItems();
|
|
390
|
+
const itemIds = items?.map(item => item.id || item.datetime) ?? [];
|
|
391
|
+
if (itemIds.includes(itemId)) {
|
|
392
|
+
return ind;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
307
395
|
};
|
|
308
396
|
|
|
309
397
|
/**
|
|
@@ -413,12 +501,20 @@ export async function mergeGeojsons(geojsonUrls) {
|
|
|
413
501
|
features: [],
|
|
414
502
|
};
|
|
415
503
|
await Promise.all(
|
|
416
|
-
geojsonUrls.map((url) =>
|
|
417
|
-
axios
|
|
504
|
+
geojsonUrls.map((url) => {
|
|
505
|
+
// Use native fetch for blob URLs to avoid axios/cache interceptor issues
|
|
506
|
+
if (url.startsWith("blob:")) {
|
|
507
|
+
return fetch(url)
|
|
508
|
+
.then(async (resp) => await resp.json())
|
|
509
|
+
.then((geojson) => {
|
|
510
|
+
merged.features.push(...(geojson.features ?? []));
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
return axios.get(url).then((resp) => {
|
|
418
514
|
const geojson = resp.data;
|
|
419
515
|
merged.features.push(...(geojson.features ?? []));
|
|
420
|
-
})
|
|
421
|
-
),
|
|
516
|
+
});
|
|
517
|
+
}),
|
|
422
518
|
);
|
|
423
519
|
|
|
424
520
|
return encodeURI(
|
|
@@ -532,16 +628,37 @@ export const addTooltipInteraction = (layer, style) => {
|
|
|
532
628
|
|
|
533
629
|
/**
|
|
534
630
|
*
|
|
535
|
-
* @param {import("stac-ts").StacLink[]} [
|
|
631
|
+
* @param {import("stac-ts").StacLink[] | import("stac-ts").StacItem[] |undefined |null} [linksOrItems]
|
|
536
632
|
*/
|
|
537
|
-
export function getDatetimeProperty(
|
|
538
|
-
if (!
|
|
633
|
+
export function getDatetimeProperty(linksOrItems) {
|
|
634
|
+
if (!linksOrItems?.length) {
|
|
539
635
|
return undefined;
|
|
540
636
|
}
|
|
637
|
+
const first = linksOrItems[0];
|
|
638
|
+
let checkProperties = false;
|
|
639
|
+
if (isSTACItem(first)) {
|
|
640
|
+
checkProperties = true;
|
|
641
|
+
}
|
|
642
|
+
|
|
541
643
|
// TODO: consider other properties for datetime ranges
|
|
542
644
|
const datetimeProperties = ["datetime", "start_datetime", "end_datetime"];
|
|
645
|
+
if (checkProperties) {
|
|
646
|
+
for (const prop of datetimeProperties) {
|
|
647
|
+
const propExists = linksOrItems.some(
|
|
648
|
+
(l) =>
|
|
649
|
+
//@ts-expect-error TODO
|
|
650
|
+
l["properties"]?.[prop] &&
|
|
651
|
+
//@ts-expect-error TODO
|
|
652
|
+
typeof l["properties"]?.[prop] === "string",
|
|
653
|
+
);
|
|
654
|
+
if (!propExists) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
return prop;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
543
660
|
for (const prop of datetimeProperties) {
|
|
544
|
-
const propExists =
|
|
661
|
+
const propExists = linksOrItems.some(
|
|
545
662
|
(l) => l[prop] && typeof l[prop] === "string",
|
|
546
663
|
);
|
|
547
664
|
if (!propExists) {
|
|
@@ -550,7 +667,82 @@ export function getDatetimeProperty(links) {
|
|
|
550
667
|
return prop;
|
|
551
668
|
}
|
|
552
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
*
|
|
672
|
+
* @param {*} stacObject
|
|
673
|
+
* @returns {stacObject is import("stac-ts").StacItem}
|
|
674
|
+
*/
|
|
675
|
+
export function isSTACItem(stacObject) {
|
|
676
|
+
return (
|
|
677
|
+
stacObject &&
|
|
678
|
+
typeof stacObject === "object" &&
|
|
679
|
+
stacObject.collection &&
|
|
680
|
+
stacObject.id &&
|
|
681
|
+
stacObject.properties &&
|
|
682
|
+
typeof stacObject.properties === "object"
|
|
683
|
+
);
|
|
684
|
+
}
|
|
553
685
|
|
|
686
|
+
/**
|
|
687
|
+
* Fetch all STAC items from a STAC API endpoint.
|
|
688
|
+
* @param {string} itemsUrl
|
|
689
|
+
* @param {string} [query]
|
|
690
|
+
* @param {number} [limit=100] - The maximum number of items to fetch per request.
|
|
691
|
+
* @param {boolean} [returnFirst] - If true, only the first page of results will be returned.
|
|
692
|
+
* @param {number} [maxNumber=1000] - if the matched number of items exceed this, only the first page will be returned.
|
|
693
|
+
*/
|
|
694
|
+
export async function fetchApiItems(
|
|
695
|
+
itemsUrl,
|
|
696
|
+
query,
|
|
697
|
+
limit = 100,
|
|
698
|
+
returnFirst = false,
|
|
699
|
+
maxNumber = 1000,
|
|
700
|
+
) {
|
|
701
|
+
itemsUrl = itemsUrl.includes("?") ? itemsUrl.split("?")[0] : itemsUrl;
|
|
702
|
+
itemsUrl += query ? `?limit=${limit}&${query}` : `?limit=${limit}`;
|
|
703
|
+
|
|
704
|
+
const itemsFeatureCollection = await axios
|
|
705
|
+
.get(itemsUrl)
|
|
706
|
+
.then((resp) => resp.data);
|
|
707
|
+
/** @type {import("stac-ts").StacItem[]} */
|
|
708
|
+
const items = itemsFeatureCollection.features;
|
|
709
|
+
const nextLink = itemsFeatureCollection.links?.find(
|
|
710
|
+
//@ts-expect-error TODO: itemsFeatureCollection is not typed
|
|
711
|
+
(link) => link.rel === "next",
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
if (!nextLink || returnFirst) {
|
|
715
|
+
return items;
|
|
716
|
+
}
|
|
717
|
+
/** @type {number} */
|
|
718
|
+
const matchedItems = itemsFeatureCollection.numberMatched;
|
|
719
|
+
// Avoid fetching too many items
|
|
720
|
+
if (matchedItems >= maxNumber) {
|
|
721
|
+
console.warn(
|
|
722
|
+
`[eodash] The number of items matched (${matchedItems}) exceeds the maximum allowed (${maxNumber})`,
|
|
723
|
+
);
|
|
724
|
+
return items;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
let [nextLinkURL, nextLinkQuery] = nextLink.href.split("?");
|
|
728
|
+
nextLinkQuery = nextLinkQuery.replace(/limit=\d+/, "");
|
|
729
|
+
if (query) {
|
|
730
|
+
const queryParams = new URLSearchParams(query);
|
|
731
|
+
const nextLinkParams = new URLSearchParams(nextLinkQuery);
|
|
732
|
+
|
|
733
|
+
for (const key of nextLinkParams.keys()) {
|
|
734
|
+
queryParams.delete(key);
|
|
735
|
+
}
|
|
736
|
+
const remainingQuery = queryParams.toString();
|
|
737
|
+
if (remainingQuery) {
|
|
738
|
+
nextLinkQuery += `&${remainingQuery}`;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const nextPage = await fetchApiItems(nextLinkURL, nextLinkQuery);
|
|
743
|
+
items.push(...nextPage);
|
|
744
|
+
return items;
|
|
745
|
+
}
|
|
554
746
|
/**
|
|
555
747
|
* @param {import ("stac-ts").StacCollection | undefined | null} collection
|
|
556
748
|
* @returns {object}
|
|
@@ -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);
|