0

How do I get a list of layers by clicking on overlapping WMS layers in the Leaflet map? The best solution is to have the list in a single popup on the map.

<!DOCTYPE html>
<html>
<head> 
    <meta charset="UTF-8">
    <title>WMS Leaflet</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
    <style> 

     body {
            padding: 0;
            margin: 0;
        }
        html, body, #map {
            height: 100%;
            width: 100%;
        }

    .leaflet-popup-content-wrapper {
    width: 420px;
    height: 400px;
    }

    iframe {
    width: 400px;
    height: 350px;
    }
    </style> 
</head>  
    <body>
    <script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/leaflet.wms@0.2.0/dist/leaflet.wms.min.js"></script> 

<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4leaflet/1.0.2/proj4leaflet.js"></script>

    <div id ="map"> </div> 
    <script>

        var map =
  L.map('map', {
    center: [43.731739, 10.401401],
    zoom: 2,
    // minZoom:12,
  });   


var options = {'transparent': true};
var source = L.WMS.source("http://geoportale.regione.lazio.it/geoserver_nsit/ptpr/wms", options);
var layer1 = source.getLayer('zone_umide');
var layer2 = source.getLayer('aree_protette');
var control = L.control.layers({}, {
    'Layer 1': layer1,
    'Layer 2': layer2
})
control.addTo(map);

    </script>
    </body> 
</html>

closed as unclear what you're asking by whyzar, Fran Raga, xunilk, nmtoken, Jochen Schwarze Nov 4 at 6:58

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • I'm not sure I quite understand the problem, You can't use a GetFeatureInfo to discover a list of layers (as per tag), because you need to specify the layers you want to query in the request... in other words you must know the list of layers before doing the query. – nmtoken Nov 1 at 6:22
  • Are you asking about features already displayed on the map or the ones on server that could be displayed? – TomazicM Nov 1 at 9:19
  • I'm asking about features already displayed on the map – Federico Maroncelli Nov 1 at 11:16
  • using getfeatureinfo I have difficulty append the results in a single popup – Federico Maroncelli Nov 1 at 11:23
  • What are those difficulties? Edit your question and add your code of what you have tried so far and doesn't work. – TomazicM Nov 1 at 11:27
1

Initially it was not mentioned in the question that leaflet.wms.js plugin is used to retrieve WMS layers. This plugin has built-in functionality to display WMS layers info in popup upon click on the map, retrieved from server with GetFeatureInfo request .

Unluckily there are several problems with implementation of this popup and also the fact that there might be CORS violation problems when retrieving feature info from server.

First problem is how info in the deafult popup looks like. It's simply all the text, retrived from server, for example:

enter image description here

This looks pretty useless, but text itsel has some structure that could be parsed and retrieve only relevant data:

Results for FeatureType 'https://geoportale.regione.lazio.it/geoserver_nsit/ptpr:aree_protette':
--------------------------------------------
geom = [GEOMETRY (Polygon) with 39 points]
nome = Monte Rufeno
vincolo = Riserva Naturale Regionale
comuni = Acquapendente
ente_gest = Comune di Acquapendente
provincia = Viterbo
anno_istit = 1983.0
istitucion = L.R. 19 settembre 1983, n. 66 (B.U.R. 10 ottobre 1983, n. 28)
nota = Il perimetro comprende i terreni del demanio regionale proveniente dai beni dell'azienda di Stato per le foreste demanial . Non ha valenza giuridica e/o probatoria. Sup. (Ha) A¨ desunta dalla presente perimetrazione
rif_cartog = IGM Serie 25/V
categoria = Riserva naturale
cod_euap = EUAP0273
admapkey = 17
pianificaz = 
data = Riferimento dati: settembre 2014
id_rl = f008
allegati = PTPR_art_37
gu_bu_data = B.U. n. 28 del 10.10.1983
objectid = 0
zona = 
id_ma = 
operatore = 
shape_area = 2968.95409049759
shape_len = 63966.45559087736
--------------------------------------------

The second problem with this partucular WMS server is that when GetFeatureInfo request is sent for more than one layer at once, it returns info only for the first layer.

But luckily this popup functionality has various hooks than can be used to manipulate what requsts are sen to WMS server and what will be displayed in popup. To not complicate to much, the simplest way to get desired info in desired form in popup is first to modify getFeatureInfo hook to send GetFeatureInfo request separately for each layer, and second, to modify showFeatureInfo hook, which gets as parameters text formated info from WMS request and lat,lng coordinates.

Since text retreived form server has some simple structure, it can be parsed to get desired info into popup.

In the code below GetFeatureInfo method of L.WMS.Source object is modified with include method to retrieve information for each layer separately. showFeatureInfo method is modified to extract only desired info from WMS feature info and then display it in popup. Simple getInfo parser is used for parsing. Code has to be inserted before WMS source creation.

function getInfo(info, start, end) {
  var i, j, retVal;

  i = info.indexOf(start);
  if (i > 0) {
    j = info.indexOf(end, i)
    retVal = info.slice((i + start.length), j);
    }
  else {
    retVal = '';
  }
  return retVal;
}

