Coming from intensive usage of Leaflet with open source tiles and remote data layers (multipolygons, markers, etc...) retrieved from a PostgreSql database, I wanted to try Geoserver capabilities to achieve - more or less - same capabilities (display polygons, managing hover and click events, display additional data etc...).

Till now, I achieved to display a polygons on a Leaflet map using TMS (perfs are so incredible to my point of view).

Next step was to implement the "mouseover"/"mouseout" event on polygons displayed from tiles, just to indicate to the user that he's mouse hovering a polygon.

So, I retrieve the tiles, then, I retrieve the bounding box of the layers to only listen on the mousemove Leaflet event in this particular area.
For each captured event, I'm using the GetFeatureInfo request to retrieve the hovered polygon, then I add the polygon to the map with a different style.

JavaScript code :

let map;

function init() {
    map = new L.Map('map');

    // GeoServer tiles URL
    const tilesURL = 'http://localhost:8080/geoserver/gwc/service/tms/1.0.0/adcf:epci_2018@EPSG:900913@jpeg/{z}/{x}/{y}.jpg';
    const attr     = 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';

    const tilesLayer = new L.TileLayer(tilesURL, {maxZoom: 18, attribution: attr, tms: true});

    map.setView(new L.LatLng(46.29381556233369, 2.3620605468750004), 6);

    // Reference to the polygon created from mousemove event
    let hovered = null;

    // Get tiles polygons bounding box to limit event triggering
    const parser = new DOMParser();
    fetch('http://localhost:8080/geoserver/wms?request=GetCapabilities').then((res) => {
        res.text().then((xml) => {
            const capabilities = parser.parseFromString(xml, 'text/xml');
            const layers = capabilities.getElementsByTagName('Layer');

            for (const layer of layers) {
                for (const child of layer.children) {
                    if (child.tagName === 'Name' && child.innerHTML === 'adcf:epci_2018') {
                        const bbox = layer.querySelectorAll('BoundingBox[CRS="EPSG:4326"]')[0];

                        // Bouding box polygon
                        const bboxLayer = new L.polygon([
                            [bbox.getAttribute('minx'), bbox.getAttribute('miny')],
                            [bbox.getAttribute('minx'), bbox.getAttribute('maxy')],
                            [bbox.getAttribute('maxx'), bbox.getAttribute('maxy')],
                            [bbox.getAttribute('maxx'), bbox.getAttribute('miny')],
                        ], {color: 'red', fillOpacity: 0})
                        // When moving over the bbox polygon, call GetFeatureInfo request to retrieve hovered polygon
                        .on('mousemove', (e) => {
                            // Build GetFeeatureInfo request parameters
                            const target = map.latLngToContainerPoint(e.latlng, map.getZoom());
                            const size   = map.getSize();

                            const sw = map.getBounds().getSouthWest();
                            const ne = map.getBounds().getNorthEast();

                            const params = {
                                request: 'GetFeatureInfo',
                                query_layers: 'adcf:epci_2018',
                                layers: 'adcf:epci_2018',
                                x: Math.round(target.x),
                                y: Math.round(target.y),
                                height: size.y,
                                width: size.x,
                                info_format: 'application/json',
                                bbox: sw.lng + ',' + sw.lat + ',' + ne.lng + ',' + ne.lat,

                            // Call GetFeatureInfo request then add hovered polygon to the map
                            fetch('http://localhost:8080/geoserver/wms' + L.Util.getParamString(params)).then((res) => {
                                res.json().then((json) => {
                                    if (hovered !== null) {
                                    if (json.features.length > 0) {
                                        hovered = L.geoJson(json).addTo(map);




Screenshots (yellow dot is cursor) : basemap : tiles and bbox (in red) retrieved

Code works nearly fine, but there is a vertical gap between the cursor position, and the layer retrieved by GetFeatureInfo request. This vertical gap is increasing depending on the zoom level (zoom less, gap more). From zoom >= level 7, it works almost precisely.

gap depending on zoom

Well, I'm wondering what's wrong with code or maybe with Geoserver layers configuration. Because the problem is only on the vertical axis, and disappear when zooming.

1 Answer 1


there is a vertical gap between the cursor position, and the layer retrieved by GetFeatureInfo request.

No, probably what you really mean to say is "there is a latitude shift between the cursor position and the latitude I'm using in the getFeatureInfo query".

Leaflet uses EPSG:3857 for display (and for screen-relative coordinates; historically called EPSG:900913), and EPSG:4326 for the latlngs. However, your code is explicitly converting the mouseover coordinates into screen-relative coordinates, effectively performing a linear interpolation of the EPSG:4326 coordinates.

Rely on e.latlng for the coordinates of the point your pointer device is hovering over, or rely on something like https://github.com/heigeo/leaflet.wms for the getFeatureInfo queries.


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.