When using terrain, rotate globe also moves pins, layers etc.

Hi everybody,

Playing around with Cesium the first time because the Google Earth Plugin will not be available anymore next month as you all probably know.

The first thing I would like to do is show some pins(markers) on a cesium map with terrain.

When the cesium map is first loaded then the pins are shown on the correct position but as soon as I start turning the globe with ctrl left mouse button the pins also move.

When zoomed in to the pin and you move the globe to left, right up or down the pin also moves to a different position.

With this the layers (pins) loose there position and you cannot display layers correctly when using terrain.

Here is an example of what I mean. Zoom in to blue pin and after that use you ctrl left mouse button to show the terrain. When you turn the globe you will see that the content also moves which in my opinion should not move.

http://17.mcyclingthealps.appspot.com/cesium2/Apps/Sandcastle/gallery/routes.html

Is there a way to fix the position of pins?

I have looked at powdertracks and there the pins are fixed. You can turn the globe around and all the pins stay on there postition.

Thanks for any feedback.

Dennis

Hi Dennis,

I really enjoy the Cycling the Alps website; I’m glad you are moving it to Cesium.

In the example you posted, the pin and polyline are draw with height = 0 relative to the WGS84 ellipsoid (not terrain). Instead, set each pin and each point in the polyline to the height of the terrain using sampleTerrain. For example, check out “Sample Everest Terrain” in this example:

https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Terrain.html&label=Showcases

Please keep us posted on your progress. I’m curious to hear how the port goes.

Patrick

Hi Patrick,

Thanks a lot.

The deeper I am getting into Cesium the more impressed I am.

A really good job!!

Thanks for the answer, already looking into it. That would be the first feature for Cycling the Alps. Show starting point and end point of each climb in the Alps

The red polyline is a kml as I have all the kml’s available from Cycling the Alps. This will probably not work with the terrain.

My idea right now is to do something similar as on the powdertacks app.

Building up the polyline along the route of all the climbs in the Alps, offering different speeds. How cool is that! Excited about it and this must be possible to do.

As soon as I have some Cesium implementation live on Cycling the Alps I will let you know.

Dennis

Dennis,

