@eeacms/volto-arcgis-block 0.1.470 → 0.1.472

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,8 +4,26 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [0.1.472](https://github.com/eea/volto-arcgis-block/compare/0.1.471...0.1.472) - 14 August 2026
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - (bug): grouplayers do not look like wmts layers keeping path for these clean [Unai Bolivar - [`f146798`](https://github.com/eea/volto-arcgis-block/commit/f146798adb952ca741a28115e3244d8fc58ac1fb)]
12
+ ### [0.1.471](https://github.com/eea/volto-arcgis-block/compare/0.1.470...0.1.471) - 14 August 2026
13
+
14
+ #### :house: Internal changes
15
+
16
+ - style: Automated code fix [eea-jenkins - [`8f37c43`](https://github.com/eea/volto-arcgis-block/commit/8f37c43a97b75692ffe14653b8295408e3b1b157)]
17
+
18
+ #### :hammer_and_wrench: Others
19
+
20
+ - Merge pull request #1197 from eea/develop [Unai Bolivar - [`d02c7a5`](https://github.com/eea/volto-arcgis-block/commit/d02c7a558aea93dd328a69ace2e90f5edec26b93)]
21
+ - (bug): fix time slider integration for dual resolution layer datasets [Unai Bolivar - [`c12c740`](https://github.com/eea/volto-arcgis-block/commit/c12c740f7f5e04dcc14f7cdecb7488832c5b61ab)]
7
22
  ### [0.1.470](https://github.com/eea/volto-arcgis-block/compare/0.1.469...0.1.470) - 13 August 2026
8
23
 
24
+ #### :hammer_and_wrench: Others
25
+
26
+ - Merge pull request #1195 from eea/develop [Unai Bolivar - [`83e7db1`](https://github.com/eea/volto-arcgis-block/commit/83e7db1ea05eafe8acecbe6169f596f65ed96df5)]
9
27
  ### [0.1.469](https://github.com/eea/volto-arcgis-block/compare/0.1.468...0.1.469) - 12 August 2026
10
28
 
11
29
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.470",
3
+ "version": "0.1.472",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -952,7 +952,17 @@ class MenuWidget extends React.Component {
952
952
  }
953
953
  }
954
954
 
955
- supportDualLayers(layer, inheritedIndexLayer, datasetCollectionId) {
955
+ supportDualLayers(
956
+ layer,
957
+ inheritedIndexLayer,
958
+ datasetCollectionId,
959
+ isTimeSeries,
960
+ datasetDownloadInformation,
961
+ viewService,
962
+ datasetId,
963
+ datasetTitle,
964
+ productId,
965
+ ) {
956
966
  const resolutionData = layer.resolution_data || {};
957
967
  const thresholdScale = Number(resolutionData.thresholdScale);
958
968
  const normalizeScaleValue = (scaleValue, fallbackValue = 0) => {
@@ -991,6 +1001,7 @@ class MenuWidget extends React.Component {
991
1001
  catalogByoc: null,
992
1002
  evalscript: null,
993
1003
  processUrl: '/++api++/@proxyrunprocessapi',
1004
+ selectedTimeRange: null,
994
1005
  },
995
1006
 
996
1007
  _resolveCatalogDateRange: function (options) {
@@ -1099,94 +1110,97 @@ class MenuWidget extends React.Component {
1099
1110
  const width = this.tileInfo.size[0];
1100
1111
  const height = this.tileInfo.size[1];
1101
1112
 
1102
- return this._resolveCatalogDateRange(options).then(
1103
- (catalogDateRange) => {
1104
- const resolvedTimeRangeFrom = catalogDateRange?.from || null;
1105
- const resolvedTimeRangeTo = catalogDateRange?.to || null;
1106
- const timeRange = {};
1107
- if (resolvedTimeRangeFrom) {
1108
- timeRange.from = resolvedTimeRangeFrom;
1109
- }
1110
- if (resolvedTimeRangeTo) {
1111
- timeRange.to = resolvedTimeRangeTo;
1112
- }
1113
- const dataFilter = {};
1114
- if (Object.keys(timeRange).length) {
1115
- dataFilter.timeRange = timeRange;
1116
- }
1113
+ const selectedTimeRange = this.selectedTimeRange || null;
1114
+ const resolveTimeRange = selectedTimeRange
1115
+ ? Promise.resolve(selectedTimeRange)
1116
+ : this._resolveCatalogDateRange(options);
1117
+
1118
+ return resolveTimeRange.then((catalogDateRange) => {
1119
+ const resolvedTimeRangeFrom = catalogDateRange?.from || null;
1120
+ const resolvedTimeRangeTo = catalogDateRange?.to || null;
1121
+ const timeRange = {};
1122
+ if (resolvedTimeRangeFrom) {
1123
+ timeRange.from = resolvedTimeRangeFrom;
1124
+ }
1125
+ if (resolvedTimeRangeTo) {
1126
+ timeRange.to = resolvedTimeRangeTo;
1127
+ }
1128
+ const dataFilter = {};
1129
+ if (Object.keys(timeRange).length) {
1130
+ dataFilter.timeRange = timeRange;
1131
+ }
1117
1132
 
1118
- const payload = {
1119
- input: {
1120
- bounds: {
1121
- bbox: [
1122
- bbox3857.west,
1123
- bbox3857.south,
1124
- bbox3857.east,
1125
- bbox3857.north,
1126
- ],
1127
- properties: {
1128
- crs: 'http://www.opengis.net/def/crs/EPSG/0/3857',
1129
- },
1130
- },
1131
- data: [
1132
- {
1133
- type: this.collectionId,
1134
- ...(Object.keys(dataFilter).length ? { dataFilter } : {}),
1135
- },
1136
- ],
1137
- },
1138
- output: {
1139
- width: width,
1140
- height: height,
1141
- responses: [
1142
- {
1143
- identifier: 'default',
1144
- format: { type: 'image/png' },
1145
- },
1133
+ const payload = {
1134
+ input: {
1135
+ bounds: {
1136
+ bbox: [
1137
+ bbox3857.west,
1138
+ bbox3857.south,
1139
+ bbox3857.east,
1140
+ bbox3857.north,
1146
1141
  ],
1142
+ properties: {
1143
+ crs: 'http://www.opengis.net/def/crs/EPSG/0/3857',
1144
+ },
1147
1145
  },
1148
- evalscript: this.evalscript,
1149
- };
1146
+ data: [
1147
+ {
1148
+ type: this.collectionId,
1149
+ ...(Object.keys(dataFilter).length ? { dataFilter } : {}),
1150
+ },
1151
+ ],
1152
+ },
1153
+ output: {
1154
+ width: width,
1155
+ height: height,
1156
+ responses: [
1157
+ {
1158
+ identifier: 'default',
1159
+ format: { type: 'image/png' },
1160
+ },
1161
+ ],
1162
+ },
1163
+ evalscript: this.evalscript,
1164
+ };
1150
1165
 
1151
- return fetch(this.processUrl, {
1152
- method: 'POST',
1153
- headers: {
1154
- 'Content-Type': 'application/json',
1155
- Accept: 'image/png',
1156
- },
1157
- body: JSON.stringify(payload),
1158
- signal: options && options.signal,
1166
+ return fetch(this.processUrl, {
1167
+ method: 'POST',
1168
+ headers: {
1169
+ 'Content-Type': 'application/json',
1170
+ Accept: 'image/png',
1171
+ },
1172
+ body: JSON.stringify(payload),
1173
+ signal: options && options.signal,
1174
+ })
1175
+ .then((response) => {
1176
+ if (!response.ok) {
1177
+ throw new Error(
1178
+ `Process API error ${response.status}: ${response.statusText}`,
1179
+ );
1180
+ }
1181
+ return response.blob();
1159
1182
  })
1160
- .then((response) => {
1161
- if (!response.ok) {
1162
- throw new Error(
1163
- `Process API error ${response.status}: ${response.statusText}`,
1164
- );
1165
- }
1166
- return response.blob();
1167
- })
1168
- .then((blob) => {
1169
- return new Promise((resolve, reject) => {
1170
- const url = URL.createObjectURL(blob);
1171
- const img = new Image();
1172
- img.onload = () => {
1173
- const canvas = document.createElement('canvas');
1174
- canvas.width = width;
1175
- canvas.height = height;
1176
- const ctx = canvas.getContext('2d');
1177
- ctx.drawImage(img, 0, 0, width, height);
1178
- URL.revokeObjectURL(url);
1179
- resolve(canvas);
1180
- };
1181
- img.onerror = (err) => {
1182
- URL.revokeObjectURL(url);
1183
- reject(err);
1184
- };
1185
- img.src = url;
1186
- });
1183
+ .then((blob) => {
1184
+ return new Promise((resolve, reject) => {
1185
+ const url = URL.createObjectURL(blob);
1186
+ const img = new Image();
1187
+ img.onload = () => {
1188
+ const canvas = document.createElement('canvas');
1189
+ canvas.width = width;
1190
+ canvas.height = height;
1191
+ const ctx = canvas.getContext('2d');
1192
+ ctx.drawImage(img, 0, 0, width, height);
1193
+ URL.revokeObjectURL(url);
1194
+ resolve(canvas);
1195
+ };
1196
+ img.onerror = (err) => {
1197
+ URL.revokeObjectURL(url);
1198
+ reject(err);
1199
+ };
1200
+ img.src = url;
1187
1201
  });
1188
- },
1189
- );
1202
+ });
1203
+ });
1190
1204
  },
1191
1205
  });
1192
1206
  const tileInfo = {
@@ -1249,6 +1263,14 @@ class MenuWidget extends React.Component {
1249
1263
  title: baseLayerTitle,
1250
1264
  visibilityMode: 'independent',
1251
1265
  layers: dualResolutionLayers,
1266
+ isTimeSeries: isTimeSeries,
1267
+ DatasetDownloadInformation: datasetDownloadInformation || {},
1268
+ datasetDownloadInformation: datasetDownloadInformation || {},
1269
+ ViewService: viewService,
1270
+ DatasetId: datasetId,
1271
+ DatasetTitle: datasetTitle,
1272
+ ProductId: productId,
1273
+ LayerTitle: baseLayerTitle,
1252
1274
  });
1253
1275
  }
1254
1276
 
@@ -2371,7 +2393,17 @@ class MenuWidget extends React.Component {
2371
2393
  ) {
2372
2394
  const datasetCollectionId =
2373
2395
  dataset_download_information?.items?.[0]?.byoc_collection || null;
2374
- this.supportDualLayers(layer, inheritedIndexLayer, datasetCollectionId);
2396
+ this.supportDualLayers(
2397
+ layer,
2398
+ inheritedIndexLayer,
2399
+ datasetCollectionId,
2400
+ isTimeSeries,
2401
+ dataset_download_information,
2402
+ viewService,
2403
+ DatasetId,
2404
+ DatasetTitle,
2405
+ ProductId,
2406
+ );
2375
2407
  } else if (viewService?.toLowerCase().includes('wms')) {
2376
2408
  viewService = viewService?.includes('?')
2377
2409
  ? viewService + '&'
@@ -4791,12 +4823,8 @@ class MenuWidget extends React.Component {
4791
4823
  this.findCheckedDatasetNoServiceToVisualize(elem);
4792
4824
  }
4793
4825
  if (this.layers[elem.id] === undefined) return;
4794
- const layerViewService =
4795
- this.layers[elem.id]?.ViewService || this.layers[elem.id]?.url || '';
4796
4826
  const isSceneViewActive = this.view && this.view.type === '3d';
4797
- const evaluateWmtsLayer =
4798
- this.layers[elem.id]?.type === 'wmts' ||
4799
- layerViewService.toLowerCase().includes('wmts');
4827
+ const evaluateWmtsLayer = this.layers[elem.id]?.type === 'wmts';
4800
4828
  if (elem.checked && evaluateWmtsLayer) {
4801
4829
  this.refreshWmtsLayerData(elem.id);
4802
4830
  const canApplyWmtsSettings = await this.applyWmtsSettingsData(
@@ -4884,9 +4912,7 @@ class MenuWidget extends React.Component {
4884
4912
  payload = await this.props.fetchCatalogApiDates(byoc, false);
4885
4913
  }
4886
4914
  if (payload) {
4887
- if (
4888
- this.layers[elem.id].ViewService.toLowerCase().includes('wmts')
4889
- ) {
4915
+ if (this.layers[elem.id].type === 'wmts') {
4890
4916
  const resolveSentinelLayer =
4891
4917
  /(?:sh\.dataspace\.copernicus\.eu|services\.sentinel-hub\.com)\/ogc\/wmts/i.test(
4892
4918
  this.layers[elem.id].ViewService ||
@@ -7262,7 +7288,7 @@ class MenuWidget extends React.Component {
7262
7288
 
7263
7289
  getLayerTitle(layer) {
7264
7290
  let title;
7265
- if (layer.url?.toLowerCase().includes('wmts')) {
7291
+ if (layer.type === 'wmts') {
7266
7292
  // CLMS-1105
7267
7293
  title = layer._wmtsTitle;
7268
7294
  } else {
@@ -45,6 +45,8 @@ class TimesliderWidget extends React.Component {
45
45
  this.layerName = this.layer.sublayers.items[0].name;
46
46
  } else if (this.layer.type === 'wmts') {
47
47
  this.layerName = this.layer.activeLayer.id; //WMTS
48
+ } else if (this.layer.type === 'group') {
49
+ this.layerName = this.layer.name || this.layer.title || this.layer.id;
48
50
  }
49
51
  this.drag = {};
50
52
  if (
@@ -61,6 +63,54 @@ class TimesliderWidget extends React.Component {
61
63
  this.containerParentNode = null;
62
64
  }
63
65
 
66
+ resolveLayerServiceUrl() {
67
+ return (
68
+ this.layer?.ViewService ||
69
+ this.layer?.viewService ||
70
+ this.layer?.url ||
71
+ null
72
+ );
73
+ }
74
+
75
+ applyLayerTimeSelection(timeValue) {
76
+ if (!timeValue) return;
77
+ if (this.layer.type === 'wmts') {
78
+ this.layer.customLayerParameters = {
79
+ SHOWLOGO: false,
80
+ };
81
+ this.layer.customLayerParameters['TIME'] = timeValue + '/' + timeValue;
82
+ this.layer.refresh();
83
+ return;
84
+ }
85
+ if (this.layer.type === 'group') {
86
+ const selectedTimeText = String(timeValue);
87
+ const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(selectedTimeText);
88
+ const selectedTimeRange = isDateOnly
89
+ ? {
90
+ from: `${selectedTimeText}T00:00:00Z`,
91
+ to: `${selectedTimeText}T23:59:59Z`,
92
+ }
93
+ : {
94
+ from: selectedTimeText,
95
+ to: selectedTimeText,
96
+ };
97
+ const childLayers = this.layer?.layers?.items || [];
98
+ childLayers.forEach((childLayer) => {
99
+ childLayer.selectedTimeRange = selectedTimeRange;
100
+ if (typeof childLayer.refresh === 'function') {
101
+ childLayer.refresh();
102
+ }
103
+ });
104
+ if (typeof this.layer.refresh === 'function') {
105
+ this.layer.refresh();
106
+ }
107
+ return;
108
+ }
109
+ this.layer.customLayerParameters = {};
110
+ this.layer.customLayerParameters['TIME'] = timeValue;
111
+ this.layer.refresh();
112
+ }
113
+
64
114
  restoreContainerContext() {
65
115
  if (
66
116
  !this.container ||
@@ -344,20 +394,9 @@ class TimesliderWidget extends React.Component {
344
394
  if (!this.container.current ? true : false) {
345
395
  this.TimesliderWidget.stop();
346
396
  }
347
- if (this.layer.type === 'wmts') {
348
- this.layer.customLayerParameters = {
349
- SHOWLOGO: false,
350
- };
351
- this.layer.customLayerParameters['TIME'] =
352
- timeDict[this.TimesliderWidget.timeExtent.end] +
353
- '/' +
354
- timeDict[this.TimesliderWidget.timeExtent.end];
355
- } else {
356
- this.layer.customLayerParameters = {};
357
- this.layer.customLayerParameters['TIME'] =
358
- timeDict[this.TimesliderWidget.timeExtent.end];
359
- }
360
- this.layer.refresh();
397
+ this.applyLayerTimeSelection(
398
+ timeDict[this.TimesliderWidget.timeExtent.end],
399
+ );
361
400
  },
362
401
  );
363
402
  this._watchHandles.push(watchHandle);
@@ -473,7 +512,8 @@ class TimesliderWidget extends React.Component {
473
512
  .whenLayerView(this.layer, this.TimesliderWidget)
474
513
  .then(async (lv) => {
475
514
  if (!this._isMounted) return;
476
- this.url = lv?.layer?.viewService ?? this.layer?.url ?? null;
515
+ this.url =
516
+ lv?.layer?.viewService ?? this.resolveLayerServiceUrl() ?? null;
477
517
  const urlNorm =
478
518
  typeof this.url === 'string'
479
519
  ? this.url.replace(/%2F/gi, '/').toLowerCase()
@@ -538,6 +578,10 @@ class TimesliderWidget extends React.Component {
538
578
  } else if (this.layer.type === 'wmts') {
539
579
  times = this.parseTimeWMTS(xml);
540
580
  }
581
+ if (!this.layerName || !times[this.layerName]) {
582
+ this.TimesliderWidget.disabled = true;
583
+ return;
584
+ }
541
585
  // Capabilities have time enabled
542
586
  if (
543
587
  times[this.layerName].hasOwnProperty('dimension') === false
@@ -639,32 +683,24 @@ class TimesliderWidget extends React.Component {
639
683
  // }
640
684
  // this.props.time.dataset.setAttribute('time-start', start);
641
685
  // this.props.time.dataset.setAttribute('time-end', end);
642
- if (this.layer.type === 'wmts') {
643
- this.layer.customLayerParameters = {};
644
- this.layer.customLayerParameters['TIME'] =
645
- timeDict[this.TimesliderWidget.timeExtent.end] +
646
- '/' +
647
- timeDict[this.TimesliderWidget.timeExtent.end];
686
+ if (times[this.layerName].hasOwnProperty('array')) {
687
+ this.applyLayerTimeSelection(
688
+ timeDict[this.TimesliderWidget.timeExtent.end],
689
+ );
648
690
  } else {
649
- this.layer.customLayerParameters = {};
650
- if (times[this.layerName].hasOwnProperty('array')) {
651
- this.layer.customLayerParameters['TIME'] =
652
- timeDict[this.TimesliderWidget.timeExtent.end];
653
- } else {
654
- const newDateTimeObject = new Date(
655
- this.TimesliderWidget.timeExtent.start.toISOString(),
656
- );
657
- newDateTimeObject.setMinutes(
658
- this.TimesliderWidget.timeExtent.start.getMinutes() +
659
- this.TimesliderWidget.stops['interval'].value,
660
- );
661
- this.layer.customLayerParameters['TIME'] =
662
- this.TimesliderWidget.timeExtent.start.toISOString() +
691
+ const newDateTimeObject = new Date(
692
+ this.TimesliderWidget.timeExtent.start.toISOString(),
693
+ );
694
+ newDateTimeObject.setMinutes(
695
+ this.TimesliderWidget.timeExtent.start.getMinutes() +
696
+ this.TimesliderWidget.stops['interval'].value,
697
+ );
698
+ this.applyLayerTimeSelection(
699
+ this.TimesliderWidget.timeExtent.start.toISOString() +
663
700
  '/' +
664
- newDateTimeObject.toISOString(); //OK
665
- }
701
+ newDateTimeObject.toISOString(),
702
+ );
666
703
  }
667
- this.layer.refresh();
668
704
  },
669
705
  );
670
706
  this._watchHandles.push(watchHandle);
@@ -721,7 +757,7 @@ class TimesliderWidget extends React.Component {
721
757
 
722
758
  componentDidUpdate(prevProps) {
723
759
  if (!this.TimesliderWidget) return;
724
- const url = this.layer?.url || '';
760
+ const url = this.resolveLayerServiceUrl() || '';
725
761
  const urlNorm =
726
762
  typeof url === 'string' ? url.replace(/%2F/gi, '/').toLowerCase() : '';
727
763
  const isCDSE = urlNorm.includes('/ogc/') || urlNorm.includes('/cdse/');