I have a Vue 2 serverless web application which uses OpenLayers. I ran into an interesting programming issue that applies to other applications as well where I need to execute 3 methods in sequence many times.
for(let i = 0 ; i < this.dateArraySurface10.length - 1 ; i )
{
waterfall([
this.setTimeSurface10(),
this.map.renderSync(),
this.myCallback(),
],
function(err){
console.log("Waterfall done",err);
});
}
This is my attempt to call the three functions which do the following three things
this.setTiimeSurface10()
: updates theTIME
parameter of the ImageWMS source layer already added to the map.this.map.renderSync()
: is a OpenLayers method called on the global map object to ensure all layers are rendered.this.myCallback()
: is a function that extracts the map canvas and adds it to a GIF object as a frame.
My problem is I need those three methods to run in that sequence 72 times and although I can hardcode them with setTimeout
I need a more abstract way of doing this so I can allow users to add many layers and export to GIF no matter what. I have tried to add an event listener on the this.map
object but something is not working properly.
Programmatically how would one ensure all three methods are excuted in sequence inside the for loop in the most pure Javascript way?
In case it helps here are the two methods :
setTimeSurface10: function () {
if (this.currentTimeSurface10 === null) {
this.currentTimeSurface10 = this.startTimeSurface10;
} else if (this.currentTimeSurface10 >= this.endTimeSurface10) {
this.currentTimeSurface10 = this.startTimeSurface10;
} else {
this.currentTimeSurface10 = new Date(
this.currentTimeSurface10.setMinutes(this.currentTimeSurface10.getMinutes() 60)
);
}
this.surface10.getSource().updateParams({ TIME: this.currentTimeSurface10.toISOString().split(".")[0] "Z" });
},
myCallback: function () {
const mapCanvas = document.createElement('canvas');
const divElement = document.querySelector(".map");
mapCanvas.width = divElement.offsetWidth;//size[0];
mapCanvas.height = divElement.offsetHeight;//size[1];
const mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
const transform = canvas.style.transform;
const matrix = transform
.match(/^matrix\(([^\(]*)\)$/)[1] //eslint-disable-line
.split(',')
.map(Number);
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix);
mapContext.drawImage(canvas, 0, 0);
}
}
);
this.gif.addFrame(mapCanvas, {copy:true, delay: 200});
}
CodePudding user response:
Answered at my other question but here is the answer again.
Thanks to some help from Mr. Hocevar at OpenLayers (whom I suggest you support if you can on Github Sponsor) I got an answer for anyone interested.
async mapToCanvasList() {
for(let i = 0 ; i < this.dateArraySurface10.length - 1 ; i )
{
this.setTimeSurface10();
await new Promise(resolve => this.map.once('rendercomplete', resolve));
this.myCallback();
}
this.gif.on('finished', function(blob) {
window.open(URL.createObjectURL(blob));
});
this.gif.render();
},
myCallback: function () {
const mapCanvas = document.createElement('canvas');
const divElement = document.querySelector(".map");
mapCanvas.width = divElement.offsetWidth;//size[0];
mapCanvas.height = divElement.offsetHeight;//size[1];
const mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
const transform = canvas.style.transform;
const matrix = transform
.match(/^matrix\(([^\(]*)\)$/)[1] //eslint-disable-line
.split(',')
.map(Number);
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix);
mapContext.drawImage(canvas, 0, 0);
}
}
);
this.gif.addFrame(mapCanvas, {copy:true, delay: 200});
}
As you can see rendering the entire method asynchronous and adding an await promise for the rendercomplete event ensures that the loop waits and executes myCallback which adds the rendered context as a frame to the GIF object.