diff --git a/src/components/layerlist/LayerLegendImage.vue b/src/components/layerlist/LayerLegendImage.vue index ab02fcbd..aa5ad3b7 100644 --- a/src/components/layerlist/LayerLegendImage.vue +++ b/src/components/layerlist/LayerLegendImage.vue @@ -10,7 +10,10 @@ import LayerLegend from '@/util/LayerLegend'; /** - * Module for one legend element. + * Component rendering a legend image for a layer. + * It reacts to: + * - map view resolution changes + * - layer source changes (e.g. WMS params updates) */ export default { name: 'wgu-layerlegendimage', @@ -21,26 +24,65 @@ export default { data () { return { resolution: this.mapView.getResolution(), - viewResolutionChanged: undefined + viewResolutionChanged: undefined, + sourceRevision: 0, + sourceChanged: undefined } }, /** - * Register for an event to update the legend on resolution change. + * Register event listeners. */ created () { - const viewResolutionChanged = function (event) { - this.resolution = event.target.getResolution(); - }.bind(this); - - this.mapView.on('change:resolution', viewResolutionChanged); - this.viewResolutionChanged = viewResolutionChanged; + this.registerViewResolutionChanged(this.mapView); + this.registerSourceChanged(this.layer?.getSource()); }, /** - * Unregister the event fired on resolution change. + * Unregister event listeners. */ unmounted () { - if (this.viewResolutionChanged) { - this.mapView.un('change:resolution', this.viewResolutionChanged); + this.unregisterViewResolutionChanged(this.mapView); + this.unregisterSourceChanged(this.layer?.getSource()); + }, + methods: { + /** + * Registers a listener for map view resolution changes. + */ + registerViewResolutionChanged (view) { + if (!view) return; + + this.viewResolutionChanged = (event) => { + this.resolution = event.target.getResolution(); + }; + + view.on('change:resolution', this.viewResolutionChanged); + }, + /** + * Unregisters the resolution change listener. + */ + unregisterViewResolutionChanged (view) { + if (view && this.viewResolutionChanged) { + view.un('change:resolution', this.viewResolutionChanged); + } + }, + /** + * Registers a listener for source changes. + */ + registerSourceChanged (source) { + if (!source) return; + + this.sourceChanged = () => { + this.sourceRevision++; + }; + + source.on('change', this.sourceChanged) + }, + /** + * Unregisters the source change listener. + */ + unregisterSourceChanged (source) { + if (source && this.sourceChanged) { + source.un('change', this.sourceChanged); + } } }, computed: { @@ -48,6 +90,11 @@ export default { * Returns a URL to the layers legend image. */ legendURL () { + // Remarks: No-op to force dependency tracking on the sourceRevision + // counter to recompute the legendURL whenever the source changes. + const trackRevision = () => { return this.sourceRevision }; + trackRevision(); + const legendUtil = new LayerLegend(this.$appConfig?.legend); const options = { language: this.$i18n.locale, @@ -56,6 +103,22 @@ export default { return legendUtil.getUrl( this.layer, this.resolution, options, this.layer.get('legendUrl')); } + }, + watch: { + /** + * Reacts to layer replacement and rebinds source listeners. + */ + layer: { + handler (newLayer, oldLayer) { + const newSource = newLayer?.getSource?.(); + const oldSource = oldLayer?.getSource?.(); + + if (newSource !== oldSource) { + this.unregisterSourceChanged(oldSource); + this.registerSourceChanged(newSource); + } + } + } } }; diff --git a/src/util/LayerLegend.js b/src/util/LayerLegend.js index 5abf8cba..bab91a23 100644 --- a/src/util/LayerLegend.js +++ b/src/util/LayerLegend.js @@ -103,14 +103,23 @@ const WMSSourceLegend = { * @returns {String} The legend URL. */ getUrl (source, resolution, options) { + const legendOptions = { ...options }; + const wmsParams = source.getParams(); const wmsVersion = ObjectUtil.getValueIgnoreCase(wmsParams, 'VERSION') || '1.3.0'; - // apply mandatory SLD_VERSION param for WMS 1.3.0 GetLegendGraphic request - if (wmsVersion === '1.3.0' && !ObjectUtil.getValueIgnoreCase(options, 'SLD_VERSION')) { - options.SLD_VERSION = '1.1.0'; + + // Apply mandatory SLD_VERSION param for WMS 1.3.0 GetLegendGraphic request. + if (wmsVersion === '1.3.0' && !ObjectUtil.getValueIgnoreCase(legendOptions, 'SLD_VERSION')) { + legendOptions.SLD_VERSION = '1.1.0'; + } + + // Forward STYLES param, which may affect the legends appearance. + const wmsStyle = ObjectUtil.getValueIgnoreCase(wmsParams, 'STYLES'); + if (wmsStyle && !ObjectUtil.getValueIgnoreCase(legendOptions, 'STYLE')) { + legendOptions.STYLE = wmsStyle; } - return source.getLegendUrl(resolution, options); + return source.getLegendUrl(resolution, legendOptions); } } diff --git a/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js b/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js index e13e68d5..c2ac1744 100644 --- a/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js +++ b/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js @@ -170,6 +170,25 @@ describe('layerlist/LayerLegendImage.vue', () => { 'SCALE=3571428.571428572&language=de&SLD_VERSION=1.1.0'); }); + it('legendURL supports WMS style parameter', async () => { + await comp.setProps({ layer: wmsLayer }); + + expect(vm.legendURL).to.equal('https://ahocevar.com/geoserver/wms?' + + 'SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&LAYER=topp%3Astates&' + + 'SCALE=3571428.571428572&language=en&SLD_VERSION=1.1.0'); + + const source = wmsLayer.getSource(); + const params = source.getParams(); + source.updateParams({ + ...params, + STYLES: 'foo' + }); + + expect(vm.legendURL).to.equal('https://ahocevar.com/geoserver/wms?' + + 'SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&LAYER=topp%3Astates&' + + 'SCALE=3571428.571428572&language=en&SLD_VERSION=1.1.0&STYLE=foo'); + }) + afterEach(() => { vm.$i18n.locale = 'en'; comp.unmount();