@eodash/eodash 5.0.0-alpha.2.26 → 5.0.0-alpha.2.27

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 (108) hide show
  1. package/core/client/asWebComponent.js +2 -3
  2. package/core/client/components/DashboardLayout.vue +35 -13
  3. package/core/client/components/Loading.vue +6 -9
  4. package/core/client/components/MobileLayout.vue +16 -14
  5. package/core/client/composables/DefineEodash.js +13 -3
  6. package/core/client/composables/DefineTemplate.js +67 -0
  7. package/core/client/composables/DefineWidgets.js +3 -2
  8. package/core/client/composables/EodashMap.js +39 -14
  9. package/core/client/composables/EodashProcess.js +574 -0
  10. package/core/client/composables/index.js +54 -11
  11. package/core/client/eodash.js +383 -125
  12. package/core/client/{utils/eodashSTAC.js → eodashSTAC/EodashCollection.js} +75 -41
  13. package/core/client/{utils → eodashSTAC}/createLayers.js +10 -8
  14. package/core/client/{utils → eodashSTAC}/helpers.js +47 -75
  15. package/core/client/eodashSTAC/triggers.js +43 -0
  16. package/core/client/plugins/vuetify.js +2 -1
  17. package/core/client/store/{Actions.js → actions.js} +16 -2
  18. package/core/client/store/index.js +4 -18
  19. package/core/client/store/stac.js +4 -4
  20. package/core/client/store/{States.js → states.js} +2 -0
  21. package/{dist/types/core/client/types.d.ts → core/client/types.ts} +47 -8
  22. package/core/client/utils/keys.js +2 -0
  23. package/core/client/utils/states.js +8 -3
  24. package/core/client/views/Dashboard.vue +6 -4
  25. package/core/client/vite-env.d.ts +1 -16
  26. package/dist/client/{DashboardLayout-E_JzgCH5.js → DashboardLayout-232tRmjz.js} +23 -25
  27. package/dist/client/{DynamicWebComponent-C9pVUfT3.js → DynamicWebComponent-Cl4LqHU6.js} +1 -1
  28. package/dist/client/{EodashDatePicker-CjU8R2ia.js → EodashDatePicker-Pok6bZwU.js} +78 -165
  29. package/dist/client/EodashItemFilter-16eMMjTV.js +151 -0
  30. package/dist/client/{EodashLayerControl-mKfwru42.js → EodashLayerControl-De7IlCm_.js} +19 -11
  31. package/dist/client/EodashLayoutSwitcher-C-3-jjn5.js +52 -0
  32. package/dist/client/{EodashMap-BpwL82-w.js → EodashMap-CMvbfI6-.js} +116 -39
  33. package/dist/client/EodashMapBtns-BeknGDtc.js +107 -0
  34. package/dist/client/EodashProcess-BwKAa9Ee.js +1476 -0
  35. package/dist/client/EodashStacInfo-_BfonNUG.js +85 -0
  36. package/dist/client/EodashTools-PD3XPYuR.js +103 -0
  37. package/dist/client/{ExportState-ByVuIAQb.js → ExportState-DOrT7M15.js} +5 -5
  38. package/dist/client/{Footer-D691KLtK.js → Footer-CCigxYBo.js} +1 -1
  39. package/dist/client/{Header-B8UBQstf.js → Header-C2cdx4gb.js} +3 -3
  40. package/dist/client/{MobileLayout-6bHjYguI.js → MobileLayout-BdiFjHg7.js} +28 -31
  41. package/dist/client/{PopUp-CdFcnKMY.js → PopUp--_xn1Cms.js} +37 -9
  42. package/dist/client/{VImg-fKGJ7xyb.js → VImg-9xu2l99m.js} +2 -2
  43. package/dist/client/{VMain-Hf5R6yWY.js → VMain-BUs3kDTd.js} +1 -1
  44. package/dist/client/{VOverlay-ClFjEtlD.js → VOverlay-D89omJis.js} +3 -3
  45. package/dist/client/VTooltip-CDu3bErh.js +86 -0
  46. package/dist/client/{WidgetsContainer-XXYJfaPR.js → WidgetsContainer-aFG9yFT6.js} +1 -1
  47. package/dist/client/{asWebComponent-BsbKnhGV.js → asWebComponent-BRGyP_j5.js} +1495 -1142
  48. package/dist/client/{style.css → eo-dash.css} +2 -2
  49. package/dist/client/eo-dash.js +1 -1
  50. package/dist/client/{forwardRefs-I2EA3z3_.js → forwardRefs-CYrR6bMw.js} +1 -1
  51. package/dist/client/{index-B3dnMr8a.js → index-BZwk0V42.js} +1 -1
  52. package/dist/client/{transition-DJ9gfiuP.js → transition-DG9nRSW4.js} +1 -1
  53. package/dist/node/cli.js +3 -3
  54. package/package.json +56 -34
  55. package/widgets/EodashDatePicker.vue +68 -54
  56. package/widgets/EodashItemFilter.vue +60 -105
  57. package/widgets/EodashLayerControl.vue +19 -8
  58. package/widgets/EodashLayoutSwitcher.vue +36 -0
  59. package/widgets/EodashMap.vue +27 -28
  60. package/widgets/EodashMapBtns.vue +41 -4
  61. package/widgets/EodashProcess.vue +143 -0
  62. package/widgets/EodashStacInfo.vue +82 -0
  63. package/widgets/EodashTools.vue +83 -0
  64. package/widgets/ExportState.vue +3 -3
  65. package/widgets/PopUp.vue +24 -2
  66. package/core/client/asWebComponent.d.ts +0 -23
  67. package/core/client/types.d.ts +0 -279
  68. package/dist/client/EodashItemFilter-VGQasaBJ.js +0 -194
  69. package/dist/client/EodashMapBtns-GNNBdBW9.js +0 -66
  70. package/dist/types/core/client/App.vue.d.ts +0 -7
  71. package/dist/types/core/client/asWebComponent.d.ts +0 -9
  72. package/dist/types/core/client/components/DashboardLayout.vue.d.ts +0 -2
  73. package/dist/types/core/client/components/DynamicWebComponent.vue.d.ts +0 -18
  74. package/dist/types/core/client/components/ErrorAlert.vue.d.ts +0 -2
  75. package/dist/types/core/client/components/Footer.vue.d.ts +0 -2
  76. package/dist/types/core/client/components/Header.vue.d.ts +0 -2
  77. package/dist/types/core/client/components/IframeWrapper.vue.d.ts +0 -7
  78. package/dist/types/core/client/components/Loading.vue.d.ts +0 -2
  79. package/dist/types/core/client/components/MobileLayout.vue.d.ts +0 -2
  80. package/dist/types/core/client/composables/DefineEodash.d.ts +0 -2
  81. package/dist/types/core/client/composables/DefineWidgets.d.ts +0 -14
  82. package/dist/types/core/client/composables/EodashMap.d.ts +0 -5
  83. package/dist/types/core/client/composables/index.d.ts +0 -29
  84. package/dist/types/core/client/eodash.d.ts +0 -8
  85. package/dist/types/core/client/main.d.ts +0 -2
  86. package/dist/types/core/client/plugins/axios.d.ts +0 -2
  87. package/dist/types/core/client/plugins/index.d.ts +0 -3
  88. package/dist/types/core/client/plugins/vuetify.d.ts +0 -82
  89. package/dist/types/core/client/render.d.ts +0 -1
  90. package/dist/types/core/client/store/Actions.d.ts +0 -11
  91. package/dist/types/core/client/store/States.d.ts +0 -21
  92. package/dist/types/core/client/store/index.d.ts +0 -2
  93. package/dist/types/core/client/store/stac.d.ts +0 -25
  94. package/dist/types/core/client/utils/createLayers.d.ts +0 -45
  95. package/dist/types/core/client/utils/eodashSTAC.d.ts +0 -82
  96. package/dist/types/core/client/utils/helpers.d.ts +0 -84
  97. package/dist/types/core/client/utils/index.d.ts +0 -2
  98. package/dist/types/core/client/utils/keys.d.ts +0 -6
  99. package/dist/types/core/client/utils/states.d.ts +0 -14
  100. package/dist/types/core/client/views/Dashboard.vue.d.ts +0 -9
  101. package/dist/types/widgets/EodashDatePicker.vue.d.ts +0 -7
  102. package/dist/types/widgets/EodashItemFilter.vue.d.ts +0 -33
  103. package/dist/types/widgets/EodashLayerControl.vue.d.ts +0 -7
  104. package/dist/types/widgets/EodashMap.vue.d.ts +0 -7
  105. package/dist/types/widgets/EodashMapBtns.vue.d.ts +0 -9
  106. package/dist/types/widgets/ExportState.vue.d.ts +0 -7
  107. package/dist/types/widgets/PopUp.vue.d.ts +0 -14
  108. package/dist/types/widgets/WidgetsContainer.vue.d.ts +0 -7
@@ -1,58 +1,13 @@
1
- import { ref, reactive, watch, onScopeDispose, effectScope, Fragment, computed, watchEffect, toRefs, capitalize, shallowRef, warn, getCurrentInstance as getCurrentInstance$1, unref, provide, inject as inject$1, defineComponent as defineComponent$1, camelize, h, toRaw, createVNode, mergeProps, onBeforeUnmount, readonly, onMounted, onDeactivated, onActivated, nextTick, shallowReactive, defineAsyncComponent, isRef, toRef, onUpdated, Text, Transition, resolveDynamicComponent, withDirectives, useModel, openBlock, createBlock, withCtx, createTextVNode, toDisplayString, createElementVNode, createCommentVNode, onErrorCaptured, Suspense, normalizeProps, createElementBlock, withAsyncContext, normalizeStyle, defineCustomElement } from 'vue';
1
+ import { ref, reactive, watch, onScopeDispose, effectScope, Fragment, computed, watchEffect, toRefs, capitalize, shallowRef, warn, getCurrentInstance as getCurrentInstance$1, unref, provide, inject as inject$1, defineComponent as defineComponent$1, camelize, h, toRaw, createVNode, mergeProps, onBeforeUnmount, readonly, onMounted, onDeactivated, onActivated, nextTick, shallowReactive, onUnmounted, isRef, toRef, onUpdated, Text, Transition, resolveDynamicComponent, withDirectives, useModel, openBlock, createBlock, withCtx, createTextVNode, toDisplayString, createElementVNode, createCommentVNode, defineAsyncComponent, onErrorCaptured, Suspense, normalizeProps, createElementBlock, withAsyncContext, normalizeStyle, defineCustomElement } from 'vue';
2
+ import { defineStore, storeToRefs, createPinia } from 'pinia';
3
+ import { setupCache } from 'axios-cache-interceptor';
2
4
  import log from 'loglevel';
5
+ import { mdiViewDashboardVariant, mdiViewDashboard, mdiAlertCircle, mdiClose, mdiChevronRight, mdiChevronLeft, mdiMenuDown, mdiPlus } from '@mdi/js';
6
+ import { useEventBus, createSharedComposable } from '@vueuse/core';
3
7
  import { toAbsolute } from 'stac-js/src/http.js';
4
- import { setupCache } from 'axios-cache-interceptor';
5
- import { defineStore, storeToRefs, createPinia } from 'pinia';
6
8
  import { Collection, Item } from 'stac-js';
7
- import { mdiAlertCircle, mdiClose, mdiChevronRight, mdiChevronLeft, mdiMenuDown } from '@mdi/js';
8
9
  import VCalendar from 'v-calendar';
9
10
 
