How to clamp images to terrain

Good morning. I am asking for feedback on the best approach to drape (or clamp) an image over the terrain.

1. A concise explanation of the problem you're experiencing.
I would like to overlay map images (.jpg, .png, etc) on the terrain. Any thoughts or corrections to my findings are greatly appreciated.

2. A minimal code example. If you've found a bug, this helps us reproduce and repair it.
I have explored three options to drape images on the terrain, but run into problems with each:

A. As a rectangle/polygon material: clamping to terrain only currently works for solid colors, not images. In the example here toggle the terrain on/off to see the effect on the Cesium logo.

var viewer = new Cesium.Viewer('cesiumContainer');

var redRectangle = viewer.entities.add({
    name : 'Red translucent rectangle',
    rectangle : {
        coordinates : Cesium.Rectangle.fromDegrees(-110.0, 20.0, -80.0, 25.0),
        material : Cesium.Color.RED.withAlpha(0.5)
    }
});

var image = viewer.entities.add({
    name : 'Cesium logo',
    rectangle : {
        coordinates : Cesium.Rectangle.fromDegrees(-110.0, 15.0, -80.0, 20.0),
        material : '../images/Cesium_Logo_overlay.png'
    }
});

viewer.zoomTo(viewer.entities);

B. As a billboard: image cannot be locked flat nor draped over the terrain model

C. As an imageryLayer: it seems that one loses the flexibility with the baseLayerPicker once imageryLayers are created (otherwise this functions well):

var viewer = new Cesium.Viewer(‚ÄėcesiumContainer‚Äô, {
    imageryProvider : new Cesium.ArcGisMapServerImageryProvider({
¬†¬†¬†¬†¬†¬†¬†¬†url : ‚Äėhttps://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer‚Äô
    }),
    baseLayerPicker : false
});
var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({
¬†¬†¬†¬†url : ‚Äėhttps://assets.agi.com/stk-terrain/v1/tilesets/world/tiles‚Äô,
    requestWaterMask : true,
    requestVertexNormals : true
});
viewer.terrainProvider = cesiumTerrainProviderMeshes;

var layers = viewer.imageryLayers;
var earth = layers.addImageryProvider(Cesium.createTileMapServiceImageryProvider({
    url : 'https://dev.virtualearth.net',
}));

layers.addImageryProvider(new Cesium.SingleTileImageryProvider({
    url : '../images/Cesium_Logo_overlay.png',
    rectangle : Cesium.Rectangle.fromDegrees(-106.4, 39.6, -106.1, 39.7)
}));
viewer.camera.flyTo({
  destination : Cesium.Rectangle.fromDegrees(-106.5, 39.5, -106.0, 39.8)
});

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

I am building virtual documentaries of battles and want turn on/off historic maps to provide more context for events. For example maps showing trading routes, or on a larger scale maps an army's location, etc

Cheers, erik

Is the image too big for singletileimageryprovider? Whats the dimensions in pixels? I had some luck with this method for overlay, but its dependent on the GPU

Erik-
here’s a code sample

