Search code examples
openlayerswmsopenlayers-6

OpenLayers 6 : Map render doesn't happen on zoom


I've been looking everywhere for days about my issue, but still can't find a good solution to this problem.

I had to do a project using openlayers, to create maps that are included in iframes on different cities websites, to show some informations on WMS layers (graves status in cemeteries, trash sorting policy, etc).

Everything works fine, except for one thing. When I zoom in or out, my app does a request to get the new WMS layers, I can see the request coming back, with the good image, but it doesn't render the map automaticaly when the request comes back. It does work perfectly while paning or using the geolocation button.

My current workaround is to add a map.render() in the "moveend" event of the app, with an adjustable delay to wait for the request.

I'm using a config file to transmit the map default coordinates, zoom and layers, so I'm wondering if the problem doesn't come from that, since my WMS layers are all created in an each loop.

Since the informations of that project are confidential I have to hide everything, but here is my code :

scripts.js

$(function () {
    //EPSG:2056 definition
    proj4.defs('EPSG:2056', '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 ' + '+x_0=2600000 +y_0=1200000 +ellps=bessel ' + '+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs');
    ol.proj.proj4.register(proj4);

    //Initialization for i18next
    i18next.init({
        lng: config.defaultLanguage,
        fallbackLng: config.fallbackLanguage,
        supportedLngs: ['fr', 'de', 'en'],
        debug: false,
        resources: translations
    });

    //Variables declaration
    const popContainer = document.getElementById('popup');
    const projectionSwiss = new ol.proj.Projection({
        code: 'EPSG:2056',
        extent: [2547692.0, 1067417.0, 2681894.0, 1177781.0],
    });
    const projectionExtent = [2547692.0, 1067417.0, 2681894.0, 1177781.0];
    var geolocationOn = false;
    var overlayGroup = new ol.layer.Group({
        title: i18next.t('layers_overlays_title'),
        layers: [],
        name: 'overlayGroup'
    });
    const overlay = new ol.Overlay({
        element: popContainer,
        autoPan: true,
        autoPanAnimation: {
            duration: 250,
        },
    });
    var overlayImgs = [];

    //Applying page title from config file
    $("title").append(config.pageTitle);

    //Control and function for geolocation feature
    class geolocationControl extends ol.control.Control {
        constructor(opt_options) {
            const options = opt_options || {};
            const button = document.createElement('button');
            button.innerHTML = '';
            const element = document.createElement('div');
            element.className = 'geolocate-btn ol-unselectable ol-control';
            element.appendChild(button);
            super({
                element: element,
                target: options.target,
            });
            button.addEventListener('click', this.geolocationFunc.bind(this), false);
        }
        geolocationFunc() {
            if (!navigator.geolocation) {
                alert(i18next.t("geolocation_unsupported"));
                return;
            }
            navigator.geolocation.getCurrentPosition(onSuccess, onError);
            function onSuccess(position) {
                geolocationOn = !geolocationOn;
                var {
                    latitude,
                    longitude
                } = position.coords;
                var geolocationCoords = [longitude, latitude];
                var geolocationCoords2056 = proj4("EPSG:4326", "EPSG:2056", geolocationCoords);
                if (geolocationOn) {
                    /*$('.geolocate-btn').css('background-color', 'red')*/
                    $('.geolocate-btn').addClass('ol-control-active');
                    const geolocationMarker = new ol.Feature();
                    geolocationMarker.setStyle(
                        new ol.style.Style({
                            image: new ol.style.Circle({
                                radius: 6,
                                fill: new ol.style.Fill({
                                    color: '#3399CC',
                                }),
                                stroke: new ol.style.Stroke({
                                    color: '#fff',
                                    width: 2,
                                }),
                            }),
                        })
                    );
                    geolocationMarker.setGeometry(new ol.geom.Point(geolocationCoords2056));
                    var geolocationVector = new ol.layer.Vector({
                        source: new ol.source.Vector({
                            features: [geolocationMarker],
                        }),
                    });
                    geolocationVector.set('name', 'geolocationCurrent');
                    map.addLayer(geolocationVector);
                }
                else {
                    /*$('.geolocate-btn').css('background-color', 'rgba(255,255,255,.4)')*/
                    $('.geolocate-btn').removeClass('ol-control-active');
                    map.getLayers().forEach(function (layer) {
                        if (layer.get('name') === 'geolocationCurrent') {
                            map.removeLayer(layer);
                        }
                    });
                }
            }
            function onError() {
                alert(i18next.t("geolocation_error"));
            }
        }
    }

    //Main map declaration
    var map = new ol.Map({
        target: 'map',
        layers: [
            new ol.layer.Group({
                title: i18next.t('layers_base_maps_title'),
                layers: [
                    new ol.layer.Tile({
                        title: i18next.t('layers_base_maps_***'),
                        type: 'base',
                        visible: config.baseMaps.***.defaultVisible,
                        source: newSourceWMTS('***', 'epsg'),
                    }),
                    new ol.layer.Tile({
                        title: i18next.t('layers_base_maps_***'),
                        type: 'base',
                        visible: config.baseMaps.***.defaultVisible,
                        source: newSourceWMTS('***', 'epsg'),
                    }),
                    new ol.layer.Tile({
                        title: i18next.t('layers_base_maps_***'),
                        type: 'base',
                        visible: config.baseMaps.***.defaultVisible,
                        source: newSourceWMTS('***', 'epsg'),
                    }),
                    new ol.layer.Tile({
                        title: i18next.t('layers_base_maps_***'),
                        type: 'base',
                        visible: config.baseMaps.***.defaultVisible,
                        source: newSourceWMTS('***', 'epsg_***'),
                    })
                ],
            }),
            overlayGroup,
        ],
        overlays: [overlay],
        view: new ol.View({
            projection: projectionSwiss,
            extent: projectionExtent,
            center: [config.centerX, config.centerY],
            zoom: config.defaultZoom,
            maxZoom: config.maxZoom,
            minZoom: config.minZoom,
        }),
        controls: ol.control.defaults({
            attributionOptions: { collapsible: true, }
        }).extend([
            new ol.control.FullScreen(),
            new ol.control.ScaleLine(),
            new ol.control.LayerSwitcher(),
            /*new ol.control.MousePosition({
                coordinateFormat: ol.coordinate.createStringXY(2)
            }),
            new ol.control.ZoomSlider(),*/
            new ol.control.Zoom(),
            new geolocationControl(),
        ])
    });

    //WMS Layers
    _.each(_.sortBy(config.wmsLayers, 'zIndex'), function (layer) {
        var imgLayer = new ol.layer.Image({
            source: new ol.source.ImageWMS({
                url: config.wmsUrl,
                params: { 'FORMAT': 'image/png', 'LAYERS': layer.name, 'ogcserver': config.wmsOgcServer },
                projection: projectionSwiss,
            }),
            type: 'overlays',
            title: '<img src="' + config.wmsUrl + '?version=1.3.0&service=WMS&request=GetLegendGraphic&ogcserver=' + config.wmsOgcServer + '&sld_version=1.1.0&layer=' + layer.name + '&format=image/png&legend_options=fontColor:0xFFFFFF;forceLabels:off"/>',
            visible: layer.defaultVisible,
        });
        imgLayer.set('wmsAttributeGroupId', layer.wmsAttributeGroupId);
        overlayGroup.getLayers().getArray().push(imgLayer);
        overlayImgs.push(imgLayer);
    });

    //WMTS Layers
    function newSourceWMTS(sourceName, matrixSet) {
        return new ol.source.WMTS({
            url: 'https://***.***.ch/***/wmts/' + sourceName + '/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png',
            cacheSize: 2048,
            tileGrid: new ol.tilegrid.WMTS({
                origin: ol.extent.getTopLeft(projectionExtent),
                resolutions: [4000, 3750, 3500, 3250, 3000, 2750, 2500, 2250, 2000, 1750, 1500, 1250, 1000, 750, 650, 500, 250, 100, 50, 20, 10, 5, 2, 1.5, 1, 0.5, 0.25, 0.1, 0.05],
                matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
            }),
            format: 'image/png',
            matrixSet: matrixSet,
            wrapX: true,
            requestEncoding: 'REST',
            attributions: i18next.t("layers_base_maps_attributions"),
        })
    };

    //Popup on click
    map.on('singleclick', function (evt) {
        var element = overlay.getElement();
        $(element).popover('dispose');
        _.each(overlayImgs, function (l) {
            if (l.getVisible() === true) {
                var coord = evt.coordinate;
                var viewResolution = map.getView().getResolution();
                var url = l.getSource().getFeatureInfoUrl(coord, viewResolution, projectionSwiss, { 'INFO_FORMAT': 'application/vnd.ogc.gml' });
                $.get(url, function (xmlData) {
                    workingData = $($.parseXML(xmlData));
                    var description = '';
                    var title = '';
                    var wmsAttributeGroup = _.find(config.wmsAttributeGroups, function (ga) { return ga.id === l.get('wmsAttributeGroupId') });
                    if (wmsAttributeGroup) {
                        var referenceAttribute = workingData.find(wmsAttributeGroup.referenceAttribute);
                        if (referenceAttribute.length > 0) {
                            var popupTitle = '';
                            var popupContent = '';
                            _.each(wmsAttributeGroup.attributes, function (attribute) {
                                var attributeValue = workingData.find(attribute.name).text();
                                if (attribute.isTitle) {
                                    popupTitle += '<h5>' + attributeValue + '</h5> ';
                                } else {
                                    if (attributeValue !== '') {
                                        if (attribute.display) {
                                            popupContent += attribute.name.charAt(0).toUpperCase() + attribute.name.slice(1) + ' : ';
                                        }
                                        popupContent += attributeValue + '<br/>';
                                    }
                                }
                            });
                            if (popupContent !== '') {
                                overlay.setPosition(evt.coordinate);
                                $(element).popover({
                                    'placement': 'top',
                                    'animation': true,
                                    'html': true,
                                    'content': popupContent,
                                    'title': popupTitle,
                                });
                                $(element).popover('show');
                            }
                        }
                    }
                });
            }
        });
    });
    map.on('moveend', function () {
        var element = overlay.getElement();
        $(element).popover('dispose');
        setTimeout(function () {
            map.render();
        }, config.renderTime);
    });

    //Translating buttons titles
    $('.geolocate-btn button').prop('title', i18next.t('btn_geolocation_title'));
    $('.ol-full-screen-false').prop('title', i18next.t('btn_fullscreen_title'));
    $('.layer-switcher button').prop('title', i18next.t('btn_layers_title'));
    $('.ol-attribution button').prop('title', i18next.t('btn_attributions_title'));
    $('.ol-zoom-in').prop('title', i18next.t('btn_zoomin_title'));
    $('.ol-zoom-out').prop('title', i18next.t('btn_zoomout_title'));

    //Remove default text from controls
    $('.ol-zoom-in').text("");
    $('.ol-zoom-out').text("");
    $('.ol-full-screen-false').text("");
    $('.ol-full-screen-true').text("");
    $('.ol-attribution button').text("");
});

