diff --git a/examples/wms-getlegendgraphic.html b/examples/wms-getlegendgraphic.html new file mode 100644 index 0000000000..d70f852436 --- /dev/null +++ b/examples/wms-getlegendgraphic.html @@ -0,0 +1,15 @@ +--- +layout: example.html +title: WMS GetLegendGraphic +shortdesc: Example of a WMS GetLegendGraphic. +docs: > + WMS supports the [getLegendGraphic request](https://docs.geoserver.org/latest/en/user/services/wms/get_legend_graphic/index.html). + This example demonstrates the use of the `getGetLegendGraphicUrl` method. + + + As a legend can be responsive to the scale it is updated on every change of the resolution. +tags: "GetLegendGraphic, getGetLegendGraphicURL, WMS" +--- +
+Legend: +
diff --git a/examples/wms-getlegendgraphic.js b/examples/wms-getlegendgraphic.js new file mode 100644 index 0000000000..e99b2171f0 --- /dev/null +++ b/examples/wms-getlegendgraphic.js @@ -0,0 +1,47 @@ +import Map from '../src/ol/Map.js'; +import View from '../src/ol/View.js'; +import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js'; +import ImageWMS from '../src/ol/source/ImageWMS.js'; +import OSM from '../src/ol/source/OSM.js'; + +const wmsSource = new ImageWMS({ + url: 'https://ahocevar.com/geoserver/wms', + params: {'LAYERS': 'topp:states'}, + ratio: 1, + serverType: 'geoserver' +}); + +const updateLegend = function(resolution) { + const graphicUrl = wmsSource.getGetLegendGraphicUrl(resolution); + const img = document.getElementById('legend'); + img.src = graphicUrl; +}; + +const layers = [ + new TileLayer({ + source: new OSM() + }), + new ImageLayer({ + extent: [-13884991, 2870341, -7455066, 6338219], + source: wmsSource + }) +]; + +const map = new Map({ + layers: layers, + target: 'map', + view: new View({ + center: [-10997148, 4569099], + zoom: 4 + }) +}); + +// Initial legend +const resolution = map.getView().getResolution(); +updateLegend(resolution); + +// Update the legend when the resolution changes +map.getView().on('change:resolution', function(event) { + const resolution = event.target.getResolution(); + updateLegend(resolution); +}); diff --git a/src/ol/source/ImageWMS.js b/src/ol/source/ImageWMS.js index 2dbf65067c..60e2fcaafc 100644 --- a/src/ol/source/ImageWMS.js +++ b/src/ol/source/ImageWMS.js @@ -191,6 +191,40 @@ class ImageWMS extends ImageSource { 1, sourceProjectionObj || projectionObj, baseParams); } + /** + * Return the GetLegendGraphic URL for the passed resolution. + * Return `undefined` if the GetLegendGraphic URL cannot be constructed. + * @param {number} resolution Resolution. + * @param {!Object} params GetLegendGraphic params. Default `FORMAT` is + * `image/png`. `VERSION` should not be specified here. + * @return {string|undefined} GetLegendGraphic URL. + * @api + */ + getGetLegendGraphicUrl(resolution, params) { + const layers = this.params_.LAYERS; + const isSingleLayer = !Array.isArray(layers) || this.params_['LAYERS'].length === 1; + if (this.url_ === undefined || !isSingleLayer) { + return undefined; + } + + const mpu = this.getProjection() ? this.getProjection().getMetersPerUnit() : 1; + const dpi = 25.4 / 0.28; + const inchesPerMeter = 39.37; + const scale = resolution * mpu * inchesPerMeter * dpi; + + const baseParams = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetLegendGraphic', + 'FORMAT': 'image/png', + 'LAYER': layers, + 'SCALE': scale + }; + assign(baseParams, params); + + return appendParams(/** @type {string} */ (this.url_), baseParams); + } + /** * Get the user-provided params, i.e. those passed to the constructor through * the "params" option, and possibly updated using the updateParams method. diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index 0e449d4fab..838b5ed3bb 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -8,7 +8,7 @@ import {assert} from '../asserts.js'; import {buffer, createEmpty} from '../extent.js'; import {assign} from '../obj.js'; import {modulo} from '../math.js'; -import {get as getProjection, transform, transformExtent} from '../proj.js'; +import {get as getProjection, transform, transformExtent, METERS_PER_UNIT} from '../proj.js'; import {calculateSourceResolution} from '../reproj.js'; import {toSize, buffer as bufferSize, scale as scaleSize} from '../size.js'; import TileImage from './TileImage.js'; @@ -211,6 +211,41 @@ class TileWMS extends TileImage { 1, sourceProjectionObj || projectionObj, baseParams); } + /** + * Return the GetLegendGraphic URL for the passed resolution. + * Return `undefined` if the GetLegendGraphic URL cannot be constructed. + * @param {number} resolution Resolution. + * @param {!Object} params GetLegendGraphic params. Default `FORMAT` is + * `image/png`. `VERSION` should not be specified here. + * @return {string|undefined} GetLegendGraphic URL. + * @api + */ + getGetLegendGraphicUrl(resolution, params) { + const layers = this.params_.LAYERS; + const isSingleLayer = !Array.isArray(layers) || this.params_['LAYERS'].length === 1; + if (this.urls[0] === undefined || !isSingleLayer) { + return undefined; + } + + const units = this.getProjection() && this.getProjection().getUnits(); + const dpi = 25.4 / 0.28; + const mpu = METERS_PER_UNIT[units || 'm']; + const inchesPerMeter = 39.37; + const scale = resolution * mpu * inchesPerMeter * dpi; + + const baseParams = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetLegendGraphic', + 'FORMAT': 'image/png', + 'LAYER': this.params_['LAYERS'], + 'SCALE': scale + }; + assign(baseParams, params); + + return appendParams(/** @type {string} */ (this.urls[0]), baseParams); + } + /** * @inheritDoc */ diff --git a/test/spec/ol/source/imagewms.test.js b/test/spec/ol/source/imagewms.test.js index 0e228a2534..e69c1e9aaf 100644 --- a/test/spec/ol/source/imagewms.test.js +++ b/test/spec/ol/source/imagewms.test.js @@ -189,7 +189,7 @@ describe('ol.source.ImageWMS', function() { const source = new ImageWMS(options); pixelRatio = 1.325; const image = - source.getImage(extent, resolution, pixelRatio, projection); + source.getImage(extent, resolution, pixelRatio, projection); const uri = new URL(image.src_); const queryData = uri.searchParams; expect(queryData.get('FORMAT_OPTIONS')).to.be('dpi:119'); @@ -330,6 +330,63 @@ describe('ol.source.ImageWMS', function() { }); }); + describe('#getGetLegendGraphicUrl', function() { + + it('returns the getLegenGraphic url as expected', function() { + const source = new ImageWMS(options); + const url = source.getGetLegendGraphicUrl(resolution); + const uri = new URL(url); + expect(uri.protocol).to.be('http:'); + expect(uri.hostname).to.be('example.com'); + expect(uri.pathname).to.be('/wms'); + const queryData = uri.searchParams; + expect(queryData.get('FORMAT')).to.be('image/png'); + expect(queryData.get('LAYER')).to.be('layer'); + expect(queryData.get('REQUEST')).to.be('GetLegendGraphic'); + expect(queryData.get('SERVICE')).to.be('WMS'); + expect(queryData.get('VERSION')).to.be('1.3.0'); + expect(queryData.get('SCALE')).to.be('357.14214285714274'); + }); + + it('adds additional params as expected', function() { + const source = new ImageWMS(options); + const url = source.getGetLegendGraphicUrl(resolution, { + STYLE: 'STYLE_VALUE', + FEATURETYPE: 'FEATURETYPE_VALUE', + RULE: 'RULE_VALUE', + SLD: 'SLD_VALUE', + SLD_BODY: 'SLD_BODY_VALUE', + FORMAT: 'FORMAT_VALUE', + WIDTH: 'WIDTH_VALUE', + HEIGHT: 'HEIGHT_VALUE', + EXCEPTIONS: 'EXCEPTIONS_VALUE', + LANGUAGE: 'LANGUAGE_VALUE' + }); + const uri = new URL(url); + expect(uri.protocol).to.be('http:'); + expect(uri.hostname).to.be('example.com'); + expect(uri.pathname).to.be('/wms'); + const queryData = uri.searchParams; + expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE'); + expect(queryData.get('LAYER')).to.be('layer'); + expect(queryData.get('REQUEST')).to.be('GetLegendGraphic'); + expect(queryData.get('SERVICE')).to.be('WMS'); + expect(queryData.get('VERSION')).to.be('1.3.0'); + expect(queryData.get('SCALE')).to.be('357.14214285714274'); + expect(queryData.get('STYLE')).to.be('STYLE_VALUE'); + expect(queryData.get('FEATURETYPE')).to.be('FEATURETYPE_VALUE'); + expect(queryData.get('RULE')).to.be('RULE_VALUE'); + expect(queryData.get('SLD')).to.be('SLD_VALUE'); + expect(queryData.get('SLD_BODY')).to.be('SLD_BODY_VALUE'); + expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE'); + expect(queryData.get('WIDTH')).to.be('WIDTH_VALUE'); + expect(queryData.get('HEIGHT')).to.be('HEIGHT_VALUE'); + expect(queryData.get('EXCEPTIONS')).to.be('EXCEPTIONS_VALUE'); + expect(queryData.get('LANGUAGE')).to.be('LANGUAGE_VALUE'); + }); + + }); + describe('#refresh()', function() { let map, source; diff --git a/test/spec/ol/source/tilewms.test.js b/test/spec/ol/source/tilewms.test.js index 3e18e5d12d..3d24e7deaa 100644 --- a/test/spec/ol/source/tilewms.test.js +++ b/test/spec/ol/source/tilewms.test.js @@ -299,6 +299,63 @@ describe('ol.source.TileWMS', function() { }); }); + describe('#getGetLegendGraphicUrl', function() { + + it('returns the getLegenGraphic url as expected', function() { + const source = new TileWMS(options); + const url = source.getGetLegendGraphicUrl(0.1); + const uri = new URL(url); + expect(uri.protocol).to.be('http:'); + expect(uri.hostname).to.be('example.com'); + expect(uri.pathname).to.be('/wms'); + const queryData = uri.searchParams; + expect(queryData.get('FORMAT')).to.be('image/png'); + expect(queryData.get('LAYER')).to.be('layer'); + expect(queryData.get('REQUEST')).to.be('GetLegendGraphic'); + expect(queryData.get('SERVICE')).to.be('WMS'); + expect(queryData.get('VERSION')).to.be('1.3.0'); + expect(queryData.get('SCALE')).to.be('357.14214285714274'); + }); + + it('adds additional params as expected', function() { + const source = new TileWMS(options); + const url = source.getGetLegendGraphicUrl(0.1, { + STYLE: 'STYLE_VALUE', + FEATURETYPE: 'FEATURETYPE_VALUE', + RULE: 'RULE_VALUE', + SLD: 'SLD_VALUE', + SLD_BODY: 'SLD_BODY_VALUE', + FORMAT: 'FORMAT_VALUE', + WIDTH: 'WIDTH_VALUE', + HEIGHT: 'HEIGHT_VALUE', + EXCEPTIONS: 'EXCEPTIONS_VALUE', + LANGUAGE: 'LANGUAGE_VALUE' + }); + const uri = new URL(url); + expect(uri.protocol).to.be('http:'); + expect(uri.hostname).to.be('example.com'); + expect(uri.pathname).to.be('/wms'); + const queryData = uri.searchParams; + expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE'); + expect(queryData.get('LAYER')).to.be('layer'); + expect(queryData.get('REQUEST')).to.be('GetLegendGraphic'); + expect(queryData.get('SERVICE')).to.be('WMS'); + expect(queryData.get('VERSION')).to.be('1.3.0'); + expect(queryData.get('SCALE')).to.be('357.14214285714274'); + expect(queryData.get('STYLE')).to.be('STYLE_VALUE'); + expect(queryData.get('FEATURETYPE')).to.be('FEATURETYPE_VALUE'); + expect(queryData.get('RULE')).to.be('RULE_VALUE'); + expect(queryData.get('SLD')).to.be('SLD_VALUE'); + expect(queryData.get('SLD_BODY')).to.be('SLD_BODY_VALUE'); + expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE'); + expect(queryData.get('WIDTH')).to.be('WIDTH_VALUE'); + expect(queryData.get('HEIGHT')).to.be('HEIGHT_VALUE'); + expect(queryData.get('EXCEPTIONS')).to.be('EXCEPTIONS_VALUE'); + expect(queryData.get('LANGUAGE')).to.be('LANGUAGE_VALUE'); + }); + + }); + describe('#setUrl()', function() { it('sets the correct url', function() { const source = new TileWMS(options);