1

I am developing a webmap application using Leaflet + QGIS Server 3.4 on a Debian platform.

I have my HTML/Leaflet page ready, based on this code. Basically, the user has the choice between several tiled layers from Google Maps or OpenStreetMap and a WMS layer served by my QGIS server. The Javascript code is destined to enable popups when the user clicks on the features of the WMS layer. The data of my WMS layer is currently the data from the QGIS Server training tutorial.

It had some troubleshooting to do due to CRS/SRS problems but it seems solved now. I have put everything in the same CRS (EPSG:3857).

<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>

     <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
   crossorigin=""/>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
   crossorigin=""></script>
</head>

<body>

    <div id="searchmap" style="height: 800px; width:  1200px;"></div>

    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>

    <script>
        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: this.wmsParams.srs,
                  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);  
        };

    </script>

    <script>
    //  Leaflet map
    var mymap = L.map('searchmap', { crs: L.CRS.EPSG3857 }).setView([ 46.0, 3.0 ], 5) ;

    // layers definition
        var OSM = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
            maxZoom: 18,
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
                '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
                'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
            id: 'mapbox/streets-v11',
            tileSize: 512,
            zoomOffset: -1
        }),
        googleSat = L.tileLayer('http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',{
            maxZoom: 20,
            subdomains:['mt0','mt1','mt2','mt3']
        }),
        googleStreets = L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
            maxZoom: 20,
            subdomains:['mt0','mt1','mt2','mt3']
        }),
        googleHybrid = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',{
            maxZoom: 20,
            subdomains:['mt0','mt1','mt2','mt3']
        }),
        googleTerrain = L.tileLayer('http://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}',{
            maxZoom: 20,
            subdomains:['mt0','mt1','mt2','mt3']
        }),
         QGISDemo_Continents = L.tileLayer.betterWms('http://qgis.demo/cgi-bin/qgis_mapserv.fcgi?MAP=/home/qgis/projects/project_cantor/sites.qgs',{
            layers: 'continents',
            service: 'WMS',
            srs: 'EPSG:3857', 
            format: 'image/png',
            version: '1.3.0'
        });

    //  base maps
       var basemaps = {
           "OpenStreetMap": OSM,
           "Google Satellite" : googleSat,
           "Google Streets": googleStreets,
           "Google Hybrid": googleHybrid,
           "Google Terrain": googleTerrain,
           "QGIS Server demo - Continents": QGISDemo_Continents
        };


    // user control
        L.control.layers(basemaps).addTo(mymap);

    // default base map
        basemaps.OpenStreetMap.addTo(mymap);

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

My problem is now with the GetFeatureInfo request done by the Javascript code, which still does not work.

I can see in /var/log/qgis/qgisserver.log the requests processed by QGIS Server and they seem fine now. The GetMap requests are processed fine, but I cannot get the GetFeatureInfo requests to be working. I see nothing of interest in /var/log/apache2/qgis.demo.access.log or /var/log/apache2/qgis.demo.error.log .

Sample logs from /var/log/qgis/qgisserver.log:

 ******************** New request ***************
