Skip to content

Commit

Permalink
Fix geosolutions-it#10739 Changing correctly resolutions limits when …
Browse files Browse the repository at this point in the history
…switching map CRS
  • Loading branch information
MV88 committed Jan 13, 2025
1 parent 799162c commit ce7f7ec
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 10 deletions.
43 changes: 41 additions & 2 deletions web/client/actions/__tests__/map-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
clickOnMap,
changeMousePointer,
changeZoomLevel,
changeCRS,
changeMapCrs,
changeMapScales,
changeMapStyle,
Expand All @@ -54,7 +55,7 @@ import {
updateMapOptions,
UPDATE_MAP_OPTIONS
} from '../map';

import {updateNode} from '../layers';

describe('Test correctness of the map actions', () => {

Expand Down Expand Up @@ -127,12 +128,50 @@ describe('Test correctness of the map actions', () => {

it('changes map crs', () => {
const testVal = 'EPSG:4326';
const retval = changeMapCrs(testVal);
const retval = changeCRS(testVal);

expect(retval).toExist();
expect(retval.type).toBe(CHANGE_MAP_CRS);
expect(retval.crs).toBe(testVal);
});
it('changes map crs and update resoolutions', () => {
const crs = 'EPSG:4326';
const thunk = changeMapCrs(crs);
expect(thunk).toExist();
const dispatchedActions = [];
const dispatch = (action) => {
dispatchedActions.push(action);
};
thunk(dispatch,
() => ({
layers: [ {
id: 'layer1',
minResolution: 2000,
maxResolution: 4000
},
{
id: 'layer2',
minResolution: 5000
}],
map: {present: {projection: "EPSG:3857"}}
}));
const expectedActions = [
updateNode('layer1', 'layer', { minResolution: 0.02197265625, maxResolution: 0.0439453125 }),
updateNode('layer2', 'layer', { minResolution: 0.0439453125 }),
changeCRS(crs)
];
for (let i = 0; i < expectedActions.length; i++) {
const expected = expectedActions[i];
const actual = dispatchedActions[i];
if (JSON.stringify(expected) !== JSON.stringify(actual)) {
throw new Error(
`Dispatched action at index ${i} does not match expected. \nExpected: ${JSON.stringify(
expected
)}\nActual: ${JSON.stringify(actual)}`
);
}
}
});

it('changeMapScales', () => {
const testScales = [100000, 50000, 25000, 10000, 5000];
Expand Down
35 changes: 32 additions & 3 deletions web/client/actions/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
* LICENSE file in the root directory of this source tree.
*/

import { getResolutions, convertResolution } from '../utils/MapUtils';
import { layersSelector } from '../selectors/layers';
import { projectionSelector } from '../selectors/map';
import { updateNode } from '../actions/layers';
import minBy from 'lodash/minBy';

export const CHANGE_MAP_VIEW = 'CHANGE_MAP_VIEW';
export const CLICK_ON_MAP = 'CLICK_ON_MAP';
Expand Down Expand Up @@ -81,10 +86,34 @@ export function changeMapView(center, zoom, bbox, size, mapStateSource, projecti
};
}

export const changeCRS = (crs) => ({
type: CHANGE_MAP_CRS,
crs: crs
});
export function changeMapCrs(crs) {
return {
type: CHANGE_MAP_CRS,
crs: crs
return (dispatch, getState = () => {}) => {
const state = getState();
const sourceCRS = projectionSelector(state);
const layersWithLimits = layersSelector(state).filter(l => l.minResolution || l.maxResolution);
layersWithLimits.forEach(layer => {
const options = {};
const newResolutions = getResolutions(crs);
if (layer.minResolution) {
options.minResolution = convertResolution(sourceCRS, crs, layer.minResolution).transformedResolution;
const diffs = newResolutions.map((resolution, zoom) => ({ diff: Math.abs(resolution - options.minResolution), zoom }));
const { zoom } = minBy(diffs, 'diff');
options.minResolution = newResolutions[zoom];
}
if (layer.maxResolution) {
options.maxResolution = convertResolution(sourceCRS, crs, layer.maxResolution).transformedResolution;
const diffs = newResolutions.map((resolution, zoom) => ({ diff: Math.abs(resolution - options.maxResolution), zoom }));
const { zoom } = minBy(diffs, 'diff');
options.maxResolution = newResolutions[zoom];
}
// the minimum difference represents the nearest zoom to the target resolution
dispatch(updateNode(layer.id, "layer", options));
});
dispatch(changeCRS(crs));
};
}

Expand Down
3 changes: 2 additions & 1 deletion web/client/components/map/openlayers/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,8 @@ class OpenlayersMap extends React.Component {
multiWorld: true,
// does not allow intermediary zoom levels
// we need this at true to set correctly the scale box
constrainResolution: true
constrainResolution: true,
resolutions: this.getResolutions(normalizeSRS(projection))
}, newOptions || {});
return new View(viewOptions);
};
Expand Down
6 changes: 3 additions & 3 deletions web/client/epics/__tests__/map-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import expect from 'expect';

import { resetLimitsOnInit, zoomToExtentEpic, checkMapPermissions } from '../map';
import { CHANGE_MAP_VIEW, zoomToExtent, CHANGE_MAP_LIMITS, changeMapCrs } from '../../actions/map';
import { CHANGE_MAP_VIEW, zoomToExtent, CHANGE_MAP_LIMITS, changeCRS } from '../../actions/map';
import { LOAD_MAP_INFO, configureMap } from '../../actions/config';
import { testEpic, addTimeoutEpic, TEST_TIMEOUT } from './epicTestUtils';
import MapUtils from '../../utils/MapUtils';
Expand Down Expand Up @@ -209,7 +209,7 @@ describe('map epics', () => {
done();
}, state);
});
it('test changeMapCrs causes limits change. ', (done) => {
it.only('test changeMapCrs causes limits change. ', (done) => {
const state = {
map: {
present: {
Expand All @@ -226,7 +226,7 @@ describe('map epics', () => {
}
}
};
testEpic(resetLimitsOnInit, 1, changeMapCrs("EPSG:1234"), ([action]) => {
testEpic(resetLimitsOnInit, 1, changeCRS("EPSG:1234"), ([action]) => {
const { restrictedExtent, type, minZoom } = action;
expect(restrictedExtent.length).toBe(4);
expect(restrictedExtent).toEqual([1, 1, 1, 1]);
Expand Down
62 changes: 62 additions & 0 deletions web/client/utils/MapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ import {
minBy,
omit
} from 'lodash';
import { get as getProjectionOL, getPointResolution, transform } from 'ol/proj';
import { get as getExtent } from 'ol/proj/projections';

import uuidv1 from 'uuid/v1';

import { getUnits, normalizeSRS, reproject } from './CoordinatesUtils';

import { getProjection } from './ProjectionUtils';

import { set } from './ImmutableUtils';
import {
saveLayer,
Expand Down Expand Up @@ -339,6 +343,64 @@ export function getScales(projection, dpi) {
const dpu = dpi2dpu(dpi, projection);
return getResolutions(projection).map((resolution) => resolution * dpu);
}

export function getScale(projection, dpi, resolution) {
const dpu = dpi2dpu(dpi, projection);
return resolution * dpu;
}
/**
* get random coordinates within CRS extent
* @param {string} crs the code of the projection for example EPSG:4346
* @returns {number[]} the point in [x,y] [lon,lat]
*/
export function getRandomPointInCRS(crs) {
const extent = getExtent(crs); // Get the projection's extent
if (!extent) {
throw new Error(`Extent not available for CRS: ${crs}`);
}
const [minX, minY, maxX, maxY] = extent.extent_;

// Check if the equator (latitude = 0) is within the CRS extent
const isEquatorWithinExtent = minY <= 0 && maxY >= 0;

// Generate a random X coordinate within the valid longitude range
const randomX = Math.random() * (maxX - minX) + minX;

// Set Y to 0 if the equator is within the extent, otherwise generate a random Y
const randomY = isEquatorWithinExtent ? 0 : Math.random() * (maxY - minY) + minY;

return [randomX, randomY];
}

/**
* convert resolution between CRSs
* @param {string} sourceCRS the code of a projection
* @param {string} targetCRS the code of a projection
* @param {number} sourceResolution the resolution to convert
* @returns the converted resolution
*/
export function convertResolution(sourceCRS, targetCRS, sourceResolution) {
const sourceProjection = getProjectionOL(sourceCRS);
const targetProjection = getProjectionOL(targetCRS);

if (!sourceProjection || !targetProjection) {
throw new Error(`Invalid CRS: ${sourceCRS} or ${targetCRS}`);
}

// Get a random point in the extent of the source CRS
const randomPoint = getRandomPointInCRS(sourceCRS);

// Transform the resolution
const transformedResolution = getPointResolution(
sourceProjection,
sourceResolution,
transform(randomPoint, sourceCRS, targetCRS),
targetProjection.getUnits()
);

return { randomPoint, transformedResolution };
}

/**
* Convert a resolution to the nearest zoom
* @param {number} targetResolution resolution to be converted in zoom
Expand Down
12 changes: 11 additions & 1 deletion web/client/utils/__tests__/MapUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import {
mapUpdated,
getZoomFromResolution,
getResolutionObject,
reprojectZoom
reprojectZoom,
getRandomPointInCRS,
convertResolution
} from '../MapUtils';
import { VisualizationModes } from '../MapTypeUtils';

Expand Down Expand Up @@ -2416,6 +2418,7 @@ describe('Test the MapUtils', () => {
const resolution = 1000; // ~zoom 7 in Web Mercator
expect(getZoomFromResolution(resolution)).toBe(7);
});

it('reprojectZoom', () => {
expect(reprojectZoom(5, 'EPSG:3857', 'EPSG:4326')).toBe(4);
expect(reprojectZoom(5.2, 'EPSG:3857', 'EPSG:4326')).toBe(4);
Expand All @@ -2431,4 +2434,11 @@ describe('Test the MapUtils', () => {
.toEqual({ resolution: 9028, scale: 34121574.80314961, zoom: 4 });
});
});
it('getRandomPointInCRS', () => {
expect(getRandomPointInCRS('EPSG:3857').length).toBe(2);
expect(getRandomPointInCRS('EPSG:4326').length).toBe(2);
});
it('convertResolution', () => {
expect(convertResolution('EPSG:3857', 'EPSG:4326', 2000).transformedResolution).toBe(0.017986440587896155);
});
});

0 comments on commit ce7f7ec

Please sign in to comment.