BUG: CallbackProperty for ImageMaterialProperty image doesn't update image

Hi Cesium folks,

On my quest to find a good (non-flashing) way to update an image that is overlaid atop a Cesium map I discovered the following bug.

If you set the image of an ImageMaterialProperty to a CallbackProperty that draws into a canvas and returns that canvas as the image, the image updates the first time but then never again.

Here’s a sandcastle example:

(Code)

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var rotation = Cesium.Math.toRadians(30);

function getRotationValue() {

rotation += 0.005;

return rotation;

}

function drawCanvas(time, result) {

var canvas = document.getElementById(“canvas”);

var context = canvas.getContext(‘2d’);

context.clearRect(0, 0, canvas.width, canvas.height); context.font = ‘italic 40pt Calibri’;

context.fillStyle = “white”;

context.fillText(time, 20, 100);

return canvas;

}

viewer.entities.add({

name: ‘Rotating rectangle with rotating texture coordinate’,

rectangle: {

coordinates: Cesium.Rectangle.fromDegrees(-92.0, 30.0, -76.0, 40.0),

material: new Cesium.ImageMaterialProperty({

image: new Cesium.CallbackProperty(drawCanvas, false),

transparent: true

}),

rotation: new Cesium.CallbackProperty(getRotationValue, false),

stRotation: new Cesium.CallbackProperty(getRotationValue, false)

}

});

viewer.zoomTo(viewer.entities);

(HTML)

@import url(../templates/bucket.css); #canvas { position: absolute; left: 0; top: 50px; }

Loading...

When you run this you’ll see the canvas at the top, changing like mad, showing the current JulianTime for each callback, happily drawn into the canvas. Below you will see the rotating rectangle containing the canvas, suck back on the time the first callback was made.

It would be wonderful if this pathway worked. I have tried so many ways of putting time-variant images on top of Cesium and they all have drawbacks, mostly flashing. Hope you guys will consider fixing this.

Thanks a ton,

Bryan

Hello,

Thanks for including the code example! This is happening because our code sees that the same canvas is being used for the image so it assumes nothing has changed and doesn’t refresh the image material.

I’ve submitted a bug report to our GitHub: https://github.com/AnalyticalGraphicsInc/cesium/issues/5080

I’m not sure what this would do for performance, but if you use var canvas = document.createElement(canvas) instead the image material updates correctly.

(I also changed context.fillText(Cesium.JulianDate.toDate(time).getTime(), 20, 100); so that the changing part of the time appears in the rectangle)

Best,

Hannah

Hi Hannah,

Thank you for adding the bug, and explaining why your code wasn’t updating the image. Using that new information, I found a good workaround. The following example basically bounces back and forth between two canvases. This works great! Honestly this is the first way I’ve found to swap an image at runtime that doesn’t flash. (See my previous post about that issue: http://cesiumjs.org/forum.html#!msg/cesium-dev/7Bda_BkaJMU/bu19zOyXBAAJ).

HTML:

@import url(../templates/bucket.css); .canvas { position: absolute; left: 10px; top: 10px; } #canvas-a { top: 10px; } #canvas-b { top: 120px; }

Loading...

Code:

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var rotation = Cesium.Math.toRadians(30);

var curCanvas = ‘a’;

function getRotationValue() {

rotation += 0.005;

return rotation;

}

function drawCanvas(time, result) {

var canvas = document.getElementById(“canvas-” + curCanvas);

var context = canvas.getContext(‘2d’);

context.clearRect(0, 0, canvas.width, canvas.height);

context.font = ‘italic 40pt Calibri’;

context.fillStyle = “white”;

context.fillText(Cesium.JulianDate.toDate(time).getTime(), 20, 100);

curCanvas = curCanvas === ‘a’ ? ‘b’ : ‘a’;

return canvas;

}

viewer.entities.add({

name: ‘Rotating rectangle with rotating texture coordinate’,

rectangle: {

coordinates: Cesium.Rectangle.fromDegrees(-92.0, 30.0, -76.0, 40.0),

material: new Cesium.ImageMaterialProperty({

image: new Cesium.CallbackProperty(drawCanvas, false),

transparent: true

}),

rotation: new Cesium.CallbackProperty(getRotationValue, false),

stRotation: new Cesium.CallbackProperty(getRotationValue, false)

}

});

Thanks for the update! Glad you were able to find a workaround, that’s really clever.

-Hannah