@eodash/eodash 5.0.0-alpha.2.9 → 5.0.0-rc
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 +1 -1
- package/core/client/App.vue +13 -1
- package/core/client/asWebComponent.js +13 -4
- package/core/client/components/DashboardLayout.vue +36 -14
- package/core/client/components/Loading.vue +6 -9
- package/core/client/components/MobileLayout.vue +16 -14
- package/core/client/composables/DefineEodash.js +14 -4
- package/core/client/composables/DefineTemplate.js +67 -0
- package/core/client/composables/DefineWidgets.js +3 -2
- package/core/client/composables/EodashMap.js +360 -0
- package/core/client/composables/EodashProcess.js +574 -0
- package/core/client/composables/index.js +136 -28
- package/core/client/eodash.js +395 -80
- package/core/client/eodashSTAC/EodashCollection.js +432 -0
- package/core/client/eodashSTAC/createLayers.js +315 -0
- package/core/client/eodashSTAC/helpers.js +375 -0
- package/core/client/eodashSTAC/triggers.js +43 -0
- package/core/client/plugins/axios.js +8 -0
- package/core/client/plugins/index.js +2 -1
- package/core/client/plugins/vuetify.js +2 -1
- package/core/client/store/actions.js +79 -0
- package/core/client/store/index.js +4 -18
- package/core/client/store/stac.js +99 -9
- package/core/client/store/states.js +37 -0
- package/core/client/{types.d.ts → types.ts} +66 -20
- package/core/client/utils/keys.js +2 -0
- package/core/client/utils/states.js +22 -0
- package/core/client/views/Dashboard.vue +22 -49
- package/core/client/vite-env.d.ts +2 -10
- package/dist/client/DashboardLayout-232tRmjz.js +84 -0
- package/dist/client/DynamicWebComponent-Cl4LqHU6.js +88 -0
- package/dist/client/EodashDatePicker-Pok6bZwU.js +306 -0
- package/dist/client/EodashItemFilter-16eMMjTV.js +151 -0
- package/dist/client/EodashLayerControl-De7IlCm_.js +120 -0
- package/dist/client/EodashLayoutSwitcher-C-3-jjn5.js +52 -0
- package/dist/client/EodashMap-CMvbfI6-.js +549 -0
- package/dist/client/EodashMapBtns-BeknGDtc.js +107 -0
- package/dist/client/EodashProcess-BwKAa9Ee.js +1476 -0
- package/dist/client/EodashStacInfo-_BfonNUG.js +85 -0
- package/dist/client/EodashTools-PD3XPYuR.js +103 -0
- package/dist/client/ExportState-DOrT7M15.js +644 -0
- package/dist/client/Footer-CCigxYBo.js +141 -0
- package/dist/client/Header-C2cdx4gb.js +437 -0
- package/dist/client/IframeWrapper-BgM9aU8f.js +28 -0
- package/dist/client/MobileLayout-BdiFjHg7.js +1207 -0
- package/dist/client/PopUp--_xn1Cms.js +410 -0
- package/dist/client/VImg-9xu2l99m.js +384 -0
- package/dist/client/VMain-BUs3kDTd.js +43 -0
- package/dist/client/VOverlay-D89omJis.js +1453 -0
- package/dist/client/VTooltip-CDu3bErh.js +86 -0
- package/dist/client/WidgetsContainer-aFG9yFT6.js +83 -0
- package/dist/client/asWebComponent-BRGyP_j5.js +11943 -0
- package/dist/client/{style.css → eo-dash.css} +2 -2
- package/dist/client/eo-dash.js +2 -6
- package/dist/client/forwardRefs-CYrR6bMw.js +245 -0
- package/dist/client/index-BZwk0V42.js +199 -0
- package/dist/client/ssrBoot-BP7SYRyC.js +22 -0
- package/dist/client/transition-DG9nRSW4.js +37 -0
- package/dist/node/cli.js +4 -4
- package/dist/node/types.d.ts +2 -0
- package/package.json +73 -38
- package/widgets/EodashDatePicker.vue +176 -134
- package/widgets/EodashItemFilter.vue +79 -38
- package/widgets/EodashLayerControl.vue +111 -0
- package/widgets/EodashLayoutSwitcher.vue +36 -0
- package/widgets/EodashMap.vue +108 -133
- package/widgets/EodashMapBtns.vue +62 -8
- package/widgets/EodashProcess.vue +143 -0
- package/widgets/EodashStacInfo.vue +82 -0
- package/widgets/EodashTools.vue +83 -0
- package/widgets/ExportState.vue +17 -13
- package/widgets/PopUp.vue +24 -2
- package/core/client/SuspensedDashboard.ce.vue +0 -105
- package/core/client/asWebComponent.d.ts +0 -23
- package/core/client/store/Actions.js +0 -14
- package/core/client/store/States.js +0 -16
- package/core/client/utils/eodashSTAC.js +0 -249
- package/core/client/utils/helpers.js +0 -38
- package/dist/client/DashboardLayout-D0ZF6V2S.js +0 -156
- package/dist/client/DynamicWebComponent-CPsMSBHi.js +0 -57
- package/dist/client/EodashDatePicker-CBQP7u2X.js +0 -252
- package/dist/client/EodashItemFilter-DL2ScI-5.js +0 -7671
- package/dist/client/EodashMap-CkKoQlmR.js +0 -86917
- package/dist/client/EodashMapBtns-yuO2QmiR.js +0 -36
- package/dist/client/ExportState-CCzOhppU.js +0 -558
- package/dist/client/Footer-BPAND0yG.js +0 -115
- package/dist/client/Header-DLhebNvG.js +0 -350
- package/dist/client/IframeWrapper-1GEMHlsW.js +0 -19
- package/dist/client/MobileLayout-mGkOYRhu.js +0 -945
- package/dist/client/PopUp-1d2bBFjw.js +0 -300
- package/dist/client/VImg-DxHcztfM.js +0 -291
- package/dist/client/VMain-BLX5vRRn.js +0 -39
- package/dist/client/VOverlay-CvrYEmLu.js +0 -967
- package/dist/client/WidgetsContainer-CmYjvGm7.js +0 -129
- package/dist/client/_commonjsHelpers-DaMA6jEr.js +0 -8
- package/dist/client/asWebComponent-B91uK0U7.js +0 -20361
- package/dist/client/basedecoder-DHcBySSe-BmCFNFnw.js +0 -88
- package/dist/client/decoder-CP4lv0Kb-B6yqkcfC.js +0 -10
- package/dist/client/deflate-BXt-9JA_-CWfClgpK.js +0 -10
- package/dist/client/eodashSTAC-DBjqe_Ho.js +0 -2788
- package/dist/client/eox-stacinfo-l7ALSV90.js +0 -13969
- package/dist/client/forwardRefs-BJJiadQP.js +0 -185
- package/dist/client/index-Q-bHLjxx.js +0 -153
- package/dist/client/jpeg-BAgeD1d3-oeHbFPUL.js +0 -514
- package/dist/client/lerc-DzVumYtB-P-KXC0TO.js +0 -1027
- package/dist/client/lzw-LAGDNbSC-DkP96qO9.js +0 -84
- package/dist/client/packbits-BlDR4Kj5-C66n1-zr.js +0 -24
- package/dist/client/pako.esm-CB1uQYY0-DB0PYm1P.js +0 -1081
- package/dist/client/raw-CMGvRjfu-BRi6E4i1.js +0 -9
- package/dist/client/ssrBoot-yo11mybw.js +0 -17
- package/dist/client/transition-CSJhuYGK.js +0 -34
- package/dist/client/webfontloader-qotgY98I.js +0 -435
- package/dist/client/webimage-BM_pbLN3-L2cGWK5l.js +0 -19
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { registerProjection } from "@/store/actions";
|
|
2
|
+
import { mapEl } from "@/store/states";
|
|
3
|
+
import {
|
|
4
|
+
extractRoles,
|
|
5
|
+
getProjectionCode,
|
|
6
|
+
createLayerID,
|
|
7
|
+
createAssetID,
|
|
8
|
+
} from "./helpers";
|
|
9
|
+
import log from "loglevel";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} collectionId
|
|
13
|
+
* @param {string} title
|
|
14
|
+
* @param {Record<string,import("stac-ts").StacAsset>} assets
|
|
15
|
+
* @param {import("stac-ts").StacItem } item
|
|
16
|
+
* @param {import("ol/layer/WebGLTile").Style} [style]
|
|
17
|
+
* @param {Record<string, unknown>} [layerConfig]
|
|
18
|
+
* @param {Record<string, unknown>} [layerDatetime]
|
|
19
|
+
* @param {object | null} [extraProperties]
|
|
20
|
+
**/
|
|
21
|
+
export async function createLayersFromAssets(
|
|
22
|
+
collectionId,
|
|
23
|
+
title,
|
|
24
|
+
assets,
|
|
25
|
+
item,
|
|
26
|
+
style,
|
|
27
|
+
layerConfig,
|
|
28
|
+
layerDatetime,
|
|
29
|
+
extraProperties,
|
|
30
|
+
) {
|
|
31
|
+
log.debug("Creating layers from assets");
|
|
32
|
+
let jsonArray = [];
|
|
33
|
+
let geoTIFFSources = [];
|
|
34
|
+
/** @type {number|null} */
|
|
35
|
+
let geoTIFFIdx = null;
|
|
36
|
+
|
|
37
|
+
for (const [idx, ast] of Object.keys(assets).entries()) {
|
|
38
|
+
// register projection if exists
|
|
39
|
+
const assetProjection =
|
|
40
|
+
/** @type {string | number | {name: string, def: string, extent?:number[]} | undefined} */ (
|
|
41
|
+
assets[ast]?.["proj:epsg"] || assets[ast]?.["eodash:proj4_def"]
|
|
42
|
+
);
|
|
43
|
+
await registerProjection(assetProjection);
|
|
44
|
+
|
|
45
|
+
if (assets[ast]?.type === "application/geo+json") {
|
|
46
|
+
const assetId = createAssetID(collectionId, item.id, idx);
|
|
47
|
+
log.debug("Creating Vector layer from GeoJSON", assetId);
|
|
48
|
+
const layer = {
|
|
49
|
+
type: "Vector",
|
|
50
|
+
source: {
|
|
51
|
+
type: "Vector",
|
|
52
|
+
url: assets[ast].href,
|
|
53
|
+
format: "GeoJSON",
|
|
54
|
+
},
|
|
55
|
+
properties: {
|
|
56
|
+
id: assetId,
|
|
57
|
+
title,
|
|
58
|
+
layerDatetime,
|
|
59
|
+
...(layerConfig && {
|
|
60
|
+
layerConfig: {
|
|
61
|
+
...layerConfig,
|
|
62
|
+
style,
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
...(!style?.variables && { style }),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
extractRoles(layer.properties, assets[ast]);
|
|
70
|
+
|
|
71
|
+
layer.properties = { ...layer.properties, ...(extraProperties ?? {}) };
|
|
72
|
+
|
|
73
|
+
jsonArray.push(layer);
|
|
74
|
+
} else if (assets[ast]?.type === "image/tiff") {
|
|
75
|
+
geoTIFFIdx = idx;
|
|
76
|
+
geoTIFFSources.push({ url: assets[ast].href });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (geoTIFFSources.length && typeof geoTIFFIdx === "number") {
|
|
81
|
+
const geotiffSourceID = collectionId + ";:;GeoTIFF";
|
|
82
|
+
log.debug("Creating WebGLTile layer from GeoTIFF", geotiffSourceID);
|
|
83
|
+
log.debug("Configured Sources", geoTIFFSources);
|
|
84
|
+
const layer = {
|
|
85
|
+
type: "WebGLTile",
|
|
86
|
+
source: {
|
|
87
|
+
type: "GeoTIFF",
|
|
88
|
+
normalize: !style,
|
|
89
|
+
interpolate: false,
|
|
90
|
+
sources: geoTIFFSources,
|
|
91
|
+
},
|
|
92
|
+
properties: {
|
|
93
|
+
id: createAssetID(collectionId, item.id, geoTIFFIdx),
|
|
94
|
+
title,
|
|
95
|
+
layerConfig,
|
|
96
|
+
layerDatetime,
|
|
97
|
+
},
|
|
98
|
+
style,
|
|
99
|
+
};
|
|
100
|
+
if (extraProperties) {
|
|
101
|
+
layer.properties = { ...layer.properties, ...extraProperties };
|
|
102
|
+
}
|
|
103
|
+
jsonArray.push(layer);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return jsonArray;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {string} collectionId
|
|
111
|
+
* @param {import('stac-ts').StacItem} item
|
|
112
|
+
* @param {string} title
|
|
113
|
+
* @param {Record<string,any>} [layerDatetime]
|
|
114
|
+
* @param {object | null} [extraProperties]
|
|
115
|
+
*/
|
|
116
|
+
export const createLayersFromLinks = async (
|
|
117
|
+
collectionId,
|
|
118
|
+
title,
|
|
119
|
+
item,
|
|
120
|
+
layerDatetime,
|
|
121
|
+
extraProperties,
|
|
122
|
+
) => {
|
|
123
|
+
log.debug("Creating layers from links");
|
|
124
|
+
/** @type {Record<string,any>[]} */
|
|
125
|
+
const jsonArray = [];
|
|
126
|
+
const wmsArray = item.links.filter((l) => l.rel === "wms");
|
|
127
|
+
const wmtsArray = item.links.filter((l) => l.rel === "wmts");
|
|
128
|
+
const xyzArray = item.links.filter((l) => l.rel === "xyz") ?? [];
|
|
129
|
+
|
|
130
|
+
// Taking projection code from main map view, as main view defines
|
|
131
|
+
// projection for comparison map
|
|
132
|
+
const viewProjectionCode = mapEl?.value?.projection || "EPSG:3857";
|
|
133
|
+
|
|
134
|
+
for (const wmsLink of wmsArray ?? []) {
|
|
135
|
+
// Registering setting sub wms link projection
|
|
136
|
+
|
|
137
|
+
const wmsLinkProjection =
|
|
138
|
+
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
139
|
+
(wmsLink?.["proj:epsg"] || wmsLink?.["eodash:proj4_def"]);
|
|
140
|
+
|
|
141
|
+
await registerProjection(wmsLinkProjection);
|
|
142
|
+
|
|
143
|
+
const linkProjectionCode =
|
|
144
|
+
getProjectionCode(wmsLinkProjection) || "EPSG:4326";
|
|
145
|
+
// Projection code need to be based on map view projection to make sure
|
|
146
|
+
// tiles are reloaded when changing projection
|
|
147
|
+
const linkId = createLayerID(
|
|
148
|
+
collectionId,
|
|
149
|
+
item.id,
|
|
150
|
+
wmsLink,
|
|
151
|
+
viewProjectionCode,
|
|
152
|
+
);
|
|
153
|
+
log.debug("WMS Layer added", linkId);
|
|
154
|
+
const tileSize = /** @type {number[]} */ (
|
|
155
|
+
"wms:tilesize" in wmsLink
|
|
156
|
+
? [wmsLink["wms:tilesize"], wmsLink["wms:tilesize"]]
|
|
157
|
+
: [512, 512]
|
|
158
|
+
);
|
|
159
|
+
let json = {
|
|
160
|
+
type: "Tile",
|
|
161
|
+
properties: {
|
|
162
|
+
id: linkId,
|
|
163
|
+
title: wmsLink.title || title || item.id,
|
|
164
|
+
layerDatetime,
|
|
165
|
+
},
|
|
166
|
+
source: {
|
|
167
|
+
type: "TileWMS",
|
|
168
|
+
url: wmsLink.href,
|
|
169
|
+
projection: linkProjectionCode,
|
|
170
|
+
tileGrid: {
|
|
171
|
+
tileSize,
|
|
172
|
+
},
|
|
173
|
+
params: {
|
|
174
|
+
LAYERS: wmsLink["wms:layers"],
|
|
175
|
+
TILED: true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
if ("wms:version" in wmsLink) {
|
|
180
|
+
// @ts-expect-error no type for eox-map
|
|
181
|
+
json.source.params["VERSION"] = wmsLink["wms:version"];
|
|
182
|
+
}
|
|
183
|
+
extractRoles(json.properties, wmsLink);
|
|
184
|
+
if ("wms:dimensions" in wmsLink) {
|
|
185
|
+
// Expand all dimensions into the params attribute
|
|
186
|
+
Object.assign(json.source.params, wmsLink["wms:dimensions"]);
|
|
187
|
+
}
|
|
188
|
+
if (extraProperties !== null) {
|
|
189
|
+
json.properties = { ...json.properties, ...extraProperties };
|
|
190
|
+
}
|
|
191
|
+
jsonArray.push(json);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (const wmtsLink of wmtsArray ?? []) {
|
|
195
|
+
// Registering setting sub wmts link projection
|
|
196
|
+
|
|
197
|
+
const wmtsLinkProjection =
|
|
198
|
+
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
199
|
+
(wmtsLink?.["proj:epsg"] || wmtsLink?.["eodash:proj4_def"]);
|
|
200
|
+
|
|
201
|
+
await registerProjection(wmtsLinkProjection);
|
|
202
|
+
const projectionCode = getProjectionCode(wmtsLinkProjection || "EPSG:3857");
|
|
203
|
+
// TODO: WARNING! This is a temporary project specific implementation
|
|
204
|
+
// that needs to be removed once catalog and wmts creation from capabilities
|
|
205
|
+
// combined with custom view projections is solved
|
|
206
|
+
let json;
|
|
207
|
+
const linkId = createLayerID(
|
|
208
|
+
collectionId,
|
|
209
|
+
item.id,
|
|
210
|
+
wmtsLink,
|
|
211
|
+
viewProjectionCode,
|
|
212
|
+
);
|
|
213
|
+
const dimensions = /** @type { {style:any} & Record<string,any> } */ (
|
|
214
|
+
wmtsLink["wmts:dimensions"] || {}
|
|
215
|
+
);
|
|
216
|
+
let { style, ...dimensionsWithoutStyle } = { ...dimensions };
|
|
217
|
+
let extractedStyle = /** @type { string } */ (style || "default");
|
|
218
|
+
|
|
219
|
+
if (wmtsLink.title === "wmts capabilities") {
|
|
220
|
+
log.debug(
|
|
221
|
+
"Warning: WMTS Layer from capabilities added, function needs to be updated",
|
|
222
|
+
linkId,
|
|
223
|
+
);
|
|
224
|
+
json = {
|
|
225
|
+
type: "Tile",
|
|
226
|
+
properties: {
|
|
227
|
+
id: linkId,
|
|
228
|
+
title: title || item.id,
|
|
229
|
+
layerDatetime,
|
|
230
|
+
},
|
|
231
|
+
source: {
|
|
232
|
+
type: "WMTS",
|
|
233
|
+
// TODO: Hard coding url as the current one set is for capabilities
|
|
234
|
+
url: "https://wmts.marine.copernicus.eu/teroWmts",
|
|
235
|
+
layer: wmtsLink["wmts:layer"],
|
|
236
|
+
style: extractedStyle,
|
|
237
|
+
// TODO: Hard coding matrixSet until we find solution to wmts creation from capabilities
|
|
238
|
+
matrixSet: "EPSG:3857",
|
|
239
|
+
projection: projectionCode,
|
|
240
|
+
tileGrid: {
|
|
241
|
+
tileSize: [128, 128],
|
|
242
|
+
},
|
|
243
|
+
dimensions: dimensionsWithoutStyle,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
} else {
|
|
247
|
+
log.debug(
|
|
248
|
+
"Warning: WMTS Layer from capabilities added, function needs to be updated",
|
|
249
|
+
linkId,
|
|
250
|
+
);
|
|
251
|
+
json = {
|
|
252
|
+
type: "Tile",
|
|
253
|
+
properties: {
|
|
254
|
+
id: linkId,
|
|
255
|
+
title: wmtsLink.title || title || item.id,
|
|
256
|
+
layerDatetime,
|
|
257
|
+
},
|
|
258
|
+
source: {
|
|
259
|
+
type: "WMTS",
|
|
260
|
+
url: wmtsLink.href,
|
|
261
|
+
layer: wmtsLink["wmts:layer"],
|
|
262
|
+
style: extractedStyle,
|
|
263
|
+
matrixSet: wmtsLink.matrixSet || "EPSG:3857",
|
|
264
|
+
projection: projectionCode,
|
|
265
|
+
tileGrid: {
|
|
266
|
+
tileSize: [512, 512],
|
|
267
|
+
},
|
|
268
|
+
dimensions: dimensionsWithoutStyle,
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
extractRoles(json.properties, wmtsLink);
|
|
273
|
+
if (extraProperties !== null) {
|
|
274
|
+
json.properties = { ...json.properties, ...extraProperties };
|
|
275
|
+
}
|
|
276
|
+
jsonArray.push(json);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (const xyzLink of xyzArray ?? []) {
|
|
280
|
+
const xyzLinkProjection =
|
|
281
|
+
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
282
|
+
(xyzLink?.["proj:epsg"] || xyzLink?.["eodash:proj4_def"]);
|
|
283
|
+
|
|
284
|
+
await registerProjection(xyzLinkProjection);
|
|
285
|
+
const projectionCode = getProjectionCode(xyzLinkProjection || "EPSG:3857");
|
|
286
|
+
const linkId = createLayerID(
|
|
287
|
+
collectionId,
|
|
288
|
+
item.id,
|
|
289
|
+
xyzLink,
|
|
290
|
+
viewProjectionCode,
|
|
291
|
+
);
|
|
292
|
+
log.debug("XYZ Layer added", linkId);
|
|
293
|
+
let json = {
|
|
294
|
+
type: "Tile",
|
|
295
|
+
properties: {
|
|
296
|
+
id: linkId,
|
|
297
|
+
title: xyzLink.title || title || item.id,
|
|
298
|
+
roles: xyzLink.roles,
|
|
299
|
+
layerDatetime,
|
|
300
|
+
},
|
|
301
|
+
source: {
|
|
302
|
+
type: "XYZ",
|
|
303
|
+
url: xyzLink.href,
|
|
304
|
+
projection: projectionCode,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
extractRoles(json.properties, xyzLink);
|
|
309
|
+
if (extraProperties !== null) {
|
|
310
|
+
json.properties = { ...json.properties, ...extraProperties };
|
|
311
|
+
}
|
|
312
|
+
jsonArray.push(json);
|
|
313
|
+
}
|
|
314
|
+
return jsonArray;
|
|
315
|
+
};
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { toAbsolute } from "stac-js/src/http.js";
|
|
2
|
+
import axios from "@/plugins/axios";
|
|
3
|
+
import log from "loglevel";
|
|
4
|
+
|
|
5
|
+
/** @param {import("stac-ts").StacLink[]} [links] */
|
|
6
|
+
export function generateFeatures(links) {
|
|
7
|
+
/**
|
|
8
|
+
* @type {import("geojson").Feature[]}
|
|
9
|
+
*/
|
|
10
|
+
const features = [];
|
|
11
|
+
links?.forEach((element) => {
|
|
12
|
+
if (element.rel === "item" && "latlng" in element) {
|
|
13
|
+
const [lat, lon] = /** @type {string} */ (element.latlng)
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((it) => Number(it));
|
|
16
|
+
features.push({
|
|
17
|
+
type: "Feature",
|
|
18
|
+
geometry: {
|
|
19
|
+
type: "Point",
|
|
20
|
+
coordinates: [lon, lat],
|
|
21
|
+
},
|
|
22
|
+
properties: { id: element.id },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const geojsonObject = {
|
|
27
|
+
type: "FeatureCollection",
|
|
28
|
+
crs: {
|
|
29
|
+
type: "name",
|
|
30
|
+
properties: {
|
|
31
|
+
name: "EPSG:4326",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
features,
|
|
35
|
+
};
|
|
36
|
+
return geojsonObject;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sperates and extracts layerConfig (jsonform schema & legend) from a style json
|
|
41
|
+
*
|
|
42
|
+
* @param { import("ol/layer/WebGLTile").Style & { jsonform?: Record<string,any> } & { legend?: Record<string,any> } } [style] */
|
|
43
|
+
export function extractLayerConfig(style) {
|
|
44
|
+
/** @type {Record<string,unknown> | undefined} */
|
|
45
|
+
let layerConfig = undefined;
|
|
46
|
+
if (style?.jsonform) {
|
|
47
|
+
layerConfig = { schema: style.jsonform, type: "style" };
|
|
48
|
+
style = { ...style };
|
|
49
|
+
delete style.jsonform;
|
|
50
|
+
if (style?.legend) {
|
|
51
|
+
layerConfig.legend = style.legend;
|
|
52
|
+
delete style.legend;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
log.debug(
|
|
56
|
+
"extracted layerConfig",
|
|
57
|
+
JSON.parse(JSON.stringify({ layerConfig, style })),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return { layerConfig, style };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Function to extract collection urls from an indicator
|
|
65
|
+
* @param {import("stac-ts").StacCatalog
|
|
66
|
+
* | import("stac-ts").StacCollection
|
|
67
|
+
* | import("stac-ts").StacItem
|
|
68
|
+
* | null
|
|
69
|
+
* } stacObject
|
|
70
|
+
* @param {string} basepath
|
|
71
|
+
*/
|
|
72
|
+
export function extractCollectionUrls(stacObject, basepath) {
|
|
73
|
+
/** @type {string[]} */
|
|
74
|
+
const collectionUrls = [];
|
|
75
|
+
// Support for two structure types, flat and indicator, simplified here:
|
|
76
|
+
// Flat assumes Catalog-Collection-Item
|
|
77
|
+
// Indicator assumes Catalog-Collection-Collection-Item
|
|
78
|
+
|
|
79
|
+
const children = stacObject?.links?.filter(
|
|
80
|
+
(link) => link.rel === "child" && link.type?.includes("json"),
|
|
81
|
+
);
|
|
82
|
+
if (!children?.length) {
|
|
83
|
+
collectionUrls.push(basepath);
|
|
84
|
+
return collectionUrls;
|
|
85
|
+
}
|
|
86
|
+
children.forEach((link) => {
|
|
87
|
+
collectionUrls.push(toAbsolute(link.href, basepath));
|
|
88
|
+
});
|
|
89
|
+
return collectionUrls;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Assign extracted roles to layer properties
|
|
94
|
+
* @param {Record<string,any>} properties
|
|
95
|
+
* @param {import("stac-ts").StacLink | import("stac-ts").StacAsset} linkOrAsset
|
|
96
|
+
* */
|
|
97
|
+
export const extractRoles = (properties, linkOrAsset) => {
|
|
98
|
+
const roles = /** @type {string[]} */ (linkOrAsset.roles);
|
|
99
|
+
roles?.forEach((role) => {
|
|
100
|
+
if (role === "visible") {
|
|
101
|
+
properties.visible = true;
|
|
102
|
+
}
|
|
103
|
+
if (role === "overlay" || role === "baselayer") {
|
|
104
|
+
properties.group = role;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return properties;
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Extracts style JSON from a STAC Item
|
|
113
|
+
* @param {import("stac-ts").StacItem} item
|
|
114
|
+
* @param {string} itemUrl
|
|
115
|
+
* @returns
|
|
116
|
+
**/
|
|
117
|
+
export const fetchStyle = async (item, itemUrl) => {
|
|
118
|
+
const styleLink = item.links.find((link) => link.rel.includes("style"));
|
|
119
|
+
if (styleLink) {
|
|
120
|
+
let url = "";
|
|
121
|
+
if (styleLink.href.startsWith("http")) {
|
|
122
|
+
url = styleLink.href;
|
|
123
|
+
} else {
|
|
124
|
+
url = toAbsolute(styleLink.href, itemUrl);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @type {import("ol/layer/WebGLTile").Style & {jsonform?:Record<string,any>}} */
|
|
128
|
+
const styleJson = await axios.get(url).then((resp) => resp.data);
|
|
129
|
+
|
|
130
|
+
log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
|
|
131
|
+
return { ...styleJson };
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Return projection code which is to be registered in `eox-map`
|
|
137
|
+
* @param {string|number|{name: string, def: string}} [projection]
|
|
138
|
+
* @returns {string}
|
|
139
|
+
*/
|
|
140
|
+
export const getProjectionCode = (projection) => {
|
|
141
|
+
let code = projection;
|
|
142
|
+
switch (typeof projection) {
|
|
143
|
+
case "number":
|
|
144
|
+
code = `EPSG:${projection}`;
|
|
145
|
+
break;
|
|
146
|
+
case "string":
|
|
147
|
+
code = projection;
|
|
148
|
+
break;
|
|
149
|
+
case "object":
|
|
150
|
+
code = projection?.name;
|
|
151
|
+
}
|
|
152
|
+
return /** @type {string} */ (code);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extracts layercontrol LayerDatetime property from STAC Links
|
|
157
|
+
* @param {import("stac-ts").StacLink[]} [links]
|
|
158
|
+
* @param {string|null} [currentStep]
|
|
159
|
+
**/
|
|
160
|
+
export const extractLayerDatetime = (links, currentStep) => {
|
|
161
|
+
if (!currentStep || !links?.length) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// check if links has a datetime value
|
|
166
|
+
// TODO: consider datetime ranges
|
|
167
|
+
const hasDatetime = links.some((l) => typeof l.datetime === "string");
|
|
168
|
+
if (!hasDatetime) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** @type {string[]} */
|
|
173
|
+
const controlValues = [];
|
|
174
|
+
try {
|
|
175
|
+
currentStep = new Date(currentStep).toISOString();
|
|
176
|
+
|
|
177
|
+
links.reduce((vals, link) => {
|
|
178
|
+
if (link.datetime && link.rel === "item") {
|
|
179
|
+
vals.push(
|
|
180
|
+
new Date(/** @type {string} */ (link.datetime)).toISOString(),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return vals;
|
|
184
|
+
}, controlValues);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
console.warn("[eodash] not supported datetime format was provided", e);
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
// not enough controlValues
|
|
190
|
+
if (controlValues.length <= 1) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// item datetime is not included in the item links datetime
|
|
195
|
+
if (!controlValues.includes(currentStep)) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
controlValues,
|
|
201
|
+
currentStep,
|
|
202
|
+
slider: true,
|
|
203
|
+
play: false,
|
|
204
|
+
displayFormat: "DD MMMM YYYY",
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Find JSON layer by ID
|
|
210
|
+
* @param {string} layer
|
|
211
|
+
* @param {Record<string, any>[]} layers
|
|
212
|
+
* @returns {Record<string,any> | undefined}
|
|
213
|
+
**/
|
|
214
|
+
export const findLayer = (layers, layer) => {
|
|
215
|
+
for (const lyr of layers) {
|
|
216
|
+
if (lyr.type === "Group") {
|
|
217
|
+
const found = findLayer(lyr.layers, layer);
|
|
218
|
+
if (!found) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
return found;
|
|
222
|
+
}
|
|
223
|
+
if (lyr.properties.id === layer) {
|
|
224
|
+
return lyr;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Removes the layer with the id provided and injects an array of layers in its position
|
|
231
|
+
* @param {Record<string,any>[]} currentLayers
|
|
232
|
+
* @param {string} oldLayer - id of the layer to be replaced
|
|
233
|
+
* @param {Record<string,any>[]} newLayers - array of layers to replace the old layer
|
|
234
|
+
* @returns {Record<string,any>[] | undefined}
|
|
235
|
+
*/
|
|
236
|
+
export const replaceLayer = (currentLayers, oldLayer, newLayers) => {
|
|
237
|
+
const oldLayerIdx = currentLayers.findIndex(
|
|
238
|
+
(l) => l.properties.id === oldLayer,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (oldLayerIdx !== -1) {
|
|
242
|
+
log.debug(
|
|
243
|
+
"Replacing layer",
|
|
244
|
+
oldLayer,
|
|
245
|
+
"with",
|
|
246
|
+
newLayers.map((l) => l.properties.id),
|
|
247
|
+
);
|
|
248
|
+
currentLayers.splice(oldLayerIdx, 1, ...newLayers);
|
|
249
|
+
return currentLayers;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const l of currentLayers) {
|
|
253
|
+
if (l.type === "Group") {
|
|
254
|
+
const updatedGroupLyrs = replaceLayer(l.layers, oldLayer, newLayers);
|
|
255
|
+
if (updatedGroupLyrs?.length) {
|
|
256
|
+
l.layers = updatedGroupLyrs;
|
|
257
|
+
return currentLayers;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extracts the STAC collection which the layer was created from.
|
|
265
|
+
*
|
|
266
|
+
* @param {import('../eodashSTAC/EodashCollection.js').EodashCollection[]} indicators
|
|
267
|
+
* @param {import('ol/layer').Layer} layer
|
|
268
|
+
*/
|
|
269
|
+
export const getColFromLayer = async (indicators, layer) => {
|
|
270
|
+
// init cols
|
|
271
|
+
const collections = await Promise.all(
|
|
272
|
+
indicators.map((ind) => ind.fetchCollection()),
|
|
273
|
+
);
|
|
274
|
+
const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
|
|
275
|
+
|
|
276
|
+
const chosen = collections.find((col) => {
|
|
277
|
+
const isInd =
|
|
278
|
+
col.id === collectionId &&
|
|
279
|
+
col.links?.some(
|
|
280
|
+
(link) => link.rel === "item" && link.href.includes(itemId),
|
|
281
|
+
);
|
|
282
|
+
return isInd;
|
|
283
|
+
});
|
|
284
|
+
return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Generates layer specific ID from STAC Links
|
|
289
|
+
* related functions are: {@link assignProjID} & {@link createAssetID}
|
|
290
|
+
*
|
|
291
|
+
* @param {string} collectionId
|
|
292
|
+
* @param {string} itemId
|
|
293
|
+
* @param {import('stac-ts').StacLink} link
|
|
294
|
+
* @param {string} projectionCode
|
|
295
|
+
*
|
|
296
|
+
*/
|
|
297
|
+
export const createLayerID = (collectionId, itemId, link, projectionCode) => {
|
|
298
|
+
const linkId = link.id || link.title || link.href;
|
|
299
|
+
let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${linkId ?? ""};:;${projectionCode ?? ""}`;
|
|
300
|
+
// If we are looking at base layers and overlays we remove the collection and item part
|
|
301
|
+
// as we want to make sure tiles are not reloaded when switching layers
|
|
302
|
+
if (
|
|
303
|
+
/** @type {string[]} */
|
|
304
|
+
(link.roles)?.find((r) => ["baselayer", "overlay"].includes(r))
|
|
305
|
+
) {
|
|
306
|
+
lId = `${linkId ?? ""};:;${projectionCode ?? ""}`;
|
|
307
|
+
}
|
|
308
|
+
log.debug("Generated Layer ID", lId);
|
|
309
|
+
return lId;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Generates layer specific ID from STAC assets, related functions are: {@link assignProjID} & {@link createLayerID}
|
|
314
|
+
*
|
|
315
|
+
* @param {string} collectionId
|
|
316
|
+
* @param {string} itemId
|
|
317
|
+
* @param {number} index
|
|
318
|
+
*
|
|
319
|
+
*/
|
|
320
|
+
export const createAssetID = (collectionId, itemId, index) => {
|
|
321
|
+
let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${index ?? ""}`;
|
|
322
|
+
log.debug("Generated Asset ID", lId);
|
|
323
|
+
return lId;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Assigns projection code to the layer ID
|
|
328
|
+
* @param {import("stac-ts").StacItem} item
|
|
329
|
+
* @param {import("stac-ts").StacLink | import("stac-ts").StacAsset} linkOrAsset
|
|
330
|
+
* @param {string} id - {@link createLayerID} & {@link extractRoles}
|
|
331
|
+
* @param {{ properties:{id:string} & Record<string, any> }& Record<string,any>} layer
|
|
332
|
+
* @returns
|
|
333
|
+
*/
|
|
334
|
+
export function assignProjID(item, linkOrAsset, id, layer) {
|
|
335
|
+
const indicatorProjection =
|
|
336
|
+
/** @type { string | undefined} */
|
|
337
|
+
(item?.["proj:epsg"]) ||
|
|
338
|
+
/** @type { {name?: string} | undefined} */
|
|
339
|
+
(item?.["eodash:mapProjection"])?.name ||
|
|
340
|
+
"EPSG:3857";
|
|
341
|
+
|
|
342
|
+
const idArr = id.split(";:;");
|
|
343
|
+
|
|
344
|
+
idArr.pop();
|
|
345
|
+
idArr.push(indicatorProjection);
|
|
346
|
+
const updatedID = idArr.join(";:;");
|
|
347
|
+
layer.properties.id = updatedID;
|
|
348
|
+
|
|
349
|
+
log.debug("Updating layer id", updatedID);
|
|
350
|
+
|
|
351
|
+
return updatedID;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* creates a structured clone from the layers and
|
|
356
|
+
* removes all properties from the clone
|
|
357
|
+
* except the ID and title
|
|
358
|
+
*
|
|
359
|
+
* @param {Record<string,any>[]} layers
|
|
360
|
+
*/
|
|
361
|
+
export const removeUnneededProperties = (layers) => {
|
|
362
|
+
const cloned = structuredClone(layers);
|
|
363
|
+
cloned.forEach((layer) => {
|
|
364
|
+
const id = layer.properties.id;
|
|
365
|
+
const title = layer.properties.title;
|
|
366
|
+
layer.properties = { id, title };
|
|
367
|
+
if (layer["interactions"]) {
|
|
368
|
+
delete layer["interactions"];
|
|
369
|
+
}
|
|
370
|
+
if (layer.type === "Group") {
|
|
371
|
+
layer.layers = removeUnneededProperties(layer.layers);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
return cloned;
|
|
375
|
+
};
|