Thanks for the kind words. We are working on KML, check out the branch and roadmap (#873 and #2179). However, we do not have KML clamp to ground for terrain yet. I expect we’ll see it soon, but in the meantime it should be straightforward to use the Cesium API for polylines and sampleTerrain().

Patrick

Hi Dennis,

I when I wrote Powder Tracks I had the same problem with polylines on terrain that you have. My solution was to write a small parsing script that converted kml polyline into a czml polyline (I needed to use czml instead of kml to get the time dynamic features I wanted). During the conversion, I utilized an online terrain elevation service to get the terrain height for each point in my polyline. I believe the service I used was the National Elevation Dataset point query web service (http://ned.usgs.gov/epqs/). It’s a bit painful, but something like this could be a good solution until they finish up the kml features.

-Greg

He Greg,

Thanks for the answer.

Currently I would like to do exactly what you have been building.

Time dynamic features to slowly build up the polyline so the route of the climb is slowely created.

Maybe offer different speeds to the user of building up the polyline.

As I have all the data available from my kml files I will follow your steps and will give it a try to implement it.

Dennis

Hey Dennis,

This might be of some use. I recently started updating Powder Tracks with some new types of data and had to update my CZML generator. I attached it if you want to use it. It’s doing a bunch of other things, but at the core, it reads data from an Excel file and writes it out to CZML. I included the latest CZML writer dll already built in the zip so you don’t have to go build it yourself.

parseFiles.zip (259 KB)

Hi Greg,

Thanks a lot! I will have a look into this.
Could you also provide a czml file that builds up the polyline like it builds up the skiroute on Powder Tracks.
I tried hard yesterday evening but could not make it to work.

Dennis

You’ll want to look at the CZML Path object instead of the Polyline object. Path is similar to Polyline except it is time dynamic (each point in the Path has an associated time). The “leadTime” and “trailTime” properties of a Path determine how much of the Path is displayed into the future and past around the current Cesium time. Search for “path” in the attached file to see the czml path used in Powder Tracks. The ParseFiles program from yesterday has an example of generating a CZML Path object from raw data using the CZML writer.

DeerValley.czml (458 KB)

Thanks a lot Greg,

I think I have all the ingredients now.

Dennis

Hi Greg,

Have a demo working.

Personally I am amazed by this. :slight_smile:

http://19.mcyclingthealps.appspot.com/cesium15/cesiumCZML.html?xStart=45.133050&yStart=7.063810&xEnd=45.0722996395&yEnd=7.05330433214&cat=HC&from=Susa&pass=Colle%20delle%20Finestre

Still have a lot of things to do but this is what I would like to show on Cycling the Alps.

Two questions you might be able to answer :slight_smile:

1.How did you make your flyto so smooth? I can’t get it to work that is why it is currently absolutly standard but looking in the wrong direction and I would also have a bit of tilt in when I fly to the beginning point.

scene.camera.flyTo({

  • destination : Cesium.Cartesian3.fromDegrees(7.063810, 45.133050, 5000.0),*

  • duration : 10*

});

  1. How did you make the camera follow the route of the path in the json file?

Thanks for any more help, very much appriciated

Dennis

Hi Dennis,
That looks great. Looks like it was a lot of fun…and work!

EntityView is the object that is used to attach the camera to an entity (CZML path object in your case). You can use the following code snippet (not totally verified) to fly from space to some offset around your object and attach to that object:

function flyToObject(scene, entity) {

disableInput(scene);

var time = cesiumWidget.clock.currentTime;

entityView = new EntityView(entity, scene);

entityView.update(time);

var objectPosition = entity.position.getValue(time);

var cameraOffset = Cartesian3.fromDegrees*(7.063810, 45.133050, 5000.0)*;

var direction = new Cartesian3();

Cartesian3.negate(Cartesian3.normalize(cameraOffset, direction), direction);

var up = new Cartesian3();

Cartesian3.cross(direction, objectPosition, up);

Cartesian3.cross(up, direction, up);

Cartesian3.normalize(up, up);

var destination = new Cartesian3();

Cartesian3.add(objectPosition, cameraOffset, destination);

scene.camera.flyTo({

destination : destination,

direction : direction,

up : up,

duration : 12.0,

complete : function() {

enableInput(scene);

}

});

}

var czmlDataSource = new Cesium.CzmlDataSource();

czmlDataSource.loadUrl(‘http://19.mcyclingthealps.appspot.com/czml-reverse/colle-delle-finestre.czml’).then(function() {

viewer.dataSources.add(czmlDataSource);

var entityCollection = czmlDataSource.entities;

// set the camera to follow the path

var lookAtObject = entityCollection.getById(“path”);

flyToObject(cesiumWidget.scene, lookAtObject);

});

viewer.dataSources.add(czmlDataSource);

Sorry looks like i got an extra “viewer.dataSources.add(czmlDataSource)” in there at the end.

I believe you’ll also have to update the entityView object when the Cesium clock changes because your entity(path) is moving. Something like the code below should do the trick:

viewer.clock.onTick.addEventListener(function() {

// update the camera position

if (typeof entityView !== ‘undefined’) {

entityView.update(viewer.clock.currentTime);

}

});

Hi Greg,

Thanks a lot.

Trying to make it work.

There are two methods calls in there.

disableInput(scene); and enableInput(scene);

Can’t figure out what they are supposed to do.

For the viewer.clock.onTick.addEventListener(function()

The entityView.update is called on the entityView = new EntityView(entity, scene); object defined in the flytoObject method correct?

Thanks again

Yes the entityView objects are the same.
Sorry about the enable/disable inputs functions. They were just simple helper functions to disable user mouse/touch input during camera transitions.

function disableInput(scene) {

var controller = scene.screenSpaceCameraController;

controller.enableInputs = false;

}

function enableInput(scene) {

var controller = scene.screenSpaceCameraController;

controller.enableInputs = true;

}

Hi Greg,

Thanks a lot again.

I think I am almost there but I need a bit more input.

I can’t figure out where to add the following code.

viewer.clock.onTick.addEventListener(function() {

// update the camera position

if (typeof entityView !== ‘undefined’) {

entityView.update(viewer.clock.currentTime);

}

});

Because of the listener this is already executed before the entityView object in the flytoObject method is created.

Dennis

You should be able to add the event listener just about anywhere. It probably will execute before the entityView object is created, but that’s why the event function checks if the entity view object exists before trying to update it.

I believe Cesium now has a utility function that makes checking if an object is defined simple. Cesium.defined

Hi Greg,

Off course, now I get the function
Still not working though. :frowning:

Maybe you can spend some additional 10 minutes to see what I am doing wrong.

I have added the DearValley.czml for testing because there I know what to expect as I was thinking that my ColleDelleFinestre.czml file was not correct.

http://19.mcyclingthealps.appspot.com/cesium15/cesiumCZML.html?xStart=45.133050&yStart=7.063810&xEnd=45.0722996395&yEnd=7.05330433214&cat=HC&from=Susa&pass=Colle%20delle%20Finestre

This is the current code that should do the job.

Thanks a lot.

var czmlDataSource = new Cesium.CzmlDataSource();

czmlDataSource.loadUrl(‘http://19.mcyclingthealps.appspot.com/czml-reverse/DeerValley.czml’).then(function() {

viewer.dataSources.add(czmlDataSource);

var entityCollection = czmlDataSource.entities;

// set the camera to follow the path

var lookAtObject = entityCollection.getById(“path”);

flyToObject(scene, lookAtObject);

});

viewer.clock.onTick.addEventListener(function() {

// update the camera position

if (typeof entityView !== ‘undefined’) {

entityView.update(viewer.clock.currentTime);

}

});

function flyToObject(scene1, entity) {

disableInput(scene1);

var time = viewer.clock.currentTime;

var entityView = new Cesium.EntityView(entity, scene);

entityView.update(time);

var objectPosition = entity.position.getValue(time);

var cameraOffset = Cesium.Cartesian3.fromDegrees(40.637181, -111.478679, 100.0);

var direction = new Cesium.Cartesian3();

Cesium.Cartesian3.negate(Cesium.Cartesian3.normalize(cameraOffset, direction), direction);

var up = new Cesium.Cartesian3();

Cesium.Cartesian3.cross(direction, objectPosition, up);

Cesium.Cartesian3.cross(up, direction, up);

Cesium.Cartesian3.normalize(up, up);

var destination = new Cesium.Cartesian3();

Cesium.Cartesian3.add(objectPosition, cameraOffset, destination);

scene1.camera.flyTo({

destination : destination,

direction : direction,

up : up,

duration : 12.0,

complete : function() {

enableInput(scene1);

}

});

}

function disableInput(scene) {

var controller = scene.screenSpaceCameraController;

controller.enableInputs = false;

}

function enableInput(scene) {

var controller = scene.screenSpaceCameraController;

controller.enableInputs = true;

}