GoogleEarth API Migration, Altitude Clamped to ground

I’m working on transitioning a project to Cesium from the Google Earth API

I had to deal with and noticed a few posts and questions related to clamping features to terrain, like the KML altitudeMode clampToGround and I thought i would share my workaround.

I’m working with the data data directly in javscript rather than a kml.

I add the placemarks / billboards individually without elevation/altitude then follow up with a sampleTerrain request with a callback to update the position to include the elevation info for the billboard and label

the following should run in Sandcastle:

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

scene = viewer.scene,

billboards = scene.primitives.add(new Cesium.BillboardCollection()),

labels = scene.primitives.add(new Cesium.LabelCollection()),

ellipsoid = Cesium.Ellipsoid.WGS84,

pinBuilder = new Cesium.PinBuilder(),

data = [

{id: 'a001', label: 'point 1', longitude: -123 , latitude: 49.40 },

{id: 'a002', label: 'point 2', longitude: -123 , latitude: 49.45 },

{id: 'a003', label: 'point 3', longitude: -123 , latitude: 49.50 },

{id: 'a004', label: 'point 4', longitude: -123 , latitude: 49.55 },

{id: 'a005', label: 'point 5', longitude: -123 , latitude: 49.60 },

{id: 'a006', label: 'point 6', longitude: -123 , latitude: 49.65 }];

scene.terrainProvider =  new Cesium.CesiumTerrainProvider({

    url : '//cesiumjs.org/stk-terrain/tilesets/world/tiles'

});

var addPlacemarks = function(items){

var position,

    cartesian ; 



items.forEach(

    function(item){

        position = new Cesium.Cartographic( Cesium.Math.toRadians(item.longitude), Cesium.Math.toRadians(item.latitude));

        cartesian = Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude) ;

        

        Cesium.when(

                Cesium.sampleTerrain(scene.terrainProvider, 11, [position]),

                function(_item, _position) {

                    return function () {

                        _item._cesiumGroundElevation = Math.round(100 * _position.height)/100 ;

                        _item._cesiumBillboard.position = ellipsoid.cartographicToCartesian(_position);

                        _item._cesiumLabel.position = ellipsoid.cartographicToCartesian(_position); 

                    };

                }(item, position));

        

        

         item._cesiumLabel = 

             labels.add({ 

                 position:  cartesian  ,

                 text: item.label,

                 id:  'label_' + item.id,

                 outlineColor: Cesium.Color.BLACK,

                 outlineWidth: 2,

                 style: Cesium.LabelStyle.FILL_AND_OUTLINE

             });

        item._cesiumBillboard = 

            billboards.add({

                image:  pinBuilder.fromText(item.id, Cesium.Color.RED, 48),

                position: cartesian ,

                id: 'billboard_' + item.id, 

                verticalOrigin: Cesium.VerticalOrigin.BOTTOM

            });

        

    }

);

};

Sandcastle.addToolbarButton(‘Add Points’, function(){

var t = 0;



if(billboards.length > 0){ 

    t = 500;

}



data.forEach(function(item){

    if(item._cesiumBillboard){

       billboards.remove(item._cesiumBillboard);

       labels.remove(item._cesiumLabel);

    }

});



setTimeout( function(){ 

    addPlacemarks(data); 

},t);

} );

Berwyn,

Thanks for sharing! This is similar to “Sample Everest Terrain” in the Terrain Sandcastle example.

sampleTerrain() is the right thing to use while we work on built-in ground clamping for placemarks, polygons, and polylines. #2179 is a good issue to watch for progress here.

Patrick

Patrick

No worries,

I worked off of the sandcastle terrain example to come up with the solution.

This approach should work with polylines and polygons also and is a pretty easy way to create the clampToGround effect.

Hi Berwyn,

This is very nice!

Is it possible for you to provide a sample on how to do this for a polyline?

Dennis

sanbox code to clamp a polyline to ground

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

scene = viewer.scene,

globe = scene.globe,

billboards = scene.primitives.add(new Cesium.BillboardCollection()),

labels = scene.primitives.add(new Cesium.LabelCollection()),

ellipsoid = Cesium.Ellipsoid.WGS84,

pinBuilder = new Cesium.PinBuilder(),

data = [

{id: 'a001', label: 'point 1', longitude: -123.00 , latitude: 49.440 },

{id: 'a002', label: 'point 2', longitude: -123.01 , latitude: 49.440 },

{id: 'a003', label: 'point 3', longitude: -123.02 , latitude: 49.450 },

{id: 'a004', label: 'point 4', longitude: -123.04 , latitude: 49.455 },

{id: 'a005', label: 'point 5', longitude: -122.99 , latitude: 49.460 },

{id: 'a006', label: 'point 6', longitude: -123.00 , latitude: 49.465 }],

redLine;

