From 66cbd6523e457adafafe4c5f27e79a7869a049c8 Mon Sep 17 00:00:00 2001 From: congchen Date: Mon, 9 Sep 2024 13:27:30 +0200 Subject: [PATCH] #10536 Enhanced Customization for Highlight Styles in Feature Selection - Enabled defining `highlightStyle` objects under `Map` and `Identify` sections in localConfig.json to allow customization of highlight styles. - Updated and added relevant tests to reflect these changes. On behalf of DB Systel GmbH --- .../data/identify/IdentifyContainer.jsx | 7 +- web/client/plugins/Identify.jsx | 1 + web/client/plugins/Map.jsx | 6 +- .../selectors/__tests__/mapInfo-test.js | 12 +- web/client/selectors/mapInfo.js | 11 +- web/client/utils/HighlightUtils.js | 124 ++++++++++-------- .../utils/__tests__/HighlightUtils-test.js | 39 +++++- 7 files changed, 132 insertions(+), 68 deletions(-) diff --git a/web/client/components/data/identify/IdentifyContainer.jsx b/web/client/components/data/identify/IdentifyContainer.jsx index 6bd5f18bfa..e52d8038c5 100644 --- a/web/client/components/data/identify/IdentifyContainer.jsx +++ b/web/client/components/data/identify/IdentifyContainer.jsx @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useEffect } from 'react'; import {Row} from 'react-bootstrap'; import { get } from 'lodash'; @@ -76,6 +76,11 @@ export default props => { onInitPlugin = () => {}, pluginCfg } = props; + + useEffect(() => { + pluginCfg?.highlightStyle && onInitPlugin({ highlightStyle: pluginCfg.highlightStyle }); + }, []); + const latlng = point && point.latlng || null; // Layer selector allows only selection of valid response's index, so target response will always be valid. diff --git a/web/client/plugins/Identify.jsx b/web/client/plugins/Identify.jsx index 30479ae208..8e33705b7e 100644 --- a/web/client/plugins/Identify.jsx +++ b/web/client/plugins/Identify.jsx @@ -197,6 +197,7 @@ const identifyDefaultProps = defaultProps({ * @prop cfg.draggable {boolean} draggable info window, when modal * @prop cfg.showHighlightFeatureButton {boolean} show the highlight feature button if the interrogation returned valid features (openlayers only) * @prop cfg.highlightEnabledFromTheStart {boolean} the highlight feature button will be activated by default if true + * @prop cfg.highlightSytle {object} custom highlight style will be merged to default if value exist * @prop cfg.viewerOptions.container {expression} the container of the viewer, expression from the context * @prop cfg.viewerOptions.header {expression} the header of the viewer, expression from the context{expression} * @prop cfg.disableCenterToMarker {bool} disable zoom to marker action diff --git a/web/client/plugins/Map.jsx b/web/client/plugins/Map.jsx index 29189553be..82944d327c 100644 --- a/web/client/plugins/Map.jsx +++ b/web/client/plugins/Map.jsx @@ -155,6 +155,7 @@ import {getHighlightLayerOptions} from "../utils/HighlightUtils"; * @class Map * @prop {array} additionalLayers static layers available in addition to those loaded from the configuration * @prop {object} mapOptions map options grouped by map type + * @prop {object} highlightStyle custom highlight Style * @prop {boolean} mapOptions.cesium.navigationTools enable cesium navigation tool (default false) * @prop {boolean} mapOptions.cesium.showSkyAtmosphere enable sky atmosphere of the globe (default true) * @prop {boolean} mapOptions.cesium.showGroundAtmosphere enable ground atmosphere of the globe (default false) @@ -209,7 +210,8 @@ class MapPlugin extends React.Component { items: PropTypes.array, onLoadingMapPlugins: PropTypes.func, onMapTypeLoaded: PropTypes.func, - pluginsCreator: PropTypes.func + pluginsCreator: PropTypes.func, + highlightStyle: PropTypes.object }; static defaultProps = { @@ -271,7 +273,7 @@ class MapPlugin extends React.Component { getHighlightLayer = (projection, index, env) => { const plugins = this.state.plugins; - const {features, ...options} = getHighlightLayerOptions({features: this.props.features}); + const {features, ...options} = getHighlightLayerOptions({features: this.props.features}, this.props?.highlightStyle); return ( { const TEST = { color: 'test' }; - // check default - expect(highlightStyleSelector({})).toEqual({ + const defaultStyle = { color: '#3388ff', weight: 4, radius: 4, dashArray: '', fillColor: '#3388ff', fillOpacity: 0.2 - }); + }; + // check default + expect(highlightStyleSelector({})).toEqual(defaultStyle); expect(highlightStyleSelector({ mapInfo: { highlightStyle: TEST } - })).toBe(TEST); + })).toEqual({ + ...defaultStyle, + ...TEST + }); }); it('test clickPointSelector', () => { expect(clickPointSelector(RESPONSE_STATE)).toBe(RESPONSE_STATE.mapInfo.clickPoint); diff --git a/web/client/selectors/mapInfo.js b/web/client/selectors/mapInfo.js index 71cc522f11..aa861cd30c 100644 --- a/web/client/selectors/mapInfo.js +++ b/web/client/selectors/mapInfo.js @@ -162,14 +162,21 @@ export const applyMapInfoStyle = style => f => ({ * @param {object} state the application state * @returns {object} style object */ -export const highlightStyleSelector = state => get(state, 'mapInfo.highlightStyle', { +const defaultHighlightStyle = { color: '#3388ff', weight: 4, radius: 4, dashArray: '', fillColor: '#3388ff', fillOpacity: 0.2 -}); +}; +// merge and replace default highlight style with custom values +export const highlightStyleSelector = state => { + return { + ...defaultHighlightStyle, + ...get(state, 'mapInfo.highlightStyle', {}) + }; +}; export const clickedPointWithFeaturesSelector = createSelector( clickPointSelector, diff --git a/web/client/utils/HighlightUtils.js b/web/client/utils/HighlightUtils.js index 7c53f07c14..ec8ba8c73f 100644 --- a/web/client/utils/HighlightUtils.js +++ b/web/client/utils/HighlightUtils.js @@ -1,62 +1,71 @@ +export const GEOMETRY_PROPERTY = '__geometry__type__'; +export function createHighlightStyle(highlightStyle = {}) { + const defaultStyle = { + color: '#f2f2f2', + lineColor: '#3075e9', + fillOpacity: 0.3, + opacity: 1, + width: 2, + radius: 10 + }; + + // Merge custom styles with default values + const style = { ...defaultStyle, ...highlightStyle }; -export const GEOMETRY_PROPERTY = '__geometry__type__'; -export const HIGH_LIGHT_STYLE = { - format: 'geostyler', - body: { - name: "highlight", - rules: [{ - name: 'Default Polygon Style', - ruleId: "defaultPolygon", - filter: ['||', - ['==', GEOMETRY_PROPERTY, 'Polygon'], - ['==', GEOMETRY_PROPERTY, 'MultiPolygon'] - ], - symbolizers: [ - { + return { + format: 'geostyler', + body: { + name: "highlight", + rules: [{ + name: 'Default Polygon Style', + ruleId: "defaultPolygon", + filter: ['||', + ['==', GEOMETRY_PROPERTY, 'Polygon'], + ['==', GEOMETRY_PROPERTY, 'MultiPolygon'] + ], + symbolizers: [{ kind: 'Fill', - color: '#f2f2f2', - fillOpacity: 0.3, - outlineColor: '#3075e9', - outlineOpacity: 1, - outlineWidth: 2 - } - ] - }, { - name: 'Default Line Style', - ruleId: "defaultLine", - filter: ['||', - ['==', GEOMETRY_PROPERTY, 'LineString'], - ['==', GEOMETRY_PROPERTY, 'MultiLineString'] - ], - symbolizers: [ - { + color: style.color, + fillOpacity: style.fillOpacity, + outlineColor: style.lineColor, + outlineOpacity: style.opacity, + outlineWidth: style.width + }] + }, { + name: 'Default Line Style', + ruleId: "defaultLine", + filter: ['||', + ['==', GEOMETRY_PROPERTY, 'LineString'], + ['==', GEOMETRY_PROPERTY, 'MultiLineString'] + ], + symbolizers: [{ kind: 'Line', - color: '#3075e9', - opacity: 1, - width: 2 - } - ] - }, { - name: 'Default Point Style', - ruleId: "defaultPoint", - filter: ['||', - ['==', GEOMETRY_PROPERTY, 'Point'], - ['==', GEOMETRY_PROPERTY, 'MultiPoint'] - ], - symbolizers: [{ - kind: 'Mark', - color: '#f2f2f2', - fillOpacity: 0.3, - strokeColor: '#3075e9', - strokeOpacity: 1, - strokeWidth: 2, - radius: 10, - wellKnownName: 'Circle', - msBringToFront: true + color: style.lineColor, + opacity: style.opacity, + width: style.width + }] + }, { + name: 'Default Point Style', + ruleId: "defaultPoint", + filter: ['||', + ['==', GEOMETRY_PROPERTY, 'Point'], + ['==', GEOMETRY_PROPERTY, 'MultiPoint'] + ], + symbolizers: [{ + kind: 'Mark', + color: style.color, + fillOpacity: style.fillOpacity, + strokeColor: style.lineColor, + strokeOpacity: style.opacity, + strokeWidth: style.width, + radius: style.radius, + wellKnownName: 'Circle', + msBringToFront: true + }] }] - }] - } -}; + } + }; +} /** * Add the the proper options to the highlight layer: @@ -64,9 +73,10 @@ export const HIGH_LIGHT_STYLE = { * - `features`: the features to highlight should be enhanced with the geometry type in properties, to allow the default style to work * - `style`: the default style applies a different style for each geometry type. The geometry type is extracted from the geometry type and added to the feature properties * @param {options} base options + * @param highlightStyle * @returns the new options with the highlight layer */ -export const getHighlightLayerOptions = ({features, ...options}) => { +export const getHighlightLayerOptions = ({ features, ...options }, highlightStyle = {}) => { return { ...options, visibility: true, // required by cesium @@ -78,6 +88,6 @@ export const getHighlightLayerOptions = ({features, ...options}) => { } })), - style: HIGH_LIGHT_STYLE + style: createHighlightStyle(highlightStyle) }; }; diff --git a/web/client/utils/__tests__/HighlightUtils-test.js b/web/client/utils/__tests__/HighlightUtils-test.js index 878b86e597..b9710efff9 100644 --- a/web/client/utils/__tests__/HighlightUtils-test.js +++ b/web/client/utils/__tests__/HighlightUtils-test.js @@ -1,5 +1,5 @@ import expect from 'expect'; -import {getHighlightLayerOptions, GEOMETRY_PROPERTY, HIGH_LIGHT_STYLE} from '../HighlightUtils'; +import {getHighlightLayerOptions, GEOMETRY_PROPERTY, createHighlightStyle} from '../HighlightUtils'; describe('HighlightUtils', () => { it('getHighlightLayerOptions', () => { @@ -29,7 +29,42 @@ describe('HighlightUtils', () => { coordinates: [0, 0] } }], - style: HIGH_LIGHT_STYLE + style: createHighlightStyle() + }); + }); + + it('getHighlightLayerOptions with custom highlight style', () => { + const costumHighlightStyle = { + color: '#33eeff', + width: 4 + }; + // adds standard options + const options = getHighlightLayerOptions({ + features: [{ + type: 'Feature', + properties: { + id: '1' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }] + }, costumHighlightStyle); + expect(options).toEqual({ + visibility: true, // required by cesium + features: [{ + type: 'Feature', + properties: { + id: '1', + [GEOMETRY_PROPERTY]: 'Point' // required by default style + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }], + style: createHighlightStyle(costumHighlightStyle) }); }); });