Leaflet – Adding popupContent into a Custom Info Control for Better User Experience

html-popupleafletqgis2web

The leaflet map (image map in CRS Simple) was created using QGIS2WEB and I am trying to display the content of pop-up at a constant position (top-corner), opening when hovered over a polygon by the function highlightFeature.

The popup Content should appear into a Custom Info Control, as explain here (https://leafletjs.com/examples/choropleth/). I don't understand (perpetual newbie here) how to write the var popupContent into the props function.

The Pop-up text is based on infos contained in a CSS file and returned through this lines

var popupContent = '<table>\
        <tr>\
          <th scope="row">\
            <td>' + (feature.properties['Themen'] !== null ? autolinker.link(feature.properties['Themen'].toLocaleString()) : '') + '</td>\
       </tr>\
       <tr>\
          <th scope="row">\
            <td>' + (feature.properties['Description'] !== null ? autolinker.link(feature.properties['Description'].toLocaleString()) : '') + '</td>\
        </tr>\
    </table>';

This is what I want to integrate into the QGIS2web export

    var info = L.control();
    
    info.onAdd = function (map) {
        this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
        this.update();
        return this._div;
    };
    
    // method that we will use to update the control based on feature properties passed
    info.update = function (props) {
        this._div.innerHTML = '<h4>US Population Density</h4>' +  (props ?
            '<b>' + props.name + '</b><br />' + props.density + ' people / mi<sup>2</sup>'
            : 'Hover over a state');
    };
    
    info.addTo(map);

Here is the full code of the QGIS2web html export

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="initial-scale=1,user-scalable=no,maximum-scale=1,width=device-width">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <link rel="stylesheet" href="css/leaflet.css">
        <link rel="stylesheet" href="css/qgis2web.css"><link rel="stylesheet" href="css/fontawesome-all.min.css">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <style>
        html, body, #map {
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 10px;
        }
        #map {
            width: 1000px;
            height: 700px;
            }
        </style>
        <title></title>
    </head>
    <body>
        <div id="map">
        </div>
        <script src="js/qgis2web_expressions.js"></script>
        <script src="js/leaflet.js"></script>
        <script src="js/leaflet.rotatedMarker.js"></script>
        <script src="js/leaflet.pattern.js"></script>
        <script src="js/leaflet-hash.js"></script>
        <script src="js/Autolinker.min.js"></script>
        <script src="js/rbush.min.js"></script>
        <script src="js/labelgun.min.js"></script>
        <script src="js/labels.js"></script>
        <script src="data/TZ_Feldarbeiten_buffer_1.js"></script>
        <script>
        var highlightLayer;
        function highlightFeature(e) {
            highlightLayer = e.target;
            highlightLayer.openPopup();
        }
        var map = L.map('map', {
        crs: L.CRS.Simple,
            zoomControl:true, maxZoom:3, minZoom:-2
        }).fitBounds([[0,0],[-2327.0,3295.0]])      
        .setView([0, 0]);;
        var bounds = L.latLngBounds([[0.0, 3295.0], [-2327.0,0]]);
        map.setMaxBounds(bounds);
        map.on('drag', function() {
        map.panInsideBounds(bounds, { animate: false });
        }); 
        
        var hash = new L.Hash(map);
        //map.attributionControl.setPrefix('<a href="https://github.com/tomchadwin/qgis2web" target="_blank">qgis2web</a> &middot; <a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a> &middot; <a href="https://qgis.org">QGIS</a>');
        var autolinker = new Autolinker({truncate: {length: 30, location: 'smart'}});
        var bounds_group = new L.featureGroup([]);
        function setBounds() {
            if (bounds_group.getLayers().length) {
                map.fitBounds(bounds_group.getBounds());
            }
        }
        
        map.createPane('pane_Wimmelbild_Interactiv4_nolabel_0');
        map.getPane('pane_Wimmelbild_Interactiv4_nolabel_0').style.zIndex = 400;
        var img_Wimmelbild_Interactiv4_nolabel_0 = 'data/Wimmelbild_Interactiv4_nolabel_0.png';
        var img_bounds_Wimmelbild_Interactiv4_nolabel_0 = [[0.0,0.0],[-2327.0,3295.0]];
        var layer_Wimmelbild_Interactiv4_nolabel_0 = new L.imageOverlay(img_Wimmelbild_Interactiv4_nolabel_0,
                                              img_bounds_Wimmelbild_Interactiv4_nolabel_0,
                                              {pane: 'pane_Wimmelbild_Interactiv4_nolabel_0'});
        bounds_group.addLayer(layer_Wimmelbild_Interactiv4_nolabel_0);
        map.addLayer(layer_Wimmelbild_Interactiv4_nolabel_0);
        function pop_TZ_Feldarbeiten_buffer_1(feature, layer) {
            layer.on({
                mouseout: function(e) {
                    if (typeof layer.closePopup == 'function') {
                        layer.closePopup();
                    } else {
                        layer.eachLayer(function(feature){
                            feature.closePopup()
                        });
                    }
                },
                mouseover: highlightFeature,
            });
            var popupContent = '<table>\
                    <tr>\
                        <th scope="row">\
                        <td>' + (feature.properties['Themen'] !== null ? autolinker.link(feature.properties['Themen'].toLocaleString()) : '') + '</td>\
                    </tr>\
                    <tr>\
                        <th scope="row"><strong></strong>\
                        <td>' + (feature.properties['Descriptio'] !== null ? autolinker.link(feature.properties['Descriptio'].toLocaleString()) : '') + '</td>\
                    </tr>\
                </table>';
            
            layer.bindPopup(popupContent, {maxHeight: 200, closeButton: false}).openPopup();
        }

        function style_TZ_Feldarbeiten_buffer_1_0() {
            return {
                pane: 'pane_TZ_Feldarbeiten_buffer_1',
                opacity: 1,
                color: 'rgba(35,35,35,1.0)',
                dashArray: '',
                lineCap: 'butt',
                lineJoin: 'miter',
                weight: 1.0, 
                fill: true,
                fillOpacity: 1,
                fillColor: 'rgba(229,182,54,1.0)',
                interactive: true,
            }
        }
        map.createPane('pane_TZ_Feldarbeiten_buffer_1');
        map.getPane('pane_TZ_Feldarbeiten_buffer_1').style.zIndex = 401;
        map.getPane('pane_TZ_Feldarbeiten_buffer_1').style['mix-blend-mode'] = 'normal';
        var layer_TZ_Feldarbeiten_buffer_1 = new L.geoJson(json_TZ_Feldarbeiten_buffer_1, {
            attribution: '',
            interactive: true,
            dataVar: 'json_TZ_Feldarbeiten_buffer_1',
            layerName: 'layer_TZ_Feldarbeiten_buffer_1',
            pane: 'pane_TZ_Feldarbeiten_buffer_1',
            onEachFeature: pop_TZ_Feldarbeiten_buffer_1,
            style: style_TZ_Feldarbeiten_buffer_1_0,
        });
        bounds_group.addLayer(layer_TZ_Feldarbeiten_buffer_1);
        map.addLayer(layer_TZ_Feldarbeiten_buffer_1);
        var title = new L.Control();
            title.onAdd = function (map) {
                this._div = L.DomUtil.create('div', 'info');
                this.update();
                return this._div;
            };
            title.update = function () {
                this._div.innerHTML = '<h2>Die Entwicklungszusammenarbeit</h2>';
            };
            title.addTo(map);
            
        var baseMaps = {};
        L.control.layers(baseMaps,{'<img src="legend/TZ_Feldarbeiten_buffer_1.png" /> TZ_Feldarbeiten_buffer': layer_TZ_Feldarbeiten_buffer_1,"Wimmelbild_Interactiv4_nolabel": layer_Wimmelbild_Interactiv4_nolabel_0,}).addTo(map);
        setBounds();
        L.ImageOverlay.include({
            getBounds: function () {
                return this._bounds;
            }
        });


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

