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);