13:08:19 INFO Server[10951]: SERVER_NAMEqgis.demo
13:08:19 INFO Server[10951]: REQUEST_URI/cgi-bin/qgis_mapserv.fcgi?MAP=/home/qgis/projects/project_cantor/sites.qgs&REQUEST=GetFeatureInfo&SERVICE=WMS&cRS=EPSG%3A3857&STYLES=&TRANSPARENT=false&VERSION=1.3.0&FORMAT=image%2Fpng&BBOX=-20.390625000000004%2C25.918526162075153%2C32.34375000000001%2C52.56299503955803&HEIGHT=800&WIDTH=1200&LAYERS=continents&QUERY_LAYERS=continents&INFO_FORMAT=text%2Fhtml&I=590&J=266&FI_POLYGON_TOLERANCE=50
13:08:19 INFO Server[10951]: REMOTE_ADDR127.0.0.1
13:08:19 INFO Server[10951]: HTTP_USER_AGENTMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
13:08:19 INFO Server[10951]: BBOX:-20.390625000000004,25.918526162075153,32.34375000000001,52.56299503955803
13:08:19 INFO Server[10951]: CRS:EPSG:3857
13:08:19 INFO Server[10951]: FI_POLYGON_TOLERANCE:50
13:08:19 INFO Server[10951]: FORMAT:image/png
13:08:19 INFO Server[10951]: HEIGHT:800
13:08:19 INFO Server[10951]: I:590
13:08:19 INFO Server[10951]: INFO_FORMAT:text/html
13:08:19 INFO Server[10951]: J:266
13:08:19 INFO Server[10951]: LAYERS:continents
13:08:19 INFO Server[10951]: MAP:/home/qgis/projects/project_cantor/sites.qgs
13:08:19 INFO Server[10951]: QUERY_LAYERS:continents
13:08:19 INFO Server[10951]: REQUEST:GetFeatureInfo
13:08:19 INFO Server[10951]: SERVICE:WMS
13:08:19 INFO Server[10951]: STYLES:
13:08:19 INFO Server[10951]: TRANSPARENT:false
13:08:19 INFO Server[10951]: VERSION:1.3.0
13:08:19 INFO Server[10951]: WIDTH:1200
13:08:19 INFO Server[10951]: WMS Request parameters:
13:08:19 INFO Server[10951]:  - CRS : EPSG:3857
13:08:19 INFO Server[10951]:  - WIDTH : 1200
13:08:19 INFO Server[10951]:  - HEIGHT : 800
13:08:19 INFO Server[10951]:  - BBOX : -20.390625000000004,25.918526162075153,32.34375000000001,52.56299503955803
13:08:19 INFO Server[10951]:  - LAYERS : continents
13:08:19 INFO Server[10951]:  - QUERY_LAYERS : continents
13:08:19 INFO Server[10951]:  - FI_POLYGON_TOLERANCE : 50
13:08:19 INFO Server[10951]:  - FORMAT : image/png
13:08:19 INFO Server[10951]:  - INFO_FORMAT : text/html
13:08:19 INFO Server[10951]:  - I : 590
13:08:19 INFO Server[10951]:  - J : 266
13:08:19 INFO Server[10951]:  - TRANSPARENT : false
13:08:19 INFO Server[10951]:  - VERSION : 1.3.0
13:08:19 WARNING [10951]: mapSettings.destinationCrs(): EPSG:3857
13:08:19 WARNING [10951]: mapSettings.extent(): -20.3906250000000036,25.9185261620751533 : 32.3437500000000071,52.5629950395580323
13:08:19 WARNING [10951]: mapSettings width = 1365 height = 689
13:08:19 WARNING [10951]: mapSettings.mapUnitsPerPixel() = 0.0386712
13:08:19 INFO Server[10951]: Request finished in 25 ms

But nothing happens when I click on the map.

Trying to debug, I copied-paste the request URL parameters in a browser to see the response from QGIS Server and make sure if the problem comes from the Javascript in the HTML page or from the QGIS Server response:

http://qgis.demo/cgi-bin/qgis_mapserv.fcgi?MAP=/home/qgis/projects/project_cantor/sites.qgs&REQUEST=GetFeatureInfo&SERVICE=WMS&cRS=EPSG%3A3857&STYLES=&TRANSPARENT=false&VERSION=1.3.0&FORMAT=image%2Fpng&BBOX=-20.390625000000004%2C25.918526162075153%2C32.34375000000001%2C52.56299503955803&HEIGHT=800&WIDTH=1200&LAYERS=continents&QUERY_LAYERS=continents&INFO_FORMAT=text%2Fhtml&I=590&J=266&FI_POLYGON_TOLERANCE=50

