@eodash/eodash 5.7.0 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/client/components/EodashOverlay.vue +27 -4
- package/core/client/composables/DefineEodash.js +0 -1
- package/core/client/composables/DefineWidgets.js +3 -1
- package/core/client/eodashSTAC/EodashCollection.js +87 -24
- package/core/client/eodashSTAC/cql.js +64 -0
- package/core/client/eodashSTAC/createLayers.js +353 -187
- package/core/client/eodashSTAC/helpers.js +694 -110
- package/core/client/eodashSTAC/mosaic.js +422 -0
- package/core/client/plugins/axios.js +31 -1
- package/core/client/store/actions.js +1 -1
- package/core/client/store/stac.js +54 -4
- package/core/client/store/states.js +20 -2
- package/core/client/types.ts +152 -28
- package/core/client/utils/bands-editor/exampleSchema.json +50 -50
- package/core/client/utils/states.js +10 -0
- package/dist/client/{DashboardLayout-kRsmsNW5.js → DashboardLayout-DEs4f_Vy.js} +2 -2
- package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-PvnV88BA.js} +1 -1
- package/dist/client/EodashChart-D-3AGKZY.js +260 -0
- package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-Dm_goZzs.js} +3 -3
- package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-CJy3zYzD.js} +2 -1
- package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-rL8UnpkA.js} +55 -33
- package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
- package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-CG7u2Aq8.js} +227 -112
- package/dist/client/EodashStacInfo-CJhhxthD.js +169 -0
- package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-CWyK8QJT.js} +4 -4
- package/dist/client/{ExportState-DbOm8hBI.js → ExportState-BXhy5TOU.js} +20 -174
- package/dist/client/{Footer-BKg-VVkj.js → Footer-DZPvnhcT.js} +1 -1
- package/dist/client/{Header-DCrwCvmY.js → Header-CiGsDE_a.js} +2 -2
- package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BJHW7x5V.js} +2 -2
- package/dist/client/{PopUp-9uimfK49.js → PopUp-7_XnhvCc.js} +4 -168
- package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-LL_UCbvc.js} +5 -5
- package/dist/client/VDivider-DWYM0WUr.js +168 -0
- package/dist/client/{VImg-CrmB76ZY.js → VImg-B75dgw2K.js} +2 -2
- package/dist/client/{VMain-OKZNpcEG.js → VMain-DvMmOL4f.js} +1 -1
- package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-ClWHUmWL.js} +2 -2
- package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-BqaWs4ch.js} +1 -1
- package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-IZanwL-A.js} +677 -379
- package/dist/client/{async-DSKj6g1f.js → async-CiNIOBVz.js} +31 -29
- package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-3S3y_kZg.js} +17 -17
- package/dist/client/dialog-transition-c4ZSnzac.js +170 -0
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-B-c1_6yo.js} +8 -87
- package/dist/client/{handling-BrcDDlgS.js → handling-NvY8Csry.js} +3 -4
- package/dist/client/{helpers-DjZVl_WF.js → helpers-BFuh19CP.js} +1094 -194
- package/dist/client/{index-1JKE68n3.js → index-1wMAyuNL.js} +219 -67
- package/dist/client/{index-CSG_JoOA.js → index-B8jGJSTA.js} +62 -56
- package/dist/client/{index-Cu3-13Xg.js → index-BKQQJkhy.js} +167 -61
- package/dist/client/index-D0jUyol8.js +3167 -0
- package/dist/client/{index-CUjl-ZsI.js → index-KGxz5Egv.js} +2 -2
- package/dist/client/mosaic-vHvSuih6.js +459 -0
- package/dist/client/templates.js +251 -121
- package/dist/client/{transition-OHcz5Qq1.js → transition-BkyUsZh_.js} +1 -1
- package/dist/node/cli.js +5 -5
- package/dist/node/types.d.ts +4 -0
- package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
- package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +57 -25
- package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
- package/dist/types/core/client/eodashSTAC/createLayers.d.ts +42 -23
- package/dist/types/core/client/eodashSTAC/helpers.d.ts +118 -19
- package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
- package/dist/types/core/client/store/stac.d.ts +38 -8
- package/dist/types/core/client/store/states.d.ts +24 -2
- package/dist/types/core/client/types.d.ts +128 -24
- package/dist/types/core/client/utils/states.d.ts +12 -0
- package/dist/types/templates/baseConfig.d.ts +1 -100
- package/dist/types/templates/compare.d.ts +32 -3
- package/dist/types/templates/expert.d.ts +18 -0
- package/dist/types/templates/explore.d.ts +10 -1
- package/dist/types/templates/lite.d.ts +11 -0
- package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
- package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
- package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
- package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
- package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
- package/dist/types/widgets/EodashLayerControl.vue.d.ts +35 -16
- package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +14 -10
- package/dist/types/widgets/EodashMap/index.vue.d.ts +86 -37
- package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
- package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
- package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
- package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
- package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
- package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
- package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
- package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
- package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
- package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
- package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
- package/package.json +34 -34
- package/templates/baseConfig.js +37 -6
- package/templates/compare.js +61 -7
- package/templates/expert.js +24 -2
- package/templates/explore.js +12 -5
- package/templates/lite.js +13 -0
- package/widgets/EodashChart.vue +162 -21
- package/widgets/EodashItemCatalog/index.vue +363 -35
- package/widgets/EodashItemCatalog/methods/filters.js +134 -70
- package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
- package/widgets/EodashItemCatalog/methods/map.js +141 -28
- package/widgets/EodashLayerControl.vue +51 -25
- package/widgets/EodashMap/EodashMapBtns.vue +109 -48
- package/widgets/EodashMap/index.vue +91 -105
- package/widgets/EodashMap/methods/btns.js +64 -31
- package/widgets/EodashMap/methods/create-layers-config.js +16 -16
- package/widgets/EodashMap/methods/index.js +71 -8
- package/widgets/EodashMap/methods/use-map-loading.js +58 -0
- package/widgets/EodashProcess/index.vue +136 -16
- package/widgets/EodashProcess/methods/composables.js +13 -0
- package/widgets/EodashProcess/methods/outputs.js +0 -1
- package/widgets/EodashProcess/methods/utils.js +29 -27
- package/widgets/EodashProcess/states.js +1 -1
- package/widgets/EodashStacInfo.vue +64 -2
- package/widgets/EodashTimeSlider/index.vue +58 -2
- package/widgets/EodashTimeSlider/methods/index.js +5 -48
- package/widgets/EodashTimeSlider/types.ts +7 -17
- package/widgets/ExportState.vue +13 -4
- package/dist/client/EodashChart-CLDqE93C.js +0 -144
- package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
- package/dist/client/index-5Jhsa8Xe.js +0 -570
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { registerProjection } from "@/store/actions";
|
|
2
2
|
import { mapEl } from "@/store/states";
|
|
3
|
-
import
|
|
3
|
+
import { useEodash } from "@/composables";
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
extractRoles,
|
|
@@ -11,10 +11,18 @@ import {
|
|
|
11
11
|
extractLayerConfig,
|
|
12
12
|
extractEoxLegendLink,
|
|
13
13
|
addTooltipInteraction,
|
|
14
|
-
|
|
14
|
+
fetchRasterForm,
|
|
15
|
+
resolveStyle,
|
|
16
|
+
getBandsProperty,
|
|
17
|
+
applyTitilerUpscaling,
|
|
18
|
+
encodeURLObject,
|
|
19
|
+
normalizeRescale,
|
|
20
|
+
normalizeNodata,
|
|
21
|
+
resolveRenders,
|
|
15
22
|
} from "./helpers";
|
|
16
23
|
import { handleAuthenticationOfLink } from "./auth";
|
|
17
24
|
import log from "loglevel";
|
|
25
|
+
import axios from "@/plugins/axios";
|
|
18
26
|
import { useSTAcStore } from "@/store/stac";
|
|
19
27
|
|
|
20
28
|
/**
|
|
@@ -41,6 +49,7 @@ function buildCapabilitiesUrl(href) {
|
|
|
41
49
|
* @param {import("stac-ts").StacItem | import("stac-ts").StacCollection } stacObject
|
|
42
50
|
* @param {Record<string, unknown>} [layerDatetime]
|
|
43
51
|
* @param {object | null} [extraProperties]
|
|
52
|
+
* @param {import("stac-ts").StacCollection | null} [collection] - Used to fall back to a collection-level style link.
|
|
44
53
|
**/
|
|
45
54
|
export async function createLayersFromAssets(
|
|
46
55
|
collectionId,
|
|
@@ -49,6 +58,7 @@ export async function createLayersFromAssets(
|
|
|
49
58
|
stacObject,
|
|
50
59
|
layerDatetime,
|
|
51
60
|
extraProperties,
|
|
61
|
+
collection,
|
|
52
62
|
) {
|
|
53
63
|
log.debug("Creating layers from assets");
|
|
54
64
|
const jsonArray = [];
|
|
@@ -60,18 +70,35 @@ export async function createLayersFromAssets(
|
|
|
60
70
|
|
|
61
71
|
const fgbIdx = [];
|
|
62
72
|
const fgbSources = [];
|
|
73
|
+
const zarrAssetIds = [];
|
|
74
|
+
const zarrIdx = [];
|
|
63
75
|
const assetIds = [];
|
|
64
76
|
|
|
65
77
|
for (const [idx, assetId] of Object.keys(assets).entries()) {
|
|
66
78
|
assetIds.push(assetId);
|
|
67
79
|
|
|
68
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
assets[assetId]?.type?.includes("application/geo+json") &&
|
|
82
|
+
assets[assetId]?.href?.includes("http")
|
|
83
|
+
) {
|
|
69
84
|
geoJsonSources.push(assets[assetId].href);
|
|
70
85
|
geoJsonIdx.push(idx);
|
|
71
|
-
} else if (
|
|
86
|
+
} else if (
|
|
87
|
+
assets[assetId]?.type?.includes("application/vnd.flatgeobuf") &&
|
|
88
|
+
assets[assetId]?.href?.includes("http")
|
|
89
|
+
) {
|
|
72
90
|
fgbSources.push(assets[assetId].href);
|
|
73
91
|
fgbIdx.push(idx);
|
|
74
|
-
} else if (
|
|
92
|
+
} else if (
|
|
93
|
+
assets[assetId]?.type ==
|
|
94
|
+
"application/vnd.zarr; version=3; profile=multiscales"
|
|
95
|
+
) {
|
|
96
|
+
zarrAssetIds.push(assetId);
|
|
97
|
+
zarrIdx.push(idx);
|
|
98
|
+
} else if (
|
|
99
|
+
assets[assetId]?.type?.includes("image/tiff") &&
|
|
100
|
+
assets[assetId]?.href?.includes("http")
|
|
101
|
+
) {
|
|
75
102
|
geoTIFFIdx.push(idx);
|
|
76
103
|
geoTIFFSources.push({
|
|
77
104
|
url: assets[assetId].href,
|
|
@@ -79,7 +106,7 @@ export async function createLayersFromAssets(
|
|
|
79
106
|
? { attributions: assets[assetId].attribution }
|
|
80
107
|
: {}),
|
|
81
108
|
});
|
|
82
|
-
} else if (assets[assetId]?.type
|
|
109
|
+
} else if (assets[assetId]?.type?.includes("application/geodb+json")) {
|
|
83
110
|
const responseData = await (await fetch(assets[assetId].href)).json();
|
|
84
111
|
geoJsonIdx.push(idx);
|
|
85
112
|
if (
|
|
@@ -140,11 +167,122 @@ export async function createLayersFromAssets(
|
|
|
140
167
|
}
|
|
141
168
|
}
|
|
142
169
|
|
|
170
|
+
if (geoTIFFSources.length) {
|
|
171
|
+
for (const [i, geotiffSource] of geoTIFFSources.entries()) {
|
|
172
|
+
const assetName = assetIds[geoTIFFIdx[i]];
|
|
173
|
+
const styles = await resolveStyle(
|
|
174
|
+
stacObject,
|
|
175
|
+
collection,
|
|
176
|
+
undefined,
|
|
177
|
+
assetName,
|
|
178
|
+
);
|
|
179
|
+
// get the correct style which is not attached to a link
|
|
180
|
+
let { layerConfig, style } = extractLayerConfig(collectionId, styles);
|
|
181
|
+
let assetLayerId = createAssetID(
|
|
182
|
+
collectionId,
|
|
183
|
+
stacObject.id,
|
|
184
|
+
geoTIFFIdx[i],
|
|
185
|
+
);
|
|
186
|
+
if (
|
|
187
|
+
assets[assetName]?.roles?.includes("overlay") ||
|
|
188
|
+
assets[assetName]?.roles?.includes("baselayer")
|
|
189
|
+
) {
|
|
190
|
+
// to prevent them being removed by date change on main dataset
|
|
191
|
+
assetLayerId = assetName;
|
|
192
|
+
}
|
|
193
|
+
log.debug("Creating WebGLTile layer from GeoTIFF", assetLayerId);
|
|
194
|
+
log.debug("Configured Sources", geoTIFFSources);
|
|
195
|
+
const sources =
|
|
196
|
+
stacObject?.["eodash:merge_assets"] !== false
|
|
197
|
+
? geoTIFFSources
|
|
198
|
+
: [geotiffSource];
|
|
199
|
+
const layer = {
|
|
200
|
+
type: "WebGLTile",
|
|
201
|
+
source: {
|
|
202
|
+
type: "GeoTIFF",
|
|
203
|
+
normalize: !style,
|
|
204
|
+
interpolate: false,
|
|
205
|
+
sources,
|
|
206
|
+
},
|
|
207
|
+
properties: {
|
|
208
|
+
id: assetLayerId,
|
|
209
|
+
title: assets[assetName]?.title || title,
|
|
210
|
+
layerConfig,
|
|
211
|
+
layerDatetime,
|
|
212
|
+
},
|
|
213
|
+
style,
|
|
214
|
+
};
|
|
215
|
+
if (extraProperties) {
|
|
216
|
+
layer.properties = { ...layer.properties, ...extraProperties };
|
|
217
|
+
}
|
|
218
|
+
extractRoles(layer.properties, assets[assetName]);
|
|
219
|
+
addTooltipInteraction(layer, style);
|
|
220
|
+
jsonArray.push(layer);
|
|
221
|
+
if (stacObject?.["eodash:merge_assets"] !== false) break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (zarrAssetIds.length) {
|
|
226
|
+
for (const [i, assetName] of zarrAssetIds.entries()) {
|
|
227
|
+
const fetchedStyle = await resolveStyle(
|
|
228
|
+
stacObject,
|
|
229
|
+
collection,
|
|
230
|
+
undefined,
|
|
231
|
+
assetName,
|
|
232
|
+
);
|
|
233
|
+
const { layerConfig, style } = extractLayerConfig(
|
|
234
|
+
collectionId,
|
|
235
|
+
fetchedStyle,
|
|
236
|
+
);
|
|
237
|
+
const bandsPath = getBandsProperty(layerConfig?.schema);
|
|
238
|
+
const defaultBands = bandsPath?.reduce(
|
|
239
|
+
(node, key) => node?.[key],
|
|
240
|
+
layerConfig?.schema,
|
|
241
|
+
)?.default ?? ["b04", "b03", "b02"];
|
|
242
|
+
|
|
243
|
+
let assetLayerId = createAssetID(collectionId, stacObject.id, zarrIdx[i]);
|
|
244
|
+
if (
|
|
245
|
+
assets[assetName]?.roles?.includes("overlay") ||
|
|
246
|
+
assets[assetName]?.roles?.includes("baselayer")
|
|
247
|
+
) {
|
|
248
|
+
assetLayerId = assetName;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
log.debug("Creating WebGLTile layer from GeoZarr", assetLayerId);
|
|
252
|
+
|
|
253
|
+
const layer = {
|
|
254
|
+
type: "WebGLTile",
|
|
255
|
+
properties: {
|
|
256
|
+
id: assetLayerId,
|
|
257
|
+
title: assets[assetName]?.title || title,
|
|
258
|
+
layerConfig,
|
|
259
|
+
layerDatetime,
|
|
260
|
+
},
|
|
261
|
+
source: {
|
|
262
|
+
type: "GeoZarr",
|
|
263
|
+
url: assets[assetName].href,
|
|
264
|
+
bands: defaultBands,
|
|
265
|
+
},
|
|
266
|
+
...(style ? { style } : {}),
|
|
267
|
+
};
|
|
268
|
+
if (extraProperties) {
|
|
269
|
+
layer.properties = { ...layer.properties, ...extraProperties };
|
|
270
|
+
}
|
|
271
|
+
extractRoles(layer.properties, assets[assetName]);
|
|
272
|
+
jsonArray.push(layer);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
143
276
|
if (geoJsonSources.length) {
|
|
144
277
|
for (const [i, geoJsonSource] of geoJsonSources.entries()) {
|
|
145
278
|
// fetch styles and separate them by their mapping between links and assets
|
|
146
279
|
const assetName = assetIds[geoJsonIdx[i]];
|
|
147
|
-
const styles = await
|
|
280
|
+
const styles = await resolveStyle(
|
|
281
|
+
stacObject,
|
|
282
|
+
collection,
|
|
283
|
+
undefined,
|
|
284
|
+
assetName,
|
|
285
|
+
);
|
|
148
286
|
// get the correct style which is not attached to a link
|
|
149
287
|
let { layerConfig, style } = extractLayerConfig(collectionId, styles);
|
|
150
288
|
let assetLayerId = createAssetID(
|
|
@@ -210,7 +348,12 @@ export async function createLayersFromAssets(
|
|
|
210
348
|
for (const [i, fgbSource] of fgbSources.entries()) {
|
|
211
349
|
// fetch styles and separate them by their mapping between links and assets
|
|
212
350
|
const assetName = assetIds[fgbIdx[i]];
|
|
213
|
-
const styles = await
|
|
351
|
+
const styles = await resolveStyle(
|
|
352
|
+
stacObject,
|
|
353
|
+
collection,
|
|
354
|
+
undefined,
|
|
355
|
+
assetName,
|
|
356
|
+
);
|
|
214
357
|
// get the correct style which is not attached to a link
|
|
215
358
|
let { layerConfig, style } = extractLayerConfig(collectionId, styles);
|
|
216
359
|
let assetLayerId = createAssetID(collectionId, stacObject.id, fgbIdx[i]);
|
|
@@ -270,56 +413,6 @@ export async function createLayersFromAssets(
|
|
|
270
413
|
}
|
|
271
414
|
}
|
|
272
415
|
|
|
273
|
-
if (geoTIFFSources.length) {
|
|
274
|
-
for (const [i, geotiffSource] of geoTIFFSources.entries()) {
|
|
275
|
-
const assetName = assetIds[geoTIFFIdx[i]];
|
|
276
|
-
const styles = await fetchStyle(stacObject, undefined, assetName);
|
|
277
|
-
// get the correct style which is not attached to a link
|
|
278
|
-
let { layerConfig, style } = extractLayerConfig(collectionId, styles);
|
|
279
|
-
let assetLayerId = createAssetID(
|
|
280
|
-
collectionId,
|
|
281
|
-
stacObject.id,
|
|
282
|
-
geoTIFFIdx[i],
|
|
283
|
-
);
|
|
284
|
-
if (
|
|
285
|
-
assets[assetName]?.roles?.includes("overlay") ||
|
|
286
|
-
assets[assetName]?.roles?.includes("baselayer")
|
|
287
|
-
) {
|
|
288
|
-
// to prevent them being removed by date change on main dataset
|
|
289
|
-
assetLayerId = assetName;
|
|
290
|
-
}
|
|
291
|
-
log.debug("Creating WebGLTile layer from GeoTIFF", assetLayerId);
|
|
292
|
-
log.debug("Configured Sources", geoTIFFSources);
|
|
293
|
-
const sources =
|
|
294
|
-
stacObject?.["eodash:merge_assets"] !== false
|
|
295
|
-
? geoTIFFSources
|
|
296
|
-
: [geotiffSource];
|
|
297
|
-
const layer = {
|
|
298
|
-
type: "WebGLTile",
|
|
299
|
-
source: {
|
|
300
|
-
type: "GeoTIFF",
|
|
301
|
-
normalize: !style,
|
|
302
|
-
interpolate: false,
|
|
303
|
-
sources,
|
|
304
|
-
},
|
|
305
|
-
properties: {
|
|
306
|
-
id: assetLayerId,
|
|
307
|
-
title: assets[assetName]?.title || title,
|
|
308
|
-
layerConfig,
|
|
309
|
-
layerDatetime,
|
|
310
|
-
},
|
|
311
|
-
style,
|
|
312
|
-
};
|
|
313
|
-
if (extraProperties) {
|
|
314
|
-
layer.properties = { ...layer.properties, ...extraProperties };
|
|
315
|
-
}
|
|
316
|
-
extractRoles(layer.properties, assets[assetName]);
|
|
317
|
-
addTooltipInteraction(layer, style);
|
|
318
|
-
jsonArray.push(layer);
|
|
319
|
-
if (stacObject?.["eodash:merge_assets"] !== false) break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
416
|
return jsonArray;
|
|
324
417
|
}
|
|
325
418
|
|
|
@@ -349,6 +442,10 @@ export const createLayersFromLinks = async (
|
|
|
349
442
|
item.links.filter((l) => l.rel === "vector-tile") ?? [];
|
|
350
443
|
const mapboxStyleDocumentArray =
|
|
351
444
|
item.links.filter((l) => l.rel === "mapbox-style-document") ?? [];
|
|
445
|
+
// An xyz link takes precedence over a tilejson link;
|
|
446
|
+
const tilejsonArray = xyzArray.length
|
|
447
|
+
? []
|
|
448
|
+
: (item.links.filter((l) => l.rel === "tilejson") ?? []);
|
|
352
449
|
// Taking projection code from main map view, as main view defines
|
|
353
450
|
// projection for comparison map
|
|
354
451
|
const viewProjectionCode = mapEl?.value?.projection || "EPSG:3857";
|
|
@@ -371,6 +468,20 @@ export const createLayersFromLinks = async (
|
|
|
371
468
|
wmsLink,
|
|
372
469
|
viewProjectionCode,
|
|
373
470
|
);
|
|
471
|
+
const rasterForm = await fetchRasterForm(
|
|
472
|
+
/** @type {string|object|undefined} */ (
|
|
473
|
+
wmsLink?.["eodash:rasterform"] ||
|
|
474
|
+
item?.["eodash:rasterform"] ||
|
|
475
|
+
collection?.["eodash:rasterform"]
|
|
476
|
+
),
|
|
477
|
+
);
|
|
478
|
+
let { layerConfig } = extractLayerConfig(
|
|
479
|
+
collectionId,
|
|
480
|
+
{},
|
|
481
|
+
rasterForm,
|
|
482
|
+
"tileUrl",
|
|
483
|
+
);
|
|
484
|
+
|
|
374
485
|
log.debug("WMS Layer added", linkId);
|
|
375
486
|
const tileSize = /** @type {number[]} */ (
|
|
376
487
|
"wms:tilesize" in wmsLink
|
|
@@ -383,6 +494,7 @@ export const createLayersFromLinks = async (
|
|
|
383
494
|
id: linkId,
|
|
384
495
|
title: wmsLink.title || title || item.id,
|
|
385
496
|
layerDatetime,
|
|
497
|
+
layerConfig,
|
|
386
498
|
},
|
|
387
499
|
source: {
|
|
388
500
|
type: "TileWMS",
|
|
@@ -438,15 +550,18 @@ export const createLayersFromLinks = async (
|
|
|
438
550
|
(wmtsLink?.["proj:epsg"] || wmtsLink?.["eodash:proj4_def"]);
|
|
439
551
|
|
|
440
552
|
await registerProjection(wmtsLinkProjection);
|
|
441
|
-
const key =
|
|
442
|
-
/** @type {string | undefined} */ (wmtsLink["key"]) || undefined;
|
|
443
553
|
|
|
444
|
-
const
|
|
445
|
-
|
|
554
|
+
const rasterForm = await fetchRasterForm(
|
|
555
|
+
/** @type {string|object|undefined} */ (
|
|
556
|
+
wmtsLink?.["eodash:rasterform"] ||
|
|
557
|
+
item?.["eodash:rasterform"] ||
|
|
558
|
+
collection?.["eodash:rasterform"]
|
|
559
|
+
),
|
|
560
|
+
);
|
|
446
561
|
const returnedLayerConfig = extractLayerConfig(
|
|
447
562
|
collectionId,
|
|
448
|
-
|
|
449
|
-
|
|
563
|
+
{},
|
|
564
|
+
rasterForm,
|
|
450
565
|
"tileUrl",
|
|
451
566
|
);
|
|
452
567
|
const projectionCode = getProjectionCode(wmtsLinkProjection || "EPSG:3857");
|
|
@@ -469,14 +584,6 @@ export const createLayersFromLinks = async (
|
|
|
469
584
|
// TODO, this does not yet work between layer time changes because we do not get
|
|
470
585
|
// updated variables from OL layer due to usage of tileurlfunction
|
|
471
586
|
|
|
472
|
-
// update dimensions with current value of style variables if applicable
|
|
473
|
-
const variables = returnedLayerConfig?.style?.variables;
|
|
474
|
-
if (variables) {
|
|
475
|
-
for (const [kk, vv] of Object.entries(variables)) {
|
|
476
|
-
dimensionsWithoutStyle[kk] = vv;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
587
|
if (wmtsLink.href.includes("marine.copernicus")) {
|
|
481
588
|
log.debug(
|
|
482
589
|
"Warning: WMTS Layer from capabilities added, function needs to be updated",
|
|
@@ -517,6 +624,7 @@ export const createLayersFromLinks = async (
|
|
|
517
624
|
id: linkId,
|
|
518
625
|
title: wmtsLink.title || title || item.id,
|
|
519
626
|
layerDatetime,
|
|
627
|
+
layerConfig: returnedLayerConfig.layerConfig,
|
|
520
628
|
},
|
|
521
629
|
source: {
|
|
522
630
|
type: "WMTSCapabilities",
|
|
@@ -546,19 +654,16 @@ export const createLayersFromLinks = async (
|
|
|
546
654
|
const xyzLinkProjection =
|
|
547
655
|
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
548
656
|
(xyzLink?.["proj:epsg"] || xyzLink?.["eodash:proj4_def"]);
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
657
|
+
const rasterForm = await fetchRasterForm(
|
|
658
|
+
/** @type {string|object|undefined} */ (
|
|
659
|
+
xyzLink?.["eodash:rasterform"] ||
|
|
660
|
+
item?.["eodash:rasterform"] ||
|
|
661
|
+
collection?.["eodash:rasterform"]
|
|
662
|
+
),
|
|
552
663
|
);
|
|
553
|
-
|
|
554
|
-
const rasterForm = rasterformURL
|
|
555
|
-
? await axios.get(rasterformURL).then((resp) => resp.data)
|
|
556
|
-
: undefined;
|
|
557
|
-
const styles = await fetchStyle(item, key);
|
|
558
|
-
// get the correct style which is attached to a link
|
|
559
|
-
let { layerConfig, style } = extractLayerConfig(
|
|
664
|
+
let { layerConfig } = extractLayerConfig(
|
|
560
665
|
collectionId,
|
|
561
|
-
|
|
666
|
+
{},
|
|
562
667
|
rasterForm,
|
|
563
668
|
"tileUrl",
|
|
564
669
|
);
|
|
@@ -571,25 +676,14 @@ export const createLayersFromLinks = async (
|
|
|
571
676
|
viewProjectionCode,
|
|
572
677
|
);
|
|
573
678
|
let xyzUrl = xyzLink.href;
|
|
574
|
-
|
|
575
|
-
// TODO, this does not yet work between layer time changes because we do not get
|
|
576
|
-
// updated variables from OL layer due to usage of tileurlfunction
|
|
577
|
-
|
|
578
|
-
// update url query params with current value of style variables if applicable
|
|
579
|
-
const variables = style?.variables;
|
|
580
|
-
if (variables) {
|
|
581
|
-
const [base, query] = xyzUrl.split("?");
|
|
582
|
-
const params = new URLSearchParams(query);
|
|
583
|
-
for (const [kk, vv] of Object.entries(variables)) {
|
|
584
|
-
params.set(kk, JSON.stringify(vv));
|
|
585
|
-
}
|
|
586
|
-
xyzUrl = `${base}?${params.toString()}`;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
679
|
const { supportedUpscalingEndpoints } = useSTAcStore();
|
|
590
|
-
const
|
|
591
|
-
|
|
680
|
+
const upscaling = applyTitilerUpscaling(
|
|
681
|
+
xyzUrl,
|
|
682
|
+
supportedUpscalingEndpoints,
|
|
592
683
|
);
|
|
684
|
+
if (upscaling) {
|
|
685
|
+
xyzUrl = upscaling.url;
|
|
686
|
+
}
|
|
593
687
|
|
|
594
688
|
// Add sharding for s2maps automatically
|
|
595
689
|
if (xyzUrl.includes("s2maps-tiles.eu")) {
|
|
@@ -608,15 +702,15 @@ export const createLayersFromLinks = async (
|
|
|
608
702
|
},
|
|
609
703
|
source: {
|
|
610
704
|
type: "XYZ",
|
|
611
|
-
url:
|
|
705
|
+
url: xyzUrl,
|
|
612
706
|
projection: projectionCode,
|
|
613
707
|
...(xyzLink.attribution ? { attributions: xyzLink.attribution } : {}),
|
|
614
708
|
},
|
|
615
709
|
};
|
|
616
|
-
if (
|
|
710
|
+
if (upscaling) {
|
|
617
711
|
// @ts-expect-error tileGrid is added here and supported in eox-map layer definition
|
|
618
712
|
json.source.tileGrid = {
|
|
619
|
-
tileSize:
|
|
713
|
+
tileSize: upscaling.tileSize,
|
|
620
714
|
};
|
|
621
715
|
}
|
|
622
716
|
if (
|
|
@@ -640,6 +734,96 @@ export const createLayersFromLinks = async (
|
|
|
640
734
|
jsonArray.push(json);
|
|
641
735
|
}
|
|
642
736
|
|
|
737
|
+
for (const tilejsonLink of tilejsonArray) {
|
|
738
|
+
// The tilejson href is a complete URL with the render params baked in by the
|
|
739
|
+
// STAC producer; fetch it and use its `tiles[0]` template as an XYZ source.
|
|
740
|
+
const tileJSON = await axios
|
|
741
|
+
.get(tilejsonLink.href)
|
|
742
|
+
.then((res) => res.data)
|
|
743
|
+
.catch((err) => {
|
|
744
|
+
console.error("[eodash] Failed to fetch item TileJSON", err);
|
|
745
|
+
return null;
|
|
746
|
+
});
|
|
747
|
+
if (!tileJSON?.tiles?.[0]) {
|
|
748
|
+
console.warn(
|
|
749
|
+
"[eodash] No tile URL in item TileJSON response",
|
|
750
|
+
tilejsonLink.href,
|
|
751
|
+
);
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
// Only raster XYZ tiles are supported; skip vector & TMS scheme TileJSON
|
|
755
|
+
if (tileJSON.vector_layers || tileJSON.scheme === "tms") {
|
|
756
|
+
console.warn(
|
|
757
|
+
"[eodash] Unsupported TileJSON (only raster XYZ is supported)",
|
|
758
|
+
tilejsonLink.href,
|
|
759
|
+
);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const tilejsonProjection =
|
|
764
|
+
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
765
|
+
(tilejsonLink?.["proj:epsg"] || tilejsonLink?.["eodash:proj4_def"]);
|
|
766
|
+
await registerProjection(tilejsonProjection);
|
|
767
|
+
const projectionCode = getProjectionCode(tilejsonProjection || "EPSG:3857");
|
|
768
|
+
const rasterForm = await fetchRasterForm(
|
|
769
|
+
/** @type {string|object|undefined} */ (
|
|
770
|
+
tilejsonLink?.["eodash:rasterform"] ||
|
|
771
|
+
item?.["eodash:rasterform"] ||
|
|
772
|
+
collection?.["eodash:rasterform"]
|
|
773
|
+
),
|
|
774
|
+
);
|
|
775
|
+
const { layerConfig } = extractLayerConfig(
|
|
776
|
+
collectionId,
|
|
777
|
+
{},
|
|
778
|
+
rasterForm,
|
|
779
|
+
"tileUrl",
|
|
780
|
+
);
|
|
781
|
+
const linkId = createLayerID(
|
|
782
|
+
collectionId,
|
|
783
|
+
item.id,
|
|
784
|
+
tilejsonLink,
|
|
785
|
+
viewProjectionCode,
|
|
786
|
+
);
|
|
787
|
+
|
|
788
|
+
log.debug("TileJSON layer added", linkId);
|
|
789
|
+
/** @type {Record<string, any>} */
|
|
790
|
+
const json = {
|
|
791
|
+
type: "Tile",
|
|
792
|
+
properties: {
|
|
793
|
+
id: linkId,
|
|
794
|
+
title: tilejsonLink.title || title || item.id,
|
|
795
|
+
roles: tilejsonLink.roles,
|
|
796
|
+
layerDatetime,
|
|
797
|
+
layerConfig,
|
|
798
|
+
},
|
|
799
|
+
source: {
|
|
800
|
+
type: "XYZ",
|
|
801
|
+
...(tileJSON.tiles.length > 1
|
|
802
|
+
? { urls: tileJSON.tiles }
|
|
803
|
+
: { url: tileJSON.tiles[0] }),
|
|
804
|
+
projection: projectionCode,
|
|
805
|
+
// Link attribution wins; the TileJSON document's own is the fallback.
|
|
806
|
+
...(tilejsonLink.attribution || tileJSON.attribution
|
|
807
|
+
? {
|
|
808
|
+
attributions: tilejsonLink.attribution || tileJSON.attribution,
|
|
809
|
+
}
|
|
810
|
+
: {}),
|
|
811
|
+
},
|
|
812
|
+
};
|
|
813
|
+
if (Number.isFinite(tileJSON.minzoom)) json.minZoom = tileJSON.minzoom;
|
|
814
|
+
if (Number.isFinite(tileJSON.maxzoom)) json.maxZoom = tileJSON.maxzoom;
|
|
815
|
+
|
|
816
|
+
extractRoles(json.properties, tilejsonLink);
|
|
817
|
+
if (extraProperties !== null) {
|
|
818
|
+
json.properties = {
|
|
819
|
+
...json.properties,
|
|
820
|
+
...extraProperties,
|
|
821
|
+
...extractEoxLegendLink(tilejsonLink),
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
jsonArray.push(json);
|
|
825
|
+
}
|
|
826
|
+
|
|
643
827
|
for (const vectorTileLink of vectorTileArray ?? []) {
|
|
644
828
|
const vectorTileLinkProjection =
|
|
645
829
|
/** @type {number | string | {name: string, def: string} | undefined} */
|
|
@@ -659,7 +843,7 @@ export const createLayersFromLinks = async (
|
|
|
659
843
|
const key =
|
|
660
844
|
/** @type {string | undefined} */ (vectorTileLink["key"]) || undefined;
|
|
661
845
|
// fetch styles and separate them by their mapping between links and assets
|
|
662
|
-
const styles = await
|
|
846
|
+
const styles = await resolveStyle(item, collection, key);
|
|
663
847
|
// get the correct style which is not attached to a link
|
|
664
848
|
let { layerConfig, style } = extractLayerConfig(collectionId, styles);
|
|
665
849
|
|
|
@@ -745,7 +929,7 @@ export const createLayersFromLinks = async (
|
|
|
745
929
|
),
|
|
746
930
|
applyOptions,
|
|
747
931
|
);
|
|
748
|
-
applyOptions = /** @type {
|
|
932
|
+
applyOptions = /** @type {object} */ (optionsObject);
|
|
749
933
|
href = url;
|
|
750
934
|
}
|
|
751
935
|
const json = {
|
|
@@ -791,61 +975,85 @@ export const createLayerFromRender = async (
|
|
|
791
975
|
item,
|
|
792
976
|
extraProperties,
|
|
793
977
|
) => {
|
|
794
|
-
|
|
978
|
+
// config renders > collection STAC renders > item renders
|
|
979
|
+
const renders = /** @type {Record<string,import("@/types").Render>} */ (
|
|
980
|
+
resolveRenders(collection, useEodash()?.options?.renders) ?? item?.renders
|
|
981
|
+
);
|
|
982
|
+
if (!collection || !item || !renders) {
|
|
795
983
|
return [];
|
|
796
984
|
}
|
|
797
985
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
? await axios.get(rasterformURL).then((resp) => resp.data)
|
|
804
|
-
: undefined;
|
|
805
|
-
let { layerConfig } = extractLayerConfig(
|
|
806
|
-
collection.id,
|
|
807
|
-
await fetchStyle(item),
|
|
808
|
-
rasterForm,
|
|
986
|
+
// Yield to a raster xyz/tilejson link — createLayersFromLinks renders it.
|
|
987
|
+
const hasMatchingTileLink = item.links?.some(
|
|
988
|
+
(link) =>
|
|
989
|
+
(link.rel === "xyz" || link.rel === "tilejson") &&
|
|
990
|
+
link.href?.includes(rasterURL),
|
|
809
991
|
);
|
|
992
|
+
if (hasMatchingTileLink) {
|
|
993
|
+
return [];
|
|
994
|
+
}
|
|
810
995
|
|
|
811
|
-
const
|
|
812
|
-
|
|
996
|
+
const rasterForm = await fetchRasterForm(
|
|
997
|
+
/** @type {string|object|undefined} */ (
|
|
998
|
+
item?.["eodash:rasterform"] || collection?.["eodash:rasterform"]
|
|
999
|
+
),
|
|
813
1000
|
);
|
|
1001
|
+
let { layerConfig } = extractLayerConfig(collection.id, {}, rasterForm);
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Resolves the first defined value of a property across a render's assets,
|
|
1005
|
+
* checking item assets before falling back to collection assets.
|
|
1006
|
+
* @param {import("@/types").Render} render
|
|
1007
|
+
* @param {string} propertyName
|
|
1008
|
+
* @returns {any}
|
|
1009
|
+
*/
|
|
1010
|
+
const getRenderAssetProperty = (render, propertyName) => {
|
|
1011
|
+
for (const assetKey of render.assets ?? []) {
|
|
1012
|
+
const asset = item?.assets?.[assetKey] ?? collection?.assets?.[assetKey];
|
|
1013
|
+
const value = asset?.[propertyName];
|
|
1014
|
+
if (value !== undefined) {
|
|
1015
|
+
return value;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return undefined;
|
|
1019
|
+
};
|
|
1020
|
+
|
|
814
1021
|
const layers = [];
|
|
815
|
-
// special case for rescale
|
|
816
1022
|
for (const key in renders) {
|
|
817
1023
|
const title = renders[key].title;
|
|
818
1024
|
|
|
819
|
-
const
|
|
820
|
-
renders[key].
|
|
1025
|
+
const expression =
|
|
1026
|
+
renders[key].expression ??
|
|
1027
|
+
getRenderAssetProperty(renders[key], "expression");
|
|
821
1028
|
|
|
822
1029
|
const paramsObject = {
|
|
823
|
-
assets
|
|
824
|
-
expression:
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
assetsCollection["assets"]?.[renders[key].assets[0]]?.nodata,
|
|
1030
|
+
// TiTiler treats assets and expression as mutually exclusive band selection
|
|
1031
|
+
assets: expression ? undefined : renders[key].assets,
|
|
1032
|
+
expression,
|
|
1033
|
+
nodata: normalizeNodata(
|
|
1034
|
+
renders[key].nodata ?? getRenderAssetProperty(renders[key], "nodata"),
|
|
1035
|
+
),
|
|
830
1036
|
resampling:
|
|
831
1037
|
renders[key].resampling ??
|
|
832
|
-
|
|
1038
|
+
getRenderAssetProperty(renders[key], "resampling"),
|
|
833
1039
|
color_formula:
|
|
834
1040
|
renders[key].color_formula ??
|
|
835
|
-
|
|
1041
|
+
getRenderAssetProperty(renders[key], "color_formula"),
|
|
836
1042
|
colormap:
|
|
837
1043
|
renders[key].colormap ??
|
|
838
|
-
|
|
1044
|
+
getRenderAssetProperty(renders[key], "colormap"),
|
|
839
1045
|
colormap_name:
|
|
840
1046
|
renders[key].colormap_name ??
|
|
841
|
-
|
|
842
|
-
rescale:
|
|
843
|
-
renders[key].rescale ??
|
|
844
|
-
|
|
1047
|
+
getRenderAssetProperty(renders[key], "colormap_name"),
|
|
1048
|
+
rescale: normalizeRescale(
|
|
1049
|
+
renders[key].rescale ?? getRenderAssetProperty(renders[key], "rescale"),
|
|
1050
|
+
),
|
|
1051
|
+
bidx: renders[key].bidx,
|
|
1052
|
+
tilesize: renders[key].tilesize,
|
|
845
1053
|
};
|
|
846
1054
|
const paramsStr = encodeURLObject(paramsObject);
|
|
847
1055
|
const url = `${rasterURL}/collections/${collection.id}/items/${item.id}/tiles/WebMercatorQuad/{z}/{x}/{y}?${paramsStr}`;
|
|
848
|
-
|
|
1056
|
+
const json = {
|
|
849
1057
|
/** @type {"Tile"} */
|
|
850
1058
|
type: "Tile",
|
|
851
1059
|
properties: {
|
|
@@ -868,57 +1076,15 @@ export const createLayerFromRender = async (
|
|
|
868
1076
|
url,
|
|
869
1077
|
projection: "EPSG:3857",
|
|
870
1078
|
},
|
|
871
|
-
}
|
|
1079
|
+
};
|
|
1080
|
+
if (renders[key].tilesize) {
|
|
1081
|
+
// @ts-expect-error tileGrid is added here and supported in eox-map layer definition
|
|
1082
|
+
json.source.tileGrid = {
|
|
1083
|
+
tileSize: [renders[key].tilesize, renders[key].tilesize],
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
layers.push(json);
|
|
872
1087
|
}
|
|
873
1088
|
|
|
874
1089
|
return layers;
|
|
875
1090
|
};
|
|
876
|
-
/**
|
|
877
|
-
*
|
|
878
|
-
* @param {Record<string,any>} obj
|
|
879
|
-
* @returns {string}
|
|
880
|
-
*/
|
|
881
|
-
function encodeURLObject(obj) {
|
|
882
|
-
let str = "";
|
|
883
|
-
for (const key in obj) {
|
|
884
|
-
const value = obj[key];
|
|
885
|
-
if (value === null || value === undefined || value === "") {
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
const valueType = Array.isArray(value) ? "array" : typeof value;
|
|
890
|
-
|
|
891
|
-
switch (valueType) {
|
|
892
|
-
case "array": {
|
|
893
|
-
// Check if any element in the array is itself an array (multi-dimensional)
|
|
894
|
-
const hasNestedArrays = value.some((/** @type {any} */ item) =>
|
|
895
|
-
Array.isArray(item),
|
|
896
|
-
);
|
|
897
|
-
|
|
898
|
-
if (hasNestedArrays) {
|
|
899
|
-
// For multi-dimensional arrays, repeat the key with different values
|
|
900
|
-
for (const val of value) {
|
|
901
|
-
if (Array.isArray(val)) {
|
|
902
|
-
str += `${key}=${val.join(",")}&`;
|
|
903
|
-
} else {
|
|
904
|
-
str += `${key}=${val}&`;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
} else {
|
|
908
|
-
// For simple arrays, join with commas
|
|
909
|
-
str += `${key}=${value.join(",")}&`;
|
|
910
|
-
}
|
|
911
|
-
break;
|
|
912
|
-
}
|
|
913
|
-
case "object": {
|
|
914
|
-
str += `${key}=${encodeURI(JSON.stringify(value))}&`;
|
|
915
|
-
break;
|
|
916
|
-
}
|
|
917
|
-
default: {
|
|
918
|
-
str += `${key}=${encodeURIComponent(value)}&`;
|
|
919
|
-
break;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
return str;
|
|
924
|
-
}
|