10
- /** setting default log level globally to warning */
11
- log.setLevel(log.levels.WARN, true);
12
-
13
- /** Currently selected STAC endpoint */
14
- const currentUrl = ref("");
15
-
16
- /** Currently selected compare STAC endpoint */
17
- const currentCompareUrl = ref("");
18
-
19
- /** Currently selected datetime */
20
- const datetime = ref(new Date().toISOString());
21
-
22
- /** Currently selected indicator */
23
- const indicator = ref("");
24
-
25
- /**
26
- * Current map position
27
- *
28
- * @type {import("vue").Ref<(number | undefined)[]>}
29
- */
30
- const mapPosition = ref([]);
31
-
32
- const registeredProjections = ["EPSG:4326", "EPSG:3857"];
33
-
34
- /** available projection to be rendered by `EodashMap` */
35
- const availableMapProjection = ref("EPSG:3857");
36
-
37
- /** @type {import("vue").Ref<HTMLElement & Record<string,any> | null>} */
38
- const mapEl = ref(null);
39
-
40
- /** @type {import("vue").Ref<HTMLElement & Record<string,any> | null>} */
41
- const mapCompareEl = ref(null);
42
-
43
- const __vite_glob_0_1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
44
- __proto__: null,
45
- availableMapProjection,
46
- currentCompareUrl,
47
- currentUrl,
48
- datetime,
49
- indicator,
50
- mapCompareEl,
51
- mapEl,
52
- mapPosition,
53
- registeredProjections
54
- }, Symbol.toStringTag, { value: 'Module' }));
55
-
56
11
  function bind(fn, thisArg) {
57
12
  return function wrap() {
58
13
  return fn.apply(thisArg, arguments);
@@ -1204,7 +1159,7 @@ function encode(val) {
1204
1159
  *
1205
1160
  * @param {string} url The base of the url (e.g., http://www.google.com)
1206
1161
  * @param {object} [params] The params to be appended
1207
- * @param {?object} options
1162
+ * @param {?(object|Function)} options
1208
1163
  *
1209
1164
  * @returns {string} The formatted url
1210
1165
  */
@@ -1216,6 +1171,12 @@ function buildURL(url, params, options) {
1216
1171
 
1217
1172
  const _encode = options && options.encode || encode;
1218
1173
 
1174
+ if (utils$1.isFunction(options)) {
1175
+ options = {
1176
+ serialize: options
1177
+ };
1178
+ }
1179
+
1219
1180
  const serializeFn = options && options.serialize;
1220
1181
 
1221
1182
  let serializedParams;
@@ -2198,68 +2159,18 @@ const progressEventDecorator = (total, throttled) => {
2198
2159
 
2199
2160
  const asyncDecorator = (fn) => (...args) => utils$1.asap(() => fn(...args));
2200
2161
 
2201
- const isURLSameOrigin = platform.hasStandardBrowserEnv ?
2202
-
2203
- // Standard browser envs have full support of the APIs needed to test
2204
- // whether the request URL is of the same origin as current location.
2205
- (function standardBrowserEnv() {
2206
- const msie = platform.navigator && /(msie|trident)/i.test(platform.navigator.userAgent);
2207
- const urlParsingNode = document.createElement('a');
2208
- let originURL;
2209
-
2210
- /**
2211
- * Parse a URL to discover its components
2212
- *
2213
- * @param {String} url The URL to be parsed
2214
- * @returns {Object}
2215
- */
2216
- function resolveURL(url) {
2217
- let href = url;
2218
-
2219
- if (msie) {
2220
- // IE needs attribute set twice to normalize properties
2221
- urlParsingNode.setAttribute('href', href);
2222
- href = urlParsingNode.href;
2223
- }
2224
-
2225
- urlParsingNode.setAttribute('href', href);
2226
-
2227
- // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
2228
- return {
2229
- href: urlParsingNode.href,
2230
- protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
2231
- host: urlParsingNode.host,
2232
- search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
2233
- hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
2234
- hostname: urlParsingNode.hostname,
2235
- port: urlParsingNode.port,
2236
- pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
2237
- urlParsingNode.pathname :
2238
- '/' + urlParsingNode.pathname
2239
- };
2240
- }
2241
-
2242
- originURL = resolveURL(window.location.href);
2243
-
2244
- /**
2245
- * Determine if a URL shares the same origin as the current location
2246
- *
2247
- * @param {String} requestURL The URL to test
2248
- * @returns {boolean} True if URL shares the same origin, otherwise false
2249
- */
2250
- return function isURLSameOrigin(requestURL) {
2251
- const parsed = (utils$1.isString(requestURL)) ? resolveURL(requestURL) : requestURL;
2252
- return (parsed.protocol === originURL.protocol &&
2253
- parsed.host === originURL.host);
2254
- };
2255
- })() :
2162
+ const isURLSameOrigin = platform.hasStandardBrowserEnv ? ((origin, isMSIE) => (url) => {
2163
+ url = new URL(url, platform.origin);
2256
2164
 
2257
- // Non standard browser envs (web workers, react-native) lack needed support.
2258
- (function nonStandardBrowserEnv() {
2259
- return function isURLSameOrigin() {
2260
- return true;
2261
- };
2262
- })();
2165
+ return (
2166
+ origin.protocol === url.protocol &&
2167
+ origin.host === url.host &&
2168
+ (isMSIE || origin.port === url.port)
2169
+ );
2170
+ })(
2171
+ new URL(platform.origin),
2172
+ platform.navigator && /(msie|trident)/i.test(platform.navigator.userAgent)
2173
+ ) : () => true;
2263
2174
 
2264
2175
  const cookies = platform.hasStandardBrowserEnv ?
2265
2176
 
@@ -2361,7 +2272,7 @@ function mergeConfig(config1, config2) {
2361
2272
  config2 = config2 || {};
2362
2273
  const config = {};
2363
2274
 
2364
- function getMergedValue(target, source, caseless) {
2275
+ function getMergedValue(target, source, prop, caseless) {
2365
2276
  if (utils$1.isPlainObject(target) && utils$1.isPlainObject(source)) {
2366
2277
  return utils$1.merge.call({caseless}, target, source);
2367
2278
  } else if (utils$1.isPlainObject(source)) {
@@ -2373,11 +2284,11 @@ function mergeConfig(config1, config2) {
2373
2284
  }
2374
2285
 
2375
2286
  // eslint-disable-next-line consistent-return
2376
- function mergeDeepProperties(a, b, caseless) {
2287
+ function mergeDeepProperties(a, b, prop , caseless) {
2377
2288
  if (!utils$1.isUndefined(b)) {
2378
- return getMergedValue(a, b, caseless);
2289
+ return getMergedValue(a, b, prop , caseless);
2379
2290
  } else if (!utils$1.isUndefined(a)) {
2380
- return getMergedValue(undefined, a, caseless);
2291
+ return getMergedValue(undefined, a, prop , caseless);
2381
2292
  }
2382
2293
  }
2383
2294
 
@@ -2435,7 +2346,7 @@ function mergeConfig(config1, config2) {
2435
2346
  socketPath: defaultToConfig2,
2436
2347
  responseEncoding: defaultToConfig2,
2437
2348
  validateStatus: mergeDirectKeys,
2438
- headers: (a, b) => mergeDeepProperties(headersToObject(a), headersToObject(b), true)
2349
+ headers: (a, b , prop) => mergeDeepProperties(headersToObject(a), headersToObject(b),prop, true)
2439
2350
  };
2440
2351
 
2441
2352
  utils$1.forEach(Object.keys(Object.assign({}, config1, config2)), function computeConfigValue(prop) {
@@ -3177,7 +3088,7 @@ function dispatchRequest(config) {
3177
3088
  });
3178
3089
  }
3179
3090
 
3180
- const VERSION = "1.7.7";
3091
+ const VERSION = "1.7.9";
3181
3092
 
3182
3093
  const validators$1 = {};
3183
3094
 
@@ -3228,6 +3139,14 @@ validators$1.transitional = function transitional(validator, version, message) {
3228
3139
  };
3229
3140
  };
3230
3141
 
3142
+ validators$1.spelling = function spelling(correctSpelling) {
3143
+ return (value, opt) => {
3144
+ // eslint-disable-next-line no-console
3145
+ console.warn(`${opt} is likely a misspelling of ${correctSpelling}`);
3146
+ return true;
3147
+ }
3148
+ };
3149
+
3231
3150
  /**
3232
3151
  * Assert object's properties type
3233
3152
  *
@@ -3297,9 +3216,9 @@ class Axios {
3297
3216
  return await this._request(configOrUrl, config);
3298
3217
  } catch (err) {
3299
3218
  if (err instanceof Error) {
3300
- let dummy;
3219
+ let dummy = {};
3301
3220
 
3302
- Error.captureStackTrace ? Error.captureStackTrace(dummy = {}) : (dummy = new Error());
3221
+ Error.captureStackTrace ? Error.captureStackTrace(dummy) : (dummy = new Error());
3303
3222
 
3304
3223
  // slice off the Error: ... line
3305
3224
  const stack = dummy.stack ? dummy.stack.replace(/^.+\n/, '') : '';
@@ -3354,6 +3273,11 @@ class Axios {
3354
3273
  }
3355
3274
  }
3356
3275
 
3276
+ validator.assertOptions(config, {
3277
+ baseUrl: validators.spelling('baseURL'),
3278
+ withXsrfToken: validators.spelling('withXSRFToken')
3279
+ }, true);
3280
+
3357
3281
  // Set config.method
3358
3282
  config.method = (config.method || this.defaults.method || 'get').toLowerCase();
3359
3283
 
@@ -3791,445 +3715,54 @@ const instance = axios$1.create();
3791
3715
 
3792
3716
  const axios = setupCache(instance, { cacheTakeover: false });
3793
3717
 
3794
- /** @param {import("stac-ts").StacLink[]} [links] */
3795
- function generateFeatures(links) {
3796
- /**
3797
- * @type {{
3798
- * type: string;
3799
- * geometry: {
3800
- * type: string;
3801
- * coordinates: [number, number];
3802
- * };
3803
- * }[]}
3804
- */
3805
- const features = [];
3806
- links?.forEach((element) => {
3807
- if (element.rel === "item" && "latlng" in element) {
3808
- const [lat, lon] = /** @type {string} */ (element.latlng)
3809
- .split(",")
3810
- .map((it) => Number(it));
3811
- features.push({
3812
- type: "Feature",
3813
- geometry: {
3814
- type: "Point",
3815
- coordinates: [lon, lat],
3816
- },
3817
- });
3818
- }
3819
- });
3820
- const geojsonObject = {
3821
- type: "FeatureCollection",
3822
- crs: {
3823
- type: "name",
3824
- properties: {
3825
- name: "EPSG:4326",
3826
- },
3827
- },
3828
- features,
3829
- };
3830
- return geojsonObject;
3831
- }
3718
+ /** setting default log level globally to warning */
3719
+ log.setLevel(log.levels.WARN, true);
3832
3720
 
3833
- /** @param { import("ol/layer/WebGLTile").Style & { jsonform?: Record<string,any> } & { legend?: Record<string,any> } } [style] */
3834
- function extractLayerConfig(style) {
3835
- /** @type {Record<string,unknown> | undefined} */
3836
- let layerConfig = undefined;
3837
- if (style?.jsonform) {
3838
- layerConfig = { schema: style.jsonform, type: "style" };
3839
- style = { ...style };
3840
- delete style.jsonform;
3841
- if (style?.legend) {
3842
- layerConfig.legend = style.legend;
3843
- delete style.legend;
3844
- }
3845
- }
3846
- log.debug(
3847
- "extracted layerConfig",
3848
- JSON.parse(JSON.stringify({ layerConfig, style })),
3849
- );
3721
+ /** Currently selected STAC endpoint */
3722
+ const currentUrl = ref("");
3850
3723
 
3851
- return { layerConfig, style };
3852
- }
3724
+ /** Currently selected compare STAC endpoint */
3725
+ const currentCompareUrl = ref("");
3853
3726
 
3854
- /**
3855
- * checks if there's a projection on the Collection and
3856
- * updates {@link availableMapProjection}
3857
- * @param {import('stac-ts').StacCollection} [STAcCollection]
3858
- */
3859
- const setMapProjFromCol = async (STAcCollection) => {
3860
- // if a projection exists on the collection level
3861
- log.debug("Checking for available map projection in indicator");
3862
- const projection =
3863
- /** @type {number | string | {name: string, def: string} | undefined} */
3864
- (
3865
- STAcCollection?.["eodash:mapProjection"] ||
3866
- STAcCollection?.["proj:epsg"] ||
3867
- STAcCollection?.["eodash:proj4_def"]
3868
- );
3869
- if (projection) {
3870
- log.debug("Projection found", projection);
3871
- await registerProjection(projection);
3872
- const projectionCode = getProjectionCode(projection);
3873
- if (availableMapProjection.value !== projectionCode) {
3874
- log.debug(
3875
- "Changing map projection",
3876
- availableMapProjection.value,
3877
- projectionCode,
3878
- );
3879
- await changeMapProjection(projection);
3880
- }
3881
- // set it for `EodashMapBtns`
3882
- availableMapProjection.value = /** @type {string} */ (projectionCode);
3883
- } else {
3884
- // reset to default projection
3885
- log.debug("Resetting projection to default EPSG:3857");
3886
- await changeMapProjection((availableMapProjection.value = ""));
3887
- }
3888
- };
3727
+ /** Currently selected datetime */
3728
+ const datetime = ref(new Date().toISOString());
3729
+
3730
+ /** Currently selected indicator */
3731
+ const indicator = ref("");
3889
3732
 
3890
3733
  /**
3891
- * Function to extract collection urls from an indicator
3892
- * @param {import("stac-ts").StacCatalog
3893
- * | import("stac-ts").StacCollection
3894
- * | import("stac-ts").StacItem
3895
- * | null
3896
- * } stacObject
3897
- * @param {string} basepath
3898
- * @returns {string[]}
3734
+ * Current map position
3735
+ *
3736
+ * @type {import("vue").Ref<(number | undefined)[]>}
3899
3737
  */
3900
- function extractCollectionUrls(stacObject, basepath) {
3901
- const collectionUrls = [];
3902
- // Support for two structure types, flat and indicator, simplified here:
3903
- // Flat assumes Catalog-Collection-Item
3904
- // Indicator assumes Catalog-Collection-Collection-Item
3905
- // TODO: this is not the most stable test approach,
3906
- // we should discuss potential other approaches
3907
- if (stacObject?.links && stacObject?.links[1]?.rel === "item") {
3908
- collectionUrls.push(basepath);
3909
- } else if (stacObject?.links[1]?.rel === "child") {
3910
- // TODO: Iterate through all children to create collections
3911
- stacObject.links.forEach((link) => {
3912
- if (link.rel === "child") {
3913
- collectionUrls.push(toAbsolute(link.href, basepath));
3914
- }
3915
- });
3916
- }
3917
- return collectionUrls;
3918
- }
3738
+ const mapPosition = ref([]);
3919
3739
 
3920
- /**
3921
- * Assign extracted roles to layer properties
3922
- * @param {Record<string,any>} properties
3923
- * @param {import("stac-ts").StacLink | import("stac-ts").StacAsset} linkOrAsset
3924
- * */
3925
- const extractRoles = (properties, linkOrAsset) => {
3926
- const roles = /** @type {string[]} */ (linkOrAsset.roles);
3927
- roles?.forEach((role) => {
3928
- if (role === "visible") {
3929
- properties.visible = true;
3930
- }
3931
- if (role === "overlay" || role === "baselayer") {
3932
- properties.group = role;
3933
- //remove all the properties and replace the random ID with baselayer
3934
- // provided ID
3935
- // const [_colId, _itemId, _isAsset, _random, proj] =
3936
- // properties.id.split(";:;");
3937
- // properties.id = ["", "", "", "", linkOrAsset.id, proj].join(";:;");
3938
- }
3740
+ const registeredProjections = ["EPSG:4326", "EPSG:3857"];
3939
3741
 
3940
- return properties;
3941
- });
3942
- };
3742
+ /** available projection to be rendered by `EodashMap` */
3743
+ const availableMapProjection = ref("EPSG:3857");
3943
3744
 
3944
- /**
3945
- * @param {import("stac-ts").StacItem} item
3946
- * @param {string} itemUrl
3947
- **/
3948
- const fetchStyle = async (item, itemUrl) => {
3949
- const styleLink = item.links.find((link) => link.rel.includes("style"));
3950
- if (styleLink) {
3951
- let url = "";
3952
- if (styleLink.href.startsWith("http")) {
3953
- url = styleLink.href;
3954
- } else {
3955
- url = toAbsolute(styleLink.href, itemUrl);
3956
- }
3745
+ /** @type {import("vue").Ref<HTMLElement & Record<string,any> | null>} */
3746
+ const mapEl = ref(null);
3957
3747
 
3958
- /** @type {import("ol/layer/WebGLTile").Style & {jsonform?:Record<string,any>}} */
3959
- const styleJson = await axios.get(url).then((resp) => resp.data);
3748
+ /** @type {import("vue").Ref<HTMLElement & Record<string,any> | null>} */
3749
+ const mapCompareEl = ref(null);
3960
3750
 
3961
- log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
3962
- return { ...styleJson };
3963
- }
3964
- };
3751
+ const activeTemplate = ref("");
3965
3752
 
3966
- /**
3967
- * Return projection code which is to be registered in `eox-map`
3968
- * @param {string|number|{name: string, def: string}} [projection]
3969
- * @returns {string}
3970
- */
3971
- const getProjectionCode = (projection) => {
3972
- let code = projection;
3973
- switch (typeof projection) {
3974
- case "number":
3975
- code = `EPSG:${projection}`;
3976
- break;
3977
- case "string":
3978
- code = projection;
3979
- break;
3980
- case "object":
3981
- code = projection?.name;
3982
- }
3983
- return /** @type {string} */ (code);
3984
- };
3985
-
3986
- /**
3987
- * @param {import("stac-ts").StacLink[]} [links]
3988
- * @param {string|null} [currentStep]
3989
- **/
3990
- const extractLayerDatetime = (links, currentStep) => {
3991
- if (!currentStep || !links?.length) {
3992
- return undefined;
3993
- }
3994
-
3995
- // check if links has a datetime value
3996
- const hasDatetime = links.some((l) => typeof l.datetime === "string");
3997
- if (!hasDatetime) {
3998
- return undefined;
3999
- }
4000
-
4001
- /** @type {string[]} */
4002
- const controlValues = [];
4003
- try {
4004
- currentStep = new Date(currentStep).toISOString();
4005
-
4006
- links.reduce((vals, link) => {
4007
- if (link.datetime && link.rel === "item") {
4008
- vals.push(
4009
- new Date(/** @type {string} */ (link.datetime)).toISOString(),
4010
- );
4011
- }
4012
- return vals;
4013
- }, controlValues);
4014
- } catch (e) {
4015
- console.warn("[eodash] not supported datetime format was provided", e);
4016
- return undefined;
4017
- }
4018
- // not enough controlValues
4019
- if (controlValues.length <= 1) {
4020
- return undefined;
4021
- }
4022
-
4023
- // item datetime is not included in the item links datetime
4024
- if (!controlValues.includes(currentStep)) {
4025
- return undefined;
4026
- }
4027
-
4028
- return {
4029
- controlValues,
4030
- currentStep,
4031
- slider: true,
4032
- disablePlay: true,
4033
- };
4034
- };
4035
-
4036
- /**
4037
- * Find layer by ID
4038
- * @param {string} layer
4039
- * @param {Record<string, any>[]} layers
4040
- * @returns {Record<string,any> | undefined}
4041
- **/
4042
- const findLayer = (layers, layer) => {
4043
- for (const lyr of layers) {
4044
- if (lyr.type === "Group") {
4045
- const found = findLayer(lyr.layers, layer);
4046
- if (!found) {
4047
- continue;
4048
- }
4049
- return found;
4050
- }
4051
- if (lyr.properties.id === layer) {
4052
- return lyr;
4053
- }
4054
- }
4055
- };
4056
-
4057
- /**
4058
- * @param {Record<string,any>[]} currentLayers
4059
- * @param {Record<string,any>} oldLayer
4060
- * @param {Record<string,any>[]} newLayers
4061
- * @returns {Record<string,any>[] | undefined}
4062
- */
4063
- const replaceLayer = (currentLayers, oldLayer, newLayers) => {
4064
- const oldLayerIdx = currentLayers.findIndex(
4065
- (l) => l.properties.id === oldLayer.properties.id,
4066
- );
4067
- if (oldLayerIdx !== -1) {
4068
- currentLayers.splice(oldLayerIdx, 1, ...newLayers);
4069
- return currentLayers;
4070
- }
4071
-
4072
- for (const l of currentLayers) {
4073
- if (l.type === "Group") {
4074
- const updatedGroupLyrs = replaceLayer(l.layers, oldLayer, newLayers);
4075
- if (updatedGroupLyrs?.length) {
4076
- l.layers = updatedGroupLyrs;
4077
- return currentLayers;
4078
- }
4079
- }
4080
- }
4081
- };
4082
- /**
4083
- * @param {import('./eodashSTAC.js').EodashCollection[]} indicators
4084
- * @param {import('ol/layer').Layer} layer
4085
- */
4086
- const getColFromLayer = async (indicators, layer) => {
4087
- // init cols
4088
- const collections = await Promise.all(
4089
- indicators.map((ind) => ind.fetchCollection()),
4090
- );
4091
- const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
4092
-
4093
- const chosen = collections.find((col) => {
4094
- const isInd =
4095
- col.id === collectionId &&
4096
- col.links?.some(
4097
- (link) => link.rel === "item" && link.href.includes(itemId),
4098
- );
4099
- return isInd ?? false;
4100
- });
4101
- return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
4102
- };
4103
-
4104
- /**
4105
- * generates layer specific ID, related functions are: {@link assignProjID} & {@link extractRoles}
4106
- * @param {string} collectionId
4107
- * @param {string} itemId
4108
- * @param {import('stac-ts').StacLink} link
4109
- * @param {string} projectionCode
4110
- *
4111
- */
4112
- const createLayerID = (collectionId, itemId, link, projectionCode) => {
4113
- const linkId = link.id || link.title || link.href;
4114
- let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${linkId ?? ""};:;${projectionCode ?? ""}`;
4115
- // If we are looking at base layers and overlays we remove the collection and item part
4116
- // as we want to make sure tiles are not reloaded when switching layers
4117
- if (
4118
- link.roles &&
4119
- // @ts-expect-error it seems roles it not defined for links yet
4120
- link.roles.find((r) => ["baselayer", "overlay"].includes(r))
4121
- ) {
4122
- lId = `${linkId ?? ""};:;${projectionCode ?? ""}`;
4123
- }
4124
- log.debug("Generated Layer ID", lId);
4125
- return lId;
4126
- };
4127
-
4128
- /**
4129
- * generates layer specific ID, related functions are: {@link assignProjID} & {@link extractRoles}
4130
- * @param {string} collectionId
4131
- * @param {string} itemId
4132
- * @param {number} index
4133
- *
4134
- */
4135
- const createAssetID = (collectionId, itemId, index) => {
4136
- let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${index ?? ""}`;
4137
- log.debug("Generated Asset ID", lId);
4138
- return lId;
4139
- };
4140
-
4141
- /**
4142
- * creates a structured clone from the layers and
4143
- * removes all properties from the clone
4144
- * except the ID and title
4145
- *
4146
- * @param {Record<string,any>[]} layers
4147
- */
4148
- const removeUnneededProperties = (layers) => {
4149
- const cloned = structuredClone(layers);
4150
- cloned.forEach((layer) => {
4151
- const id = layer.properties.id;
4152
- const title = layer.properties.title;
4153
- layer.properties = { id, title };
4154
- if (layer["interactions"]) {
4155
- delete layer["interactions"];
4156
- }
4157
- if (layer.type === "Group") {
4158
- layer.layers = removeUnneededProperties(layer.layers);
4159
- }
4160
- });
4161
- return cloned;
4162
- };
4163
-
4164
- /**
4165
- * Returns the current layers of {@link mapEl}
4166
- * @returns {Record<string,any>[]}
4167
- */
4168
- const getLayers = () => mapEl.value?.layers.toReversed();
4169
-
4170
- /**
4171
- * Returns the current layers of {@link mapCompareEl}
4172
- * @returns {Record<string,any>[]}
4173
- */
4174
- const getCompareLayers = () => mapCompareEl.value?.layers.toReversed();
4175
-
4176
- /**
4177
- * Register EPSG projection in `eox-map`
4178
- * @param {string|number|{name: string, def: string, extent?:number[]}} [projection]*/
4179
- const registerProjection = async (projection) => {
4180
- let code = getProjectionCode(projection);
4181
- if (!code || registeredProjections.includes(code)) {
4182
- return;
4183
- }
4184
- log.debug("Unregistered projection found, registering it", code);
4185
- registeredProjections.push(code);
4186
- if (typeof projection === "object") {
4187
- // registering whole projection definition
4188
- await mapEl.value?.registerProjection(
4189
- code,
4190
- projection.def,
4191
- projection.extent,
4192
- );
4193
- // also registering for comparison map
4194
- await mapCompareEl.value?.registerProjection(
4195
- code,
4196
- projection.def,
4197
- projection.extent,
4198
- );
4199
- } else {
4200
- await mapEl.value?.registerProjectionFromCode(code);
4201
- // also registering for comparison map
4202
- await mapCompareEl.value?.registerProjectionFromCode(code);
4203
- }
4204
- };
4205
- /**
4206
- * Change `eox-map` projection from an `EPSG` projection
4207
- * @param {string|number|{name: string, def: string}} [projection]*/
4208
- const changeMapProjection = async (projection) => {
4209
- let code = getProjectionCode(projection);
4210
-
4211
- if (!code) {
4212
- mapEl.value?.setAttribute("projection", "EPSG:3857");
4213
- mapCompareEl.value?.setAttribute("projection", "EPSG:3857");
4214
- return;
4215
- }
4216
-
4217
- if (!registeredProjections.includes(code)) {
4218
- await registerProjection(projection);
4219
- }
4220
-
4221
- code = mapEl.value?.getAttribute("projection") === code ? "EPSG:3857" : code;
4222
- mapEl.value?.setAttribute("projection", code);
4223
- mapCompareEl.value?.setAttribute("projection", code);
4224
- };
4225
-
4226
- const __vite_glob_0_0 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
4227
- __proto__: null,
4228
- changeMapProjection,
4229
- getCompareLayers,
4230
- getLayers,
4231
- registerProjection
4232
- }, Symbol.toStringTag, { value: 'Module' }));
3753
+ const states = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3754
+ __proto__: null,
3755
+ activeTemplate,
3756
+ availableMapProjection,
3757
+ currentCompareUrl,
3758
+ currentUrl,
3759
+ datetime,
3760
+ indicator,
3761
+ mapCompareEl,
3762
+ mapEl,
3763
+ mapPosition,
3764
+ registeredProjections
3765
+ }, Symbol.toStringTag, { value: 'Module' }));
4233
3766
 
4234
3767
  /**
4235
3768
  * Reactive Edoash Instance Object. provided globally in the app, and used as an
@@ -4240,165 +3773,423 @@ const __vite_glob_0_0 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.definePro
4240
3773
  const eodash = reactive({
4241
3774
  id: "demo",
4242
3775
  stacEndpoint:
4243
- "https://eodashcatalog.eox.at/test-style/trilateral/catalog.json",
3776
+ // "https://eurodatacube.github.io/eodash-catalog/RACE/catalog.json",
3777
+ // "https://gtif-cerulean.github.io/catalog/cerulean/catalog.json",
3778
+ "https://eodashcatalog.eox.at/samplecatalog/samples/catalog.json",
3779
+ // "https://eodashcatalog.eox.at/test-style/trilateral/catalog.json",
4244
3780
  // "https://gtif-cerulean.github.io/catalog/cerulean/catalog.json",
4245
3781
  brand: {
4246
3782
  noLayout: true,
4247
3783
  name: "Demo",
4248
3784
  theme: {
4249
3785
  colors: {
4250
- primary: "#fff",
4251
- secondary: "#fff",
4252
- surface: "#fff",
3786
+ primary: "#002742",
3787
+ secondary: "#0071C2",
3788
+ surface: "#ffff",
3789
+ },
3790
+ variables: {
3791
+ "surface-opacity": 0.6,
3792
+ "primary-opacity": 0.8,
4253
3793
  },
4254
3794
  },
4255
3795
  footerText: "Demo configuration of eodash client",
4256
3796
  },
4257
- template: {
4258
- loading: {
4259
- id: Symbol(),
4260
- type: "web-component",
4261
- widget: {
4262
- // https://uiball.com/ldrs/
4263
- link: "https://cdn.jsdelivr.net/npm/ldrs/dist/auto/mirage.js",
4264
- tagName: "l-mirage",
4265
- properties: {
4266
- class: "align-self-center justify-self-center",
4267
- size: "120",
4268
- speed: "2.5",
4269
- color: "#004170",
4270
- },
4271
- },
4272
- },
4273
- background: {
4274
- id: Symbol(),
4275
- type: "internal",
4276
- widget: {
4277
- name: "EodashMap",
4278
- properties: {
4279
- enableCompare: true,
4280
- },
4281
- },
4282
- },
4283
- widgets: [
4284
- {
3797
+ templates: {
3798
+ light: {
3799
+ gap: 16,
3800
+ loading: {
4285
3801
  id: Symbol(),
4286
- type: "internal",
4287
- title: "Indicators",
4288
- layout: { x: 0, y: 0, w: 3, h: 6 },
3802
+ type: "web-component",
4289
3803
  widget: {
4290
- name: "EodashItemFilter",
3804
+ // https://uiball.com/ldrs/
3805
+ link: "https://cdn.jsdelivr.net/npm/ldrs/dist/auto/mirage.js",
3806
+ tagName: "l-mirage",
4291
3807
  properties: {
4292
- enableCompare: true,
4293
- aggregateResults: "collection_group",
3808
+ class: "align-self-center justify-self-center",
3809
+ size: "120",
3810
+ speed: "2.5",
3811
+ color: "#004170",
4294
3812
  },
4295
3813
  },
4296
3814
  },
4297
- {
3815
+ background: {
4298
3816
  id: Symbol(),
4299
3817
  type: "internal",
4300
- title: "Layer Control",
4301
- layout: { x: 0, y: 6, w: 3, h: 6 },
4302
3818
  widget: {
4303
- name: "EodashLayerControl",
3819
+ name: "EodashMap",
3820
+ properties: {
3821
+ enableCompare: true,
3822
+ },
4304
3823
  },
4305
3824
  },
4306
- /*
4307
- {
4308
- defineWidget: (selectedCompareStac) => {
4309
- return selectedCompareStac
4310
- ? {
4311
- id: Symbol(),
4312
- title: "Layer Control Comparison",
4313
- layout: { x: 9, y: 6, w: 3, h: 6 },
4314
- type: "internal",
4315
- widget: {
4316
- name: "EodashLayerControl",
4317
- properties: {
4318
- map: "second",
4319
- },
4320
- },
4321
- }
4322
- : null;
3825
+ widgets: [
3826
+ {
3827
+ id: Symbol(),
3828
+ type: "internal",
3829
+ title: "Tools",
3830
+ layout: { x: 0, y: 0, w: 3, h: 1 },
3831
+ widget: {
3832
+ name: "EodashTools",
3833
+ properties: {
3834
+ layoutTarget: "expert",
3835
+ layoutIcon: mdiViewDashboardVariant,
3836
+ itemFilterConfig: {
3837
+ resultType: "cards",
3838
+ filtersTitle: "",
3839
+ style: "--padding: 72px",
3840
+ filterProperties: [],
3841
+ resultsTitle: "Explore more indicators",
3842
+ subTitleProperty: "subtitle",
3843
+ imageProperty: "thumbnail",
3844
+ },
3845
+ },
3846
+ },
4323
3847
  },
4324
- },
4325
- */
4326
- {
4327
- defineWidget: (selectedSTAC) => {
4328
- return selectedSTAC
4329
- ? {
4330
- id: "Information",
4331
- title: "Information",
4332
- layout: { x: 9, y: 0, w: 3, h: 6 },
4333
- type: "web-component",
4334
- widget: {
4335
- link: async () => await import('@eox/stacinfo'),
4336
- properties: {
4337
- for: currentUrl,
4338
- allowHtml: "true",
4339
- styleOverride: `.single-property {columns: 1!important;}
4340
- h1 {margin:0px!important;font-size:16px!important;}
4341
- header h1:after {
4342
- content:' ';
4343
- display:block;
4344
- border:1px solid #d0d0d0;
4345
- }
4346
- h2 {font-size:15px}
4347
- h3 {font-size:14px}
4348
- summary {cursor: pointer;}
4349
- #properties li > .value { font-weight: normal !important;}
4350
- main {padding-bottom: 10px;}
4351
- .footer-container {line-height:1;}
4352
- .footer-container button {margin-top: -10px;}
4353
- .footer-container small {font-size:10px;line-height:1;}`,
4354
- header: '["title"]',
4355
- tags: '["themes"]',
4356
- subheader: "[]",
4357
- properties: '["satellite","sensor","agency","extent"]',
4358
- featured: '["description","providers","assets","links"]',
4359
- footer: '["sci:citation"]',
3848
+ {
3849
+ defineWidget: (selectedSTAC) => {
3850
+ return selectedSTAC
3851
+ ? {
3852
+ id: "layercontrol-light",
3853
+ type: "internal",
3854
+ title: "Layers",
3855
+ layout: { x: 0, y: 1, w: 3, h: 3 },
3856
+ widget: {
3857
+ name: "EodashLayerControl",
3858
+ properties: {
3859
+ slider: false,
3860
+ tools: ["datetime", "info", "legend"],
3861
+ cssVars: {
3862
+ "--list-padding": "0",
3863
+ "--tools-button-visibility": "none",
3864
+ "--layer-input-visibility": "none",
3865
+ "--layer-type-visibility": "none",
3866
+ "--padding": "16px",
3867
+ "--padding-vertical": "16px",
3868
+ "--layer-tools-button-visibility": "none",
3869
+ "--layer-summary-visibility": "none",
3870
+ },
3871
+ },
4360
3872
  },
4361
- tagName: "eox-stacinfo",
4362
- },
4363
- }
4364
- : null;
3873
+ }
3874
+ : null;
3875
+ },
4365
3876
  },
4366
- },
4367
- {
4368
- defineWidget: (selectedSTAC) => {
4369
- return selectedSTAC
4370
- ? {
4371
- id: "Datepicker",
4372
- type: "internal",
4373
- layout: { x: 5, y: 10, w: 1, h: 1 },
4374
- title: "Datepicker",
4375
- widget: {
4376
- name: "EodashDatePicker",
4377
- properties: {
4378
- hintText: `<b>Hint:</b> closest available date is displayed <br />
4379
- on map (see Analysis Layers)`,
3877
+ {
3878
+ defineWidget: (selectedSTAC) => {
3879
+ return selectedSTAC
3880
+ ? {
3881
+ id: "stacinfo-light",
3882
+ type: "internal",
3883
+ title: "Information",
3884
+ layout: { x: 9, y: 0, w: 3, h: 6 },
3885
+ widget: {
3886
+ name: "EodashStacInfo",
3887
+ properties: {
3888
+ tags: [],
3889
+ header: [],
3890
+ footer: [],
3891
+ body: ["description"],
3892
+ styleOverride: "",
3893
+ featured: [],
3894
+ },
4380
3895
  },
4381
- },
4382
- }
4383
- : null;
3896
+ }
3897
+ : null;
3898
+ },
4384
3899
  },
4385
- },
4386
- {
4387
- defineWidget: (selected) => {
4388
- return selected
4389
- ? {
4390
- id: "Buttons",
4391
- layout: { x: 8, y: 0, w: 1, h: 1 },
4392
- title: "Buttons",
4393
- type: "internal",
4394
- widget: {
4395
- name: "EodashMapBtns",
4396
- },
4397
- }
4398
- : null;
3900
+ {
3901
+ defineWidget: (selectedSTAC) => {
3902
+ return selectedSTAC
3903
+ ? {
3904
+ id: "Datepicker",
3905
+ type: "internal",
3906
+ layout: { x: 5, y: 8, w: 2, h: 4 },
3907
+ title: "Date",
3908
+ widget: {
3909
+ name: "EodashDatePicker",
3910
+ properties: {
3911
+ hideArrows: true,
3912
+ hideInputField: true,
3913
+ hintText: `<b>Hint:</b> closest available date is displayed <br />
3914
+ on map (see Analysis Layers)`,
3915
+ },
3916
+ },
3917
+ }
3918
+ : null;
3919
+ },
3920
+ },
3921
+ ],
3922
+ },
3923
+ expert: {
3924
+ loading: {
3925
+ id: Symbol(),
3926
+ type: "web-component",
3927
+ widget: {
3928
+ // https://uiball.com/ldrs/
3929
+ link: "https://cdn.jsdelivr.net/npm/ldrs/dist/auto/mirage.js",
3930
+ tagName: "l-mirage",
3931
+ properties: {
3932
+ class: "align-self-center justify-self-center",
3933
+ size: "120",
3934
+ speed: "2.5",
3935
+ color: "#004170",
3936
+ },
3937
+ },
3938
+ },
3939
+ background: {
3940
+ id: Symbol(),
3941
+ type: "internal",
3942
+ widget: {
3943
+ name: "EodashMap",
3944
+ properties: {
3945
+ enableCompare: true,
3946
+ },
3947
+ },
3948
+ },
3949
+ widgets: [
3950
+ {
3951
+ id: Symbol(),
3952
+ type: "internal",
3953
+ title: "Tools",
3954
+ layout: { x: 0, y: 0, w: 3, h: 1 },
3955
+ widget: {
3956
+ name: "EodashTools",
3957
+ properties: {
3958
+ layoutTarget: "light",
3959
+ layoutIcon: mdiViewDashboard,
3960
+ itemFilterConfig: {
3961
+ resultType: "cards",
3962
+ subTitleProperty: "subtitle",
3963
+ imageProperty: "thumbnail",
3964
+ aggregateResults: "collection_group",
3965
+ style: {
3966
+ "--form-flex-direction": "row",
3967
+ },
3968
+ },
3969
+ },
3970
+ },
3971
+ },
3972
+ {
3973
+ id: Symbol(),
3974
+ type: "internal",
3975
+ title: "Layers",
3976
+ layout: { x: 0, y: 1, w: 3, h: 6 },
3977
+ widget: {
3978
+ name: "EodashLayerControl",
3979
+ },
3980
+ },
3981
+ {
3982
+ defineWidget: (selectedSTAC) => {
3983
+ return selectedSTAC
3984
+ ? {
3985
+ id: "Information",
3986
+ title: "Information",
3987
+ layout: { x: 9, y: 0, w: 3, h: 6 },
3988
+ type: "internal",
3989
+ widget: {
3990
+ name: "EodashStacInfo",
3991
+ properties: {
3992
+ showIndicatorsBtn: false,
3993
+ showLayoutSwitcher: false,
3994
+ },
3995
+ },
3996
+ }
3997
+ : null;
3998
+ },
3999
+ },
4000
+ {
4001
+ defineWidget: (selectedSTAC) => {
4002
+ return selectedSTAC
4003
+ ? {
4004
+ id: "Datepicker",
4005
+ type: "internal",
4006
+ layout: { x: 5, y: 8, w: 2, h: 4 },
4007
+ title: "Date",
4008
+ widget: {
4009
+ name: "EodashDatePicker",
4010
+ properties: {
4011
+ hintText: `<b>Hint:</b> closest available date is displayed <br />
4012
+ on map (see Analysis Layers)`,
4013
+ },
4014
+ },
4015
+ }
4016
+ : null;
4017
+ },
4018
+ },
4019
+ {
4020
+ defineWidget: (selected) => {
4021
+ return selected
4022
+ ? {
4023
+ id: "Buttons",
4024
+ layout: { x: 8, y: 0, w: 1, h: 2 },
4025
+ title: "Buttons",
4026
+ type: "internal",
4027
+ widget: {
4028
+ name: "EodashMapBtns",
4029
+ },
4030
+ }
4031
+ : null;
4032
+ },
4033
+ },
4034
+ {
4035
+ defineWidget: (selectedSTAC) =>
4036
+ selectedSTAC?.links.some((l) => l.rel === "service")
4037
+ ? {
4038
+ id: Symbol(),
4039
+ type: "internal",
4040
+ title: "Processes",
4041
+ layout: { x: 0, y: 7, w: 3, h: 5 },
4042
+ widget: {
4043
+ name: "EodashProcess",
4044
+ },
4045
+ }
4046
+ : null,
4047
+ },
4048
+ ],
4049
+ },
4050
+ compare: {
4051
+ gap: 16,
4052
+ loading: {
4053
+ id: Symbol(),
4054
+ type: "web-component",
4055
+ widget: {
4056
+ // https://uiball.com/ldrs/
4057
+ link: "https://cdn.jsdelivr.net/npm/ldrs/dist/auto/mirage.js",
4058
+ tagName: "l-mirage",
4059
+ properties: {
4060
+ class: "align-self-center justify-self-center",
4061
+ size: "120",
4062
+ speed: "2.5",
4063
+ color: "#004170",
4064
+ },
4065
+ },
4066
+ },
4067
+ background: {
4068
+ id: Symbol(),
4069
+ type: "internal",
4070
+ widget: {
4071
+ name: "EodashMap",
4072
+ properties: {
4073
+ enableCompare: true,
4074
+ },
4399
4075
  },
4400
4076
  },
4401
- ],
4077
+ widgets: [
4078
+ {
4079
+ id: Symbol(),
4080
+ type: "internal",
4081
+ title: "Tools",
4082
+ layout: { x: 0, y: 0, w: 3, h: 1 },
4083
+ widget: {
4084
+ name: "EodashTools",
4085
+ properties: {
4086
+ layoutTarget: "expert",
4087
+ layoutIcon: mdiViewDashboardVariant,
4088
+ itemFilterConfig: {
4089
+ cssVars: {
4090
+ "--form-flex-direction": "row",
4091
+ },
4092
+ },
4093
+ },
4094
+ },
4095
+ },
4096
+ // compare indicators
4097
+ {
4098
+ id: Symbol(),
4099
+ type: "internal",
4100
+ title: "Tools",
4101
+ layout: { x: 9, y: 0, w: 3, h: 1 },
4102
+ widget: {
4103
+ name: "EodashTools",
4104
+ properties: {
4105
+ showLayoutSwitcher: false,
4106
+ indicatorBtnText: "Select an indicator to compare",
4107
+ itemFilterConfig: {
4108
+ enableCompare: true,
4109
+ // resultsTitle:"Select an indicator to compare",
4110
+ filtersTitle: "Select an indicator to compare",
4111
+ // filterProperties: [],
4112
+ cssVars: {
4113
+ "--form-flex-direction": "row",
4114
+ },
4115
+ },
4116
+ },
4117
+ },
4118
+ },
4119
+ {
4120
+ id: Symbol(),
4121
+ type: "internal",
4122
+ title: "Layers",
4123
+ layout: { x: 0, y: 1, w: 3, h: 6 },
4124
+ widget: {
4125
+ name: "EodashLayerControl",
4126
+ },
4127
+ },
4128
+ {
4129
+ id: Symbol(),
4130
+ title: "Comparison Layers",
4131
+ layout: { x: 9, y: 1, w: 3, h: 6 },
4132
+ type: "internal",
4133
+ widget: {
4134
+ name: "EodashLayerControl",
4135
+ properties: {
4136
+ map: "second",
4137
+ },
4138
+ },
4139
+ },
4140
+ {
4141
+ defineWidget: (selectedSTAC) =>
4142
+ selectedSTAC?.links.some((l) => l.rel === "service")
4143
+ ? {
4144
+ id: Symbol(),
4145
+ type: "internal",
4146
+ title: "Processes",
4147
+ layout: { x: 0, y: 7, w: 3, h: 5 },
4148
+ widget: {
4149
+ name: "EodashProcess",
4150
+ },
4151
+ }
4152
+ : null,
4153
+ },
4154
+ {
4155
+ defineWidget: (selected) => {
4156
+ return selected
4157
+ ? {
4158
+ id: "Buttons",
4159
+ layout: { x: 8, y: 0, w: 1, h: 2 },
4160
+ title: "Buttons",
4161
+ type: "internal",
4162
+ widget: {
4163
+ name: "EodashMapBtns",
4164
+ properties: {
4165
+ compareIndicators: false,
4166
+ },
4167
+ },
4168
+ }
4169
+ : null;
4170
+ },
4171
+ },
4172
+ {
4173
+ defineWidget: (selectedSTAC) => {
4174
+ return selectedSTAC
4175
+ ? {
4176
+ id: "Datepicker",
4177
+ type: "internal",
4178
+ layout: { x: 5, y: 8, w: 2, h: 4 },
4179
+ title: "Date",
4180
+ widget: {
4181
+ name: "EodashDatePicker",
4182
+ properties: {
4183
+ hintText: `<b>Hint:</b> closest available date is displayed <br />
4184
+ on map (see Analysis Layers)`,
4185
+ },
4186
+ },
4187
+ }
4188
+ : null;
4189
+ },
4190
+ },
4191
+ ],
4192
+ },
4402
4193
  },
4403
4194
  });
4404
4195
 
@@ -5544,6 +5335,11 @@ const en = {
5544
5335
  counter: '{0} files',
5545
5336
  counterSize: '{0} files ({1} in total)'
5546
5337
  },
5338
+ fileUpload: {
5339
+ title: 'Drag and drop files here',
5340
+ divider: 'or',
5341
+ browse: 'Browse Files'
5342
+ },
5547
5343
  timePicker: {
5548
5344
  am: 'AM',
5549
5345
  pm: 'PM',
@@ -6885,7 +6681,8 @@ const aliases = {
6885
6681
  calendar: 'mdi-calendar',
6886
6682
  treeviewCollapse: 'mdi-menu-down',
6887
6683
  treeviewExpand: 'mdi-menu-right',
6888
- eyeDropper: 'mdi-eyedropper'
6684
+ eyeDropper: 'mdi-eyedropper',
6685
+ upload: 'mdi-cloud-upload'
6889
6686
  };
6890
6687
  const mdi = {
6891
6688
  // Not using mergeProps here, functional components merge props by default (?)
@@ -7719,7 +7516,7 @@ function createVuetify() {
7719
7516
  goTo
7720
7517
  };
7721
7518
  }
7722
- const version = "3.7.2";
7519
+ const version = "3.7.6";
7723
7520
  createVuetify.version = version;
7724
7521
  function inject(key) {
7725
7522
  const vm = this.$;
@@ -7729,8 +7526,38 @@ function inject(key) {
7729
7526
  }
7730
7527
  }
7731
7528
 
7529
+ /**
7530
+ * `eodash` injection key.
7531
+ *
7532
+ * @see {@link "@/plugins/index.js"}
7533
+ */
7534
+ const eodashKey = Symbol("eodash");
7535
+ /** @type {import("@vueuse/core").EventBusKey<"layers:updated"|"time:updated">} */
7536
+ const eoxLayersKey = Symbol("eoxMapLayers");
7537
+
7538
+ /**
7539
+ * Array of eodash STAC Collections extracted from the current selected indicator.
7540
+ * Updated in {@link file://./../store/stac.js `loadSelectedSTAC`} widget
7541
+ *
7542
+ * @type {import('../eodashSTAC/EodashCollection').EodashCollection[]}
7543
+ * @private
7544
+ */
7545
+ const eodashCollections = shallowReactive([]);
7546
+
7547
+ /**
7548
+ * Array of eodash STAC Collections extracted from the current selected COMPARE indicator.
7549
+ * Updated in {@link file://./../store/stac.js ` loadSelectedCompareSTAC`} widget
7550
+ *
7551
+ * @type {import('../eodashSTAC/EodashCollection').EodashCollection[]}
7552
+ * @private
7553
+ */
7554
+ const eodashCompareCollections = shallowReactive([]);
7555
+
7556
+ /** whether the map postion was set in URL params on first load */
7557
+ const posIsSetFromUrl = ref(false);
7558
+
7732
7559
  // functions of this folder can only be consumed inside setup stores,
7733
- // setup functions or vue composition api components
7560
+ // setup functions or vue composition api components https://vuejs.org/guide/reusability/composables
7734
7561
 
7735
7562
 
7736
7563
  /**
@@ -7740,11 +7567,11 @@ function inject(key) {
7740
7567
  * @param {string} [base=eodash.stacEndpoint] - Base URL, default value is the
7741
7568
  * root stac catalog. Default is `eodash.stacEndpoint`
7742
7569
  * @returns {import("vue").Ref<string>} - Returns `currentUrl`
7743
- * @see {@link '@/store/States.js'}
7570
+ * @see {@link '@/store/states.js'}
7744
7571
  */
7745
7572
  const useAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
7746
7573
  if (!rel || rel.includes("http")) {
7747
- currentUrl.value = base;
7574
+ currentUrl.value = rel;
7748
7575
  return currentUrl;
7749
7576
  }
7750
7577
 
@@ -7763,181 +7590,616 @@ const useAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
7763
7590
  };
7764
7591
 
7765
7592
  /**
7766
- * Use the absolute compare URL from a relative link
7593
+ * Use the absolute compare URL from a relative link
7594
+ *
7595
+ * @param {string} [rel=''] Default is `''`
7596
+ * @param {string} [base=eodash.stacEndpoint] - Base URL, default value is the
7597
+ * root stac catalog. Default is `eodash.stacEndpoint`
7598
+ * @returns {import("vue").Ref<string>} - Returns `currentUrl`
7599
+ * @see {@link '@/store/states.js'}
7600
+ */
7601
+ const useCompareAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
7602
+ if (!rel || rel.includes("http")) {
7603
+ currentCompareUrl.value = base;
7604
+ return currentCompareUrl;
7605
+ }
7606
+
7607
+ const st = base.split("/");
7608
+ const arr = rel.split("/");
7609
+ st.pop();
7610
+
7611
+ for (let i = 0; i < arr.length; i++) {
7612
+ if (arr[i] == ".") continue;
7613
+ if (arr[i] == "..") st.pop();
7614
+ else st.push(arr[i]);
7615
+ }
7616
+
7617
+ currentCompareUrl.value = st.join("/");
7618
+ return currentCompareUrl;
7619
+ };
7620
+
7621
+ /**
7622
+ * Updates an existing Vuetify theme. updates only the values provided in the
7623
+ * `ThemeDefinition`
7624
+ *
7625
+ * @param {string} themeName - Name of the theme to be updated
7626
+ * @param {import("vuetify").ThemeDefinition} [themeDefinition={}] - New
7627
+ * defintion to be updated to. Default is `{}`
7628
+ * @returns {import("vuetify").ThemeInstance}
7629
+ */
7630
+ const useUpdateTheme = (themeName, themeDefinition = {}) => {
7631
+ const theme = useTheme();
7632
+
7633
+ /** @type {(keyof import("vuetify").ThemeDefinition)[]} */ (
7634
+ Object.keys(themeDefinition)
7635
+ ).forEach((key) => {
7636
+ if (key === "dark") {
7637
+ theme.themes.value[themeName][key] = /** @type {Boolean} */ (
7638
+ themeDefinition[key]
7639
+ );
7640
+ } else {
7641
+ //@ts-expect-error to do
7642
+ theme.themes.value[themeName][key] = {
7643
+ ...theme.themes.value[themeName][key],
7644
+ ...themeDefinition[key],
7645
+ };
7646
+ }
7647
+ });
7648
+ return theme;
7649
+ };
7650
+
7651
+ /** Composable that syncs store and URLSearchParameters */
7652
+ const useURLSearchParametersSync = () => {
7653
+ onMounted(async () => {
7654
+ // Analyze currently set url params when first loaded and set them in the store
7655
+ if (window.location.search) {
7656
+ const searchParams = new URLSearchParams(window.location.search);
7657
+
7658
+ /** @type {number | undefined} */
7659
+ let x,
7660
+ /** @type {number | undefined} */
7661
+ y,
7662
+ /** @type {number | undefined} */
7663
+ z;
7664
+ for (const [key, value] of searchParams) {
7665
+ switch (key) {
7666
+ case "template": {
7667
+ activeTemplate.value = value;
7668
+ break;
7669
+ }
7670
+ case "indicator": {
7671
+ log.debug("Found indicator key in url");
7672
+ const { loadSelectedSTAC, stac } = useSTAcStore();
7673
+ const match = stac?.find((link) => link.id == value);
7674
+ if (match) {
7675
+ log.debug("Found match, loading stac item", match);
7676
+ await loadSelectedSTAC(match.href);
7677
+ }
7678
+ break;
7679
+ }
7680
+
7681
+ case "x":
7682
+ x = Number(value);
7683
+ break;
7684
+
7685
+ case "y":
7686
+ y = Number(value);
7687
+ break;
7688
+
7689
+ case "z":
7690
+ z = Number(value);
7691
+ break;
7692
+
7693
+ case "datetime":
7694
+ try {
7695
+ const datetimeiso = new Date(value).toISOString();
7696
+ log.debug("Valid datetime found", datetimeiso);
7697
+ datetime.value = datetimeiso;
7698
+ } catch {
7699
+ datetime.value = new Date().toISOString();
7700
+ }
7701
+ break;
7702
+ }
7703
+ }
7704
+
7705
+ if (x && y && z) {
7706
+ log.debug("Coordinates found, applying map poisition", x, y, z);
7707
+ mapPosition.value = [x, y, z];
7708
+ if (!posIsSetFromUrl.value) {
7709
+ posIsSetFromUrl.value = true;
7710
+ }
7711
+ }
7712
+ }
7713
+
7714
+ watch(
7715
+ [indicator, mapPosition, datetime, activeTemplate],
7716
+ ([
7717
+ updatedIndicator,
7718
+ updatedMapPosition,
7719
+ updatedDatetime,
7720
+ updatedTemplate,
7721
+ ]) => {
7722
+ if ("URLSearchParams" in window) {
7723
+ const searchParams = new URLSearchParams(window.location.search);
7724
+ if (updatedIndicator !== "") {
7725
+ searchParams.set("indicator", updatedIndicator);
7726
+ }
7727
+
7728
+ if (updatedMapPosition && updatedMapPosition.length === 3) {
7729
+ searchParams.set("x", updatedMapPosition[0]?.toFixed(4) ?? "");
7730
+ searchParams.set("y", updatedMapPosition[1]?.toFixed(4) ?? "");
7731
+ searchParams.set("z", updatedMapPosition[2]?.toFixed(4) ?? "");
7732
+ }
7733
+
7734
+ if (updatedDatetime) {
7735
+ searchParams.set("datetime", updatedDatetime.split("T")?.[0] ?? "");
7736
+ }
7737
+ if (updatedTemplate) {
7738
+ searchParams.set("template", updatedTemplate);
7739
+ }
7740
+ const newRelativePathQuery =
7741
+ window.location.pathname + "?" + searchParams.toString();
7742
+ history.pushState(null, "", newRelativePathQuery);
7743
+ }
7744
+ },
7745
+ );
7746
+ });
7747
+ };
7748
+
7749
+ /**
7750
+ * Converts eox-layout-item to transparent
7751
+ *
7752
+ * @param {import("vue").Ref<HTMLElement|null>} root - components root element ref*/
7753
+ const makePanelTransparent = (root) => {
7754
+ onMounted(() => {
7755
+ const eoxItem = root.value?.parentElement;
7756
+ if (eoxItem?.tagName === "EOX-LAYOUT-ITEM") {
7757
+ eoxItem.classList.remove("bg-surface");
7758
+ }
7759
+ });
7760
+ };
7761
+
7762
+ /**
7763
+ * Listens to the `layers:updated` and `time:updated` events and calls
7764
+ *
7765
+ * @param {import("@vueuse/core").EventBusListener<
7766
+ * "layers:updated"|"time:updated",
7767
+ * {layers:Record<string,any>[]| undefined}
7768
+ * >} listener
7769
+ */
7770
+ const useOnLayersUpdate = (listener) => {
7771
+ const layersEvents = useEventBus(eoxLayersKey);
7772
+
7773
+ const unsubscribe = layersEvents.on(listener);
7774
+
7775
+ onUnmounted(() => {
7776
+ unsubscribe();
7777
+ });
7778
+ };
7779
+
7780
+ /** @param {import("stac-ts").StacLink[]} [links] */
7781
+ function generateFeatures(links) {
7782
+ /**
7783
+ * @type {import("geojson").Feature[]}
7784
+ */
7785
+ const features = [];
7786
+ links?.forEach((element) => {
7787
+ if (element.rel === "item" && "latlng" in element) {
7788
+ const [lat, lon] = /** @type {string} */ (element.latlng)
7789
+ .split(",")
7790
+ .map((it) => Number(it));
7791
+ features.push({
7792
+ type: "Feature",
7793
+ geometry: {
7794
+ type: "Point",
7795
+ coordinates: [lon, lat],
7796
+ },
7797
+ properties: { id: element.id },
7798
+ });
7799
+ }
7800
+ });
7801
+ const geojsonObject = {
7802
+ type: "FeatureCollection",
7803
+ crs: {
7804
+ type: "name",
7805
+ properties: {
7806
+ name: "EPSG:4326",
7807
+ },
7808
+ },
7809
+ features,
7810
+ };
7811
+ return geojsonObject;
7812
+ }
7813
+
7814
+ /**
7815
+ * Sperates and extracts layerConfig (jsonform schema & legend) from a style json
7816
+ *
7817
+ * @param { import("ol/layer/WebGLTile").Style & { jsonform?: Record<string,any> } & { legend?: Record<string,any> } } [style] */
7818
+ function extractLayerConfig(style) {
7819
+ /** @type {Record<string,unknown> | undefined} */
7820
+ let layerConfig = undefined;
7821
+ if (style?.jsonform) {
7822
+ layerConfig = { schema: style.jsonform, type: "style" };
7823
+ style = { ...style };
7824
+ delete style.jsonform;
7825
+ if (style?.legend) {
7826
+ layerConfig.legend = style.legend;
7827
+ delete style.legend;
7828
+ }
7829
+ }
7830
+ log.debug(
7831
+ "extracted layerConfig",
7832
+ JSON.parse(JSON.stringify({ layerConfig, style })),
7833
+ );
7834
+
7835
+ return { layerConfig, style };
7836
+ }
7837
+
7838
+ /**
7839
+ * Function to extract collection urls from an indicator
7840
+ * @param {import("stac-ts").StacCatalog
7841
+ * | import("stac-ts").StacCollection
7842
+ * | import("stac-ts").StacItem
7843
+ * | null
7844
+ * } stacObject
7845
+ * @param {string} basepath
7846
+ */
7847
+ function extractCollectionUrls(stacObject, basepath) {
7848
+ /** @type {string[]} */
7849
+ const collectionUrls = [];
7850
+ // Support for two structure types, flat and indicator, simplified here:
7851
+ // Flat assumes Catalog-Collection-Item
7852
+ // Indicator assumes Catalog-Collection-Collection-Item
7853
+
7854
+ const children = stacObject?.links?.filter(
7855
+ (link) => link.rel === "child" && link.type?.includes("json"),
7856
+ );
7857
+ if (!children?.length) {
7858
+ collectionUrls.push(basepath);
7859
+ return collectionUrls;
7860
+ }
7861
+ children.forEach((link) => {
7862
+ collectionUrls.push(toAbsolute(link.href, basepath));
7863
+ });
7864
+ return collectionUrls;
7865
+ }
7866
+
7867
+ /**
7868
+ * Assign extracted roles to layer properties
7869
+ * @param {Record<string,any>} properties
7870
+ * @param {import("stac-ts").StacLink | import("stac-ts").StacAsset} linkOrAsset
7871
+ * */
7872
+ const extractRoles = (properties, linkOrAsset) => {
7873
+ const roles = /** @type {string[]} */ (linkOrAsset.roles);
7874
+ roles?.forEach((role) => {
7875
+ if (role === "visible") {
7876
+ properties.visible = true;
7877
+ }
7878
+ if (role === "overlay" || role === "baselayer") {
7879
+ properties.group = role;
7880
+ }
7881
+
7882
+ return properties;
7883
+ });
7884
+ };
7885
+
7886
+ /**
7887
+ * Extracts style JSON from a STAC Item
7888
+ * @param {import("stac-ts").StacItem} item
7889
+ * @param {string} itemUrl
7890
+ * @returns
7891
+ **/
7892
+ const fetchStyle = async (item, itemUrl) => {
7893
+ const styleLink = item.links.find((link) => link.rel.includes("style"));
7894
+ if (styleLink) {
7895
+ let url = "";
7896
+ if (styleLink.href.startsWith("http")) {
7897
+ url = styleLink.href;
7898
+ } else {
7899
+ url = toAbsolute(styleLink.href, itemUrl);
7900
+ }
7901
+
7902
+ /** @type {import("ol/layer/WebGLTile").Style & {jsonform?:Record<string,any>}} */
7903
+ const styleJson = await axios.get(url).then((resp) => resp.data);
7904
+
7905
+ log.debug("fetched styles JSON", JSON.parse(JSON.stringify(styleJson)));
7906
+ return { ...styleJson };
7907
+ }
7908
+ };
7909
+
7910
+ /**
7911
+ * Return projection code which is to be registered in `eox-map`
7912
+ * @param {string|number|{name: string, def: string}} [projection]
7913
+ * @returns {string}
7914
+ */
7915
+ const getProjectionCode = (projection) => {
7916
+ let code = projection;
7917
+ switch (typeof projection) {
7918
+ case "number":
7919
+ code = `EPSG:${projection}`;
7920
+ break;
7921
+ case "string":
7922
+ code = projection;
7923
+ break;
7924
+ case "object":
7925
+ code = projection?.name;
7926
+ }
7927
+ return /** @type {string} */ (code);
7928
+ };
7929
+
7930
+ /**
7931
+ * Extracts layercontrol LayerDatetime property from STAC Links
7932
+ * @param {import("stac-ts").StacLink[]} [links]
7933
+ * @param {string|null} [currentStep]
7934
+ **/
7935
+ const extractLayerDatetime = (links, currentStep) => {
7936
+ if (!currentStep || !links?.length) {
7937
+ return undefined;
7938
+ }
7939
+
7940
+ // check if links has a datetime value
7941
+ // TODO: consider datetime ranges
7942
+ const hasDatetime = links.some((l) => typeof l.datetime === "string");
7943
+ if (!hasDatetime) {
7944
+ return undefined;
7945
+ }
7946
+
7947
+ /** @type {string[]} */
7948
+ const controlValues = [];
7949
+ try {
7950
+ currentStep = new Date(currentStep).toISOString();
7951
+
7952
+ links.reduce((vals, link) => {
7953
+ if (link.datetime && link.rel === "item") {
7954
+ vals.push(
7955
+ new Date(/** @type {string} */ (link.datetime)).toISOString(),
7956
+ );
7957
+ }
7958
+ return vals;
7959
+ }, controlValues);
7960
+ } catch (e) {
7961
+ console.warn("[eodash] not supported datetime format was provided", e);
7962
+ return undefined;
7963
+ }
7964
+ // not enough controlValues
7965
+ if (controlValues.length <= 1) {
7966
+ return undefined;
7967
+ }
7968
+
7969
+ // item datetime is not included in the item links datetime
7970
+ if (!controlValues.includes(currentStep)) {
7971
+ return undefined;
7972
+ }
7973
+
7974
+ return {
7975
+ controlValues,
7976
+ currentStep,
7977
+ slider: true,
7978
+ play: false,
7979
+ displayFormat: "DD MMMM YYYY",
7980
+ };
7981
+ };
7982
+
7983
+ /**
7984
+ * Find JSON layer by ID
7985
+ * @param {string} layer
7986
+ * @param {Record<string, any>[]} layers
7987
+ * @returns {Record<string,any> | undefined}
7988
+ **/
7989
+ const findLayer = (layers, layer) => {
7990
+ for (const lyr of layers) {
7991
+ if (lyr.type === "Group") {
7992
+ const found = findLayer(lyr.layers, layer);
7993
+ if (!found) {
7994
+ continue;
7995
+ }
7996
+ return found;
7997
+ }
7998
+ if (lyr.properties.id === layer) {
7999
+ return lyr;
8000
+ }
8001
+ }
8002
+ };
8003
+
8004
+ /**
8005
+ * Removes the layer with the id provided and injects an array of layers in its position
8006
+ * @param {Record<string,any>[]} currentLayers
8007
+ * @param {string} oldLayer - id of the layer to be replaced
8008
+ * @param {Record<string,any>[]} newLayers - array of layers to replace the old layer
8009
+ * @returns {Record<string,any>[] | undefined}
8010
+ */
8011
+ const replaceLayer = (currentLayers, oldLayer, newLayers) => {
8012
+ const oldLayerIdx = currentLayers.findIndex(
8013
+ (l) => l.properties.id === oldLayer,
8014
+ );
8015
+
8016
+ if (oldLayerIdx !== -1) {
8017
+ log.debug(
8018
+ "Replacing layer",
8019
+ oldLayer,
8020
+ "with",
8021
+ newLayers.map((l) => l.properties.id),
8022
+ );
8023
+ currentLayers.splice(oldLayerIdx, 1, ...newLayers);
8024
+ return currentLayers;
8025
+ }
8026
+
8027
+ for (const l of currentLayers) {
8028
+ if (l.type === "Group") {
8029
+ const updatedGroupLyrs = replaceLayer(l.layers, oldLayer, newLayers);
8030
+ if (updatedGroupLyrs?.length) {
8031
+ l.layers = updatedGroupLyrs;
8032
+ return currentLayers;
8033
+ }
8034
+ }
8035
+ }
8036
+ };
8037
+
8038
+ /**
8039
+ * Extracts the STAC collection which the layer was created from.
8040
+ *
8041
+ * @param {import('../eodashSTAC/EodashCollection.js').EodashCollection[]} indicators
8042
+ * @param {import('ol/layer').Layer} layer
8043
+ */
8044
+ const getColFromLayer = async (indicators, layer) => {
8045
+ // init cols
8046
+ const collections = await Promise.all(
8047
+ indicators.map((ind) => ind.fetchCollection()),
8048
+ );
8049
+ const [collectionId, itemId, ..._other] = layer.get("id").split(";:;");
8050
+
8051
+ const chosen = collections.find((col) => {
8052
+ const isInd =
8053
+ col.id === collectionId &&
8054
+ col.links?.some(
8055
+ (link) => link.rel === "item" && link.href.includes(itemId),
8056
+ );
8057
+ return isInd;
8058
+ });
8059
+ return indicators.find((ind) => ind.collectionStac?.id === chosen?.id);
8060
+ };
8061
+
8062
+ /**
8063
+ * Generates layer specific ID from STAC Links
8064
+ * related functions are: {@link assignProjID} & {@link createAssetID}
8065
+ *
8066
+ * @param {string} collectionId
8067
+ * @param {string} itemId
8068
+ * @param {import('stac-ts').StacLink} link
8069
+ * @param {string} projectionCode
8070
+ *
8071
+ */
8072
+ const createLayerID = (collectionId, itemId, link, projectionCode) => {
8073
+ const linkId = link.id || link.title || link.href;
8074
+ let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${linkId ?? ""};:;${projectionCode ?? ""}`;
8075
+ // If we are looking at base layers and overlays we remove the collection and item part
8076
+ // as we want to make sure tiles are not reloaded when switching layers
8077
+ if (
8078
+ /** @type {string[]} */
8079
+ (link.roles)?.find((r) => ["baselayer", "overlay"].includes(r))
8080
+ ) {
8081
+ lId = `${linkId ?? ""};:;${projectionCode ?? ""}`;
8082
+ }
8083
+ log.debug("Generated Layer ID", lId);
8084
+ return lId;
8085
+ };
8086
+
8087
+ /**
8088
+ * Generates layer specific ID from STAC assets, related functions are: {@link assignProjID} & {@link createLayerID}
8089
+ *
8090
+ * @param {string} collectionId
8091
+ * @param {string} itemId
8092
+ * @param {number} index
7767
8093
  *
7768
- * @param {string} [rel=''] Default is `''`
7769
- * @param {string} [base=eodash.stacEndpoint] - Base URL, default value is the
7770
- * root stac catalog. Default is `eodash.stacEndpoint`
7771
- * @returns {import("vue").Ref<string>} - Returns `currentUrl`
7772
- * @see {@link '@/store/States.js'}
7773
8094
  */
7774
- const useCompareAbsoluteUrl = (rel = "", base = eodash.stacEndpoint) => {
7775
- if (!rel || rel.includes("http")) {
7776
- currentCompareUrl.value = base;
7777
- return currentCompareUrl;
7778
- }
7779
-
7780
- const st = base.split("/");
7781
- const arr = rel.split("/");
7782
- st.pop();
7783
-
7784
- for (let i = 0; i < arr.length; i++) {
7785
- if (arr[i] == ".") continue;
7786
- if (arr[i] == "..") st.pop();
7787
- else st.push(arr[i]);
7788
- }
7789
-
7790
- currentCompareUrl.value = st.join("/");
7791
- return currentCompareUrl;
8095
+ const createAssetID = (collectionId, itemId, index) => {
8096
+ let lId = `${collectionId ?? ""};:;${itemId ?? ""};:;${index ?? ""}`;
8097
+ log.debug("Generated Asset ID", lId);
8098
+ return lId;
7792
8099
  };
7793
8100
 
7794
8101
  /**
7795
- * Updates an existing Vuetify theme. updates only the values provided in the
7796
- * `ThemeDefinition`
8102
+ * creates a structured clone from the layers and
8103
+ * removes all properties from the clone
8104
+ * except the ID and title
7797
8105
  *
7798
- * @param {string} themeName - Name of the theme to be updated
7799
- * @param {import("vuetify").ThemeDefinition} [themeDefinition={}] - New
7800
- * defintion to be updated to. Default is `{}`
7801
- * @returns {import("vuetify").ThemeInstance}
8106
+ * @param {Record<string,any>[]} layers
7802
8107
  */
7803
- const useUpdateTheme = (themeName, themeDefinition = {}) => {
7804
- const theme = useTheme();
7805
-
7806
- /** @type {(keyof import("vuetify").ThemeDefinition)[]} */ (
7807
- Object.keys(themeDefinition)
7808
- ).forEach((key) => {
7809
- if (key === "dark") {
7810
- theme.themes.value[themeName][key] = /** @type {Boolean} */ (
7811
- themeDefinition[key]
7812
- );
7813
- } else {
7814
- //@ts-expect-error to do
7815
- theme.themes.value[themeName][key] = {
7816
- ...theme.themes.value[themeName][key],
7817
- ...themeDefinition[key],
7818
- };
8108
+ const removeUnneededProperties = (layers) => {
8109
+ const cloned = structuredClone(layers);
8110
+ cloned.forEach((layer) => {
8111
+ const id = layer.properties.id;
8112
+ const title = layer.properties.title;
8113
+ layer.properties = { id, title };
8114
+ if (layer["interactions"]) {
8115
+ delete layer["interactions"];
8116
+ }
8117
+ if (layer.type === "Group") {
8118
+ layer.layers = removeUnneededProperties(layer.layers);
7819
8119
  }
7820
8120
  });
7821
- return theme;
8121
+ return cloned;
7822
8122
  };
7823
8123
 
7824
- /** Composable that syncs store and URLSearchParameters */
7825
- const useURLSearchParametersSync = () => {
7826
- onMounted(async () => {
7827
- // Analyze currently set url params when first loaded and set them in the store
7828
- if (window.location.search) {
7829
- const searchParams = new URLSearchParams(window.location.search);
7830
-
7831
- /** @type {number | undefined} */
7832
- let x,
7833
- /** @type {number | undefined} */
7834
- y,
7835
- /** @type {number | undefined} */
7836
- z;
7837
- for (const [key, value] of searchParams) {
7838
- switch (key) {
7839
- case "indicator": {
7840
- log.debug("Found indicator key in url");
7841
- const { loadSelectedSTAC, stac } = useSTAcStore();
7842
- const match = stac?.find((link) => link.id == value);
7843
- if (match) {
7844
- log.debug("Found match, loading stac item", match);
7845
- await loadSelectedSTAC(match.href);
7846
- }
7847
- break;
7848
- }
7849
-
7850
- case "x":
7851
- x = Number(value);
7852
- break;
7853
-
7854
- case "y":
7855
- y = Number(value);
7856
- break;
7857
-
7858
- case "z":
7859
- z = Number(value);
7860
- break;
7861
-
7862
- case "datetime":
7863
- try {
7864
- const datetimeiso = new Date(value).toISOString();
7865
- log.debug("Valid datetime found", datetimeiso);
7866
- datetime.value = datetimeiso;
7867
- } catch {
7868
- datetime.value = new Date().toISOString();
7869
- }
7870
- break;
7871
- }
7872
- }
7873
-
7874
- if (x && y && z) {
7875
- log.debug("Coordinates found, applying map poisition", x, y, z);
7876
- mapPosition.value = [x, y, z];
7877
- }
7878
- }
7879
-
7880
- watch(
7881
- [indicator, mapPosition, datetime],
7882
- ([updatedIndicator, updatedMapPosition, updatedDatetime]) => {
7883
- if ("URLSearchParams" in window) {
7884
- const searchParams = new URLSearchParams(window.location.search);
7885
- if (updatedIndicator !== "") {
7886
- searchParams.set("indicator", updatedIndicator);
7887
- }
8124
+ /**
8125
+ * Returns the current layers of {@link mapEl}
8126
+ * @returns {Record<string,any>[]}
8127
+ */
8128
+ const getLayers = () => mapEl.value?.layers.toReversed();
7888
8129
 
7889
- if (updatedMapPosition && updatedMapPosition.length === 3) {
7890
- searchParams.set("x", updatedMapPosition[0]?.toFixed(4) ?? "");
7891
- searchParams.set("y", updatedMapPosition[1]?.toFixed(4) ?? "");
7892
- searchParams.set("z", updatedMapPosition[2]?.toFixed(4) ?? "");
7893
- }
8130
+ /**
8131
+ * Returns the current layers of {@link mapCompareEl}
8132
+ * @returns {Record<string,any>[]}
8133
+ */
8134
+ const getCompareLayers = () => mapCompareEl.value?.layers.toReversed();
7894
8135
 
7895
- if (updatedDatetime) {
7896
- searchParams.set("datetime", updatedDatetime.split("T")?.[0] ?? "");
7897
- }
7898
- const newRelativePathQuery =
7899
- window.location.pathname + "?" + searchParams.toString();
7900
- history.pushState(null, "", newRelativePathQuery);
7901
- }
7902
- },
8136
+ /**
8137
+ * Register EPSG projection in `eox-map`
8138
+ * @param {string|number|{name: string, def: string, extent?:number[]}} [projection]*/
8139
+ const registerProjection = async (projection) => {
8140
+ let code = getProjectionCode(projection);
8141
+ if (!code || registeredProjections.includes(code)) {
8142
+ return;
8143
+ }
8144
+ log.debug("Unregistered projection found, registering it", code);
8145
+ registeredProjections.push(code);
8146
+ if (typeof projection === "object") {
8147
+ // registering whole projection definition
8148
+ await mapEl.value?.registerProjection(
8149
+ code,
8150
+ projection.def,
8151
+ projection.extent,
7903
8152
  );
7904
- });
8153
+ // also registering for comparison map
8154
+ await mapCompareEl.value?.registerProjection(
8155
+ code,
8156
+ projection.def,
8157
+ projection.extent,
8158
+ );
8159
+ } else {
8160
+ await mapEl.value?.registerProjectionFromCode(code);
8161
+ // also registering for comparison map
8162
+ await mapCompareEl.value?.registerProjectionFromCode(code);
8163
+ }
7905
8164
  };
8165
+ /**
8166
+ * Change `eox-map` projection from an `EPSG` projection
8167
+ * @param {string|number|{name: string, def: string}} [projection]*/
8168
+ const changeMapProjection = async (projection) => {
8169
+ let code = getProjectionCode(projection);
7906
8170
 
7907
- /** @param {import("vue").Ref<HTMLElement|null>} root - components root element ref*/
7908
- const makePanelTransparent = (root) => {
7909
- onMounted(() => {
7910
- const eoxItem = root.value?.parentElement;
7911
- if (eoxItem?.tagName === "EOX-LAYOUT-ITEM") {
7912
- eoxItem.classList.remove("bg-surface");
7913
- eoxItem.style.background = "transparent";
7914
- eoxItem.style.border = "transparent";
7915
- }
7916
- });
8171
+ if (!code) {
8172
+ mapEl.value?.setAttribute("projection", "EPSG:3857");
8173
+ mapCompareEl.value?.setAttribute("projection", "EPSG:3857");
8174
+ return;
8175
+ }
8176
+
8177
+ if (!registeredProjections.includes(code)) {
8178
+ await registerProjection(projection);
8179
+ }
8180
+
8181
+ code = mapEl.value?.getAttribute("projection") === code ? "EPSG:3857" : code;
8182
+ mapEl.value?.setAttribute("projection", code);
8183
+ mapCompareEl.value?.setAttribute("projection", code);
7917
8184
  };
7918
8185
 
7919
8186
  /**
7920
- * `eodash` injection key.
7921
8187
  *
7922
- * @see {@link "@/plugins/index.js"}
7923
- */
7924
- const eodashKey = Symbol("eodash");
7925
-
7926
- /**
7927
- * Array of eodash STAC Collections extracted from the current selected indicator.
7928
- * Updated in {@link file://./../store/stac.js `loadSelectedSTAC`} widget
7929
- * @type {import('./eodashSTAC').EodashCollection[]}
7930
- * @private
8188
+ * @param {string} template
7931
8189
  */
7932
- const eodashCollections = shallowReactive([]);
8190
+ const setActiveTemplate = (template) => {
8191
+ activeTemplate.value = template;
8192
+ log.debug("Setting active template to", template);
8193
+ };
7933
8194
 
7934
- /**
7935
- * Array of eodash STAC Collections extracted from the current selected COMPARE indicator.
7936
- * Updated in {@link file://./../store/stac.js ` loadSelectedCompareSTAC`} widget
7937
- * @type {import('./eodashSTAC').EodashCollection[]}
7938
- * @private
7939
- */
7940
- const eodashCompareCollections = shallowReactive([]);
8195
+ const actions = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
8196
+ __proto__: null,
8197
+ changeMapProjection,
8198
+ getCompareLayers,
8199
+ getLayers,
8200
+ registerProjection,
8201
+ setActiveTemplate
8202
+ }, Symbol.toStringTag, { value: 'Module' }));
7941
8203
 
7942
8204
  /**
7943
8205
  * @param {string} collectionId
@@ -7996,10 +8258,11 @@ async function createLayersFromAssets(
7996
8258
  },
7997
8259
  ...(!style?.variables && { style }),
7998
8260
  };
8261
+
7999
8262
  extractRoles(layer.properties, assets[ast]);
8000
- if (extraProperties !== null) {
8001
- layer.properties = { ...layer.properties, ...extraProperties };
8002
- }
8263
+
8264
+ layer.properties = { ...layer.properties, ...(extraProperties ?? {}) };
8265
+
8003
8266
  jsonArray.push(layer);
8004
8267
  } else if (assets[ast]?.type === "image/tiff") {
8005
8268
  geoTIFFIdx = idx;
@@ -8027,7 +8290,7 @@ async function createLayersFromAssets(
8027
8290
  },
8028
8291
  style,
8029
8292
  };
8030
- if (extraProperties !== null) {
8293
+ if (extraProperties) {
8031
8294
  layer.properties = { ...layer.properties, ...extraProperties };
8032
8295
  }
8033
8296
  jsonArray.push(layer);
@@ -8081,10 +8344,11 @@ const createLayersFromLinks = async (
8081
8344
  viewProjectionCode,
8082
8345
  );
8083
8346
  log.debug("WMS Layer added", linkId);
8084
- const tileSize =
8347
+ const tileSize = /** @type {number[]} */ (
8085
8348
  "wms:tilesize" in wmsLink
8086
8349
  ? [wmsLink["wms:tilesize"], wmsLink["wms:tilesize"]]
8087
- : [512, 512];
8350
+ : [512, 512]
8351
+ );
8088
8352
  let json = {
8089
8353
  type: "Tile",
8090
8354
  properties: {
@@ -8249,11 +8513,6 @@ class EodashCollection {
8249
8513
  /** @type {import("stac-ts").StacCollection | undefined} */
8250
8514
  #collectionStac;
8251
8515
 
8252
- // read only
8253
- get collectionStac() {
8254
- return this.#collectionStac;
8255
- }
8256
-
8257
8516
  /**
8258
8517
  * @type {import("stac-ts").StacLink
8259
8518
  * | import("stac-ts").StacItem
@@ -8261,6 +8520,11 @@ class EodashCollection {
8261
8520
  */
8262
8521
  selectedItem;
8263
8522
 
8523
+ // read only
8524
+ get collectionStac() {
8525
+ return this.#collectionStac;
8526
+ }
8527
+
8264
8528
  /** @param {string} collectionUrl */
8265
8529
  constructor(collectionUrl) {
8266
8530
  this.#collectionUrl = collectionUrl;
@@ -8309,9 +8573,7 @@ class EodashCollection {
8309
8573
  this.selectedItem = this.getItem();
8310
8574
 
8311
8575
  if (this.selectedItem) {
8312
- layersJson = /** @type {Record<string,any>[]} */ (
8313
- await this.createLayersJson(this.selectedItem)
8314
- );
8576
+ layersJson = await this.createLayersJson(this.selectedItem);
8315
8577
  } else {
8316
8578
  console.warn(
8317
8579
  "[eodash] the selected collection does not include any items",
@@ -8337,7 +8599,7 @@ class EodashCollection {
8337
8599
  * @param {string} title
8338
8600
  * @param {boolean} isGeoDB
8339
8601
  * @param {string} [itemDatetime]
8340
- * @returns {Promise<Record<string,any>[]>} arrays
8602
+ * @returns {Promise<Record<string,any>[]>} layers
8341
8603
  * */
8342
8604
  async buildJsonArray(item, itemUrl, title, isGeoDB, itemDatetime) {
8343
8605
  log.debug(
@@ -8360,30 +8622,9 @@ class EodashCollection {
8360
8622
 
8361
8623
  const jsonArray = [];
8362
8624
 
8363
- if (isGeoDB) {
8364
- const allFeatures = generateFeatures(this.#collectionStac?.links);
8365
-
8366
- return [
8367
- {
8368
- type: "Vector",
8369
- properties: {
8370
- id: this.#collectionStac?.id ?? "",
8371
- title: this.#collectionStac?.title || item.id,
8372
- },
8373
- source: {
8374
- type: "Vector",
8375
- url: "data:," + encodeURIComponent(JSON.stringify(allFeatures)),
8376
- format: "GeoJSON",
8377
- },
8378
- style: {
8379
- "circle-radius": 5,
8380
- "circle-fill-color": "#00417077",
8381
- "circle-stroke-color": "#004170",
8382
- "fill-color": "#00417077",
8383
- "stroke-color": "#004170",
8384
- },
8385
- },
8386
- ];
8625
+ if (isGeoDB) {
8626
+ // handled by getGeoDBLayer
8627
+ return [];
8387
8628
  }
8388
8629
 
8389
8630
  // I propose following approach, we "manually" create configurations
@@ -8416,7 +8657,7 @@ class EodashCollection {
8416
8657
  let extraProperties = null;
8417
8658
  if (this.#collectionStac?.assets?.legend?.href) {
8418
8659
  extraProperties = {
8419
- description: `<div style="text-align:center; width: 100%">
8660
+ description: `<div style="width: 100%">
8420
8661
  <img src="${this.#collectionStac.assets.legend.href}" style="max-height:70px; margin-top:-15px; margin-bottom:-20px;" />
8421
8662
  </div>`,
8422
8663
  };
@@ -8570,14 +8811,14 @@ class EodashCollection {
8570
8811
  currentLayers = getCompareLayers();
8571
8812
  }
8572
8813
 
8573
- const oldLayer = findLayer(currentLayers, layer);
8814
+ /** @type {string | undefined} */
8815
+ const oldLayerID = findLayer(currentLayers, layer)?.properties.id;
8574
8816
 
8575
- const updatedLayers = replaceLayer(
8576
- currentLayers,
8577
- /** @type {Record<string,any> & { properties:{ id:string; title:string } } } */
8578
- (oldLayer),
8579
- newLayers,
8580
- );
8817
+ if (!oldLayerID) {
8818
+ return;
8819
+ }
8820
+
8821
+ const updatedLayers = replaceLayer(currentLayers, oldLayerID, newLayers);
8581
8822
 
8582
8823
  return updatedLayers;
8583
8824
  }
@@ -8620,6 +8861,63 @@ class EodashCollection {
8620
8861
  )),
8621
8862
  ];
