Skip to content

Commit

Permalink
#10737: Interactive legend for TOC layers [Vector Layer part] (#10798)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: stefano bovio <stefano.bovio@geosolutionsgroup.com>
  • Loading branch information
mahmoudadel54 and allyoucanmap authored Feb 21, 2025
1 parent d193f55 commit 1e493c5
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 21 deletions.
2 changes: 1 addition & 1 deletion web/client/components/TOC/fragments/settings/Display.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ export default class extends React.Component {
</Col>
</div>
</Row>}
{this.props.element.type === "wfs" && <Row>
{['wfs', 'vector'].includes(this.props.element.type) && <Row>
<div className={"legend-options"}>
{experimentalInteractiveLegend && <Col xs={12} className={"legend-label"}>
<label key="legend-options-title" className="control-label"><Message msgId="layerProperties.legendOptions.title" /></label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,5 +407,43 @@ describe('test Layer Properties Display module component', () => {
expect(spy.calls[0].arguments[0]).toEqual("enableInteractiveLegend");
expect(spy.calls[0].arguments[1]).toEqual(true);
});
it('tests vector Layer Properties Legend component events', () => {
const l = {
name: 'layer00',
title: 'Layer',
visibility: true,
storeIndex: 9,
type: 'vector',
url: 'fakeurl',
legendOptions: {
legendWidth: 15,
legendHeight: 15
},
enableInteractiveLegend: false
};
const settings = {
options: {
opacity: 1
}
};
const handlers = {
onChange() {}
};
let spy = expect.spyOn(handlers, "onChange");
const comp = ReactDOM.render(<Display element={l} settings={settings} onChange={handlers.onChange}/>, document.getElementById("container"));
expect(comp).toBeTruthy();
const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" );
const legendPreview = ReactTestUtils.scryRenderedDOMComponentsWithClass( comp, "legend-preview" );
expect(legendPreview).toBeTruthy();
expect(inputs).toBeTruthy();
expect(inputs.length).toBe(6);
let interactiveLegendConfig = document.querySelector(".legend-options input[data-qa='display-interactive-legend-option']");
// change enableInteractiveLegend to enable interactive legend
interactiveLegendConfig.checked = true;
ReactTestUtils.Simulate.change(interactiveLegendConfig);
expect(spy).toHaveBeenCalled();
expect(spy.calls[0].arguments[0]).toEqual("enableInteractiveLegend");
expect(spy.calls[0].arguments[1]).toEqual(true);
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default ({
<Message msgId="catalog.fetchMetadata.label" />&nbsp;<InfoPopover text={<Message msgId="catalog.fetchMetadata.tooltip" />} />
</Checkbox>
</FormGroup>}
{experimentalInteractiveLegend && ['wfs'].includes(service.type) && <FormGroup className="wfs-interactive-legend" controlId="enableInteractiveLegend" key="enableInteractiveLegend">
{experimentalInteractiveLegend && ['wfs', 'vector'].includes(service.type) && <FormGroup className="wfs-vector-interactive-legend" controlId="enableInteractiveLegend" key="enableInteractiveLegend">
<Checkbox data-qa="display-interactive-legend-option"
onChange={(e) => onChangeServiceProperty("layerOptions", { ...service.layerOptions, enableInteractiveLegend: e.target.checked})}
checked={!isNil(service.layerOptions?.enableInteractiveLegend) ? service.layerOptions?.enableInteractiveLegend : false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,23 @@ describe('Test common advanced settings', () => {
expect(spyOn).toHaveBeenCalled();
expect(spyOn.calls[1].arguments).toEqual([ 'fetchMetadata', false ]);
});
it('test showing/hiding interactive legend checkbox', () => {
it('test showing/hiding interactive legend checkbox for WFS', () => {
ReactDOM.render(<CommonAdvancedSettings
service={{type: "wfs"}}
/>, document.getElementById("container"));
const interactiveLegendCheckboxInput = document.querySelector(".wfs-interactive-legend .checkbox input[data-qa='display-interactive-legend-option']");
const interactiveLegendCheckboxInput = document.querySelector(".wfs-vector-interactive-legend .checkbox input[data-qa='display-interactive-legend-option']");
expect(interactiveLegendCheckboxInput).toBeTruthy();
const interactiveLegendLabel = document.querySelector(".wfs-interactive-legend .checkbox span");
const interactiveLegendLabel = document.querySelector(".wfs-vector-interactive-legend .checkbox span");
expect(interactiveLegendLabel).toBeTruthy();
expect(interactiveLegendLabel.innerHTML).toEqual('layerProperties.enableInteractiveLegendInfo.label');
});
it('test showing/hiding interactive legend checkbox for vector', () => {
ReactDOM.render(<CommonAdvancedSettings
service={{type: "vector"}}
/>, document.getElementById("container"));
const interactiveLegendCheckboxInput = document.querySelector(".wfs-vector-interactive-legend .checkbox input[data-qa='display-interactive-legend-option']");
expect(interactiveLegendCheckboxInput).toBeTruthy();
const interactiveLegendLabel = document.querySelector(".wfs-vector-interactive-legend .checkbox span");
expect(interactiveLegendLabel).toBeTruthy();
expect(interactiveLegendLabel.innerHTML).toEqual('layerProperties.enableInteractiveLegendInfo.label');
});
Expand Down
4 changes: 3 additions & 1 deletion web/client/components/map/BaseMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from 'react';

import PropTypes from 'prop-types';
import { isString } from 'lodash';
import { createVectorFeatureFilter } from '../../utils/FilterUtils';

/**
* Base map component that renders a map.
Expand Down Expand Up @@ -104,7 +105,8 @@ class BaseMap extends React.Component {
if (layer.features && layer.type === "vector") {
const { plugins } = this.props;
const { Feature } = plugins;
return layer.features.map((feature) => {
const vectorFeatureFilter = createVectorFeatureFilter(layer);
return layer.features.filter(vectorFeatureFilter).map((feature) => {
return (
<Feature
key={feature.id}
Expand Down
67 changes: 66 additions & 1 deletion web/client/components/map/cesium/__tests__/Layer-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import '../plugins/ArcGISLayer';
import '../plugins/ModelLayer';

import {setStore} from '../../../../utils/SecurityUtils';
import ConfigUtils from '../../../../utils/ConfigUtils';
import ConfigUtils, { setConfigProp } from '../../../../utils/ConfigUtils';
import MockAdapter from 'axios-mock-adapter';
import axios from '../../../../libs/ajax';

Expand All @@ -43,6 +43,7 @@ describe('Cesium layer', () => {
document.body.innerHTML = '<div id="map"></div><div id="container"></div><div id="container2"></div>';
map = new Cesium.Viewer("map");
map.imageryLayers.removeAll();
setConfigProp('miscSettings', { experimentalInteractiveLegend: true });
setTimeout(done);
});

Expand All @@ -55,6 +56,7 @@ describe('Cesium layer', () => {
} catch(e) {}
/* eslint-enable */
document.body.innerHTML = '';
setConfigProp('miscSettings', { });
setTimeout(done);
});
it('missing layer', () => {
Expand Down Expand Up @@ -1491,6 +1493,69 @@ describe('Cesium layer', () => {
expect(cmp.layer.styledFeatures._queryable).toBe(false);
expect(cmp.layer.styledFeatures._features.length).toBe(1);
});
it('should create a vector layer with interactive legend filter', () => {
const options = {
type: 'vector',
features: [
{ type: 'Feature', properties: { "prop1": 0 }, geometry: { type: 'Point', coordinates: [0, 0] } },
{ type: 'Feature', properties: { "prop1": 2 }, geometry: { type: 'Point', coordinates: [1, 0] } },
{ type: 'Feature', properties: { "prop1": 5 }, geometry: { type: 'Point', coordinates: [2, 0] } }
],
title: 'Title',
visibility: true,
bbox: {
crs: 'EPSG:4326',
bounds: {
minx: -180,
miny: -90,
maxx: 180,
maxy: 90
}
},
enableInteractiveLegend: true,
layerFilter: {
filters: [{
"id": "interactiveLegend",
"format": "logic",
"version": "1.0.0",
"logic": "OR",
"filters": [
{
"format": "geostyler",
"version": "1.0.0",
"body": [
"&&",
[
">",
"prop1",
"0"
], [
"<",
"prop1",
"3"
]
],
"id": "&&,>,prop1,0,<,prop1,3"
}
]
}]
}
};
// create layers
const cmp = ReactDOM.render(
<CesiumLayer
type="vector"
options={options}
map={map}
/>, document.getElementById('container'));
expect(cmp).toBeTruthy();
expect(cmp.layer).toBeTruthy();
expect(cmp.layer.styledFeatures).toBeTruthy();
expect(cmp.layer.detached).toBe(true);
const renderedFeatsNum = cmp.layer.styledFeatures._features.filter(cmp.layer.styledFeatures._featureFilter).length;
const filteredFeatsNum = 1;
expect(renderedFeatsNum).toEqual(filteredFeatsNum);
});
it('should create a wfs layer', () => {
const options = {
type: 'wfs',
Expand Down
11 changes: 8 additions & 3 deletions web/client/components/map/cesium/plugins/VectorLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '../../../../utils/VectorStyleUtils';
import { applyDefaultStyleToVectorLayer } from '../../../../utils/StyleUtils';
import GeoJSONStyledFeatures from '../../../../utils/cesium/GeoJSONStyledFeatures';
import { createVectorFeatureFilter } from '../../../../utils/FilterUtils';

const createLayer = (options, map) => {

Expand All @@ -27,13 +28,14 @@ const createLayer = (options, map) => {
}

const features = flattenFeatures(options?.features || [], ({ style, ...feature }) => feature);

const vectorFeatureFilter = createVectorFeatureFilter(options);
let styledFeatures = new GeoJSONStyledFeatures({
features,
id: options?.id,
map: map,
opacity: options.opacity,
queryable: options.queryable === undefined || options.queryable
queryable: options.queryable === undefined || options.queryable,
featureFilter: vectorFeatureFilter // make filter for features if filter is existing
});

layerToGeoStylerStyle(options)
Expand All @@ -43,7 +45,6 @@ const createLayer = (options, map) => {
styledFeatures.setStyleFunction(styleFunc);
});
});

return {
detached: true,
styledFeatures,
Expand All @@ -62,6 +63,10 @@ Layers.registerType('vector', {
if (!isEqual(newOptions.features, oldOptions.features)) {
return createLayer(newOptions, map);
}
if (layer?.styledFeatures && !isEqual(newOptions?.layerFilter, oldOptions?.layerFilter)) {
const vectorFeatureFilter = createVectorFeatureFilter(newOptions);
layer.styledFeatures.setFeatureFilter(vectorFeatureFilter);
}

if (layer?.styledFeatures && !isEqual(newOptions.style, oldOptions.style)) {
layerToGeoStylerStyle(newOptions)
Expand Down
Loading

0 comments on commit 1e493c5

Please sign in to comment.