scene.terrainProvider = new Cesium.CesiumTerrainProvider({ url : ‘//cesiumjs.org/stk-terrain/tilesets/world/tiles’ });

globe.depthTestAgainstTerrain = true;

scene.camera.flyTo({

destination:  new Cesium.Cartesian3.fromDegrees(-123.03, 49.44,4000)

});

var interpolateCartographicLine = function (start, end, maxDistanceBetweenPoints) {

var geodesic = new Cesium.EllipsoidGeodesic(start, end, ellipsoid),

    distance = geodesic.surfaceDistance,

    points = [start],

    stepCount,

    stepPercent,

    i ;

if (distance> maxDistanceBetweenPoints && maxDistanceBetweenPoints > 0) {

    stepCount = Math.pow(2, Math.ceil(Math.log2(distance / maxDistanceBetweenPoints)));

    stepPercent = 1.0 / stepCount;

    for (i = 1; i < stepCount; i++) {

        points.push(geodesic.interpolateUsingFraction(i * stepPercent));

    }

}

points.push(end);

return {points: points, distance: distance, stepCount: stepCount};

};

var clampToGroundBillboard = function (options) {

var billboard = options.billboard,

    callBack = options.callBack || (function () { }),

    cartographic = ellipsoid.cartesianToCartographic(billboard.position);

Cesium.when(Cesium.sampleTerrain(scene.terrainProvider, 14, [cartographic]),

            function (_billboard, _cartographic, _callBack) {

                return function () {

                    _cartographic.height = _cartographic.height + 1;

                    _billboard.position = ellipsoid.cartographicToCartesian(_cartographic);

                    if (_callBack) {

                        _callBack(_billboard, _cartographic);

                    }

                };

            }(billboard, cartographic, callBack)

        );

};

var clampToGroundPolyLine = function (options) {

var polyLine = options.polyLine,

   callBack = options.callBack || (function () {}),

   org = ellipsoid.cartesianArrayToCartographicArray(polyLine.geometry._positions),

   loopCount = org.length - 1,

   cartos = [],

   newPoints, 

   i ;

for (i = 0; i < loopCount  ; i++) {

    cartos.slice[0, -1]; 

    cartos = cartos.concat(interpolateCartographicLine(org[i], org[i + 1], 5).points); 

}

Cesium.when(Cesium.sampleTerrain(scene.terrainProvider, 14, cartos),

             function (_polyLine, _cartographics, _callBack) {

                 return function () {

                     _polyLine.geometry._positions = ellipsoid.cartographicArrayToCartesianArray(_cartographics);

                     if (_callBack) {

                         _callBack(_polyLine, _cartographics);

                     }

                 };

             }(polyLine, cartos, callBack)

         );

};

var addPlacemarks = function(items){

var position,

    cartesian,

    linePositions = []; 



items.forEach(

    function(item){

        position = new Cesium.Cartographic( Cesium.Math.toRadians(item.longitude), Cesium.Math.toRadians(item.latitude));

        cartesian = Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude) ;

        linePositions.push(cartesian);

        

        item._cesiumBillboard =  billboards.add({

                image:  pinBuilder.fromText(item.id, Cesium.Color.RED, 48),

                position: cartesian ,

                id: 'billboard_' + item.id, 

                verticalOrigin: Cesium.VerticalOrigin.BOTTOM

            });

        

        clampToGroundBillboard({

            billboard: item._cesiumBillboard,

            callBack: function(_item){

                return function(billboard, cartographic){

                    console.log(cartographic.height);

                    _item._cesiumLabel =  labels.add({ 

                             position:  billboard.position,

                             text: item.label,

                             id:  'label_' + item.id,

                             outlineColor: Cesium.Color.BLACK,

                             outlineWidth: 4,

                             style: Cesium.LabelStyle.FILL_AND_OUTLINE,

                             pixelOffset: new Cesium.Cartesian2(0.0, 5.0)

                    });

                }; }(item)

        });

    }

);

console.log(linePositions.length);



redLine = new Cesium.GeometryInstance({

            geometry: new Cesium.PolylineGeometry({

            positions:linePositions,

            width: 3.0,

            vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT

        }),

        attributes:{color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)}

});



clampToGroundPolyLine({ polyLine: redLine ,

                    callBack: function (line) {

                        scene.primitives.add(new Cesium.Primitive({

                                    geometryInstances:  line

                                    ,appearance: new Cesium.PolylineColorAppearance()

                                }));

                    }

                });

};

Sandcastle.addToolbarButton(‘Clamp To Ground’, function(){

scene.camera.position = new Cesium.Cartesian3.fromDegrees(-123.03, 49.44,4000);

scene.camera.tilt = ((90 - 30) * Math.PI / 180);

scene.camera.heading = -45 * Math.PI / 180;



var t = 0;



if(billboards.length > 0){ 

    t = 500;

}



data.forEach(function(item){

    if(item._cesiumBillboard){

       scene.primitives.remove(redLine);

       billboards.remove(item._cesiumBillboard);

       labels.remove(item._cesiumLabel);

    }

});



setTimeout( function(){ 

    addPlacemarks(data); 

},t);

} );

Hi All,
Thanks for your always pro-active help.
Has the code for clamping polyline to ground reached official cesium release?

Thanks in advance.

Hello,

Please see my response to this post: https://groups.google.com/d/msg/cesium-dev/S1AhCjKsbdE/b7CLz3_PBQAJ

-Hannah

Hello Hannah,
Thanks for the reply.
Regards,
S.