Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#10684: Legend filtering for GeoServer WMS layers #10718

Merged
merged 7 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions web/client/components/TOC/fragments/settings/Display.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
* LICENSE file in the root directory of this source tree.
*/

import { clamp, isNil, isNumber } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import clamp from 'lodash/clamp';
import isNil from 'lodash/isNil';
import isNumber from 'lodash/isNumber';
import pick from 'lodash/pick';
import PropTypes from 'prop-types';
import {Checkbox, Col, ControlLabel, FormGroup, Glyphicon, Grid, Row, Button as ButtonRB } from 'react-bootstrap';

import tooltip from '../../../misc/enhancers/buttonTooltip';
const Button = tooltip(ButtonRB);
import IntlNumberFormControl from '../../../I18N/IntlNumberFormControl';
Expand All @@ -26,6 +30,7 @@ import ThreeDTilesSettings from './ThreeDTilesSettings';
import ModelTransformation from './ModelTransformation';
import StyleBasedWMSJsonLegend from '../../../../plugins/TOC/components/StyleBasedWMSJsonLegend';
import { getMiscSetting } from '../../../../utils/ConfigUtils';

export default class extends React.Component {
static propTypes = {
opacityText: PropTypes.node,
Expand All @@ -38,6 +43,8 @@ export default class extends React.Component {
isLocalizedLayerStylesEnabled: PropTypes.bool,
isCesiumActive: PropTypes.bool,
projection: PropTypes.string,
mapSize: PropTypes.object,
mapBbox: PropTypes.object,
resolutions: PropTypes.array,
zoom: PropTypes.number,
hideInteractiveLegendOption: PropTypes.bool
Expand Down Expand Up @@ -122,6 +129,9 @@ export default class extends React.Component {
}
return null;
};
getLegendProps = () => {
return pick(this.props, ['projection', 'mapSize', 'mapBbox']);
}
render() {
const formatValue = this.props.element && this.props.element.format || "image/png";
const experimentalInteractiveLegend = getMiscSetting('experimentalInteractiveLegend', false);
Expand Down Expand Up @@ -324,6 +334,7 @@ export default class extends React.Component {
this.useLegendOptions() && this.state.legendOptions.legendWidth || undefined}
language={
this.props.isLocalizedLayerStylesEnabled ? this.props.currentLocaleLanguage : undefined}
{...this.getLegendProps()}
/> :
<Legend
style={this.setOverFlow() && {} || undefined}
Expand All @@ -334,6 +345,7 @@ export default class extends React.Component {
this.useLegendOptions() && this.state.legendOptions.legendWidth || undefined}
language={
this.props.isLocalizedLayerStylesEnabled ? this.props.currentLocaleLanguage : undefined}
{...this.getLegendProps()}
/>}
</div>
</Col>
Expand Down
3 changes: 3 additions & 0 deletions web/client/components/widgets/builder/wizard/map/TOC.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ function WidgetTOC({
visualizationMode: map?.visualizationMode,
layerOptions: {
legendOptions: {
projection: map?.projection,
mapSize: map?.size,
mapBbox: map?.bbox,
WMSLegendOptions: 'forceLabels:on',
scaleDependent: true,
legendWidth: 12,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export default compose(
: 1
}
},
projection: map.projection,
mapSize: map.size,
mapBbox: map.bbox,
groups: get(splitMapAndLayers(map), 'layers.groups')
})),
// adapter for handlers
Expand Down
37 changes: 33 additions & 4 deletions web/client/components/widgets/enhancers/legendWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,63 @@
*/
import {compose, withHandlers, withProps} from 'recompose';

import { castArray, get } from 'lodash';
import { castArray, get, isEmpty, find } from 'lodash';
import deleteWidget from './deleteWidget';
import { editableWidget, defaultIcons, withHeaderTools } from './tools';
import { getScales } from '../../../utils/MapUtils';
import { WIDGETS_MAPS_REGEX } from "../../../actions/widgets";
import { getInactiveNode, DEFAULT_GROUP_ID } from '../../../utils/LayersUtils';
import { composeFilterObject } from './utils';
import { toCQLFilter } from '../../../utils/FilterUtils';
import { arrayUpdate } from '../../../utils/ImmutableUtils';
import { optionsToVendorParams } from '../../../utils/VendorParamsUtils';

/**
* map dependencies to layers, scales and current zoom level to show legend items for current zoom.
* Add also base tools and menu to the widget
*/
export default compose(
withProps(({ dependencies = {}, dependenciesMap = {} }) => {
withProps(({ dependencies = {}, dependenciesMap = {}, mapSync }) => {
const allLayers = dependencies[dependenciesMap.layers] || dependencies.layers || [];
const groups = castArray(dependencies[dependenciesMap.groups] || dependencies.groups || []);
const layers = allLayers
let layers = allLayers
// filter backgrounds and inactive layer
// the inactive layers are the one with a not visible parent group
.filter((layer = {}) =>
layer.group !== 'background' && !getInactiveNode(layer?.group || DEFAULT_GROUP_ID, groups)
)
.map(({ group, ...layer }) => layer);
const targetLayerName = dependencies && dependencies.layer && dependencies.layer.name;
const filterObj = dependencies.filter || {};
const layerInCommon = find(layers, {name: targetLayerName}) || {};
let filterObjCollection = {};
let layersUpdatedWithCql = {};
let cqlFilter = undefined;

if (mapSync && !isEmpty(layerInCommon) && (filterObj.featureTypeName ? filterObj.featureTypeName === targetLayerName : true)) {
if (dependencies.quickFilters) {
filterObjCollection = {...filterObjCollection, ...composeFilterObject(filterObj, dependencies.quickFilters, dependencies.options)};
}
cqlFilter = toCQLFilter(filterObjCollection);
if (!isEmpty(filterObjCollection) && cqlFilter) {
layersUpdatedWithCql = arrayUpdate(false,
{...layerInCommon, params: optionsToVendorParams({ params: {CQL_FILTER: cqlFilter}})},
{name: targetLayerName},
layers
);
}
} else {
layersUpdatedWithCql = layers.map(l => ({...l, params: {...l.params, CQL_FILTER: undefined}}));
}
layers = layersUpdatedWithCql;
dsuren1 marked this conversation as resolved.
Show resolved Hide resolved
return {
allLayers,
map: {
layers,
// use empty so it creates the default group that will be hidden in the layers tree
groups: []
groups: [],
projection: dependencies.projection,
bbox: dependencies.viewport
},
dependencyMapPath: dependenciesMap.layers || '',
scales: getScales(
Expand Down
7 changes: 6 additions & 1 deletion web/client/components/widgets/widget/LegendView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ export default ({
scales,
zoom: currentZoomLvl,
layerOptions: {
legendOptions: legendProps,
legendOptions: {
...legendProps,
projection: map?.projection,
mapSize: map?.size,
mapBbox: map?.bbox
},
hideFilter: true
}
}}
Expand Down
3 changes: 2 additions & 1 deletion web/client/plugins/Print.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,8 @@ export default {
this.props.onBeforePrint();
this.props.printingService.print({
layers: this.getMapConfiguration()?.layers,
scales: this.props.useFixedScales ? getPrintScales(this.props.capabilities) : undefined
scales: this.props.useFixedScales ? getPrintScales(this.props.capabilities) : undefined,
bbox: this.props.map?.bbox
})
.then((spec) =>
this.props.onPrint(this.props.capabilities.createURL, { ...spec, ...this.props.overrideOptions })
Expand Down
49 changes: 24 additions & 25 deletions web/client/plugins/TOC/components/Legend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
* LICENSE file in the root directory of this source tree.
*/

import urlUtil from 'url';

import { isArray, isNil } from 'lodash';
import assign from 'object-assign';
import PropTypes from 'prop-types';
import React from 'react';
import urlUtil from 'url';
import isArray from 'lodash/isArray';
import isNil from 'lodash/isNil';
import pick from 'lodash/pick';

import {
addAuthenticationToSLD,
Expand All @@ -21,6 +21,8 @@ import Message from '../../../components/I18N/Message';
import SecureImage from '../../../components/misc/SecureImage';

import { randomInt } from '../../../utils/RandomUtils';
import { normalizeSRS } from '../../../utils/CoordinatesUtils';
import { getWMSLegendConfig, LEGEND_FORMAT } from '../../../utils/LegendUtils';

/**
* Legend renders the wms legend image
Expand All @@ -44,7 +46,10 @@ class Legend extends React.Component {
currentZoomLvl: PropTypes.number,
scales: PropTypes.array,
scaleDependent: PropTypes.bool,
language: PropTypes.string
language: PropTypes.string,
projection: PropTypes.string,
mapSize: PropTypes.object,
bbox: PropTypes.object
};

static defaultProps = {
Expand Down Expand Up @@ -86,26 +91,20 @@ class Legend extends React.Component {

const cleanParams = clearNilValuesForParams(layer.params);
const scale = this.getScale(props);
let query = assign(
{},
{
service: "WMS",
request: "GetLegendGraphic",
format: "image/png",
height: props.legendHeight,
width: props.legendWidth,
layer: layer.name,
style: layer.style || null,
version: layer.version || "1.3.0",
SLD_VERSION: "1.1.0",
LEGEND_OPTIONS: props.legendOptions
},
layer.legendParams || {},
props.language && layer.localizedLayerStyles ? {LANGUAGE: props.language} : {},
addAuthenticationToSLD(cleanParams || {}, props.layer),
cleanParams && cleanParams.SLD_BODY ? {SLD_BODY: cleanParams.SLD_BODY} : {},
scale !== null ? { SCALE: scale } : {}
);
const projection = normalizeSRS(this.props.projection || 'EPSG:3857', layer.allowedSRS);
const query = {
...getWMSLegendConfig({
layer,
format: LEGEND_FORMAT.IMAGE,
...pick(props, ['legendHeight', 'legendWidth', 'mapSize', 'legendOptions', 'mapBbox']),
projection
}),
...layer.legendParams,
...(props.language && layer.localizedLayerStyles ? { LANGUAGE: props.language } : {}),
...addAuthenticationToSLD(cleanParams || {}, props.layer),
...(cleanParams && cleanParams.SLD_BODY ? { SLD_BODY: cleanParams.SLD_BODY } : {}),
...(scale !== null ? { SCALE: scale } : {})
};

return urlUtil.format({
host: urlObj.host,
Expand Down
Loading
Loading