@eeacms/volto-arcgis-block 0.1.467 → 0.1.469

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,33 @@ 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.469](https://github.com/eea/volto-arcgis-block/compare/0.1.468...0.1.469) - 12 August 2026
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - (chore): fix linting [Unai Bolivar - [`df6b223`](https://github.com/eea/volto-arcgis-block/commit/df6b223aa3b8b2e0bf098e191e210ec4022f059f)]
12
+ - (bug): dynamically pull time range from catalog for hi/lo res tile requests [Unai Bolivar - [`810b22d`](https://github.com/eea/volto-arcgis-block/commit/810b22d9b555fb18d9f8d5a810a5099f6ae29ab6)]
13
+ ### [0.1.468](https://github.com/eea/volto-arcgis-block/compare/0.1.467...0.1.468) - 12 August 2026
14
+
15
+ #### :rocket: New Features
16
+
17
+ - feat: bind processapi endpoint to mapviewer [ana-oprea - [`066c9ea`](https://github.com/eea/volto-arcgis-block/commit/066c9ea8d33a1a1091fbec5e40effd9025d199df)]
18
+
19
+ #### :house: Internal changes
20
+
21
+ - style: Automated code fix [eea-jenkins - [`c670ae0`](https://github.com/eea/volto-arcgis-block/commit/c670ae0acf614ead0662eee6b9c1d747cef97d33)]
22
+ - style: Automated code fix [eea-jenkins - [`8ad01ad`](https://github.com/eea/volto-arcgis-block/commit/8ad01ad521477bb5f7cc2235531a65a37703ce92)]
23
+
24
+ #### :hammer_and_wrench: Others
25
+
26
+ - Merge pull request #1190 from eea/develop [Unai Bolivar - [`5d9e33f`](https://github.com/eea/volto-arcgis-block/commit/5d9e33f1470e6776920657aac835360aff91e47c)]
27
+ - Merge pull request #1189 from eea/CLMS-304632-REFACTOR [Unai Bolivar - [`ffba286`](https://github.com/eea/volto-arcgis-block/commit/ffba286c6368fae6167ffedf3c89a18d74a31edd)]
28
+ - (task): refactor dual layers support [Unai Bolivar - [`c13aaa0`](https://github.com/eea/volto-arcgis-block/commit/c13aaa089d91f476f1fa02e94673dc4d847704c9)]
29
+ - (task): refactor dual layer support [Unai Bolivar - [`7c3a1cf`](https://github.com/eea/volto-arcgis-block/commit/7c3a1cfe18ed8be53b80fb2205b54cce90f0ad68)]
30
+ - CLMS-303649 (bug): Swipe widget solved [Urkorue - [`9f0aea8`](https://github.com/eea/volto-arcgis-block/commit/9f0aea8855a437b63f57e88cb4bd1446bd8f33df)]
31
+ - (feat): implemented a complete refactor to process hi/lo res dataset based on values from internal api [Unai Bolivar - [`f34e881`](https://github.com/eea/volto-arcgis-block/commit/f34e88157a77f90e4fc3ace6b20ffa287efafc5a)]
32
+ - (chore): port refactor [Unai Bolivar - [`fb98e70`](https://github.com/eea/volto-arcgis-block/commit/fb98e70d1750c3d06b961db887509f542412e080)]
33
+ - CLMS-304632 (feat): 10 metre resolution [Urkorue - [`ce9517c`](https://github.com/eea/volto-arcgis-block/commit/ce9517c47eeea1f5d5e752bde7fe19217f2efcac)]
7
34
  ### [0.1.467](https://github.com/eea/volto-arcgis-block/compare/0.1.466...0.1.467) - 12 August 2026
8
35
 
9
36
  ### [0.1.466](https://github.com/eea/volto-arcgis-block/compare/0.1.465...0.1.466) - 7 August 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.467",
3
+ "version": "0.1.469",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -22,7 +22,8 @@ var WMSLayer,
22
22
  MapImageLayer,
23
23
  projection,
24
24
  SpatialReference,
25
- WebMercatorUtils;
25
+ WebMercatorUtils,
26
+ GroupLayer;
26
27
 
27
28
  const popupSettings = {
28
29
  basic: true,
@@ -583,6 +584,7 @@ class MenuWidget extends React.Component {
583
584
  'esri/geometry/projection',
584
585
  'esri/geometry/SpatialReference',
585
586
  'esri/geometry/support/webMercatorUtils',
587
+ 'esri/layers/GroupLayer',
586
588
  ]).then(
587
589
  ([
588
590
  _WMSLayer,
@@ -598,6 +600,7 @@ class MenuWidget extends React.Component {
598
600
  _projection,
599
601
  _SpatialReference,
600
602
  _WebMercatorUtils,
603
+ _GroupLayer,
601
604
  ]) => {
602
605
  [
603
606
  WMSLayer,
@@ -613,6 +616,7 @@ class MenuWidget extends React.Component {
613
616
  projection,
614
617
  SpatialReference,
615
618
  WebMercatorUtils,
619
+ GroupLayer,
616
620
  ] = [
617
621
  _WMSLayer,
618
622
  _WMTSLayer,
@@ -627,6 +631,7 @@ class MenuWidget extends React.Component {
627
631
  _projection,
628
632
  _SpatialReference,
629
633
  _WebMercatorUtils,
634
+ _GroupLayer,
630
635
  ];
631
636
  },
632
637
  );
@@ -843,7 +848,7 @@ class MenuWidget extends React.Component {
843
848
 
844
849
  this.props.restorePanelScroll();
845
850
  }
846
-
851
+ //this.pruebasLayer()
847
852
  this.setState({ showMapMenu: true });
848
853
  }
849
854
  // if (this.loadFirst && this.container.current) {
@@ -947,6 +952,294 @@ class MenuWidget extends React.Component {
947
952
  }
948
953
  }
949
954
 
955
+ supportDualLayers(layer, inheritedIndexLayer, datasetCollectionId) {
956
+ const resolutionData = layer.resolution_data || {};
957
+ const thresholdScale = Number(resolutionData.thresholdScale);
958
+ const normalizeScaleValue = (scaleValue, fallbackValue = 0) => {
959
+ const normalizedValue = Number(scaleValue);
960
+ return Number.isFinite(normalizedValue) ? normalizedValue : fallbackValue;
961
+ };
962
+ const lowResSourceData = resolutionData.lowRes || layer.lowRes || {};
963
+ const highResSourceData = resolutionData.highRes || layer.highRes || {};
964
+ const baseLayerTitle = layer.Title || layer.LayerId || '';
965
+ const lowResLayerTitle = lowResSourceData.title || baseLayerTitle;
966
+ const highResLayerTitle = highResSourceData.title || baseLayerTitle;
967
+ const lowResLayerData = {
968
+ collectionId: lowResSourceData.collectionId,
969
+ evalscript: lowResSourceData.evalscript,
970
+ minScale: normalizeScaleValue(lowResSourceData.minScale, 0),
971
+ maxScale: normalizeScaleValue(
972
+ lowResSourceData.maxScale,
973
+ Number.isFinite(thresholdScale) ? thresholdScale : 0,
974
+ ),
975
+ };
976
+ const highResLayerData = {
977
+ collectionId: highResSourceData.collectionId,
978
+ evalscript: highResSourceData.evalscript,
979
+ minScale: normalizeScaleValue(
980
+ highResSourceData.minScale,
981
+ Number.isFinite(thresholdScale) ? thresholdScale + 1 : 0,
982
+ ),
983
+ maxScale: normalizeScaleValue(highResSourceData.maxScale, 0),
984
+ };
985
+ if (!lowResLayerData.collectionId && !highResLayerData.collectionId) {
986
+ return;
987
+ }
988
+ const CDSEProcessTileLayer = BaseTileLayer.createSubclass({
989
+ properties: {
990
+ collectionId: null,
991
+ catalogByoc: null,
992
+ evalscript: null,
993
+ processUrl: '/++api++/@proxyrunprocessapi',
994
+ },
995
+
996
+ _resolveCatalogDateRange: function (options) {
997
+ const rawCollectionId = this.catalogByoc || '';
998
+ const normalizedCollectionId = String(rawCollectionId).replace(
999
+ /^byoc-/i,
1000
+ '',
1001
+ );
1002
+ if (!normalizedCollectionId) {
1003
+ return Promise.resolve(null);
1004
+ }
1005
+
1006
+ const cacheByCollection =
1007
+ this.constructor._catalogDateRangeCache ||
1008
+ (this.constructor._catalogDateRangeCache = {});
1009
+ const pendingByCollection =
1010
+ this.constructor._catalogDateRangePending ||
1011
+ (this.constructor._catalogDateRangePending = {});
1012
+
1013
+ if (cacheByCollection[normalizedCollectionId]) {
1014
+ return Promise.resolve(cacheByCollection[normalizedCollectionId]);
1015
+ }
1016
+ if (pendingByCollection[normalizedCollectionId]) {
1017
+ return pendingByCollection[normalizedCollectionId];
1018
+ }
1019
+
1020
+ const requestUrl =
1021
+ '/++api++/@get_catalogapi_dates?byoc=' +
1022
+ encodeURIComponent(normalizedCollectionId) +
1023
+ '&force_refresh=false';
1024
+
1025
+ pendingByCollection[normalizedCollectionId] = fetch(requestUrl, {
1026
+ headers: {
1027
+ Accept: 'application/json',
1028
+ },
1029
+ signal: options && options.signal,
1030
+ })
1031
+ .then((response) => {
1032
+ if (!response.ok) {
1033
+ return null;
1034
+ }
1035
+ return response.json();
1036
+ })
1037
+ .then((payload) => {
1038
+ const dates = Array.isArray(payload?.dates) ? payload.dates : [];
1039
+ const resolvedDateRange =
1040
+ dates.length > 0
1041
+ ? {
1042
+ from: dates[0],
1043
+ to: dates[dates.length - 1],
1044
+ }
1045
+ : null;
1046
+ cacheByCollection[normalizedCollectionId] = resolvedDateRange;
1047
+ delete pendingByCollection[normalizedCollectionId];
1048
+ return resolvedDateRange;
1049
+ })
1050
+ .catch(() => {
1051
+ delete pendingByCollection[normalizedCollectionId];
1052
+ return null;
1053
+ });
1054
+
1055
+ return pendingByCollection[normalizedCollectionId];
1056
+ },
1057
+
1058
+ // Computes the tile bounding box in EPSG:3857 (without projections)
1059
+ _tileToBBox3857: function (level, row, col) {
1060
+ const tileInfo = this.tileInfo;
1061
+ const origin = tileInfo.origin;
1062
+ const res = tileInfo.lods[level].resolution;
1063
+ const size = tileInfo.size[0];
1064
+
1065
+ const xmin = origin.x + col * size * res;
1066
+ const ymax = origin.y - row * size * res;
1067
+ const xmax = xmin + size * res;
1068
+ const ymin = ymax - size * res;
1069
+
1070
+ return {
1071
+ west: xmin,
1072
+ south: ymin,
1073
+ east: xmax,
1074
+ north: ymax,
1075
+ };
1076
+ },
1077
+
1078
+ // Main method that renders each tile
1079
+ fetchTile: function (level, row, col, options) {
1080
+ // Invert row if it is TMS (south origin)
1081
+ if (this.tms) {
1082
+ var rowmax = 1 << level;
1083
+ row = rowmax - row - 1;
1084
+ }
1085
+
1086
+ const bbox3857 = this._tileToBBox3857(level, row, col);
1087
+ const width = this.tileInfo.size[0];
1088
+ const height = this.tileInfo.size[1];
1089
+
1090
+ return this._resolveCatalogDateRange(options).then(
1091
+ (catalogDateRange) => {
1092
+ const resolvedTimeRangeFrom = catalogDateRange?.from || null;
1093
+ const resolvedTimeRangeTo = catalogDateRange?.to || null;
1094
+ const timeRange = {};
1095
+ if (resolvedTimeRangeFrom) {
1096
+ timeRange.from = resolvedTimeRangeFrom;
1097
+ }
1098
+ if (resolvedTimeRangeTo) {
1099
+ timeRange.to = resolvedTimeRangeTo;
1100
+ }
1101
+ const dataFilter = {};
1102
+ if (Object.keys(timeRange).length) {
1103
+ dataFilter.timeRange = timeRange;
1104
+ }
1105
+
1106
+ const payload = {
1107
+ input: {
1108
+ bounds: {
1109
+ bbox: [
1110
+ bbox3857.west,
1111
+ bbox3857.south,
1112
+ bbox3857.east,
1113
+ bbox3857.north,
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
+ },
1134
+ ],
1135
+ },
1136
+ evalscript: this.evalscript,
1137
+ };
1138
+
1139
+ return fetch(this.processUrl, {
1140
+ method: 'POST',
1141
+ headers: {
1142
+ 'Content-Type': 'application/json',
1143
+ Accept: 'image/png',
1144
+ },
1145
+ body: JSON.stringify(payload),
1146
+ signal: options && options.signal,
1147
+ })
1148
+ .then((response) => {
1149
+ if (!response.ok) {
1150
+ throw new Error(
1151
+ `Process API error ${response.status}: ${response.statusText}`,
1152
+ );
1153
+ }
1154
+ return response.blob();
1155
+ })
1156
+ .then((blob) => {
1157
+ return new Promise((resolve, reject) => {
1158
+ const url = URL.createObjectURL(blob);
1159
+ const img = new Image();
1160
+ img.onload = () => {
1161
+ const canvas = document.createElement('canvas');
1162
+ canvas.width = width;
1163
+ canvas.height = height;
1164
+ const ctx = canvas.getContext('2d');
1165
+ ctx.drawImage(img, 0, 0, width, height);
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
+ });
1175
+ });
1176
+ },
1177
+ );
1178
+ },
1179
+ });
1180
+ const tileInfo = {
1181
+ size: [512, 512],
1182
+ origin: { x: -20037508.342787, y: 20037508.342787 },
1183
+ spatialReference: { wkid: 3857 },
1184
+ lods: Array.from({ length: 21 }, (_, i) => ({
1185
+ level: i,
1186
+ resolution: 156543.033928 / Math.pow(2, i),
1187
+ scale: 591657527.591555 / Math.pow(2, i),
1188
+ })),
1189
+ };
1190
+ const fullExtent = new Extent({
1191
+ xmin: -20037508.342787,
1192
+ ymin: -20037508.342787,
1193
+ xmax: 20037508.342787,
1194
+ ymax: 20037508.342787,
1195
+ spatialReference: { wkid: 3857 },
1196
+ });
1197
+ const sharedLayerProps = {
1198
+ tms: false,
1199
+ tileInfo: tileInfo,
1200
+ fullExtent: fullExtent,
1201
+ };
1202
+
1203
+ //const lowResolutionScaleLimit = layer.thresholdScale;
1204
+
1205
+ const dualResolutionLayers = [];
1206
+ if (lowResLayerData.collectionId && lowResLayerData.evalscript) {
1207
+ dualResolutionLayers.push(
1208
+ new CDSEProcessTileLayer({
1209
+ ...sharedLayerProps,
1210
+ collectionId: lowResLayerData.collectionId,
1211
+ catalogByoc: datasetCollectionId || null,
1212
+ evalscript: lowResLayerData.evalscript,
1213
+ minScale: lowResLayerData.minScale,
1214
+ maxScale: lowResLayerData.maxScale,
1215
+ title: lowResLayerTitle,
1216
+ }),
1217
+ );
1218
+ }
1219
+ if (highResLayerData.collectionId && highResLayerData.evalscript) {
1220
+ dualResolutionLayers.push(
1221
+ new CDSEProcessTileLayer({
1222
+ ...sharedLayerProps,
1223
+ collectionId: highResLayerData.collectionId,
1224
+ catalogByoc: datasetCollectionId || null,
1225
+ evalscript: highResLayerData.evalscript,
1226
+ minScale: highResLayerData.minScale,
1227
+ maxScale: highResLayerData.maxScale,
1228
+ title: highResLayerTitle,
1229
+ }),
1230
+ );
1231
+ }
1232
+ if (!dualResolutionLayers.length) {
1233
+ return;
1234
+ }
1235
+
1236
+ this.layers[layer.LayerId + '_' + inheritedIndexLayer] = new GroupLayer({
1237
+ title: baseLayerTitle,
1238
+ visibilityMode: 'independent',
1239
+ layers: dualResolutionLayers,
1240
+ });
1241
+ }
1242
+
950
1243
  /**
951
1244
  * This method is executed after the render method is executed
952
1245
  */
@@ -2060,6 +2353,13 @@ class MenuWidget extends React.Component {
2060
2353
  LayerTitle: layer.Title,
2061
2354
  });
2062
2355
  //iterate sublayers fetching all sublayer data
2356
+ } else if (
2357
+ layer.hasLowRes === true ||
2358
+ (layer.resolution_data?.lowRes && layer.resolution_data?.highRes)
2359
+ ) {
2360
+ const datasetCollectionId =
2361
+ dataset_download_information?.items?.[0]?.byoc_collection || null;
2362
+ this.supportDualLayers(layer, inheritedIndexLayer, datasetCollectionId);
2063
2363
  } else if (viewService?.toLowerCase().includes('wms')) {
2064
2364
  viewService = viewService?.includes('?')
2065
2365
  ? viewService + '&'
@@ -6950,7 +7250,7 @@ class MenuWidget extends React.Component {
6950
7250
 
6951
7251
  getLayerTitle(layer) {
6952
7252
  let title;
6953
- if (layer.url.toLowerCase().includes('wmts')) {
7253
+ if (layer.url?.toLowerCase().includes('wmts')) {
6954
7254
  // CLMS-1105
6955
7255
  title = layer._wmtsTitle;
6956
7256
  } else {