This is an example of an easy method to implement a custom GetFeatureInfo function on any WMS layer loaded with Leaflet JS.
Technologies Used: Web (HTML,JavaScript,CSS) and Leaflet JS
The WMS GetFeatureInfo
is not supported by Leaflet out of the box, despite the fact it is very useful and provides equivalent performance and accuracy to WFS GetFeature
. Furthermore, it saves the step of having to get the WFS data as JSON and converting it to a Leaflet layer using L.GeoJSON.
Before attempting to add this functionality to your map, make sure the WMS service you are using is in fact queryable. This can be determined by a GetCapabilities
request to the WMS service (e.g. GeoServer), specifing the service, WMS version, and the request. Example:
https://demo.boundlessgeo.com/geoserver/ows?service=WMS&version=1.3.0&request=GetCapabilities
In the XML file generated, verify that for the layer you wish to query the queryable
parameter is equal to 1. Likewise, keep in mind the CRS of the layer as this must be match the CRS in the GetFeatureInfo
request:
If it is, then you can query the layer using a GetFeatureInfo request. The following JavaScript code shows a Leaflet implementation:
var wmsUrl = 'https://demo.boundlessgeo.com/geoserver/ows?';
var countryLayer = L.tileLayer.wms(wmsUrl, {
layers: 'opengeo:countries',
transparent: false,
format: 'image/png',
maxZoom: 20
}).addTo(map);
var roadsLayer = L.tileLayer.wms(wmsUrl, {
layers: 'ne:ne_10m_roads',
transparent: true,
format: 'image/png',
maxZoom: 20
}).addTo(map);
function identify (e,layers) {
var BBOX = map.getBounds().toBBoxString();
var WIDTH = map.getSize().x;
var HEIGHT = map.getSize().y;
var X = Math.round(map.layerPointToContainerPoint(e.layerPoint).x);
var Y = Math.round(map.layerPointToContainerPoint(e.layerPoint).y);
var getUrl = wmsUrl+'SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo'+'&layers='+layers+'&BBOX='+BBOX+'&FEATURE_COUNT=5&info_format=application/json&HEIGHT='+HEIGHT+'&WIDTH='+WIDTH+'&query_layers='+layers+'&SRS=EPSG:4326&X='+X+'&Y='+Y;
$.ajax({
url: getUrl,
success: function (data, status, xhr) {
var result = data; //all the GetFeatureInfo result compiled into one json file
var txt ="";
for (i=0; i<result.features.length; i++) {
txt = txt.concat(""+result.features[i].id.split('.')[0].toUpperCase()+ ":"+ '
' + formatFeaturecontent(result.features[i]) + '
'); //JSON.stringify(result.features[i].properties) - get ALL Data
};
if (txt != ""){
L.popup()
.setLatLng(e.latlng)
.setContent(txt)
.openOn(map);
}
},
error: function (xhr, status, error) {
console.log(error);
}
});
}
function formatFeaturecontent(featureJSON) {
layerName = featureJSON.id.split('.')[0]; //Remove everything after the period in the layer name
switch(layerName) { //Extract only relevant data for each layer finding
case "countries":
return "Name: "+featureJSON.properties.sovereignt + "
"+ "Type: "+ featureJSON.properties.type+"
"+ "Population Estimate: "+ featureJSON.properties.pop_est.toLocaleString()+"
"+ "Income Group: "+ featureJSON.properties.income_grp+"
";
case "ne_10m_roads":
return "Road Name: "+featureJSON.properties.name + "
"+ "Length in KM: "+ featureJSON.properties.length_km+"
"+ "Road Type: "+ featureJSON.properties.type+"
"+ "Country: "+ featureJSON.properties.sov_a3+"
"+ "Continent: "+ featureJSON.properties.continent;
default:
return JSON.stringify(featureJSON.properties);
}
}
function getLayerNames(clickedLocation) {
var layersUrl =[];
var layers = clickedLocation.target._layers
for (var layer in layers){
if (layers[layer]._url==wmsUrl) {
layersUrl.push(layers[layer].options.layers);
}
};
return layersUrl.toLocaleString();
}
var clickLocation;
map.on('click', function(e) {
clickedLocation=e;
layers=getLayerNames(e);
identify(e, layers);
});
There you have it! The identify function should run every time the user clicks on a location with one or more of the WMS layers. Furthermore, if a click event contains more result from more than one WMS layer, this implementation will aggregate this data and display in the pop-up.
Back to my projects →