8622
8863
  }
8864
+
8865
+ /**
8866
+ * Returns GeoDB layer from a list of EodashCollections
8867
+ *
8868
+ * @param {EodashCollection[]} eodashCollections
8869
+ *
8870
+ **/
8871
+ static getGeoDBLayer(eodashCollections) {
8872
+ const allFeatures = [];
8873
+ for (const collection of eodashCollections) {
8874
+ const isGeoDB = collection.#collectionStac?.endpointtype === "GeoDB";
8875
+ if (!isGeoDB) {
8876
+ continue;
8877
+ }
8878
+ const collectionFeatures = generateFeatures(
8879
+ collection.#collectionStac?.links,
8880
+ ).features;
8881
+ if (collectionFeatures.length) {
8882
+ allFeatures.push(
8883
+ generateFeatures(collection.#collectionStac?.links).features,
8884
+ );
8885
+ }
8886
+ }
8887
+ if (allFeatures.length) {
8888
+ const featureCollection = {
8889
+ type: "FeatureCollection",
8890
+ crs: {
8891
+ type: "name",
8892
+ properties: {
8893
+ name: "EPSG:4326",
8894
+ },
8895
+ },
8896
+ features: allFeatures.flat(),
8897
+ };
8898
+ return {
8899
+ type: "Vector",
8900
+ properties: {
8901
+ id: "geodb-collection",
8902
+ title: "GeoDB Collection",
8903
+ },
8904
+ source: {
8905
+ type: "Vector",
8906
+ url: "data:," + encodeURIComponent(JSON.stringify(featureCollection)),
8907
+ format: "GeoJSON",
8908
+ },
8909
+ style: {
8910
+ "circle-radius": 5,
8911
+ "circle-fill-color": "#00417077",
8912
+ "circle-stroke-color": "#004170",
8913
+ "fill-color": "#00417077",
8914
+ "stroke-color": "#004170",
8915
+ },
8916
+ interactions: [],
8917
+ };
8918
+ }
8919
+ return null;
8920
+ }
8623
8921
  }