L.WMS.Source = L.WMS.Source.include({
  'showFeatureInfo': function(latlng, info) {
    console.log(info);
    var popupContent = '';
    info.forEach(function(info) {
      var layerId = getInfo(info, '/ptpr:', "'");
      if (layerId == 'aree_protette') {
        popupContent += '<p><u>Aree protete</u><br>' +
                       getInfo(info, 'nome = ', '\n') + '<br>' +
                       getInfo(info, 'vincolo = ', '\n');
        }
      else if (layerId == 'zone_umide') {
        popupContent += '<p><u>Zone umide</u><br>' +
                       getInfo(info, 'Denominazione = ', '\n') + '<br>';
      }
    });
    if (popupContent.length > 0) {
      L.popup({className: 'popup'})
        .setLatLng(latlng)
        .setContent(popupContent)
        .openOn(map);
    }
  },

  'getFeatureInfo': function(point, latlng, layers, callback) {
    var i;
    var j = 0;
    var allText = [];
    for (i = 0; i < layers.length; i++) {
      var tmpLayer = [0];
      tmpLayer[0] = layers[i];
      var params = this.getFeatureInfoParams(point, tmpLayer);
      var url = this._url + L.Util.getParamString(params, this._url);

      this.showWaiting();
      this.ajax(url, done);

      function done(result) {
        this.hideWaiting();
        var text = this.parseFeatureInfo(result, url);
        allText[j++] = text;
        if (j == layers.length) {
          callback.call(this, latlng, allText);
        }
      }
    }
  }
});

Now upon click on point in the map with two overlapping layers, the following popup appears:

enter image description here

With this particaular WMS server there still remains CORS violation problem. Plugin tries to deal with it by retrieving GetFeatureInfo info through <iframe> and in this case we have even less control over it. If we are running our page on our own server, simple solution is to write simple 'proxy' page that retrieves infor for us from the server.

In my case server is an old MS IIS with ASP, on which such 'proxy' page can look like this:

<!DOCTYPE html>
<html>
<head> 
<meta charset="UTF-8">
</head>  
<body>
<%
  Dim result, site, xmlhttp

  site = URLDecode(request.querystring("site"))

  Response.expires = -1

  Set xmlhttp = Server.CreateObject("MSXML2.ServerXMLHTTP")

  Call xmlhttp.Open("GET", site, False)
  Call xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=UTF-8")
  Call xmlhttp.send()

  If (Err.Number <> 0) Then
    Response.write(Err.Number & " Source: " & Err.Source & " Desc: " &  Err.Description)
    Err.Clear
  Else
    result = xmlhttp.ResponseText
    Response.Write(result)
  End If

  Function URLDecode(StringToDecode)  
    Dim TempAns
    Dim CurChr

    CurChr = 1    
    Do Until CurChr - 1 = Len(StringToDecode)
      Select Case Mid(StringToDecode, CurChr, 1)
        Case "+"
          TempAns = TempAns & " "
        Case "%"
          TempAns = TempAns & Chr(CLng("&h" & Mid(StringToDecode, CurChr + 1, 2)))
           CurChr = CurChr + 2
        Case Else
          TempAns = TempAns & Mid(StringToDecode, CurChr, 1)
      End Select
      CurChr = CurChr + 1
    Loop

    URLDecode = TempAns
  End Function
%>
</body> 
</html>

In case of PHP, such 'proxy' page is extremely simple:

<?php
$content = file_get_contents($_GET["site"]);
echo $content;
?> 

In the case of using such proxy, getFeatureInfo hook has to be modified to route request through proxy, which in my case is lib/asp/proxy.asp?site=:

'getFeatureInfo': function(point, latlng, layers, callback) {
  var i;
  var j = 0;
  var allText = [];
  for (i = 0; i < layers.length; i++) {
    var tmpLayer = [0];
    tmpLayer[0] = layers[i];
    var params = this.getFeatureInfoParams(point, tmpLayer);
    var url = this._url + L.Util.getParamString(params, this._url);
    var proxyUrl = 'lib/asp/proxy.asp?site=' + encodeURIComponent(url);

    this.showWaiting();
    this.ajax(proxyUrl, done);

    function done(result) {
      this.hideWaiting();
      var text = this.parseFeatureInfo(result, url);
      allText[j++] = text;
      if (j == layers.length) {
        callback.call(this, latlng, allText);
      }
    }
  }
}

Remark: .leaflet-popup-content-wrapper style definition from question has to be removed to get popups of normal size.

  • I added your code before the wms source but no popup opens. – Federico Maroncelli Nov 1 at 19:22
  • When I tested, I did it in IE11, and there it works OK. Now I checked also in Firefox and Chrome. If you check in browser debugger console, you'll see it reports CORS (cross origin request) policy violation when accessing GetFeatureInfo. I have no idea why since it's the same site as WMS layer. – TomazicM Nov 1 at 20:06
  • clicking on the map is another way to understand which layers are overlapped and have a list of names in a popup? – Federico Maroncelli Nov 1 at 22:00
  • See my modified answer. – TomazicM Nov 3 at 11:55
  • My problem is that I have xampp apache and I have trouble seeing the proxy page like you did with asp – Federico Maroncelli Nov 3 at 17:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.