No KML support for polygons draped on terrain? Failing sandcastle demo included.

In the SandCastle example below, you can switch between a czml and kml which should render the exact same thing: a polygon draped on the terrain. However, while the czml gets properly draped on the terrain, the kml is rendered as it’s own object floating below the terrain (you can see this if you switch to wireframe mode using the Inspector).

Is this something that can be fixed by modifying the kml file or the code below, or something that is coming from the Cesium folks at some point, or something that if want fixed I should delve into the Cesium code and try to fix myself? Any help/suggestions would be appreciated.

There are so many kml’s that use polygons in this way, and while this works on a flat earth, the wonder of CesiumJS is the terrain, so it would be so useful for CesiumJS to support this.

Here’s a saved link to the SandCastle demo:

https://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=bfc1bbed83fd03b01524aeded5c882f9

Here’s the code in the demo:

// CZML polygon gets overlaid on terrain perfectly

// KML (which is an exact copy of the czml) does not get overlaid on terrain

// (if you switch to wireframe rendering using Inspector

// you can see that it is floating below terrain).

var kml = ‘https://gist.githubusercontent.com/rumicuna/25fcf46b77cc01f64690814a6d5844e5/raw/4a9fa47df362c75cc1c7acd763f254c98e77c438/test.kml’;

var czml = [{

"id" : "document",

"name" : "CZML Geometries: Polygon",

"version" : "1.0"

}, {

"id" : "redPolygon",

"name" : "Red polygon on surface",

"polygon" : {

    "positions" : {

        "cartographicDegrees" : [

            -118.6314919067867,39.39082247764545,0,

            -118.6253096252535,39.39267143352271,0,

            -118.6291720945795,39.39846179949413,0,

            -118.6357552771416,39.39591095741704,0,

            -118.6314919067867,39.39082247764545,0

        ]

    },

    "material" : {

        "solidColor" : {

            "color" : {

                "rgba" : [255, 0, 0, 100]

            }

        }

    }

}

}];

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

var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({

url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles',

requestWaterMask : true,

requestVertexNormals : true

});

viewer.terrainProvider = cesiumTerrainProviderMeshes;

//Add Cesium Inspector

viewer.extend(Cesium.viewerCesiumInspectorMixin);

Sandcastle.addToolbarMenu([{

text : 'CZML Polygon',

onselect : function() {

    var dataSourcePromise = Cesium.CzmlDataSource.load(czml);

    viewer.dataSources.add(dataSourcePromise);

    viewer.zoomTo(dataSourcePromise);

}

}, {

text : 'KMZ polygon',

onselect : function() {

    var options = {

        camera : viewer.scene.camera,

        canvas : viewer.scene.canvas

    };

    viewer.dataSources.add(Cesium.KmlDataSource.load(kml, options));

}

}], ‘toolbar’);

Sandcastle.reset = function() {

viewer.dataSources.removeAll();

};

Hi there,

Ground clamping for KML polygons is definitely supported. It looks like the height was being set to zero, which disables ground clamping. You can correct this by setting the height to undefined.

Here’s the code to do that: http://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=f8a8da4ebeb2f101e05c62ce316c40df

By the way, in the future you can more easily share code snippets by clicking the share button to save Sandcastle code as a gist and generate a link to it. See here for more instructions.

Hope that helps!

  • Rachel

Thanks Rachel! You are the best! The code works like a charm.
The new demo was crashing because the kml was being added twice. Here’s an updated working copy for folks that might be interested in this thread:

I’m trying to improve it even further. Since a kml might have some polygons draped and other that are not I’m trying to fix your code so that it does not change all polygons so they all drape, just the ones that it should because they are marked in the kml as “clampToGround”. In the kml standard, when this flag is set, it just ignores the height value (google earth creates draped kml polygons with this flag and with height=0).

I’m trying two things (the folllowing gist is a broken version of your code which tries both things below):

https://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=1b3e41370595b9ad5b6d551caee1693b

  1. Fix the kml

I’ve tried modifying the kml by removing the altitude value, setting it to null and undefined. Is there a way to change the kml so that height comes back as undefined and we don’t have to do it in code as a post-processing step.

  1. Set height to undefined, but only for polygons that have the following set:

clampToGround

I modified the kml to add this flag, but when I try to read it so that the code only changes the heights to undefined if clampToGround is set, it says that entity.polygon.heightReference is undefined.

if (entity.polygon.heightReference === Cesium.HeightReference.CLAMP_TO_GROUND)

entity.polygon.height = undefined;

Is there a way to read this flag back out? It would fix this issue as I can just make the code part of all my cesium code to make all kml polygons work.

Thanks again!

-Martin

Hi Martin,

Unfortunately, out KML support is incomplete, but we can definitely find a work around! Also, my mistake! Polygon doesn’t have a heightReference property. Instead, it is ground clamped by default when height is set to undefined.

