Skip to content

Commit

Permalink
[Backport 2024.02.xx] #10684 & #10236 & #10241 - Legend filtering enh…
Browse files Browse the repository at this point in the history
…ancement (#10756)
  • Loading branch information
dsuren1 authored Jan 15, 2025
1 parent 6ee9e84 commit 7fd404f
Show file tree
Hide file tree
Showing 27 changed files with 829 additions and 176 deletions.
12 changes: 3 additions & 9 deletions web/client/api/WMS.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,20 +321,14 @@ export const getSupportedFormat = (url, includeGFIFormats = false) => {
.catch(() => includeGFIFormats ? { imageFormats: [], infoFormats: [] } : []);
};

let layerLegendJsonData = {};
export const getJsonWMSLegend = (url) => {
const request = layerLegendJsonData[url]
? () => Promise.resolve(layerLegendJsonData[url])
: () => axios.get(url).then((response) => {
return axios.get(url)
.then((response) => {
if (typeof response?.data === 'string' && response.data.includes("Exception")) {
throw new Error("Faild to get json legend");
}
layerLegendJsonData[url] = response?.data?.Legend;
return response?.data?.Legend || [];
});
return request().then((data) => data).catch(err => {
throw err;
});
}).catch(err => { throw err; });
};

const Api = {
Expand Down
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 @@ -332,6 +342,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 @@ -342,6 +353,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
8 changes: 6 additions & 2 deletions web/client/components/widgets/enhancers/legendWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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 { updateLayerWithLegendFilters } from '../../../utils/LegendUtils';

/**
* map dependencies to layers, scales and current zoom level to show legend items for current zoom.
Expand All @@ -22,19 +23,22 @@ export default compose(
withProps(({ dependencies = {}, dependenciesMap = {} }) => {
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);
layers = updateLayerWithLegendFilters(layers, dependencies);
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
9 changes: 8 additions & 1 deletion web/client/epics/__tests__/styleeditor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { testEpic } from './epicTestUtils';

import MockAdapter from 'axios-mock-adapter';
import axios from '../../libs/ajax';
import { INTERACTIVE_LEGEND_ID } from '../../utils/LegendUtils';

let mockAxios;

Expand Down Expand Up @@ -474,7 +475,11 @@ describe('Test styleeditor epics', () => {
name: 'layerName',
url: 'base/web/client/test-resources/geoserver/',
describeFeatureType: {},
style: 'test_style'
style: 'test_style',
layerFilter: {
filters: [{id: INTERACTIVE_LEGEND_ID, "test": "test"}]
},
enableInteractiveLegend: true
}
],
selected: [
Expand Down Expand Up @@ -503,6 +508,7 @@ describe('Test styleeditor epics', () => {
case UPDATE_SETTINGS_PARAMS:
const styleName = action.newParams.style.split('___');
expect(styleName[0]).toBe('style_title');
expect(action.newParams.layerFilter).toBeTruthy();
expect(action.update).toBe(true);
break;
case UPDATE_STATUS:
Expand Down Expand Up @@ -568,6 +574,7 @@ describe('Test styleeditor epics', () => {
case UPDATE_SETTINGS_PARAMS:
const styleName = action.newParams.style.split('___');
expect(styleName[0]).toBe(`${workspace}:style_title`);
expect(action.newParams.layerFilter).toBeFalsy();
expect(action.update).toBe(true);
break;
case UPDATE_STATUS:
Expand Down
7 changes: 5 additions & 2 deletions web/client/epics/styleeditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { getSelectedLayer, layerSettingSelector } from '../selectors/layers';
import { generateTemporaryStyleId, generateStyleId, STYLE_OWNER_NAME, getNameParts, detectStyleCodeChanges } from '../utils/StyleEditorUtils';
import { updateStyleService } from '../api/StyleEditor';
import { getDefaultUrl } from '../utils/URLUtils';
import { resetLayerLegendFilter } from '../utils/FilterUtils';

/*
* Observable to get code of a style, it works only in edit status
Expand Down Expand Up @@ -528,6 +529,7 @@ export const createStyleEpic = (action$, store) =>
const format = formatStyleSelector(state);
const { title = '', _abstract = '' } = action.settings || {};
const { baseUrl = '' } = styleServiceSelector(state);
const layerFilter = resetLayerLegendFilter(layer, 'style', styleName);

const editorMetadata = {
msStyleJSON: null,
Expand Down Expand Up @@ -559,7 +561,7 @@ export const createStyleEpic = (action$, store) =>
)
.switchMap(() => Rx.Observable.of(
updateOptionsByOwner(STYLE_OWNER_NAME, [{}]),
updateSettingsParams({style: styleName || ''}, true),
updateSettingsParams({ ...(layerFilter && {layerFilter}), style: styleName || ''}, true),
updateStatus(''),
loadedStyle())
.merge(
Expand Down Expand Up @@ -637,7 +639,8 @@ export const updateStyleCodeEpic = (action$, store) =>
'layer',
{
_v_: Date.now(),
availableStyles
availableStyles,
styleVersion: `${styleName}-${Date.now()}`
}),
updateSettingsParams({
availableStyles
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

0 comments on commit 7fd404f

Please sign in to comment.