How should it be written?

Best Answer

If you look at the cited Leaflet choropleth example , you'll see that info.update method updates contents of info control. info.update is called within highlightFeature event processing feature when mouse gets over a feature, taking as parameter properties object of the feature.

You just have to apply the same logic in you case. Relevant code could then look something like this:

var info = L.control();

info.onAdd = function (map) {
  this._div = L.DomUtil.create('div', 'info');
  this.update();
  return this._div;
};

info.update = function (props) {
  var infoContent = '<h4>Info about feature</h4>';
  if (props)
    infoContent += '<table>\
      <tr>\
        <th scope="row">\
        <td>' + (props['Themen'] !== null ? autolinker.link(props['Themen'].toLocaleString()) : '') + '</td>\
      </tr>\
      <tr>\
        <th scope="row"><strong></strong>\
        <td>' + (props['Descriptio'] !== null ? autolinker.link(props['Descriptio'].toLocaleString()) : '') + '</td>\
      </tr>\
    </table>';
  else {
    infoContent += 'Hover over a feature';
  }
  this._div.innerHTML = infoContent;
};
info.addTo(map);

function highlightFeature(e) {
  layer = e.target;
  info.update(layer.feature.properties);
}

function resetHighlight(e) {
  info.update();
}

function pop_TZ_Feldarbeiten_buffer_1(feature, layer) {
  layer.on({
    mouseout: resetHighlight,
    mouseover: highlightFeature
  });
}