In general when you load a kml file, the entities from the KmlDataSource store some of the original KML values in a KmlDataFeature, accessible at entity.kml : http://cesiumjs.org/Cesium/Build/Documentation/KmlFeatureData.html

However, it looks like the altitudeMode your file is using isn’t recorded in the KmlFeatureData. Here are a few options:

  1. Separate your geometry into two files, one that requires height and one to be ground clamped. Load with different options or post-processing

  2. Modify your kml to use a known value like -1 to indicate when an entity should be ground clamped. Then do the post processing as before.

Btw, for the future when you call KmlDataSource.load, you can pass in “clampToGround : true” in the options object to set all heights to undefined and clamp all the associated entities.

Hope that helps!

  • Rachel

Thanks Rachel!,
I opted for option 2, which actually is perfect as usually when a kml is supposed to be ground clamped, the height values are all set to zero. It’s not perfect, but will make most kml files work without any changes.

I’ve updated the sandcastle demo to do this (if all height values are zero then set entity.polygon.height = undefined):

https://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=bdd6d61516ddfb588f3e92e0f69ad38e

I’m also posting the new code below so it’s indexed by google and easier to find when folks search for help with this:

// Demo shows how to force KML polygons which should be draped on the terrain do this.

// Cesium support for KML is incomplete so this is a work around so Cesium works

// a little better when it comes to KML draped polygons.

var kml = “https://gist.githubusercontent.com/rumicuna/25fcf46b77cc01f64690814a6d5844e5/raw/743ecd85b5a287d0348210c7fc0914b908976a33/test.kml”;

console.log(“You can see the kml file here:”);

console.log(kml);

var czml = [{

"id" : "document",

"name" : "CZML Geometries: Polygon",

"version" : "1.0"

}, {

"id" : "redPolygon",

"name" : "Red polygon on surface",

"polygon" : {

    "positions" : {

        "cartographicDegrees" : [

            -118.6314919067867,39.39082247764545,0,

            -118.6253096252535,39.39267143352271,0,

            -118.6291720945795,39.39846179949413,0,

            -118.6357552771416,39.39591095741704,0,

            -118.6314919067867,39.39082247764545,0

        ]

    },

    "material" : {

        "solidColor" : {

            "color" : {

                "rgba" : [255, 0, 0, 100]

            }

        }

    }

}

}];

function showObject(obj) {

var result = “”;

for (var p in obj) {

if( obj.hasOwnProperty(p) ) {

  result += p + " , " + obj[p] + "\n";

} 

}

return result;

}

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

var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({

url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles',

requestWaterMask : true,

requestVertexNormals : true

});

viewer.terrainProvider = cesiumTerrainProviderMeshes;

// Add Cesium Inspector

viewer.extend(Cesium.viewerCesiumInspectorMixin);

Sandcastle.addToolbarMenu([{

text : 'Draped CZML Polygon',

onselect : function() {

    var dataSourcePromise = Cesium.CzmlDataSource.load(czml);

    viewer.dataSources.add(dataSourcePromise);

    viewer.zoomTo(dataSourcePromise);

}

}, {

text : 'Draped KML polygon',

onselect : function() {

    var options = {

        camera : viewer.scene.camera,

        canvas : viewer.scene.canvas

    };

    var promise = viewer.dataSources.add(Cesium.KmlDataSource.load(kml, options));

    promise.then(function(dataSource) {

        // Get the array of entities

        var entities = dataSource.entities.values;

        // Find all polygons

        for (var i = 0; i < entities.length; i++) {

            var entity = entities[i];

            if (Cesium.defined(entity.polygon)) {

                //console.log(entity.polygon.hierarchy);

                // Loop through all polygon point positions, if the heights are 

                // all zero then we assume it should be ground clamped and we set

                // entity.polygon.height to undefined which forces Cesium to 

                // clamp it to the ground

                var clampToGround = true;

                var positions = entity.polygon.hierarchy.getValue().positions;

                for (var n = 0; n < positions.length; n++) {

                    // Convert cartesian back to cartographic to get the values that

                    // where in the kml file

                    var cartographicPosition  = viewer.scene.globe.ellipsoid.cartesianToCartographic(positions[n]);

                    if (cartographicPosition.height !== 0) {

                        clampToGround = false;

                        break;

                    }

                }

                if (clampToGround) {

                    entity.polygon.height = undefined;

                }

            }

        }

    }).otherwise(function(error){

    //Display any errrors encountered while loading.

    window.alert(error);

    });

}

}], ‘toolbar’);

Sandcastle.reset = function() {

viewer.dataSources.removeAll();

};

``

Hi Martin,

Glad you were able to get something working and thanks for sharing! I’ve opened a new issue for ground clamped kml support based on this issue: https://github.com/AnalyticalGraphicsInc/cesium/issues/5600

Hopefully, in the future we’ll have more direct support.

Best,

  • Rachel