@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.
Files changed (113) hide show
  1. package/README.md +1 -1
  2. package/core/client/App.vue +13 -1
  3. package/core/client/asWebComponent.js +13 -4
  4. package/core/client/components/DashboardLayout.vue +36 -14
  5. package/core/client/components/Loading.vue +6 -9
  6. package/core/client/components/MobileLayout.vue +16 -14
  7. package/core/client/composables/DefineEodash.js +14 -4
  8. package/core/client/composables/DefineTemplate.js +67 -0
  9. package/core/client/composables/DefineWidgets.js +3 -2
  10. package/core/client/composables/EodashMap.js +360 -0
  11. package/core/client/composables/EodashProcess.js +574 -0
  12. package/core/client/composables/index.js +136 -28
  13. package/core/client/eodash.js +395 -80
  14. package/core/client/eodashSTAC/EodashCollection.js +432 -0
  15. package/core/client/eodashSTAC/createLayers.js +315 -0
  16. package/core/client/eodashSTAC/helpers.js +375 -0
  17. package/core/client/eodashSTAC/triggers.js +43 -0
  18. package/core/client/plugins/axios.js +8 -0
  19. package/core/client/plugins/index.js +2 -1
  20. package/core/client/plugins/vuetify.js +2 -1
  21. package/core/client/store/actions.js +79 -0
  22. package/core/client/store/index.js +4 -18
  23. package/core/client/store/stac.js +99 -9
  24. package/core/client/store/states.js +37 -0
  25. package/core/client/{types.d.ts → types.ts} +66 -20
  26. package/core/client/utils/keys.js +2 -0
  27. package/core/client/utils/states.js +22 -0
  28. package/core/client/views/Dashboard.vue +22 -49
  29. package/core/client/vite-env.d.ts +2 -10
  30. package/dist/client/DashboardLayout-232tRmjz.js +84 -0
  31. package/dist/client/DynamicWebComponent-Cl4LqHU6.js +88 -0
  32. package/dist/client/EodashDatePicker-Pok6bZwU.js +306 -0
  33. package/dist/client/EodashItemFilter-16eMMjTV.js +151 -0
  34. package/dist/client/EodashLayerControl-De7IlCm_.js +120 -0
  35. package/dist/client/EodashLayoutSwitcher-C-3-jjn5.js +52 -0
  36. package/dist/client/EodashMap-CMvbfI6-.js +549 -0
  37. package/dist/client/EodashMapBtns-BeknGDtc.js +107 -0
  38. package/dist/client/EodashProcess-BwKAa9Ee.js +1476 -0
  39. package/dist/client/EodashStacInfo-_BfonNUG.js +85 -0
  40. package/dist/client/EodashTools-PD3XPYuR.js +103 -0
  41. package/dist/client/ExportState-DOrT7M15.js +644 -0
  42. package/dist/client/Footer-CCigxYBo.js +141 -0
  43. package/dist/client/Header-C2cdx4gb.js +437 -0
  44. package/dist/client/IframeWrapper-BgM9aU8f.js +28 -0
  45. package/dist/client/MobileLayout-BdiFjHg7.js +1207 -0
  46. package/dist/client/PopUp--_xn1Cms.js +410 -0
  47. package/dist/client/VImg-9xu2l99m.js +384 -0
  48. package/dist/client/VMain-BUs3kDTd.js +43 -0
  49. package/dist/client/VOverlay-D89omJis.js +1453 -0
  50. package/dist/client/VTooltip-CDu3bErh.js +86 -0
  51. package/dist/client/WidgetsContainer-aFG9yFT6.js +83 -0
  52. package/dist/client/asWebComponent-BRGyP_j5.js +11943 -0
  53. package/dist/client/{style.css → eo-dash.css} +2 -2
  54. package/dist/client/eo-dash.js +2 -6
  55. package/dist/client/forwardRefs-CYrR6bMw.js +245 -0
  56. package/dist/client/index-BZwk0V42.js +199 -0
  57. package/dist/client/ssrBoot-BP7SYRyC.js +22 -0
  58. package/dist/client/transition-DG9nRSW4.js +37 -0
  59. package/dist/node/cli.js +4 -4
  60. package/dist/node/types.d.ts +2 -0
  61. package/package.json +73 -38
  62. package/widgets/EodashDatePicker.vue +176 -134
  63. package/widgets/EodashItemFilter.vue +79 -38
  64. package/widgets/EodashLayerControl.vue +111 -0
  65. package/widgets/EodashLayoutSwitcher.vue +36 -0
  66. package/widgets/EodashMap.vue +108 -133
  67. package/widgets/EodashMapBtns.vue +62 -8
  68. package/widgets/EodashProcess.vue +143 -0
  69. package/widgets/EodashStacInfo.vue +82 -0
  70. package/widgets/EodashTools.vue +83 -0
  71. package/widgets/ExportState.vue +17 -13
  72. package/widgets/PopUp.vue +24 -2
  73. package/core/client/SuspensedDashboard.ce.vue +0 -105
  74. package/core/client/asWebComponent.d.ts +0 -23
  75. package/core/client/store/Actions.js +0 -14
  76. package/core/client/store/States.js +0 -16
  77. package/core/client/utils/eodashSTAC.js +0 -249
  78. package/core/client/utils/helpers.js +0 -38
  79. package/dist/client/DashboardLayout-D0ZF6V2S.js +0 -156
  80. package/dist/client/DynamicWebComponent-CPsMSBHi.js +0 -57
  81. package/dist/client/EodashDatePicker-CBQP7u2X.js +0 -252
  82. package/dist/client/EodashItemFilter-DL2ScI-5.js +0 -7671
  83. package/dist/client/EodashMap-CkKoQlmR.js +0 -86917
  84. package/dist/client/EodashMapBtns-yuO2QmiR.js +0 -36
  85. package/dist/client/ExportState-CCzOhppU.js +0 -558
  86. package/dist/client/Footer-BPAND0yG.js +0 -115
  87. package/dist/client/Header-DLhebNvG.js +0 -350
  88. package/dist/client/IframeWrapper-1GEMHlsW.js +0 -19
  89. package/dist/client/MobileLayout-mGkOYRhu.js +0 -945
  90. package/dist/client/PopUp-1d2bBFjw.js +0 -300
  91. package/dist/client/VImg-DxHcztfM.js +0 -291
  92. package/dist/client/VMain-BLX5vRRn.js +0 -39
  93. package/dist/client/VOverlay-CvrYEmLu.js +0 -967
  94. package/dist/client/WidgetsContainer-CmYjvGm7.js +0 -129
  95. package/dist/client/_commonjsHelpers-DaMA6jEr.js +0 -8
  96. package/dist/client/asWebComponent-B91uK0U7.js +0 -20361
  97. package/dist/client/basedecoder-DHcBySSe-BmCFNFnw.js +0 -88
  98. package/dist/client/decoder-CP4lv0Kb-B6yqkcfC.js +0 -10
  99. package/dist/client/deflate-BXt-9JA_-CWfClgpK.js +0 -10
  100. package/dist/client/eodashSTAC-DBjqe_Ho.js +0 -2788
  101. package/dist/client/eox-stacinfo-l7ALSV90.js +0 -13969
  102. package/dist/client/forwardRefs-BJJiadQP.js +0 -185
  103. package/dist/client/index-Q-bHLjxx.js +0 -153
  104. package/dist/client/jpeg-BAgeD1d3-oeHbFPUL.js +0 -514
  105. package/dist/client/lerc-DzVumYtB-P-KXC0TO.js +0 -1027
  106. package/dist/client/lzw-LAGDNbSC-DkP96qO9.js +0 -84
  107. package/dist/client/packbits-BlDR4Kj5-C66n1-zr.js +0 -24
  108. package/dist/client/pako.esm-CB1uQYY0-DB0PYm1P.js +0 -1081
  109. package/dist/client/raw-CMGvRjfu-BRi6E4i1.js +0 -9
  110. package/dist/client/ssrBoot-yo11mybw.js +0 -17
  111. package/dist/client/transition-CSJhuYGK.js +0 -34
  112. package/dist/client/webfontloader-qotgY98I.js +0 -435
  113. 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
+ };