diff --git a/web/client/components/map/openlayers/Map.jsx b/web/client/components/map/openlayers/Map.jsx
index 3e5550da1e9..069c8f51b20 100644
--- a/web/client/components/map/openlayers/Map.jsx
+++ b/web/client/components/map/openlayers/Map.jsx
@@ -353,14 +353,34 @@ class OpenlayersMap extends React.Component {
* - custom grid set with custom extent. You need to customize the projection definition extent to make it work.
* - custom grid set is partially supported by mapOptions.view.resolutions but this is not managed by projection change yet
* - custom tile sizes
- *
+ * ** NOTES**: If mapOptions.view.resolutions + mapOptions.view.projection are provided and match → will use them.
+ * - Else → will compute resolutions for provided/mapView projection.
*/
getResolutions = (srs) => {
- if (this.props.mapOptions && this.props.mapOptions.view && this.props.mapOptions.view.resolutions) {
- return this.props.mapOptions.view.resolutions;
+ // Resolve requested projection
+ const requestedProj = srs
+ ? getProjection(srs)
+ : (this.map?.getView()?.getProjection());
+ const requestedSRS = normalizeSRS(srs || requestedProj.getCode());
+
+ // Check for explicitly configured resolutions + matching projection
+ const viewOptions = this.props?.mapOptions?.view || {};
+ const configuredResolutions = viewOptions.resolutions;
+ const configuredResProjection = viewOptions.projection && normalizeSRS(viewOptions.projection);
+
+ // If resolutions are explicitly configured *and* tied to a projection that matches our target,
+ // return them directly — avoids recomputation and ensures consistency with custom tile sources.
+ if (
+ configuredResolutions &&
+ Array.isArray(configuredResolutions) &&
+ configuredResProjection &&
+ requestedSRS === configuredResProjection
+ ) {
+ return configuredResolutions;
}
- const projection = srs ? getProjection(srs) : this.map.getView().getProjection();
- const extent = projection.getExtent();
+
+ // Otherwise compute dynamically
+ const extent = requestedProj.getExtent();
return getResolutionsForProjection(
srs ?? this.map.getView().getProjection().getCode(),
{
@@ -513,15 +533,34 @@ class OpenlayersMap extends React.Component {
};
createView = (center, zoom, projection, options, limits = {}) => {
+ const srs = normalizeSRS(projection);
// limit has a crs defined
const extent = limits.restrictedExtent && limits.crs && reprojectBbox(limits.restrictedExtent, limits.crs, normalizeSRS(projection));
- const newOptions = !options || (options && !options.view) ? Object.assign({}, options, { extent }) : Object.assign({}, options);
+
+ // Determine whether to use configured resolutions
+ const configuredResolutions = options?.resolutions;
+ const configuredProj = normalizeSRS(options?.projection);
+
+ let resolutionsToUse;
+ if (configuredResolutions && configuredProj === normalizeSRS(projection)) {
+ // use provided resolutions (keep backward compatibility)
+ resolutionsToUse = configuredResolutions;
+ } else {
+ // compute resolutions dynamically (e.g., EPSG:4326)
+ resolutionsToUse = this.getResolutions(normalizeSRS(projection));
+ }
+ const newOptions = {
+ ...options,
+ projection: srs,
+ resolutions: resolutionsToUse,
+ extent: options?.extent !== undefined ? options.extent : extent
+ };
/*
* setting the zoom level in the localConfig file is co-related to the projection extent(size)
* it is recommended to use projections with the same coverage area (extent). If you want to have the same restricted zoom level (minZoom)
*/
const viewOptions = Object.assign({}, {
- projection: normalizeSRS(projection),
+ projection: srs,
center: [center.x, center.y],
zoom: zoom,
minZoom: limits.minZoom,
@@ -530,8 +569,10 @@ class OpenlayersMap extends React.Component {
// does not allow intermediary zoom levels
// we need this at true to set correctly the scale box
constrainResolution: true,
- resolutions: this.getResolutions(normalizeSRS(projection))
- }, newOptions || {});
+ resolutions: this.getResolutions(srs)
+ },
+ newOptions || {}
+ );
return new View(viewOptions);
};
diff --git a/web/client/components/map/openlayers/__tests__/Map-test.jsx b/web/client/components/map/openlayers/__tests__/Map-test.jsx
index 7af254ce988..ec40f1c62b8 100644
--- a/web/client/components/map/openlayers/__tests__/Map-test.jsx
+++ b/web/client/components/map/openlayers/__tests__/Map-test.jsx
@@ -1404,7 +1404,7 @@ describe('OpenlayersMap', () => {
, document.getElementById("map")
@@ -1460,7 +1460,7 @@ describe('OpenlayersMap', () => {
, document.getElementById("map")
@@ -1523,4 +1523,41 @@ describe('OpenlayersMap', () => {
// center is modified
expect(map.map.getView().getCenter()).toEqual([10.3346773790, 43.9323234388]);
});
+ it('should correctly apply view projection without propagating to zoom changes', () => {
+ const resolutions = [0.0005, 0.0004, 0.0003, 0.0002];
+ const map = ReactDOM.render(
+ ,
+ document.getElementById("map")
+ );
+
+ const view = map.map.getView();
+ expect(view.getProjection().getCode()).toBe('EPSG:4326');
+ expect(view.getResolutions()).toEqual(resolutions); // Custom resolutions applied
+
+ // Simulate a zoom change
+ view.setZoom(3);
+ expect(view.getProjection().getCode()).toBe('EPSG:4326');
+
+ // Simulate receiving new props with a different projection
+ ReactDOM.render(
+ ,
+ document.getElementById("map")
+ );
+
+ const updatedView = map.map.getView();
+ updatedView.setZoom(5);
+ expect(updatedView.getProjection().getCode()).toBe('EPSG:3857');
+ expect(updatedView.getResolutions()).toNotEqual(resolutions);
+ });
+
});
diff --git a/web/client/components/print/MapPreview.jsx b/web/client/components/print/MapPreview.jsx
index 49a5d44b90a..0861f58de16 100644
--- a/web/client/components/print/MapPreview.jsx
+++ b/web/client/components/print/MapPreview.jsx
@@ -149,7 +149,8 @@ class MapPreview extends React.Component {
let mapOptions = !isEmpty(resolutions) || !isNil(this.props.rotation) ? {
view: {
...(!isEmpty(resolutions) && {resolutions}),
- rotation: !isNil(this.props.rotation) ? Number(this.props.rotation) : 0
+ rotation: !isNil(this.props.rotation) ? Number(this.props.rotation) : 0,
+ projection
}
} : {};