The response I get in the browser is an empty HTML table with no rows except the headers. I changed the format to text/xml and also got an empty response:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<GetFeatureInfoResponse>
<Layer name="continents"/>
</GetFeatureInfoResponse>

So, for a reason I cannot identify, QGIS server does receive the WMS GetFeatureInfo request but returns an empty response.

However, during my previous debugging of the CRS/SRS problems, at some point, I got some non-empty response, albeit with the wrong location (the feature was in Somalia instead of France).

I added manually to the requested URL the parameter FI_POLYGONE_TOLERANCE=500, hoping it would 'catch' some close feature. That did not work.

I am stuck and don't know how to debug further. I have 2 hypothesis:

  • The GetFeatureInfo request is pinpointed to an empty location (no feature there), although I can see the features on my HTML/Leaflet page.
  • QGIS Server is somehow configured wrongly and returns an empty response.

Any debugging tips?

7
  • Latest news : I suspected there could be a problem with the Javascript function ``` _map.latLngToContainerPoint() ``` whose call was taken from the code I had copied ( Leaflet WMS + GetFeatureInfo on GitHub). If this function uses a point location in degrees (to comply with EPSG:4326) while the rest is in EPSG:3857 (unit = meters) that could be a bug source. So I changed the SRS from 3857 to 4326 in the Javascript code managed to get again some non-empty result (Somalia again, while I was clicking somewhere in central Europe). So it seems to be the CRS/SRS problem again. Commented Jun 11, 2020 at 12:14
  • 2
    Your issue is that the bbox parameter for the getFeatureInfo request is not in the same CRS that you specify. The magic bit is getting EPSG:3857 coordinates from the EPSG:4326 bounding box, see e.g. github.com/Flexberry/Leaflet-WMS/blob/… Commented Jun 11, 2020 at 12:28
  • I confirm, it's a problem with the CRS/SRS in the Javascript : I manually edited an URL for the GetFeatureInfo request and got the answer I expected. Now I need to debug the Javascript. Commented Jun 11, 2020 at 12:29
  • The GetFeatureInfo request is pinpointed to an empty location (no feature there), although I can see the features on my HTML/Leaflet page. ~ If you do a GetMap request like : qgis.demo/cgi-bin/qgis_mapserv.fcgi?MAP=/home/qgis/projects/… are there any features at 590 pixels down and 266 pixels across?
    – nmtoken
    Commented Jun 11, 2020 at 12:55
  • 1
    The issue you had Somalia, vs France when you use EPSG:4326 is probably because, you were/are passing the BBOX coordinates in the wrong order. For EPSG:4326 x is latitude
    – nmtoken
    Commented Jun 11, 2020 at 13:25

1 Answer 1

1

I found the solution in another forum thread, just right here : WMS GetFeatureInfo and Leaflet

Stefan added the few lines of code that make it right :

getFeatureInfoUrl: function (latlng) {
        // Construct a GetFeatureInfo request URL given a point
        var point = this._map.latLngToContainerPoint(latlng, this._map.getZoom()),
        size = this._map.getSize(),

        // this crs is used to show layer added to map
        crs = this.options.crs || this._map.options.crs,

        // these are the SouthWest and NorthEast points 
        // projected from LatLng into used crs
        sw = crs.project(this._map.getBounds().getSouthWest()),
        ne = crs.project(this._map.getBounds().getNorthEast()),

    params = {
        request: 'GetFeatureInfo',
        service: 'WMS',

        // this is the code of used crs
        srs: crs.code,
        styles: this.wmsParams.styles,
        transparent: this.wmsParams.transparent,
        version: this.wmsParams.version,
        format: this.wmsParams.format,

        // these are bbox defined by SouthWest and NorthEast coords
        bbox: sw.x + ',' + sw.y + ',' + ne.x + ',' + ne.y,
        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);
    }
1
  • If this is the solution to your problem, you can mark it as accepted yourself.
    – TomazicM
    Commented Jul 13, 2020 at 12:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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