Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
congchen1101 committed Sep 9, 2024
1 parent 1affe02 commit 66cbd65
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 68 deletions.
7 changes: 6 additions & 1 deletion web/client/components/data/identify/IdentifyContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions web/client/plugins/Identify.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions web/client/plugins/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 (<plugins.Layer type="vector"
srs={projection}
position={index}
Expand Down
12 changes: 8 additions & 4 deletions web/client/selectors/__tests__/mapInfo-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,24 @@ describe('Test mapinfo selectors', () => {
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);
Expand Down
11 changes: 9 additions & 2 deletions web/client/selectors/mapInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
124 changes: 67 additions & 57 deletions web/client/utils/HighlightUtils.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,82 @@
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:
* - `visibility`: `true`
* - `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
Expand All @@ -78,6 +88,6 @@ export const getHighlightLayerOptions = ({features, ...options}) => {
}

})),
style: HIGH_LIGHT_STYLE
style: createHighlightStyle(highlightStyle)
};
};
39 changes: 37 additions & 2 deletions web/client/utils/__tests__/HighlightUtils-test.js
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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)
});
});
});

0 comments on commit 66cbd65

Please sign in to comment.