Skip to content

Commit

Permalink
geosolutions-it#10487: Custom Tile Grids settings for WMS service are…
Browse files Browse the repository at this point in the history
… not retained when adding a Background layer from Catalog [resolve review]

- revert the prev changes
- handle the requirements in addBackgroundPropertiesEpic
- create a new wms util 'getCustomTileGridProperties'
- add unit tests
  • Loading branch information
mahmoudadel54 committed Jul 31, 2024
1 parent a1dcd22 commit 5df6e20
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 58 deletions.
58 changes: 21 additions & 37 deletions web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,12 @@ const infoText = {
* @prop {object} layer layer configuration
* @prop {boolean} disableTileGrids disable tile grids toolbar
* @prop {function} onChange callback triggered after changing the form
* @prop {string} owner the owner component name like: 'background-dialog'
*/
function WMSCacheOptions({
layer = {},
projection,
onChange,
disableTileGrids,
owner = ""
disableTileGrids
}) {

const [tileGridLoading, setTileGridLoading] = useState(false);
Expand Down Expand Up @@ -268,41 +266,8 @@ function WMSCacheOptions({
.finally(() => setTimeout(() => setTileGridLoading(false), 500));
};

const handleGetTileGridSettings = () => {
const newTileGridStrategy = layer.tileGridStrategy !== 'custom'
? 'custom'
: undefined;
const promise = newTileGridStrategy === 'custom'
&& ((layer?.tileGrids?.length || 0) === 0 || !layer?.tileGridCacheSupport)
? onTileMatrixSetsFetch(layer)
: Promise.resolve(undefined);
return promise.then(({ tileGrids, tileGridCacheSupport } = {}) => {
const hasTileGrids = (tileGrids?.length || 0) > 0;
const tileGridStrategy = hasTileGrids
? newTileGridStrategy
: undefined;
let tileChangedData = {
tileGridCacheSupport,
tileGridStrategy,
tileGrids
};
if (owner === 'background-dialog' && newTileGridStrategy === 'custom') {
tileChangedData = {
...tileChangedData, tiled: true, tileGridStrategy: 'custom'
};
}
handleOnChange(tileChangedData);
});
};

const InfoText = infoText[layer.tileGridStrategy] || infoText.standard;

// fetching grid data in case background layers
React.useEffect(() => {
if (layer.remoteTileGrids && owner === 'background-dialog') {
handleGetTileGridSettings();
}
}, []);
return (
<div className="ms-wms-cache-options">
<div className="ms-wms-cache-options-content">
Expand Down Expand Up @@ -355,7 +320,26 @@ function WMSCacheOptions({
glyph={layer.tileGridStrategy === 'custom' ? 'grid-custom' : 'grid-regular'}
bsStyle={layer.tileGridStrategy === 'custom' ? 'success' : 'primary'}
className="square-button-md"
onClick={handleGetTileGridSettings}
onClick={() => {
const newTileGridStrategy = layer.tileGridStrategy !== 'custom'
? 'custom'
: undefined;
const promise = newTileGridStrategy === 'custom'
&& ((layer?.tileGrids?.length || 0) === 0 || !layer?.tileGridCacheSupport)
? onTileMatrixSetsFetch(layer)
: Promise.resolve(undefined);
return promise.then(({ tileGrids, tileGridCacheSupport } = {}) => {
const hasTileGrids = (tileGrids?.length || 0) > 0;
const tileGridStrategy = hasTileGrids
? newTileGridStrategy
: undefined;
handleOnChange({
tileGridCacheSupport,
tileGridStrategy,
tileGrids
});
});
}}
/>
</div>}
</div>
Expand Down
206 changes: 206 additions & 0 deletions web/client/epics/__tests__/backgroundselector-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* Copyright 2014, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import expect from 'expect';

import { addBackgroundProperties, SET_BACKGROUND_MODAL_PARAMS } from '../../actions/backgroundselector';
import { testEpic } from './epicTestUtils';
import backgroundEpics from '../backgroundselector';
import MockAdapter from 'axios-mock-adapter';
import axios from '../../libs/ajax';
let mockAxios;

describe('addBackgroundPropertiesEpic Epics', () => {
beforeEach((done) => {
mockAxios = new MockAdapter(axios);
setTimeout(done);
});
afterEach((done) => {
mockAxios.restore();
setTimeout(done);
});
const capabilitiesWMSXmlResponse = `<?xml version="1.0" encoding="UTF-8"?>
<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
<Contents>
<Layer>
<Title/>
<Abstract/>
<!--Limited list of EPSG projections:-->
<BoundingBox CRS="CRS:84" minx="-124.73142200000001" miny="-43.648056" maxx="148.47914100000003" maxy="49.371735"/>
<Layer queryable="1" opaque="0">
<Name>states_test</Name>
<Title>states_test</Title>
<Abstract>states_test</Abstract>
<KeywordList>
<Keyword>features</Keyword>
<Keyword>states</Keyword>
</KeywordList>
<CRS>EPSG:4326</CRS>
<CRS>CRS:84</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>-124.73142200000001</westBoundLongitude>
<eastBoundLongitude>-66.969849</eastBoundLongitude>
<southBoundLatitude>24.955967</southBoundLatitude>
<northBoundLatitude>49.371735</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="CRS:84" minx="-124.73142200000001" miny="24.955967" maxx="-66.969849" maxy="49.371735"/>
<BoundingBox CRS="EPSG:4326" minx="24.955967" miny="-124.73142200000001" maxx="49.371735" maxy="-66.969849"/>
<Style>
<Name>polygon</Name>
<Title>A boring default style</Title>
<Abstract>A sample style that just prints out a transparent red interior with a red outline</Abstract>
<LegendURL width="20" height="20">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://gs-stable.geo-solutions.it/geoserver/topp/ows?service=WMS&amp;request=GetLegendGraphic&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=states_test"/>
</LegendURL>
</Style>
<Format>image/png</Format>
<Format>image/jpeg</Format>
<Format>image/png8</Format>
</Layer>
</Layer>
</Contents>
</Capabilities>`;

const capabilitiesWMTSXmlResponse = `<?xml version="1.0" encoding="UTF-8"?><WMS_Capabilities version="1.3.0" updateSequence="5167" xmlns="http://www.opengis.net/wms" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wms http://gs-stable.geo-solutions.it/geoserver/schemas/wms/1.3.0/capabilities_1_3_0.xsd">
<Capability>
<Layer>
<Title/>
<Abstract/>
<!--Limited list of EPSG projections:-->
<BoundingBox CRS="CRS:84" minx="-124.73142200000001" miny="-43.648056" maxx="148.47914100000003" maxy="49.371735"/>
<Layer queryable="1" opaque="0">
<Name>states_test</Name>
<Title>states_test</Title>
<Abstract>states_test</Abstract>
<KeywordList>
<Keyword>features</Keyword>
<Keyword>states</Keyword>
</KeywordList>
<CRS>EPSG:4326</CRS>
<CRS>CRS:84</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>-124.73142200000001</westBoundLongitude>
<eastBoundLongitude>-66.969849</eastBoundLongitude>
<southBoundLatitude>24.955967</southBoundLatitude>
<northBoundLatitude>49.371735</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="CRS:84" minx="-124.73142200000001" miny="24.955967" maxx="-66.969849" maxy="49.371735"/>
<BoundingBox CRS="EPSG:4326" minx="24.955967" miny="-124.73142200000001" maxx="49.371735" maxy="-66.969849"/>
<Style>
<Name>polygon</Name>
<Title>A boring default style</Title>
<Abstract>A sample style that just prints out a transparent red interior with a red outline</Abstract>
<LegendURL width="20" height="20">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://gs-stable.geo-solutions.it/geoserver/topp/ows?service=WMS&amp;request=GetLegendGraphic&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=states_test"/>
</LegendURL>
</Style>
</Layer>
</Layer>
</Capability>
</WMS_Capabilities>`;
it('test add normal background layer', (done) => {
mockAxios.onGet().reply(200, capabilitiesWMSXmlResponse);
let addBackgroundPropAction = addBackgroundProperties({
layer: {
id: "layer01", allowedSRS: {
"EPSG:3857": true,
"EPSG:900913": true,
"EPSG:4326": true
},
title: "states_test",
type: 'wms',
name: "states_test",
url: ['/geoserver/wms'],
group: "background"
},
editing: false
});

testEpic(backgroundEpics.addBackgroundPropertiesEpic, 2, [addBackgroundPropAction], (res) => {
const action1 = res[0];
const action2 = res[1];
expect(action1).toExist();
expect(action1.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action1.modalParams.loading).toEqual(true);
expect(action2).toExist();
expect(action2.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action2.modalParams.loading).toEqual(false);
expect(action2.modalParams.capabilities).toExist();
done();
}, {});

});
it('test add wms as a background layer without enabling remote custom tile grid', (done) => {
mockAxios.onGet().reply(200, capabilitiesWMSXmlResponse);
let addBackgroundPropAction = addBackgroundProperties({
layer: {
id: "layer01", allowedSRS: {
"EPSG:3857": true,
"EPSG:900913": true,
"EPSG:4326": true
},
title: "states_test",
type: 'wms',
name: "states_test",
url: '/geoserver/wms',
group: "background"
},
editing: false
});

testEpic(backgroundEpics.addBackgroundPropertiesEpic, 2, [addBackgroundPropAction], (res) => {
const action1 = res[0];
const action2 = res[1];
expect(action1).toExist();
expect(action1.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action1.modalParams.loading).toEqual(true);
expect(action2).toExist();
expect(action2.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action2.modalParams.loading).toEqual(false);
expect(action2.modalParams.capabilities).toExist();
done();
}, {});
});
it('test add wms as a background layer with enabling remote custom tile grid', (done) => {
mockAxios.onGet().replyOnce(200, capabilitiesWMSXmlResponse);
mockAxios.onGet().replyOnce(200, capabilitiesWMTSXmlResponse);
let addBackgroundPropAction = addBackgroundProperties({
layer: {
id: "layer01", allowedSRS: {
"EPSG:3857": true,
"EPSG:900913": true,
"EPSG:4326": true
},
title: "states_test",
type: 'wms',
name: "states_test",
url: '/geoserver/wms',
group: "background",
remoteTileGrids: true
},
editing: false
});

testEpic(backgroundEpics.addBackgroundPropertiesEpic, 2, [addBackgroundPropAction], (res) => {
const action1 = res[0];
const action2 = res[1];
expect(action1).toExist();
expect(action1.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action1.modalParams.loading).toEqual(true);
expect(action2).toExist();
expect(action2.type).toEqual(SET_BACKGROUND_MODAL_PARAMS);
expect(action2.modalParams.loading).toEqual(false);
expect(action2.modalParams.capabilities).toExist();
expect(action2.modalParams.layer.tiled).toEqual(true);
expect(action2.modalParams.layer.tileGridStrategy).toEqual('custom');
done();
}, {});
});
});
20 changes: 15 additions & 5 deletions web/client/epics/backgroundselector.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import { UPDATE_NODE, ADD_LAYER, changeLayerProperties, removeNode} from '../act
import { getLayerFromId, currentBackgroundSelector } from '../selectors/layers';
import { backgroundLayersSelector } from '../selectors/backgroundselector';
import { getLayerCapabilities } from '../observables/wms';
import { getLayerOptions } from '../utils/WMSUtils';
import { getCustomTileGridProperties, getLayerOptions } from '../utils/WMSUtils';
import { getLayerTileMatrixSetsInfo } from '../api/WMTS';
import { generateGeoServerWMTSUrl } from '../utils/WMTSUtils';

const accessMetadataExplorer = (action$) =>
action$.ofType(ADD_BACKGROUND)
Expand All @@ -46,12 +48,20 @@ const addBackgroundPropertiesEpic = (action$) =>
action$.ofType(ADD_BACKGROUND_PROPERTIES)
.switchMap(({modalParams}) => {
const defaultAction = Rx.Observable.of(setBackgroundModalParams({...modalParams, loading: false}));
const isTileGridNeeded = (!modalParams.editing && modalParams?.layer?.remoteTileGrids);
return modalParams.layer && modalParams.layer.type === 'wms' ?
Rx.Observable.of(setBackgroundModalParams({...modalParams, loading: true}))
.concat(getLayerCapabilities(modalParams.layer)
.switchMap(capabilities => Rx.Observable.of(
setBackgroundModalParams({...modalParams, loading: false, capabilities: getLayerOptions(capabilities)})
))
.concat(Rx.Observable.forkJoin(getLayerCapabilities(modalParams.layer), (!isTileGridNeeded) ?
Rx.Observable.of(null) :
Rx.Observable.defer(() => getLayerTileMatrixSetsInfo(generateGeoServerWMTSUrl(modalParams.layer), modalParams.layer.name, modalParams.layer))
.catch(() => Rx.Observable.of(null)))

.switchMap(([capabilities, tileGridData]) => {
const tileGridProperties = tileGridData ? getCustomTileGridProperties(tileGridData) : {};
return Rx.Observable.of(
setBackgroundModalParams({...modalParams, layer: {...modalParams.layer, ...tileGridProperties}, loading: false, capabilities: getLayerOptions(capabilities)})
);
})
.catch(() => defaultAction)
)
: defaultAction;
Expand Down
16 changes: 2 additions & 14 deletions web/client/epics/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ import { extractGeometryType } from '../utils/WFSLayerUtils';
import { createDefaultStyle } from '../utils/StyleUtils';
import { removeDuplicateLines } from '../utils/StringUtils';
import { logError } from '../utils/DebugUtils';
import { isProjectionAvailable } from '../utils/ProjectionUtils';
import { getCustomTileGridProperties } from '../utils/WMSUtils';
import {getLayerTileMatrixSetsInfo} from '../api/WMTS';
import { getLayerMetadata } from '../api/ArcGIS';

Expand Down Expand Up @@ -283,19 +283,7 @@ export default (API) => ({
.catch(() => Rx.Observable.of(null))
)
.switchMap(([results, tileGridData]) => {
let tileGridProperties = {};
if (tileGridData) {
const filteredTileGrids = tileGridData.tileGrids.filter(({ crs }) => isProjectionAvailable(CoordinatesUtils.normalizeSRS(crs)));
tileGridProperties = tileGridData !== undefined ? {
tiled: true,
tileGrids: tileGridData.tileGrids,
tileGridStrategy: 'custom',
tileGridCacheSupport: filteredTileGrids?.length > 0 ?
tileGridData.formats ? {formats: tileGridData.formats} : {}
: undefined
} : {};

}
const tileGridProperties = tileGridData ? getCustomTileGridProperties(tileGridData) : {};
if (results) {
let description = find(results, (desc) => desc.name === layer.name );
if (description && description.owsType === 'WFS') {
Expand Down
21 changes: 20 additions & 1 deletion web/client/utils/WMSUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/
import { uniq, isObject, castArray } from 'lodash';
import { getAvailableInfoFormat } from "./MapInfoUtils";
import { normalizeSRS } from "./CoordinatesUtils";
import CoordinatesUtils, { normalizeSRS } from "./CoordinatesUtils";
import { isProjectionAvailable } from './ProjectionUtils';

// this list provides the supported GetMap formats
// and it will be used to validate GetMap formats coming from capabilities
Expand Down Expand Up @@ -112,3 +113,21 @@ export const getTileGridFromLayerOptions = ({
&& !!((tileGrid.tileSizes?.[0]?.[0] || tileGrid.tileSize?.[0]) === tileSize)
);
};

/**
* Return the tileGrids properties like tiled, tileGrids, tileGridCacheSupport and tileGridStrategy
* @param {object} tileGridData tile grid object that includes tileGrids array, formats, tileMatrixSets ..etc
* @return {object} the needed tile grid properties to appended to the layer object
*/
export const getCustomTileGridProperties = (tileGridData) => {
const filteredTileGrids = tileGridData.tileGrids.filter(({ crs }) => isProjectionAvailable(CoordinatesUtils.normalizeSRS(crs)));
const tileGridProperties = tileGridData !== undefined ? {
tiled: true,
tileGrids: tileGridData.tileGrids,
tileGridStrategy: 'custom',
tileGridCacheSupport: filteredTileGrids?.length > 0 ?
tileGridData.formats ? {formats: tileGridData.formats} : {}
: undefined
} : {};
return tileGridProperties;
};
Loading

0 comments on commit 5df6e20

Please sign in to comment.