Setting a BillboardGraphics image to an invalid URL throws an error and shows no image

Hi,

I'm working on an extremely minimal emulation of the GEPlugin API and ran into an issue when trying to load invalid image URLs. This shouldn't happen in normal use, but it was happening due to cross-origin security issues in our development environment. Google Earth Plugin will put up a "red X" in this situation, which is nice because there is something to click on if the name of the entity is not set. This is difficult to work around in the client code since the error is thrown on display by Cesium (rendering?) and not when the image URL is set.

I understand that this might be outside the bounds of what Cesium's goals are. If so, do you have any recommendations for working around this? If not, should I file an issue?

Thanks!
Tom

Hello Tom,

It doesn’t look like there is any good way to do this right now. This is something that a few people have asked for, so I created an issue here: https://github.com/AnalyticalGraphicsInc/cesium/issues/3117

If you wanted to work on improving this, we would love the contribution! The image load error is handled here: https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/Billboard.js#L952-L956

I would probably add a Promise to Billboard that resolves when the image is loaded or rejects if it doesn’t. That will have to be handled in BillboardVisualizer and connected to BillboardGraphics somehow.

Thanks for asking here in the forum =)

Best,

Hannah

I will give it a try sometime in the near future. Thanks for responding!

Hanna,

I modified the billboard loadImage to handle the rendering of a default canvas image when the original URL fails to load. I added a new promise (imageIndexPromise2 ) as you recommended. The getDefaultIcon() is a custom function in my project that returns the canvas, URL, and offset values. The canvas can be an X image like in GE. This code can be improved, but it does the job of loading the default icon when loading KML in Cesium. The first time when Cesium tries to load a non accessible URL image, the billboard is rendered on the map without the image. Soon after (due to a timeout) the first promise comes back with the failure and then the imageIndexPromise2 kicks in to add the default image.

Thanks,

Alberto

// Integrate the use of a default icon like in GE when the image fails to load

    Billboard.prototype._loadImage = function() {
            
            var atlas = this._billboardCollection._textureAtlas;
   
            var imageId = this._imageId;
            var image = this._image;
            var imageSubRegion = this._imageSubRegion;
            var imageIndexPromise;
   
            if (defined(image)) {
                imageIndexPromise = atlas.addImage(imageId, image);
            }
            if (defined(imageSubRegion)) {
                imageIndexPromise = atlas.addSubRegion(imageId, imageSubRegion);
            }
   
            this._imageIndexPromise = imageIndexPromise;
   
            if (!defined(imageIndexPromise)) {
                return;
            }
   
            var that = this;
            that._id = this._id;
            imageIndexPromise.then(function(index) {
                if (that._imageId !== imageId || that._image !== image || !BoundingRectangle.equals(that._imageSubRegion, imageSubRegion)) {
                    // another load occurred before this one finished, ignore the index
                    return;
                }
                else if (that._imageId === getProxyUrl() + "?" + getDefaultIcon().iconUrl)
                {
                     return;
                }
   
                // fill in imageWidth and imageHeight
                var textureCoordinates = atlas.textureCoordinates[index];
                that._imageWidth = atlas.texture.width * textureCoordinates.width;
                that._imageHeight = atlas.texture.height * textureCoordinates.height;
   
                that._imageIndex = index;
                that._ready = true;
                that._image = undefined;
                that._imageIndexPromise = undefined;
                makeDirty(that, IMAGE_INDEX_INDEX);
            }).otherwise(function(error) {
                /*global console*/
                var atlas = that._billboardCollection._textureAtlas;
                var imageId = that._imageId;
                var image = that._image;
                var imageSubRegion = that._imageSubRegion;
                var imageIndexPromise2;
                //start acevedo edit
                //add default icon when Cesium fails to load a billboard icon
                if (!(that._imageId === getProxyUrl() + "?" + getDefaultIcon().iconUrl) )
                {
                    that._imageId = empGlobe.getProxyUrl() + "?" + getDefaultIcon().iconUrl;
                    that._image = empDefaultIconCanvas;// this is the default canvas image. It can be an X image like in GE.
                    that._imageWidth = getDefaultIcon().offset.width;
                    that._imageHeight = getDefaultIcon().offset.height;
                    that.pixelOffset = new Cesium.Cartesian2(isNaN(getDefaultIcon().offset.x, getDefaultIcon().offset.y));
                    that._alignedAxis = Cesium.Cartesian3.ZERO;
                    that._verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
                    that._imageIndexPromise = undefined;
                }
                if (defined(image)) {
                imageIndexPromise2 = atlas.addImage(that._imageId, that.image);
            }
            if (defined(imageSubRegion)) {
                imageIndexPromise2 = atlas.addSubRegion(that._imageId, that._imageSubRegion);
            }
   
            that._imageIndexPromise = imageIndexPromise2;
   
            if (!defined(imageIndexPromise2)) {
                return;
            }
   
            var that2 = that;
            that2._id = that._id;
            imageIndexPromise2.then(function(index) {
                // fill in imageWidth and imageHeight
                var textureCoordinates = atlas.textureCoordinates[index];
                that2._imageWidth = getDefaultIcon().offset.width;
                that2._imageHeight = getDefaultIcon().offset.height;
                that2.pixelOffset = new Cesium.Cartesian2(isNaN(getDefaultIcon().offset.x, getDefaultIcon().offset.y));
                that2._alignedAxis = Cesium.Cartesian3.ZERO;
                that2._verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
                that2._imageIndex = index;
                that2._ready = true;
                that2._image = undefined;
                that2._imageIndexPromise = undefined;  
                makeDirty(that2, IMAGE_INDEX_INDEX);
            }).otherwise(function(error) {
                /*global console*/
               
                console.error('Error loading image for billboard: ' + error);
                that2._imageIndexPromise = undefined;
                that2.imageLoaded = false;
            });
               
            });
    };