Skip to content

Instantly share code, notes, and snippets.

@rclark
Last active July 9, 2024 20:16
Show Gist options
  • Save rclark/6908938 to your computer and use it in GitHub Desktop.
Save rclark/6908938 to your computer and use it in GitHub Desktop.
Leaflet WMS + GetFeatureInfo
<!doctype html>
<html>
<head>
<title>WMS GetFeatureInfo</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.ie.css" />
<![endif]-->
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
<style type="text/css">
html, body, #map {
margin: 0px;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="L.TileLayer.BetterWMS.js"></script>
<script>
var map = L.map('map', {
center: [34,-111],
zoom: 7
});
var url = 'http://data.azgs.az.gov/arizona/wms';
L.tileLayer('http://{s}.tiles.mapbox.com/v3/rclark.map-wm3i8w1a/{z}/{x}/{y}.png').addTo(map);
L.tileLayer.betterWms(url, {
layers: 'azgs:mapunitpolys',
transparent: true,
format: 'image/png'
}).addTo(map);
</script>
</body>
</html>

There are a bunch of reasons why this is convoluted, mostly in building the URL to make the request:

  1. You have to rely on an AJAX request, this example uses jQuery
  2. To make a GetFeatureInfo request, you must provide a BBOX for a image, and the pixel coordinates for the part of the image that you want info from. A couple of squirrely lines of Leaflet code can give you that.
  3. Output formats. The info_format parameter in the request. We don't know a priori which will be supported by a WMS that we might make a request to. See Geoserver's docs for what formats are available from Geoserver. That won't be the same from WMS to WMS, however.
  4. WMS services return XML docs when there's a mistake in the request or in processing. This sends an HTTP 200, which jQuery doesn't think is an error.
L.TileLayer.BetterWMS = L.TileLayer.WMS.extend({
onAdd: function (map) {
// Triggered when the layer is added to a map.
// Register a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onAdd.call(this, map);
map.on('click', this.getFeatureInfo, this);
},
onRemove: function (map) {
// Triggered when the layer is removed from a map.
// Unregister a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onRemove.call(this, map);
map.off('click', this.getFeatureInfo, this);
},
getFeatureInfo: function (evt) {
// Make an AJAX request to the server and hope for the best
var url = this.getFeatureInfoUrl(evt.latlng),
showResults = L.Util.bind(this.showGetFeatureInfo, this);
$.ajax({
url: url,
success: function (data, status, xhr) {
var err = typeof data === 'string' ? null : data;
showResults(err, evt.latlng, data);
},
error: function (xhr, status, error) {
showResults(error);
}
});
},
getFeatureInfoUrl: function (latlng) {
// Construct a GetFeatureInfo request URL given a point
var point = this._map.latLngToContainerPoint(latlng, this._map.getZoom()),
size = this._map.getSize(),
params = {
request: 'GetFeatureInfo',
service: 'WMS',
srs: 'EPSG:4326',
styles: this.wmsParams.styles,
transparent: this.wmsParams.transparent,
version: this.wmsParams.version,
format: this.wmsParams.format,
bbox: this._map.getBounds().toBBoxString(),
height: size.y,
width: size.x,
layers: this.wmsParams.layers,
query_layers: this.wmsParams.layers,
info_format: 'text/html'
};
params[params.version === '1.3.0' ? 'i' : 'x'] = point.x;
params[params.version === '1.3.0' ? 'j' : 'y'] = point.y;
return this._url + L.Util.getParamString(params, this._url, true);
},
showGetFeatureInfo: function (err, latlng, content) {
if (err) { console.log(err); return; } // do nothing if there's an error
// Otherwise show the content in a popup, or something.
L.popup({ maxWidth: 800})
.setLatLng(latlng)
.setContent(content)
.openOn(this._map);
}
});
L.tileLayer.betterWms = function (url, options) {
return new L.TileLayer.BetterWMS(url, options);
};
@emmexx
Copy link

emmexx commented Mar 8, 2022

I created a layer that uses cql_filter and env and the styling server side displays features based on said cql_filter and on values of env.
I had to add those 2 parameters in getFeatureInfoUrl to get BetterWMS getFeatureInfo to work correctly.

if(this.wmsParams.env)	
{
     params['env'] = this.wmsParams.env
}
if(this.wmsParams.cql_filter)	
{
     params['cql_filter'] = this.wmsParams.cql_filter
}

@ultramenid
Copy link

Super awesome!

Question: How do i select a single value of the wms? So, i want just one popup, for one value. Somewhere there is a selection needed, but how??

Please help :)

did you solve this issue?

@inzamambaig
Copy link

inzamambaig commented Jan 24, 2023

Super awesome!
Question: How do i select a single value of the wms? So, i want just one popup, for one value. Somewhere there is a selection needed, but how??
Please help :)

did you solve this issue?

@ultramenid Did you find any solution?

image

@inzamambaig
Copy link

@ultramenid I Found the solution. In getFeatureInfoUrl function just specify the attributes you want to see in propertyName parameter. e.g propertyName: "attr1,attr2,attr3,attr4,attr5"

@ragnarheidar
Copy link

Amazing! Works out of the box with jquery 3.6.3 and Leaflt 1.9.2. Thanks 👍

@ydrea
Copy link

ydrea commented Nov 4, 2023

I needed pictures in (html) popups, so I made the following changes:

  // with thumbs
	showGetFeatureInfo: function (
		err,
		latlng,
		content
		// signaturaFromUrl
	) {
		if (err) {
			console.log(err);
			return;
		}

		var tempDiv = document.createElement('div');
		tempDiv.innerHTML = content;
		var rows = tempDiv.querySelectorAll('tr');

		// var shouldFlyTo = false; // Initialize the flag

		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			var cells = row.querySelectorAll('th, td');

			// console.log(header);

			if (cells.length >= 2) {
				var header = cells[0].textContent.trim();
				var value = cells[1].textContent.trim();

				if (header === 'signatura') {
					console.log('Header:', header);
					console.log('Value:', value);
				}

				if (header === 'foto_url') {
					var imgUrl = value.replace(/["']/g, '');
					var thumbUrl;
					var publicIndex = imgUrl.indexOf('/public/');
					if (publicIndex !== -1) {
						thumbUrl =
							imgUrl.slice(0, publicIndex + '/public/'.length) +
							'thumbs/' +
							imgUrl.slice(publicIndex + '/public/'.length);
					}
					console.log(imgUrl, thumbUrl);

					var newRow = document.createElement('tr');
					newRow.style.width = '100%';
					var newCell = document.createElement('td');
					newCell.colSpan = 2;
					var image = document.createElement('img');
					image.src = thumbUrl;
					image.alt = 'thumb';
					image.style.minWidth = '300px';
					image.style.width = '100%';
					image.style.marginTop = '8px';

					newCell.appendChild(image);
					newRow.appendChild(newCell);
					//
					row.parentNode.replaceChild(newRow, row);
				} else {
					if (value === 'NULL' || value === '') {
						row.parentNode.removeChild(row);
					}
				}
			}
		}

		var filteredContent = tempDiv.innerHTML;
		// console.log(filteredContent);
		L.popup({ minWidth: '500px', width: '100%' })
			.setLatLng(latlng)
			.setContent(filteredContent)
			.openOn(this._map);

	},


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment