dgvid
dgvid

Reputation: 26633

How to get scene change events in cesium?

I'd like to detect every time the camera position, heading, pitch or roll changes on Cesium view so that I can update a display showing these values. After quite a bit of searching, I eventually discovered that I can add an event handler to the not-at-all-intuitive preRender or postRender events on the widget's Scene object. However, those events both fire continuously, hundreds of times per second. I would guess they are firing once per clock tick. Is there another event I can register for that will simply fire after the view of the map has been changed? I'm looking for something close to Leaflet's moveend event and preRender and postRender aren't it.

Failing that, is there any way I can get preRender or postRender to fire only when something has actually changed?

Upvotes: 7

Views: 13433

Answers (5)

Andrew
Andrew

Reputation: 1

Actual answer:

viewer.scene.morphStart.addEventListener(function(ignore, previousMode, newMode) {

});

There's also morphComplete.

https://www.cesium.com/docs/cesiumjs-ref-doc/Scene.html?classFilter=scene#morphComplete

Upvotes: 1

TotalAMD
TotalAMD

Reputation: 1016

In addition to the answer https://stackoverflow.com/a/42044819/3092437 you probably want to set viewer.camera.percentageChanged to some small value close to 0 (but with 0 value camera tends to generate change events all the time). I use 0.001, it gives pretty good events frequency at all zoom levels.

Upvotes: 2

Christopher DeFreitas
Christopher DeFreitas

Reputation: 39

Using cesium v1.30, you can use this code:

viewer.camera.changed.addEventListener(function() {
  var deg = Math.round( Cesium.Math.toDegrees(viewer.camera.heading))
  console.log('Heading:', deg)

  var deg = Math.round( Cesium.Math.toDegrees(viewer.camera.pitch))
  console.log('Pitch:', deg)
})

Upvotes: 3

Zac
Zac

Reputation: 2211

Another solution that doesn't use a timer is to listen for the cameras move start and move end events.

viewer.camera.moveStart.addEventListener(function() { 
     // the camera started to move
});
viewer.camera.moveEnd.addEventListener(function() { 
     // the camera stopped moving
});

These sound more like the events you are looking for.

[Edit] : While the inertia spin and zoom are enabled by default and look kinda cool, they can be disabled very easily. This will make it so the moveEnd event is fired as expected. Here is how you can disable the inertia.

var viewer = new Cesium.Viewer();

viewer.scene.screenSpaceCameraController.inertiaSpin = 0;
viewer.scene.screenSpaceCameraController.inertiaTranslate = 0;
viewer.scene.screenSpaceCameraController.inertiaZoom = 0;

Upvotes: 18

Mike LP
Mike LP

Reputation: 709

I would recommend one of the following solutions:

  • set an interval that is polling the camera position at whatever frequency makes sense for your application.

    var intervalHandle = setInterval(function() {
      var camera = viewer.scene.camera,
        store = {  position: camera.position.clone(),
                   direction: camera.direction.clone(),
                   up: camera.up.clone(),
                   right: camera.right.clone(),
                   transform: camera.transform.clone(),
                   frustum: camera.frustum.clone()
                };
      //update UI elements
      ...
     }, 1000); // every 1 second whatever is best for your application
    ...
    
  • Attach an event trigger on the instigator of the change. If a mouse, then on mouseup. If keyboard, attach on keyup.

    The CameraEventAggregator object has a member called "anyButtonDown" which would be useful for mouse state changes.

  • You may also be able to use the Camera's FlightCompleteCallback as well. That should trigger whenever the camera is done moving if you used the helper methods to move it.

Upvotes: 0

Related Questions