简体   繁体   中英

OpenLayers: How to force a refresh of view and tiles within a loop

In OpenLayers 4.6.5, I need to force a refresh of the map view within a loop, and wait for the refresh to be completed before proceeding to the next iteration of the loop.

For some context: I'm doing repetitive pixel calculations on small polygon-bounded sections of a TileImage source. I've been able to get my calculation function working with one polygon at a time, but when I try to put it into a loop to run through all of the polygons, it won't work unless the whole polygon is visible in the map view, and the tiles within the polygon have loaded.

So I'm now trying to get a loop to work which, for each polygon:

  1. Repositions the map view so it is centered on the polygon at zoom level 14
  2. Refreshes the view to ensure all tiles have loaded
  3. Runs the calculation for the pixels in the polygon

I've tried playing around with async functions and promises, but can't get it to work properly - no matter what I try, my forceRefresh function runs 200 times (I have 200 polygons), and then all my tiles load for the current view, and then the calculation is attempted for each polygon, including those not in the current view.

Unfortunately I can't just zoom to the extent of all of the polygons and run all of the calculations from that zoom level. Due to differences in pixel sizes at different zoom levels, the results vary considerably when the calculation is run at say zoom level 8 vs zoom level 14.

These are the relevant parts of my code:

function forceRefresh(source,ms) {
    return new Promise(resolve => {
        source.refresh();
        map.updateSize();
        setTimeout(() => {
            resolve('Map should now be refreshed');
        }, ms);
    });
}

async function getCellAverage(featureID,equation,calcZoom=14) {
    return new Promise(resolve => {
        initialZoom = view.getZoom();
        initialCenter = view.getCenter();
        layerSource = window["layer" + featureID].getSource();
        window["allTilesLoaded"] = false;
        zoomToFit("source"+featureID);
        if(view.getZoom() > calcZoom) {
            view.setZoom(calcZoom);
        }
        layerSource.getFeatures().forEach(async function(feature) {
            var result = await forceRefresh(window['L8SourceNRG'],100);
            console.log(result);
            var geom = feature.getGeometry();
            var size = map.getSize();
            var ndviArray = [];
            map.addEventListener('postrender', function() {
                if(window['L8SourceNRG'].allTilesLoaded == true) {
                    console.log('Confirmed all L8SourceNRG tiles loaded');
                    map.removeEventListener('postrender');
                    if (geom.intersectsExtent(map.getView().calculateExtent(size))) {
                        for (var i=0; i<size[0]; i++) {
                            for (var j=0; j<size[1]; j++) {
                                var coordinate = map.getCoordinateFromPixel([i,j]);
                                if (geom.intersectsCoordinate(coordinate)) {
                                    let tileCoord = L8SourceTileGrid.getTileCoordForCoordAndResolution(coordinate, map.getView().getResolution());
                                    let key = tileCoord.join('-');
                                    if (key in window['tiles']) {
                                        let origin = L8SourceTileGrid.getOrigin(tileCoord[0]);
                                        let res = L8SourceTileGrid.getResolution(tileCoord[0]);
                                        let tileSize = L8SourceTileGrid.getTileSize(tileCoord[0]);
                                        var w = Math.floor(((coordinate[0] - origin[0]) / res) % (tileSize[0] | tileSize));
                                        var h = Math.floor(((origin[1] - coordinate[1]) / res) % (tileSize[1] | tileSize));

                                        var canvas = document.createElement("canvas");
                                        canvas.width = tiles[key].width;
                                        canvas.height = tiles[key].height;

                                        var ctx = canvas.getContext("2d");
                                        ctx.drawImage(tiles[key], 0, 0);

                                        let img = ctx.getImageData(0, 0, canvas.width, canvas.height);
                                        let imgData = img.data;

                                        let index = (w + h * 256) * 4;
                                        let pixel = [imgData[index + 0], imgData[index + 1], imgData[index + 2], imgData[index + 3]];
                                        ndviArray.push((( pixel[0] - pixel[1] ) / ( pixel[0] + pixel[1] )));

                                    }
                                }
                            }
                        }
                    }

                    var ndviSum = ndviArray.reduce((a, b) => a + b, 0);
                    var ndviAvg = ndviSum / ndviArray.length;

                    console.log("Average NDVI: "+ndviAvg);

                    makePolygonMask("avgNDVI",featureID,geom,ndviAvg);
                    window['cellAverageDone'] = true;
                }
            });
        });

        setTimeout(() => {
            resolve('resolved');
        }, 100);
    });
}

async function getAllAverages(equation) {
    map.getLayers().forEach(async function(layer) {
        window['cellAverageDone'] = false;
        if (layer.get('type') == "cell") {
            layerID = layer.get('name').substring(9);
            var result = await getCellAverage(layer.get('name').substring(5),'NDVI');
        }
    });
}

NB the "allTilesLoaded" property is set by a helper function, which for brevity I've left out as I don't think it's relevant to the question here.

The output in the console is as follows:

(200x) Map should now be refreshed
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.37295137830077313
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.38384215219470413
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.44356048105512613
...
Confirmed all L8SourceNRG tiles loaded
Average NDVI: NaN

The last line above shows what happens when the calculation is attempted for a polygon which is outside the current view.

I am expecting to see the following instead:

Map should now be refreshed
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.37295137830077313
Map should now be refreshed
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.38384215219470413
Map should now be refreshed
Confirmed all L8SourceNRG tiles loaded
Average NDVI: 0.44356048105512613
...

EDIT: Note that I'm working in OL 4.6.5, so 'rendercomplete' unfortunately doesn't work.

Use 'rendercomplete' instead of 'postrender'. This is a new event that is "triggered when rendering is complete, ie all sources and tiles have finished loading for the current viewport, and all tiles are faded in."

https://openlayers.org/en/latest/apidoc/module-ol_render_Event-RenderEvent.html#event:rendercomplete

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM