# GoogleEarth API Migration, Altitude Clamped to ground

**URL:** <https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817>\
**Category:** CesiumJS\
**Created:** [December 31, 2014, 5:34am UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817 "2014-12-31T05:34:53Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Berwyn\_McKilligan](https://avatars.discourse-cdn.com/v4/letter/b/977dab/32.png) [@Berwyn\_McKilligan](https://community.cesium.com/u/Berwyn_McKilligan)\
**Post date:** [December 31, 2014, 5:34am UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/1 "2014-12-31T05:34:53Z")

</div>

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);

```

} );

---

<div class="post-metadata">

**Author:** ![patrick](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/patrick/32/2045_2.png) [@patrick](https://community.cesium.com/u/patrick)\
**Post date:** [December 31, 2014, 2:09pm UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/2 "2014-12-31T14:09:21Z")

</div>

Berwyn,

Thanks for sharing! This is similar to “Sample Everest Terrain” in the [Terrain Sandcastle example](http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Terrain.html&label=Showcases).

sampleTerrain() is the right thing to use while we work on built-in ground clamping for placemarks, polygons, and polylines. [#2179](https://github.com/AnalyticalGraphicsInc/cesium/issues/2179) is a good issue to watch for progress here.

Patrick

---

<div class="post-metadata">

**Author:** ![Berwyn\_McKilligan](https://avatars.discourse-cdn.com/v4/letter/b/977dab/32.png) [@Berwyn\_McKilligan](https://community.cesium.com/u/Berwyn_McKilligan)\
**Post date:** [January 11, 2015, 10:50pm UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/3 "2015-01-11T22:50:57Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Dennis\_Wegewijs](https://avatars.discourse-cdn.com/v4/letter/d/b5ac83/32.png) [@Dennis\_Wegewijs](https://community.cesium.com/u/Dennis_Wegewijs)\
**Post date:** [January 12, 2015, 1:56pm UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/4 "2015-01-12T13:56:18Z")

</div>

Hi Berwyn,

This is very nice!

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

Dennis

---

<div class="post-metadata">

**Author:** ![Berwyn\_McKilligan](https://avatars.discourse-cdn.com/v4/letter/b/977dab/32.png) [@Berwyn\_McKilligan](https://community.cesium.com/u/Berwyn_McKilligan)\
**Post date:** [January 29, 2015, 1:47am UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/5 "2015-01-29T01:47:10Z")

</div>

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](https://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);

```

} );

---

<div class="post-metadata">

**Author:** ![Sb\_B](https://avatars.discourse-cdn.com/v4/letter/s/6a8cbe/32.png) [@Sb\_B](https://community.cesium.com/u/Sb_B)\
**Post date:** [June 14, 2016, 12:43pm UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/6 "2016-06-14T12:43:33Z")

</div>

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

Thanks in advance.

---

<div class="post-metadata">

**Author:** ![hannah](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/hannah/32/4509_2.png) [@hannah](https://community.cesium.com/u/hannah)\
**Post date:** [June 14, 2016, 3:21pm UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/7 "2016-06-14T15:21:06Z")

</div>

Hello,

Please see my response to this post: [https://groups.google.com/d/msg/cesium-dev/S1AhCjKsbdE/b7CLz3\_PBQAJ](https://groups.google.com/d/msg/cesium-dev/S1AhCjKsbdE/b7CLz3_PBQAJ)

-Hannah

---

<div class="post-metadata">

**Author:** ![Sb\_B](https://avatars.discourse-cdn.com/v4/letter/s/6a8cbe/32.png) [@Sb\_B](https://community.cesium.com/u/Sb_B)\
**Post date:** [June 15, 2016, 1:03am UTC](https://community.cesium.com/t/googleearth-api-migration-altitude-clamped-to-ground/1817/8 "2016-06-15T01:03:47Z")

</div>

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