@eeacms/volto-arcgis-block 0.1.469 → 0.1.471
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,6 +4,20 @@ 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.471](https://github.com/eea/volto-arcgis-block/compare/0.1.470...0.1.471) - 14 August 2026
|
|
8
|
+
|
|
9
|
+
#### :house: Internal changes
|
|
10
|
+
|
|
11
|
+
- style: Automated code fix [eea-jenkins - [`8f37c43`](https://github.com/eea/volto-arcgis-block/commit/8f37c43a97b75692ffe14653b8295408e3b1b157)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
- (bug): fix time slider integration for dual resolution layer datasets [Unai Bolivar - [`c12c740`](https://github.com/eea/volto-arcgis-block/commit/c12c740f7f5e04dcc14f7cdecb7488832c5b61ab)]
|
|
16
|
+
### [0.1.470](https://github.com/eea/volto-arcgis-block/compare/0.1.469...0.1.470) - 13 August 2026
|
|
17
|
+
|
|
18
|
+
#### :hammer_and_wrench: Others
|
|
19
|
+
|
|
20
|
+
- Merge pull request #1195 from eea/develop [Unai Bolivar - [`83e7db1`](https://github.com/eea/volto-arcgis-block/commit/83e7db1ea05eafe8acecbe6169f596f65ed96df5)]
|
|
7
21
|
### [0.1.469](https://github.com/eea/volto-arcgis-block/compare/0.1.468...0.1.469) - 12 August 2026
|
|
8
22
|
|
|
9
23
|
#### :hammer_and_wrench: Others
|
package/package.json
CHANGED
|
@@ -952,7 +952,17 @@ class MenuWidget extends React.Component {
|
|
|
952
952
|
}
|
|
953
953
|
}
|
|
954
954
|
|
|
955
|
-
supportDualLayers(
|
|
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) {
|
|
@@ -1036,11 +1047,23 @@ class MenuWidget extends React.Component {
|
|
|
1036
1047
|
})
|
|
1037
1048
|
.then((payload) => {
|
|
1038
1049
|
const dates = Array.isArray(payload?.dates) ? payload.dates : [];
|
|
1050
|
+
const resolveTimeValue = (value, isStart) => {
|
|
1051
|
+
if (!value) {
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
const stringValue = String(value);
|
|
1055
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(stringValue)) {
|
|
1056
|
+
return isStart
|
|
1057
|
+
? `${stringValue}T00:00:00Z`
|
|
1058
|
+
: `${stringValue}T23:59:59Z`;
|
|
1059
|
+
}
|
|
1060
|
+
return stringValue;
|
|
1061
|
+
};
|
|
1039
1062
|
const resolvedDateRange =
|
|
1040
1063
|
dates.length > 0
|
|
1041
1064
|
? {
|
|
1042
|
-
from: dates[0],
|
|
1043
|
-
to: dates[dates.length - 1],
|
|
1065
|
+
from: resolveTimeValue(dates[0], true),
|
|
1066
|
+
to: resolveTimeValue(dates[dates.length - 1], false),
|
|
1044
1067
|
}
|
|
1045
1068
|
: null;
|
|
1046
1069
|
cacheByCollection[normalizedCollectionId] = resolvedDateRange;
|
|
@@ -1087,94 +1110,97 @@ class MenuWidget extends React.Component {
|
|
|
1087
1110
|
const width = this.tileInfo.size[0];
|
|
1088
1111
|
const height = this.tileInfo.size[1];
|
|
1089
1112
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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
|
+
}
|
|
1105
1132
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
],
|
|
1115
|
-
properties: {
|
|
1116
|
-
crs: 'http://www.opengis.net/def/crs/EPSG/0/3857',
|
|
1117
|
-
},
|
|
1118
|
-
},
|
|
1119
|
-
data: [
|
|
1120
|
-
{
|
|
1121
|
-
type: this.collectionId,
|
|
1122
|
-
...(Object.keys(dataFilter).length ? { dataFilter } : {}),
|
|
1123
|
-
},
|
|
1124
|
-
],
|
|
1125
|
-
},
|
|
1126
|
-
output: {
|
|
1127
|
-
width: width,
|
|
1128
|
-
height: height,
|
|
1129
|
-
responses: [
|
|
1130
|
-
{
|
|
1131
|
-
identifier: 'default',
|
|
1132
|
-
format: { type: 'image/png' },
|
|
1133
|
-
},
|
|
1133
|
+
const payload = {
|
|
1134
|
+
input: {
|
|
1135
|
+
bounds: {
|
|
1136
|
+
bbox: [
|
|
1137
|
+
bbox3857.west,
|
|
1138
|
+
bbox3857.south,
|
|
1139
|
+
bbox3857.east,
|
|
1140
|
+
bbox3857.north,
|
|
1134
1141
|
],
|
|
1142
|
+
properties: {
|
|
1143
|
+
crs: 'http://www.opengis.net/def/crs/EPSG/0/3857',
|
|
1144
|
+
},
|
|
1135
1145
|
},
|
|
1136
|
-
|
|
1137
|
-
|
|
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
|
+
};
|
|
1138
1165
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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();
|
|
1147
1182
|
})
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
URL.revokeObjectURL(url);
|
|
1167
|
-
resolve(canvas);
|
|
1168
|
-
};
|
|
1169
|
-
img.onerror = (err) => {
|
|
1170
|
-
URL.revokeObjectURL(url);
|
|
1171
|
-
reject(err);
|
|
1172
|
-
};
|
|
1173
|
-
img.src = url;
|
|
1174
|
-
});
|
|
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;
|
|
1175
1201
|
});
|
|
1176
|
-
|
|
1177
|
-
);
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1178
1204
|
},
|
|
1179
1205
|
});
|
|
1180
1206
|
const tileInfo = {
|
|
@@ -1237,6 +1263,15 @@ class MenuWidget extends React.Component {
|
|
|
1237
1263
|
title: baseLayerTitle,
|
|
1238
1264
|
visibilityMode: 'independent',
|
|
1239
1265
|
layers: dualResolutionLayers,
|
|
1266
|
+
isTimeSeries: isTimeSeries,
|
|
1267
|
+
DatasetDownloadInformation: datasetDownloadInformation || {},
|
|
1268
|
+
datasetDownloadInformation: datasetDownloadInformation || {},
|
|
1269
|
+
ViewService: viewService,
|
|
1270
|
+
url: viewService,
|
|
1271
|
+
DatasetId: datasetId,
|
|
1272
|
+
DatasetTitle: datasetTitle,
|
|
1273
|
+
ProductId: productId,
|
|
1274
|
+
LayerTitle: baseLayerTitle,
|
|
1240
1275
|
});
|
|
1241
1276
|
}
|
|
1242
1277
|
|
|
@@ -2359,7 +2394,17 @@ class MenuWidget extends React.Component {
|
|
|
2359
2394
|
) {
|
|
2360
2395
|
const datasetCollectionId =
|
|
2361
2396
|
dataset_download_information?.items?.[0]?.byoc_collection || null;
|
|
2362
|
-
this.supportDualLayers(
|
|
2397
|
+
this.supportDualLayers(
|
|
2398
|
+
layer,
|
|
2399
|
+
inheritedIndexLayer,
|
|
2400
|
+
datasetCollectionId,
|
|
2401
|
+
isTimeSeries,
|
|
2402
|
+
dataset_download_information,
|
|
2403
|
+
viewService,
|
|
2404
|
+
DatasetId,
|
|
2405
|
+
DatasetTitle,
|
|
2406
|
+
ProductId,
|
|
2407
|
+
);
|
|
2363
2408
|
} else if (viewService?.toLowerCase().includes('wms')) {
|
|
2364
2409
|
viewService = viewService?.includes('?')
|
|
2365
2410
|
? viewService + '&'
|
|
@@ -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
|
-
|
|
348
|
-
this.
|
|
349
|
-
|
|
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 =
|
|
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.
|
|
643
|
-
this.
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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()
|
|
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.
|
|
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/');
|