8624
8922
 
8625
8923
  const useSTAcStore = defineStore("stac", () => {
@@ -8739,7 +9037,7 @@ const useSTAcStore = defineStore("stac", () => {
8739
9037
  .get(absoluteUrl.value)
8740
9038
  .then(async (resp) => {
8741
9039
  // init eodash collections
8742
- const collectionUrls = extractCollectionUrls(
9040
+ const collectionUrls = await extractCollectionUrls(
8743
9041
  resp.data,
8744
9042
  absoluteUrl.value,
8745
9043
  );
@@ -8783,31 +9081,12 @@ const useSTAcStore = defineStore("stac", () => {
8783
9081
  };
8784
9082
  });
8785
9083
 
8786
- const __vite_glob_0_2 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
8787
- __proto__: null,
8788
- useSTAcStore
8789
- }, Symbol.toStringTag, { value: 'Module' }));
9084
+ //export all actions, states, and pinia stores
8790
9085
 
8791
- const storesImport = /* #__PURE__ */ Object.assign({"./Actions.js": __vite_glob_0_0,"./States.js": __vite_glob_0_1,"./stac.js": __vite_glob_0_2});
8792
-
8793
- const store = /** @type {import("@/types").EodashStore} */ (
8794
- (() => {
8795
- const stores = {};
8796
- for (const [filePath, importedstore] of Object.entries(storesImport)) {
8797
- const storeType =
8798
- filePath.split("/").at(-1)?.slice(0, -3).toLowerCase() ?? "";
8799
- if (!["keys"].includes(storeType)) {
8800
- //@ts-expect-error `importedstore` cant be typed individually
8801
- stores[storeType] = importedstore;
8802
- }
8803
- }
8804
- return stores;
8805
- })()
8806
- );
9086
+ const store = { stac: { useSTAcStore }, actions, states };
8807
9087
 
8808
9088
  /**
8809
- * Sets user defined instance on runtime. Consumes `/@fs/config.js` and assign
8810
- * it to `eodash`
9089
+ * Handles importing user defined instance of Eodash
8811
9090
  *
8812
9091
  * @async
8813
9092
  * @param {string | undefined} runtimeConfig
@@ -8818,6 +9097,17 @@ const useEodashRuntime = async (runtimeConfig) => {
8818
9097
  const eodash = /** @type {import("@/types").Eodash} */ (inject$1(eodashKey));
8819
9098
  /** @param {import("@/types").Eodash} config */
8820
9099
  const assignInstance = (config) => {
9100
+ if ("template" in config) {
9101
+ //@ts-expect-error to do
9102
+ delete eodash.templates;
9103
+ //@ts-expect-error to do
9104
+ eodash.template = config.template;
9105
+ } else if ("templates" in config) {
9106
+ //@ts-expect-error to do
9107
+ delete eodash.template;
9108
+ //@ts-expect-error to do
9109
+ eodash.templates = config.templates;
9110
+ }
8821
9111
  /** @type {(keyof import("@/types").Eodash)[]} */ (
8822
9112
  Object.keys(eodash)
8823
9113
  ).forEach((key) => {
@@ -8837,232 +9127,74 @@ const useEodashRuntime = async (runtimeConfig) => {
8837
9127
  return eodash;
8838
9128
  }
8839
9129
 
8840
- try {
8841
- const configJs = "/config.js";
8842
- assignInstance(
8843
- (await import(/* @vite-ignore */ new URL(configJs, import.meta.url).href))
8844
- .default,
8845
- );
8846
- } catch {
8847
- try {
8848
- assignInstance(
8849
- await Promise.resolve().then(() => eodash$1).then(async (m) => await m["default"]),
8850
- );
8851
- } catch {
8852
- console.error("no dashboard configuration defined");
8853
- }
8854
- }
8855
- return eodash;
8856
- };
8857
-
8858
- /**
8859
- * Loads font in the app using `webfontloader`
8860
- *
8861
- * @param {string} [family]
8862
- * @param {string} [link]
8863
- * @param {boolean} [isWebComponent]
8864
- * @returns {Promise<string>} - Font family name
8865
- * @see {@link "https://github.com/typekit/webfontloader"}
8866
- */
8867
- const loadFont = async (
8868
- family = "",
8869
- link = "",
8870
- isWebComponent = false,
8871
- ) => {
8872
- if (family && link) {
8873
- const WebFontLoader = (await import('webfontloader')).default;
8874
- WebFontLoader.load({
8875
- classes: false,
8876
- custom: {
8877
- // Use FVD notation to include families https://github.com/typekit/fvd
8878
- families: [family],
8879
- // Path to stylesheet that defines font-face
8880
- urls: [link],
8881
- },
8882
- fontactive(familyName, _fvd) {
8883
- const stylesheet = new CSSStyleSheet();
8884
- const styles = isWebComponent
8885
- ? `eo-dash {font-family: ${familyName};}`
8886
- : `* {font-family: ${familyName};}`;
8887
- stylesheet.replaceSync(styles);
8888
- document.adoptedStyleSheets.push(stylesheet);
8889
- },
8890
- fontinactive(familyName, _fvd) {
8891
- throw new Error(`error loading font: ${familyName}`);
8892
- },
8893
- });
8894
- }
8895
- return family;
8896
- };
8897
-
8898
- /**
8899
- * @param {string} text
8900
- * @param {import("vue").Ref<boolean>} showIcon
8901
- **/
8902
- const copyToClipBoard = async (text, showIcon) => {
8903
- await navigator.clipboard.writeText(text);
8904
- showIcon.value = true;
8905
- setTimeout(() => {
8906
- showIcon.value = false;
8907
- }, 2000);
8908
- };
8909
-
8910
- /**
8911
- * @typedef {{
8912
- * component: import("vue").Component | null;
8913
- * props: Record<string, unknown>;
8914
- * title: string;
8915
- * id: string | number | symbol;
8916
- * layout: { x: number; y: number; h: number; w: number };
8917
- * }} DefinedWidget
8918
- */
8919
-
8920
- /** @typedef {import("vue").ShallowRef<DefinedWidget>} ReactiveDefinedWidget */
8921
-
8922
- const internalWidgets = (() => {
8923
- /** @type {Record<string, () => Promise<import("vue").Component>>} */
8924
- const importMap = {
8925
- .../* #__PURE__ */ Object.assign({"/widgets/EodashDatePicker.vue": () => import('./EodashDatePicker-CjU8R2ia.js'),"/widgets/EodashItemFilter.vue": () => import('./EodashItemFilter-VGQasaBJ.js'),"/widgets/EodashLayerControl.vue": () => import('./EodashLayerControl-mKfwru42.js'),"/widgets/EodashMap.vue": () => import('./EodashMap-BpwL82-w.js'),"/widgets/EodashMapBtns.vue": () => import('./EodashMapBtns-GNNBdBW9.js'),"/widgets/ExportState.vue": () => import('./ExportState-ByVuIAQb.js'),"/widgets/PopUp.vue": () => import('./PopUp-CdFcnKMY.js'),"/widgets/WidgetsContainer.vue": () => import('./WidgetsContainer-XXYJfaPR.js')}),
8926
- .../* #__PURE__ */ Object.assign({}),
8927
- };
8928
- for (const key in importMap) {
8929
- const newKey = /** @type {string} */ (key.split("/").at(-1)).slice(0, -4);
8930
- Object.defineProperty(
8931
- importMap,
8932
- newKey,
8933
- /** @type {PropertyDescriptor} */ (
8934
- Object.getOwnPropertyDescriptor(importMap, key)
8935
- ),
8936
- );
8937
- delete importMap[key];
8938
- }
8939
- return importMap;
8940
- })();
8941
-
8942
- /**
8943
- * Composable that converts widgets Configurations to defined imported widgets
8944
- *
8945
- * @param {(
8946
- * | import("@/types").Widget
8947
- * | import("@/types").BackgroundWidget
8948
- * | Omit<import("@/types").Widget, "layout">
8949
- * | undefined
8950
- * )[]
8951
- * | undefined} widgetConfigs
8952
- * @returns {ReactiveDefinedWidget[]}
8953
- */
8954
- const useDefineWidgets = (widgetConfigs) => {
8955
- /** @type {ReactiveDefinedWidget[]} */
8956
- const definedWidgets = [];
8957
-
8958
- for (const config of widgetConfigs ?? []) {
8959
- /** @type {ReactiveDefinedWidget} */
8960
- const definedWidget = shallowRef({
8961
- component: null,
8962
- props: {},
8963
- title: "",
8964
- id: Symbol(),
8965
- layout: { x: 0, y: 0, h: 0, w: 0 },
8966
- });
8967
-
8968
- if ("defineWidget" in (config ?? {})) {
8969
- const { selectedStac } = storeToRefs(useSTAcStore());
8970
- watch(
8971
- selectedStac,
8972
- (updatedStac) => {
8973
- let definedConfig =
8974
- /** @type {import("@/types").FunctionalWidget} */ (
8975
- config
8976
- )?.defineWidget(updatedStac);
8977
- if (definedConfig) {
8978
- definedConfig = reactive(definedConfig);
8979
- }
8980
- definedWidget.value =
8981
- definedWidget.value.id === definedConfig?.id
8982
- ? definedWidget.value
8983
- : getWidgetDefinition(definedConfig);
8984
- },
8985
- { immediate: true },
8986
- );
8987
- } else {
8988
- definedWidget.value = getWidgetDefinition(
8989
- /** @type {import("@/types").StaticWidget} */ (config),
8990
- );
8991
- }
8992
- definedWidgets.push(definedWidget);
8993
- }
8994
- return definedWidgets;
8995
- };
8996
-
8997
- /**
8998
- * Converts a static widget configuration to a defined imported widget
8999
- *
9000
- * @param {import("@/types").StaticWidget
9001
- * | Omit<import("@/types").StaticWidget, "layout">
9002
- * | undefined
9003
- * | null} [config]
9004
- * @returns {DefinedWidget}
9005
- */
9006
- const getWidgetDefinition = (config) => {
9007
- /** @type {DefinedWidget} */
9008
- const importedWidget = {
9009
- component: null,
9010
- props: {},
9011
- title: "",
9012
- id: Symbol(),
9013
- layout: reactive({ x: 0, y: 0, h: 0, w: 0 }),
9014
- };
9015
- switch (config?.type) {
9016
- case "internal":
9017
- importedWidget.component = defineAsyncComponent({
9018
- loader:
9019
- internalWidgets[
9020
- /** @type {import("@/types").InternalComponentWidget} * */ (config)
9021
- ?.widget.name
9022
- ],
9023
- suspensible: true,
9024
- });
9025
- importedWidget.props = reactive(
9026
- /** @type {import("@/types").InternalComponentWidget} * */ (config)
9027
- ?.widget.properties ?? {},
9028
- );
9029
-
9030
- break;
9031
-
9032
- case "web-component":
9033
- importedWidget.component = defineAsyncComponent({
9034
- loader: () => import('./DynamicWebComponent-C9pVUfT3.js'),
9035
- suspensible: true,
9036
- });
9037
- importedWidget.props = reactive(config.widget);
9038
-
9039
- break;
9040
- case "iframe":
9041
- importedWidget.component = defineAsyncComponent({
9042
- loader: () => import('./IframeWrapper-BgM9aU8f.js'),
9043
- suspensible: true,
9044
- });
9045
- importedWidget.props = reactive(config.widget);
9046
- break;
9047
-
9048
- default:
9049
- if (!config) {
9050
- return importedWidget;
9051
- } else {
9052
- console.error("Widget type not found");
9053
- }
9054
- break;
9130
+ try {
9131
+ const configJs = "/config.js";
9132
+ assignInstance(
9133
+ (await import(/* @vite-ignore */ new URL(configJs, import.meta.url).href))
9134
+ .default,
9135
+ );
9136
+ } catch {
9137
+ try {
9138
+ assignInstance(
9139
+ await Promise.resolve().then(() => eodash$1).then(async (m) => await m["default"]),
9140
+ );
9141
+ } catch {
9142
+ console.error("no dashboard configuration defined");
9143
+ }
9055
9144
  }
9056
- importedWidget.title = config?.title ?? "";
9057
- importedWidget.id = config?.id ?? importedWidget.id;
9145
+ return eodash;
9146
+ };
9058
9147
 
9059
- if ("layout" in config) {
9060
- importedWidget.layout.x = config.layout.x;
9061
- importedWidget.layout.y = config.layout.y;
9062
- importedWidget.layout.h = config.layout.h;
9063
- importedWidget.layout.w = config.layout.w;
9148
+ /**
9149
+ * Loads font in the app using `webfontloader`
9150
+ *
9151
+ * @param {string} [family]
9152
+ * @param {string} [link]
9153
+ * @param {boolean} [isWebComponent]
9154
+ * @returns {Promise<string>} - Font family name
9155
+ * @see {@link "https://github.com/typekit/webfontloader"}
9156
+ */
9157
+ const loadFont = async (
9158
+ family = "",
9159
+ link = "",
9160
+ isWebComponent = false,
9161
+ ) => {
9162
+ if (family && link) {
9163
+ const WebFontLoader = (await import('webfontloader')).default;
9164
+ WebFontLoader.load({
9165
+ classes: false,
9166
+ custom: {
9167
+ // Use FVD notation to include families https://github.com/typekit/fvd
9168
+ families: [family],
9169
+ // Path to stylesheet that defines font-face
9170
+ urls: [link],
9171
+ },
9172
+ fontactive(familyName, _fvd) {
9173
+ const stylesheet = new CSSStyleSheet();
9174
+ const styles = isWebComponent
9175
+ ? `eo-dash {font-family: ${familyName};}`
9176
+ : `* {font-family: ${familyName};}`;
9177
+ stylesheet.replaceSync(styles);
9178
+ document.adoptedStyleSheets.push(stylesheet);
9179
+ },
9180
+ fontinactive(familyName, _fvd) {
9181
+ throw new Error(`error loading font: ${familyName}`);
9182
+ },
9183
+ });
9064
9184
  }
9065
- return importedWidget;
9185
+ return family;
9186
+ };
9187
+
9188
+ /**
9189
+ * @param {string} text
9190
+ * @param {import("vue").Ref<boolean>} showIcon
9191
+ **/
9192
+ const copyToClipBoard = async (text, showIcon) => {
9193
+ await navigator.clipboard.writeText(text);
9194
+ showIcon.value = true;
9195
+ setTimeout(() => {
9196
+ showIcon.value = false;
9197
+ }, 2000);
9066
9198
  };
9067
9199
 
9068
9200
  const _export_sfc = (sfc, props) => {
@@ -11042,6 +11174,226 @@ return (_ctx, _cache) => {
11042
11174
  };
11043
11175
  const ErrorAlert = /*#__PURE__*/_export_sfc(_sfc_main$3, [['__scopeId',"data-v-ac10aa7f"]]);
11044
11176
 
11177
+ /**
11178
+ * @typedef {{
11179
+ * component: import("vue").Component | null;
11180
+ * props: Record<string, unknown>;
11181
+ * title: string;
11182
+ * id: string | number | symbol;
11183
+ * layout: { x: number; y: number; h: number; w: number };
11184
+ * }} DefinedWidget
11185
+ */
11186
+
11187
+ /** @typedef {import("vue").ShallowRef<DefinedWidget>} ReactiveDefinedWidget */
11188
+ /**
11189
+ * Widgets import map that is created from eodash internals and user defined widgets
11190
+ */
11191
+ const internalWidgets = (() => {
11192
+ /** @type {Record<string, () => Promise<import("vue").Component>>} */
11193
+ const importMap = {
11194
+ .../* #__PURE__ */ Object.assign({"/widgets/EodashDatePicker.vue": () => import('./EodashDatePicker-Pok6bZwU.js'),"/widgets/EodashItemFilter.vue": () => import('./EodashItemFilter-16eMMjTV.js'),"/widgets/EodashLayerControl.vue": () => import('./EodashLayerControl-De7IlCm_.js'),"/widgets/EodashLayoutSwitcher.vue": () => import('./EodashLayoutSwitcher-C-3-jjn5.js'),"/widgets/EodashMap.vue": () => import('./EodashMap-CMvbfI6-.js'),"/widgets/EodashMapBtns.vue": () => import('./EodashMapBtns-BeknGDtc.js'),"/widgets/EodashProcess.vue": () => import('./EodashProcess-BwKAa9Ee.js'),"/widgets/EodashStacInfo.vue": () => import('./EodashStacInfo-_BfonNUG.js'),"/widgets/EodashTools.vue": () => import('./EodashTools-PD3XPYuR.js'),"/widgets/ExportState.vue": () => import('./ExportState-DOrT7M15.js'),"/widgets/PopUp.vue": () => import('./PopUp--_xn1Cms.js'),"/widgets/WidgetsContainer.vue": () => import('./WidgetsContainer-aFG9yFT6.js')}),
11195
+ .../* #__PURE__ */ Object.assign({}),
11196
+ };
11197
+ for (const key in importMap) {
11198
+ const newKey = /** @type {string} */ (key.split("/").at(-1)).slice(0, -4);
11199
+ Object.defineProperty(
11200
+ importMap,
11201
+ newKey,
11202
+ /** @type {PropertyDescriptor} */ (
11203
+ Object.getOwnPropertyDescriptor(importMap, key)
11204
+ ),
11205
+ );
11206
+ delete importMap[key];
11207
+ }
11208
+ return importMap;
11209
+ })();
11210
+
11211
+ /**
11212
+ * Composable that converts widgets Configurations to defined imported widgets
11213
+ *
11214
+ * @param {(
11215
+ * | import("@/types").Widget
11216
+ * | import("@/types").BackgroundWidget
11217
+ * | Omit<import("@/types").Widget, "layout">
11218
+ * | undefined
11219
+ * )[]
11220
+ * | undefined} widgetConfigs
11221
+ */
11222
+ const useDefineWidgets = (widgetConfigs) => {
11223
+ /** @type {ReactiveDefinedWidget[]} */
11224
+ const definedWidgets = [];
11225
+
11226
+ for (const config of widgetConfigs ?? []) {
11227
+ /** @type {ReactiveDefinedWidget} */
11228
+ const definedWidget = shallowRef({
11229
+ component: null,
11230
+ props: {},
11231
+ title: "",
11232
+ id: Symbol(),
11233
+ layout: { x: 0, y: 0, h: 0, w: 0 },
11234
+ });
11235
+
11236
+ if ("defineWidget" in (config ?? {})) {
11237
+ const { selectedStac } = storeToRefs(useSTAcStore());
11238
+ watch(
11239
+ selectedStac,
11240
+ (updatedStac) => {
11241
+ let definedConfig =
11242
+ /** @type {import("@/types").FunctionalWidget} */ (
11243
+ config
11244
+ )?.defineWidget(updatedStac);
11245
+ if (definedConfig) {
11246
+ definedConfig = reactive(definedConfig);
11247
+ }
11248
+ definedWidget.value =
11249
+ definedWidget.value.id === definedConfig?.id
11250
+ ? definedWidget.value
11251
+ : getWidgetDefinition(definedConfig);
11252
+ },
11253
+ { immediate: true },
11254
+ );
11255
+ } else {
11256
+ definedWidget.value = getWidgetDefinition(
11257
+ /** @type {import("@/types").StaticWidget} */ (config),
11258
+ );
11259
+ }
11260
+ definedWidgets.push(definedWidget);
11261
+ }
11262
+ return definedWidgets;
11263
+ };
11264
+
11265
+ /**
11266
+ * Converts a static widget configuration to a defined imported widget
11267
+ *
11268
+ * @param {import("@/types").StaticWidget
11269
+ * | Omit<import("@/types").StaticWidget, "layout">
11270
+ * | undefined
11271
+ * | null} [config]
11272
+ * @returns {DefinedWidget}
11273
+ */
11274
+ const getWidgetDefinition = (config) => {
11275
+ /** @type {DefinedWidget} */
11276
+ const importedWidget = {
11277
+ component: null,
11278
+ props: {},
11279
+ title: "",
11280
+ id: Symbol(),
11281
+ layout: reactive({ x: 0, y: 0, h: 0, w: 0 }),
11282
+ };
11283
+ switch (config?.type) {
11284
+ case "internal":
11285
+ importedWidget.component = defineAsyncComponent({
11286
+ loader:
11287
+ internalWidgets[
11288
+ /** @type {import("@/types").InternalComponentWidget} * */ (config)
11289
+ ?.widget.name
11290
+ ],
11291
+ suspensible: true,
11292
+ });
11293
+ importedWidget.props = reactive(
11294
+ /** @type {import("@/types").InternalComponentWidget} * */ (config)
11295
+ ?.widget.properties ?? {},
11296
+ );
11297
+
11298
+ break;
11299
+
11300
+ case "web-component":
11301
+ importedWidget.component = defineAsyncComponent({
11302
+ loader: () => import('./DynamicWebComponent-Cl4LqHU6.js'),
11303
+ suspensible: true,
11304
+ });
11305
+ importedWidget.props = reactive(config.widget);
11306
+
11307
+ break;
11308
+ case "iframe":
11309
+ importedWidget.component = defineAsyncComponent({
11310
+ loader: () => import('./IframeWrapper-BgM9aU8f.js'),
11311
+ suspensible: true,
11312
+ });
11313
+ importedWidget.props = reactive(config.widget);
11314
+ break;
11315
+
11316
+ default:
11317
+ if (!config) {
11318
+ return importedWidget;
11319
+ } else {
11320
+ console.error("Widget type not found");
11321
+ }
11322
+ break;
11323
+ }
11324
+ importedWidget.title = config?.title ?? "";
11325
+ importedWidget.id = config?.id ?? importedWidget.id;
11326
+
11327
+ if ("layout" in config) {
11328
+ importedWidget.layout.x = config.layout.x;
11329
+ importedWidget.layout.y = config.layout.y;
11330
+ importedWidget.layout.h = config.layout.h;
11331
+ importedWidget.layout.w = config.layout.w;
11332
+ }
11333
+ return importedWidget;
11334
+ };
11335
+
11336
+ /**
11337
+ * @typedef {{
11338
+ * bgWidget:ReturnType< typeof import("./DefineWidgets").useDefineWidgets>[number]| import("vue").ShallowRef<null>
11339
+ * loading: ReturnType< typeof import("./DefineWidgets").useDefineWidgets>[number]| import("vue").ShallowRef<null>
11340
+ * importedWidgets:ReturnType< typeof import("./DefineWidgets").useDefineWidgets>
11341
+ * gap: import("vue").Ref<number>
11342
+ * }} DefinedTemplate
11343
+ **/
11344
+
11345
+ const useTemplate = () => {
11346
+ const eodash = /** @type {import("@/types").Eodash} */ (inject$1(eodashKey));
11347
+
11348
+ /** @type {DefinedTemplate} */
11349
+ const definedTemplate = shallowReactive({
11350
+ bgWidget: shallowRef(null),
11351
+ importedWidgets: [],
11352
+ loading: shallowRef(null),
11353
+ gap: ref(16),
11354
+ });
11355
+
11356
+ if ("template" in eodash) {
11357
+ [definedTemplate.bgWidget] = useDefineWidgets([eodash.template.background]);
11358
+ [definedTemplate.loading] = useDefineWidgets([eodash.template.loading]);
11359
+ definedTemplate.importedWidgets = useDefineWidgets(eodash.template.widgets);
11360
+ definedTemplate.gap.value = eodash.template.gap ?? 16;
11361
+ } else {
11362
+ watch(
11363
+ activeTemplate,
11364
+ (template) => {
11365
+ log.debug("Active template watcher triggered, changing to:", template);
11366
+ if (!template) {
11367
+ template = Object.keys(eodash.templates)[0];
11368
+ activeTemplate.value = template ?? "";
11369
+ log.debug("No template found, setting to first template", template);
11370
+ }
11371
+
11372
+ if (!template || !eodash.templates[template]) {
11373
+ console.error(`[eodash] template not found`);
11374
+ return;
11375
+ }
11376
+
11377
+ const templateConfig = eodash.templates[template];
11378
+ [definedTemplate.bgWidget] = useDefineWidgets([
11379
+ templateConfig.background,
11380
+ ]);
11381
+
11382
+ [definedTemplate.loading] = useDefineWidgets([templateConfig.loading]);
11383
+
11384
+ definedTemplate.importedWidgets = useDefineWidgets(
11385
+ templateConfig.widgets,
11386
+ );
11387
+ definedTemplate.gap.value = templateConfig.gap ?? 16;
11388
+ },
11389
+ { immediate: true },
11390
+ );
11391
+ }
11392
+ return toRefs(definedTemplate);
11393
+ };
11394
+
11395
+ const useDefineTemplate = createSharedComposable(useTemplate);
11396
+
11045
11397
  // Styles
11046
11398
  const breakpointProps = (() => {
11047
11399
  return breakpoints.reduce((props, val) => {
@@ -11286,9 +11638,7 @@ const _sfc_main$2 = {
11286
11638
  __name: 'Loading',
11287
11639
  setup(__props) {
11288
11640
 
11289
- const eodash = /** @type {import("@/types").Eodash} */ (inject$1(eodashKey));
11290
-
11291
- const [loading] = useDefineWidgets([eodash.template.loading]);
11641
+ const { loading } = useDefineTemplate();
11292
11642
 
11293
11643
  const error = ref("");
11294
11644
  onErrorCaptured((e, inst, info) => {
@@ -11316,8 +11666,8 @@ return (_ctx, _cache) => {
11316
11666
  createElementVNode("div", { class: "text-center" }, "Loading...", -1 /* HOISTED */)
11317
11667
  ])),
11318
11668
  default: withCtx(() => [
11319
- (unref(loading).component)
11320
- ? (openBlock(), createBlock(resolveDynamicComponent(unref(loading).component), normalizeProps(mergeProps({ key: 0 }, unref(loading).props)), null, 16 /* FULL_PROPS */))
11669
+ (unref(loading)?.component)
11670
+ ? (openBlock(), createBlock(resolveDynamicComponent(unref(loading)?.component), normalizeProps(mergeProps({ key: 0 }, unref(loading)?.props)), null, 16 /* FULL_PROPS */))
11321
11671
  : (openBlock(), createElementBlock("div", _hoisted_1$1, "Loading..."))
11322
11672
  ]),
11323
11673
  _: 1 /* STABLE */
@@ -11381,15 +11731,17 @@ const { loadSTAC } = useSTAcStore();
11381
11731
  );
11382
11732
 
11383
11733
  const { smAndDown } = useDisplay();
11384
- const TemplateComponent = smAndDown.value
11385
- ? defineAsyncComponent(() => import('./MobileLayout-6bHjYguI.js'))
11386
- : defineAsyncComponent(() => import('./DashboardLayout-E_JzgCH5.js'));
11734
+ const TemplateComponent = computed(() =>
11735
+ smAndDown.value
11736
+ ? defineAsyncComponent(() => import('./MobileLayout-BdiFjHg7.js'))
11737
+ : defineAsyncComponent(() => import('./DashboardLayout-232tRmjz.js')),
11738
+ );
11387
11739
 
11388
11740
  const HeaderComponent = defineAsyncComponent(
11389
- () => import('./Header-B8UBQstf.js'),
11741
+ () => import('./Header-C2cdx4gb.js'),
11390
11742
  );
11391
11743
  const FooterComponent = defineAsyncComponent(
11392
- () => import('./Footer-D691KLtK.js'),
11744
+ () => import('./Footer-CCigxYBo.js'),
11393
11745
  );
11394
11746
 
11395
11747
  const templateHeight = props.isWebComponent ? "100%" : "100dvh";
@@ -11541,6 +11893,7 @@ const vuetify = createVuetify({
11541
11893
  next: [mdiChevronRight],
11542
11894
  prev: [mdiChevronLeft],
11543
11895
  subgroup: [mdiMenuDown],
11896
+ plus: [mdiPlus],
11544
11897
  },
11545
11898
  },
11546
11899
  theme: {
@@ -11574,7 +11927,7 @@ function registerPlugins(app) {
11574
11927
  * @type {import("vue").VueElementConstructor<
11575
11928
  * import("vue").ExtractPropTypes<{ config: string }>>}
11576
11929
  * */
11577
- const Eodash = defineCustomElement(_sfc_main, {
11930
+ const EodashConstructor = defineCustomElement(_sfc_main, {
11578
11931
  shadowRoot: false,
11579
11932
  configureApp(app) {
11580
11933
  registerPlugins(app);
@@ -11582,9 +11935,9 @@ const Eodash = defineCustomElement(_sfc_main, {
11582
11935
  });
11583
11936
 
11584
11937
  function register() {
11585
- customElements.define("eo-dash", Eodash);
11938
+ customElements.define("eo-dash", EodashConstructor);
11586
11939
  }
11587
11940
 
11588
11941
  register();
11589
11942
 
11590
- export { useSTAcStore as $, makeDensityProps as A, useDensity as B, useBackgroundColor as C, provideDefaults as D, isObject as E, eodashKey as F, useDefineWidgets as G, useLayout as H, IconValue as I, makeDimensionProps as J, useDimension as K, makeBorderProps as L, makeElevationProps as M, makeRoundedProps as N, useBorder as O, useElevation as P, useRounded as Q, VDefaultsProvider as R, clamp as S, consoleWarn as T, makeLayoutItemProps as U, VBtn as V, useToggleScope as W, useLayoutItem as X, getUid as Y, datetime as Z, _export_sfc as _, useRender as a, eodashCollections as a0, VRow as a1, mapCompareEl as a2, mapEl as a3, eodashCompareCollections as a4, getColFromLayer as a5, setMapProjFromCol as a6, EodashCollection as a7, mapPosition as a8, makePanelTransparent as a9, destructComputed as aA, parseAnchor as aB, flipSide as aC, flipAlign as aD, flipCorner as aE, consoleError as aF, getAxis as aG, defer as aH, templateRef as aI, matchesSelector as aJ, useRouter as aK, useBackButton as aL, Eodash as aM, register as aN, store as aO, availableMapProjection as aa, changeMapProjection as ab, createSimpleFunctional as ac, makeSizeProps as ad, makeVariantProps as ae, useVariant as af, useSize as ag, genOverlays as ah, makeLoaderProps as ai, makeLocationProps as aj, makePositionProps as ak, makeRouterProps as al, Ripple as am, useLoader as an, useLocation as ao, usePosition as ap, useLink as aq, LoaderSlot as ar, getLayers as as, removeUnneededProperties as at, VCol as au, copyToClipBoard as av, SUPPORTS_INTERSECTION as aw, getCurrentInstance as ax, isOn as ay, eventName as az, makeComponentProps as b, makeTagProps as c, makeThemeProps as d, provideTheme as e, useRtl as f, genericComponent as g, useLocale as h, useGroup as i, useProxiedModel as j, keys as k, makeGroupItemProps as l, makeVBtnProps as m, useGroupItem as n, omit as o, propsFactory as p, convertToUnit as q, makeDisplayProps as r, makeGroupProps as s, useDisplay as t, useTextColor as u, useResizeObserver as v, useGoTo as w, IN_BROWSER as x, VIcon as y, focusableChildren as z };
11943
+ export { useSTAcStore as $, makeDensityProps as A, useDensity as B, useBackgroundColor as C, provideDefaults as D, isObject as E, useDefineTemplate as F, useLayout as G, makeDimensionProps as H, IconValue as I, useDimension as J, makeBorderProps as K, makeElevationProps as L, makeRoundedProps as M, useBorder as N, useElevation as O, useRounded as P, VDefaultsProvider as Q, clamp as R, consoleWarn as S, makeLayoutItemProps as T, useToggleScope as U, VBtn as V, useLayoutItem as W, eodashKey as X, consoleError as Y, datetime as Z, _export_sfc as _, useRender as a, eodashCollections as a0, makePanelTransparent as a1, mapCompareEl as a2, mapEl as a3, eodashCompareCollections as a4, getColFromLayer as a5, registerProjection as a6, getProjectionCode as a7, availableMapProjection as a8, changeMapProjection as a9, useLink as aA, LoaderSlot as aB, removeUnneededProperties as aC, VRow as aD, VCol as aE, copyToClipBoard as aF, SUPPORTS_INTERSECTION as aG, getCurrentInstance as aH, useDefineWidgets as aI, isOn as aJ, eventName as aK, destructComputed as aL, parseAnchor as aM, flipSide as aN, flipAlign as aO, flipCorner as aP, getAxis as aQ, defer as aR, templateRef as aS, matchesSelector as aT, useRouter as aU, useBackButton as aV, EodashConstructor as aW, register as aX, store as aY, eoxLayersKey as aa, posIsSetFromUrl as ab, EodashCollection as ac, mapPosition as ad, setActiveTemplate as ae, axios$1 as af, getLayers as ag, extractLayerConfig as ah, useOnLayersUpdate as ai, currentUrl as aj, activeTemplate as ak, getUid as al, createSimpleFunctional as am, makeSizeProps as an, makeVariantProps as ao, useVariant as ap, useSize as aq, genOverlays as ar, makeLoaderProps as as, makeLocationProps as at, makePositionProps as au, makeRouterProps as av, Ripple as aw, useLoader as ax, useLocation as ay, usePosition as az, makeComponentProps as b, makeTagProps as c, makeThemeProps as d, provideTheme as e, useRtl as f, genericComponent as g, useLocale as h, useGroup as i, useProxiedModel as j, keys as k, makeGroupItemProps as l, makeVBtnProps as m, useGroupItem as n, omit as o, propsFactory as p, convertToUnit as q, makeDisplayProps as r, makeGroupProps as s, useDisplay as t, useTextColor as u, useResizeObserver as v, useGoTo as w, IN_BROWSER as x, VIcon as y, focusableChildren as z };