From 5df6e20a1038a83b63554229e722d3e55388b3a3 Mon Sep 17 00:00:00 2001 From: mahmoudadel Date: Wed, 31 Jul 2024 19:10:53 +0300 Subject: [PATCH] #10487: Custom Tile Grids settings for WMS service are 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 --- .../fragments/settings/WMSCacheOptions.jsx | 58 ++--- .../__tests__/backgroundselector-test.js | 206 ++++++++++++++++++ web/client/epics/backgroundselector.js | 20 +- web/client/epics/catalog.js | 16 +- web/client/utils/WMSUtils.js | 21 +- web/client/utils/__tests__/WMSUtils-test.js | 35 ++- 6 files changed, 298 insertions(+), 58 deletions(-) create mode 100644 web/client/epics/__tests__/backgroundselector-test.js diff --git a/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx b/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx index 803b313f16..0df967a465 100644 --- a/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx +++ b/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx @@ -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); @@ -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 (
@@ -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 + }); + }); + }} />
}
diff --git a/web/client/epics/__tests__/backgroundselector-test.js b/web/client/epics/__tests__/backgroundselector-test.js new file mode 100644 index 0000000000..87f7092ddf --- /dev/null +++ b/web/client/epics/__tests__/backgroundselector-test.js @@ -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 = ` + + + + + <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 + states_test + + features + states + + EPSG:4326 + CRS:84 + + -124.73142200000001 + -66.969849 + 24.955967 + 49.371735 + + + + + image/png + image/jpeg + image/png8 + + + +`; + + const capabilitiesWMTSXmlResponse = ` + + + + <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 + states_test + + features + states + + EPSG:4326 + CRS:84 + + -124.73142200000001 + -66.969849 + 24.955967 + 49.371735 + + + + + + + +`; + 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(); + }, {}); + }); +}); diff --git a/web/client/epics/backgroundselector.js b/web/client/epics/backgroundselector.js index 7f99ca160d..7ea5aee5c1 100644 --- a/web/client/epics/backgroundselector.js +++ b/web/client/epics/backgroundselector.js @@ -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) @@ -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; diff --git a/web/client/epics/catalog.js b/web/client/epics/catalog.js index 7a9c0beeaf..19cc9ea87a 100644 --- a/web/client/epics/catalog.js +++ b/web/client/epics/catalog.js @@ -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'; @@ -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') { diff --git a/web/client/utils/WMSUtils.js b/web/client/utils/WMSUtils.js index 120df9aa37..81439c2de4 100644 --- a/web/client/utils/WMSUtils.js +++ b/web/client/utils/WMSUtils.js @@ -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 @@ -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; +}; diff --git a/web/client/utils/__tests__/WMSUtils-test.js b/web/client/utils/__tests__/WMSUtils-test.js index 6c604655d9..1784efd574 100644 --- a/web/client/utils/__tests__/WMSUtils-test.js +++ b/web/client/utils/__tests__/WMSUtils-test.js @@ -11,7 +11,8 @@ import { isValidGetMapFormat, isValidGetFeatureInfoFormat, getLayerOptions, - getTileGridFromLayerOptions + getTileGridFromLayerOptions, + getCustomTileGridProperties } from '../WMSUtils'; describe('Test the WMSUtils', () => { @@ -63,4 +64,36 @@ describe('Test the WMSUtils', () => { expect(getTileGridFromLayerOptions({ projection: 'EPSG:32122', tileSize: 512, tileGrids })).toBe(tileGrids[0]); expect(getTileGridFromLayerOptions({ projection: 'EPSG:4326', tileSize: 256, tileGrids })).toBe(undefined); }); + it('test getCustomTileGridProperties', () => { + const tileGridData = { + formats: ['image/jpeg', 'image/png'], + tileGrids: [ + { + id: 'EPSG:32122', + crs: 'EPSG:32122', + scales: [ 2557541.55271451, 1278770.776357255, 639385.3881786275 ], + origins: [ [ 403035.4105968763, 414783 ], [ 403035.4105968763, 414783 ], [ 403035.4105968763, 323121 ] ], + tileSizes: [[ 512, 512 ], [ 512, 512 ], [ 512, 512 ]] + }, + { + id: 'EPSG:900913', + crs: 'EPSG:900913', + scales: [ 559082263.9508929, 279541131.97544646, 139770565.98772323 ], + origin: [ -20037508.34, 20037508 ], + tileSize: [ 256, 256 ] + } + ], + tileMatrixSetLinks: [], + tileMatrixSets: [], + styles: [] + }; + expect(getCustomTileGridProperties(tileGridData)).toEqual({ + tileGrids: tileGridData.tileGrids, + tiled: true, + tileGridStrategy: 'custom', + tileGridCacheSupport: { + formats: tileGridData.formats + } + }); + }); });