config.js

let config = {
    //Availables languages : fr, de, en
    defaultLanguage: 'fr',
    fallbackLanguage: 'fr',
    //Title of the page on the browser
    pageTitle: 'Default',
    //Coordinates of the center of the map, use EPSG:2056 standard
    centerX: ***,
    centerY: ***,
    //Default zoom and limits
    defaultZoom: 11,
    maxZoom: 16,
    minZoom: 0,
    //Time between the WMS request and the render of the map
    renderTime: 1000,
    //Set default map, only one map can be set to true
    baseMaps: {
        ***: { defaultVisible: false },
        ***: { defaultVisible: false },
        ***: { defaultVisible: false },
        ***: { defaultVisible: true }
    },
    //WMS Config
    wmsUrl: 'https://***.***.ch/***/***/***',
    wmsOgcServer: 'source for None',
    wmsLayers: [
        { name: '***', defaultVisible: true, zIndex: -1, wmsAttributeGroupId: 1 },
        { name: '***', defaultVisible: true, zIndex: -1, wmsAttributeGroupId: -1 },
        { name: '***', defaultVisible: true, zIndex: -1, wmsAttributeGroupId: -1 },
        { name: '***', defaultVisible: true, zIndex: -1, wmsAttributeGroupId: -1 },
        { name: '***', defaultVisible: true, zIndex: -1, wmsAttributeGroupId: -1 },
    ],
    wmsAttributeGroups: [
        {
            id: 1, referenceAttribute: '***', attributes: [
                { name: '***', display: true, isTitle: true },
                { name: '***', display: false, isTitle: false },
            ]
        }
    ],
}