addCesium_SteepSlopesOverlay: function (CesiumMapViewer){

/* DEBUGGING IMAGE ISSUES: the dimensions of original JPG or PNG images exported from ArcGIS GRID are too large,

  • even when exporting only the eastern or western half of the GRID,

they crash my laptop’s GL trying to load in the Cesium map; I had to downsize the resolutions to 50%;

eastern half: slope_wmnf7_GRID_WebMercator.png 9533 x 9933 -> 50% 4766 x 4966

western half: slope_wmnf8_GRID_WebMercator.png 8072 x 8444 -> 50% 4036 x 4222

THIS JPG image loads OK in GoogleEarth and Cesium: https://lh4.googleusercontent.com/-O74t4PZXYmk/UlBfYStSTOI/AAAAAAAAK3o/2CcmalDh_Ug/s800/slope_wmnf-71.5_44.0_-71.0_44.375.jpg

and a higher-res copy loads OK: https://lh4.googleusercontent.com/-O74t4PZXYmk/UlBfYStSTOI/AAAAAAAAK3o/2CcmalDh_Ug/s2400/slope_wmnf-71.5_44.0_-71.0_44.375.jpg

BUT JPG has no transparency;

20150101, I re-exported PNG images from ArcGIS DeskTop Steep Slopes GRID format (raster) data, as symbolized with 6 class-breaks in ArcMap,

using the Layer’s context menu to Export>Data, with the PNG force-RGB option, and no-data as 256; I wrote a whole Word document of details on that export SOP;

20150112, but ESRI’s default PNG export had only RGB channels, no Alpha channel to suppport transparency;

I downloaded GIMP from www.gimp.org, TO EDIT the PNG, ADD ALPHA channel to RGB, select & move the black ‚Äėno-data‚Äô pixels to Alpha,

so no-data pixels will be transparent, when rendered as a map overlay…; then RESIZE the image to 50% of the original resolution, under GL maximum thresholds…

then CONVERT IMAGE COLOR MODE FROM RGB TO INDEXED; as an Indexed image, it only needs a colormap of 6 colors for the original 6 class-break values,

then EXPORT the changes from GIMP as a new PNG; the Indexed PNG downsized to 50% of it’s original RGB resolution was only 10% of it’s original file size;

*/

SteepSlopesAnalysisJpgImageLayerDataSource = new Cesium.SingleTileImageryProvider({

url : wp_plugin_url.concat(‚Äėlftgly/images/SingleTileImageryProviders/slope_wmnf7_GRID_WebMercator_transx050.png‚Äô),

rectangle : Cesium.Rectangle.fromDegrees(-71.5, 44.0, -71.0, 44.375)

});

Cesium.when(SteepSlopesAnalysisJpgImageLayerDataSource,function(){

SteepSlopesLayer = CesiumMapViewer.scene.imageryLayers.addImageryProvider(SteepSlopesAnalysisJpgImageLayerDataSource);

console.log("…added a new Steep Slopes layer…");

SteepSlopesLayer.show;

console.log("…turned on new Steep Slopes layer.");

});

}

//try this for debugging single image tile rendering issues:

function canvasGetContextWebGlContextGetParameterMaxTextureSize(CesiumMapViewer){

//troubleshooting error loading very large JPG:

// "WebGL: INVALID_VALUE: texImage2D: width or height out of range..."

// "[WebGLRenderingContext]RENDER WARNING: texture bound to texture unit 2 is not renderable.

// That message means the texture is larger than the GPU supports safely. You can check the maximum size allowed by calling gl.getParameter(gl.MAX_TEXTURE_SIZE)

var gl = null;

console.log("CesiumMapViewer.scene.canvas.getContext begin...");

try{

    gl = CesiumMapViewer.scene.canvas.getContext('experimental-webgl');

    if(gl == null){

        gl = CesiumMapViewer.scene.canvas.getContext('webgl');

    }

}

catch(error){

	console.log("a .getContext error occurred getting WebGL" + error.message);

}

finally{

	console.log("CesiumMapViewer.scene.canvas.getContext done...", gl);

};

if(gl != null){

    // WebGL is supported 

	console.log("WebGL is supported, but the maximum size the GPU supports safely is:  " + gl.getParameter(gl.MAX_TEXTURE_SIZE)); 

}

else{

    // WebGL is not supported

	console.log("WebGL is not supported");

};

}

Hi Erik,

We are currently working on clamping to terrain with with rectangle/polygon materials, so stay tuned to #5025.

In the meantime, I would say the best workaround would be an imagery layer, specifically the singleTileImageryProvider that Left Gully mentioned (and thanks for the code sample as well!).

Thanks,

Gabby

Hello! We’ve just added support for images on ground geometry and the feature will be included in the Cesium 1.46 release available on June 1st.

Thanks,

Hannah

Hi Hannah, Where is this new feature documented? Is there a sandcastle example?

Try this? I think you could learn more about GroundPrimitive and classificationType which may be helpful for you.

1 Like