index.html

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="cache-control" content="no-cache">
<title></title>
<!-- CSS styling-->
<link rel="stylesheet" href="js/lib/openlayers/css/ol.css" type="text/css">
<link rel="stylesheet" href="js/lib/bootstrap/dist/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="js/lib/ol-layerswitcher/src/ol-layerswitcher.css" type="text/css">
<link rel="stylesheet" href="css/styles.css" type="text/css">
<link rel="icon" href="favicon.ico" />
<!-- JS libraries -->
<script src="js/lib/i18next/i18next.js"></script>
<script src="js/lib/jquery/dist/jquery.js"></script>
<script src="js/lib/proj4/dist/proj4.js"></script>
<script src="js/lib/openlayers/build/ol.js"></script>
<script src="js/lib/underscore/underscore.js"></script>
<script src="js/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/lib/ol-layerswitcher/dist/ol-layerswitcher.js"></script>
<!-- Configs -->
<script src="js/config.js"></script>
<script src="js/translate.js"></script>



<body>
    <div id="map" class="map"></div>
    <div id="popup"></div>
    <script src="js/scripts.js"></script>
    <a href="https://***.***.ch" target="_blank">
        <div id="logo" class="logo-img"></div>
    </a>
</body>



</html>

Solution

  • The comment from @Mike on the main question was the solution to my problem.

    The getArray() function in overlayGroup.getLayers().getArray().push(imgLayer); on my each loop was blocking the events dispatch, as explained there : https://openlayers.org/en/latest/apidoc/module-ol_Collection-Collection.html#getArray