Synchronizing aircraft movement and object on ground

Hello list,

We are getting to grips with Cesium and would like to synchronize a few moving objects.

An example would be an aircraft that has its position updated say every second and we would like to show a circle on the surface at the same position as the aircraft.

Currently we update the aircraft and circle one after the other and unsurprisingly they do not move at the same time.

Can we group them in some way so they move at the same time?

Thanks

Ian

The Monster Milktruck has a shadow under the truck, you can check out how they work it.

This is easy to do and there’s no reason that they shouldn’t move at the same time. Can you tell us more about your app? Are you using Entities or Primitives? Are the aircraft positions known ahead of time, or are you receiving data on the fly? How exactly are you updating them?

Details below.

Positions are updated here :-

aircraftLocationCircle.position = currentPosition;

var this_heading = Cesium.Math.toRadians(flightDataJson.fmc.head -90);

aircraftModel.orientation = Cesium.Transforms.headingPitchRollQuaternion(currentPosition, this_heading, 0, 0);

Declared as :-

aircraftModel = viewer.entities.add({

name : url,

position : position,

orientation : orientation,

model : {

uri : url,

minimumPixelSize : 256

}

});

aircraftLocationCircle = viewer.entities.add({

name : ‘Aircraft location circle’,

ellipse : {

semiMinorAxis : 3500.0,

semiMajorAxis : 3500.0,

material : Cesium.Color.WHITE.withAlpha(0.5),

outline : true,

outlineColor : Cesium.Color.WHITE

}

});

On Behalf Of Matthew Amato

After the entities are created, how do you obtain position and orientation? To get position I’ve had to use a private property ._position._value, although setting it is straightforward. According to your code it seems that setting orientation is straightforward as well, however how do you obtain orientation? Or perhaps you don’t, you only set it?

We only set it as we get the next aircraft position. So the aircraft and circle positions are set one after the other which I assume is why we see the aircraft positon move
first and then the circle a moment later.

What we are trying to do is get them to move at the same time.

Thanks, Ian

On Behalf Of Hyper Sonic

The problem is you are setting the position as if they are a static object. You need to use a dynamic property so that Cesium knows things will be changing. Assuming you don’t care about animating backwards into the past or interpolating between positions, try this

//Assume currentPosition is the current position;

var positionProperty = new CallbackProperty(function(time, result){

return currentPosition;

}, false);

Now assign position for both of your entities to be positionProperty. When currentPosition changes, it will be automatically reflected in both places and the ellipse will be in dynamic mode so that the geometry is kept separated and is created synchronously.

I recently provided some additional info on this subject here: https://groups.google.com/d/msg/cesium-dev/BqhefLtPsLg/0tq7MD8HH98J

I really need to write a new tutorial to cover this stuff.

So each frame the entities automatically seek their new positions by calling the callback function, so you only have to worry about calculating the currentPosition value. I assume you could do the same for orientation.

Matthew,

That works great thanks.

Now how do we animate/interpolate between the position updates so the aircraft keeps moving?

Regards

Ian

On Behalf Of Matthew Amato

There’s a SandCastle interpolation example here

This looks like it does something similar but is flying along a pre-computed path.

What we have is a position update every second or couple of seconds.

Can we use these new functions to fly an entity with live data?

Thanks, Ian

On Behalf Of Hyper Sonic

Do you wait until you have 2 points to move between before moving between them? I suppose you could use your previous positions to make a decent guess of how to curve to the next point. Maybe you can tap into the interpolation library functions to use them in a real time scenario, and not just in a pre-computed flight path scenario.

Ian, while the example that Hyper linked to uses pre-known data, SampledPositionProperty is still what you want. You can add new time-tagged positions to it as you receive them, so my original example above largely stays the same but instead of a callback property you do

var positionProperty = new SampledPositionProperty();

//Whenever you receive a new position, add it to the property.

positionProperty.addSample(time, position);

By default, this will provide no position outside of the bonds for which you provided data for, and it will linearly interpolate between the points. You can use the forwardExtrapolationType and backwarExtrapolationType and related settings to decide on the behavior when outside of the bounds. For example, if you are receiving data in real-time, you could have Cesium extrapolate forward for up to X seconds while waiting for the next position or you can set it to simply hold the position for some threshold until the track is considered lost. You’ll also be able to animated backward/forward and show the path now that you’re using a sampled property. You can also use something like LaGrange or Hermite to have smoother interpolation than simple linear. I would check out the doc to see all of the options: http://cesiumjs.org/Cesium/Build/Documentation/SampledPositionProperty.html

Hope that helps, If you run into any questions, just let us know.

Matthew,

Thanks for this, what do we pass as ‘time’?

positionProperty.addSample(time, position);

Thanks

Ian

On Behalf Of Matthew Amato

To use the current time:

     var time = Cesium.JulianDate.now:

For other conversions such as a string of JavaScript datetime, see the JulianDate documentation
https://cesiumjs.org/Cesium/Build/Documentation/JulianDate.html

Thanks, that did the trick.

     var time = Cesium.JulianDate.now();

Ian

I was looking at SampledProperty.prototype.getValue https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/DataSources/SampledProperty.js#L373

It checks for extrapolation early on, so it seems that the extrapolations can be curved (if using LaGrange or Hermite) as from then on in that function it no longer checks for extrapolation.

So as you’re collecting real time data you can view the past or present, and travel in time at any speed or direction. Looks like a great way to simultaneously view real time data and record the flight path.

Matthew,

We used this approach and now we have developed our project further it is not working quite as we need.

The goal is to move a model so it tracks live position date which is updated around once per second and to stop the model jumping between points.

We have 3 issues :-

The model disappears once it has reached the new point, is this because the data has then ended? How do we stop this?

The speed the model moves can be slower than the position updates so it can get quite far behind. How do we change the speed?

If the position has a step change the model moves too slowly to the new position (really the same issue as 2). How do we reset the position when we know there is
a step change?

Thanks

Ian

On Behalf Of Matthew Amato

  1.   The model disappears once it has reached the new point, is this because the data has then ended? How do we stop this?
    

You can set the forwardExtrapolationType of SampledPositionProperty to Cesium.ExtrapolationType.HOLD; and then set the duration to the amount of time to hold. This will prevent it from disappearing if you’re behind on your data. Check the SampledPositionProperty doc for full details

  1.   The speed the model moves can be slower than the position updates so it can get quite far behind. How do we change the speed?
    

I’m not sure what you mean here. The speed is based on the data. Are your position updates not properly time-tagged with the correct information?

  1.   If the position has a step change the model moves too slowly to the new position (really the same issue as 2). How do we reset the position when we know there is a step change?
    

Again, I’m not really sure what you mean here. You provide real-world time tagged position and Cesium is just reflecting what you tell it. Can you provide a more completely example of what’s going on? I’m sure I’m misunderstanding something on my end.

I want to show cone with moving 3d model(Aircraft). I am able move cone but not able to set cone should start underneath aircraft. Following is my whole example.Please advise

var viewer = new Cesium.Viewer('cesiumContainer');
    var allCaseEntitiesDateDetail = ;
    var datetime = new Date()

    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:40 GMT+0530 (India Standard Time)'), entityDataID: "218482", lat: "20.6302076230157", long: "-103.405801628597" });
    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:35 GMT+0530 (India Standard Time)'), entityDataID: "218446", lat: "20.6389415253327", long: "-103.391647700067" });
    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:50 GMT+0530 (India Standard Time)'), entityDataID: "218483", lat: "20.6443830470659", long: "-103.384094441214" });
    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:45 GMT+0530 (India Standard Time)'), entityDataID: "218447", lat: "20.6499932551147", long: "-103.359079582771" });

    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:52 GMT+0530 (India Standard Time)'), entityDataID: "218448", lat: "20.6468583251195", long: "-103.349857794942" });

    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:53 GMT+0530 (India Standard Time)'), entityDataID: "218449", lat: "20.642558225936", long: "-103.341783577804" });

    allCaseEntitiesDateDetail.push({ date: new Date('Thu Mar 02 2017 10:26:40 GMT+0530 (India Standard Time)'), entityDataID: "218450", lat: "20.6386638174114", long: "-103.335686099735" });
  
    setTimeout(function () {
        for (var i = 0; i < allCaseEntitiesDateDetail.length; i++) {

            var entityName = "Test " + (i + 1);
            var entityColor = '#000000';
            var entity = viewer.entities.add({
                name: "Test " + (i + 1),
                position: Cesium.Cartesian3.fromDegrees(parseFloat(allCaseEntitiesDateDetail[i].long), parseFloat(allCaseEntitiesDateDetail[i].lat), (parseInt(2000) * 0.3048)),
                id: allCaseEntitiesDateDetail[i].entityDataID,
                description: "Test",
                billboard: {
                    image: "/images/LargeEntities/DefaultPerson.png",
                    width: 40,
                    height: 40,
                    eyeOffset: new Cesium.Cartesian3(0, 0, -50)
                },
                label: {
                    text: entityName,
                    font: 'bold 13pt sans-serif',
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    pixelOffset: new Cesium.Cartesian2(0, 40),
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 3,
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    fillColor: Cesium.Color.fromCssColorString(entityColor),
                    eyeOffset: new Cesium.Cartesian3(0, 0, -50),

                }
            });
        }
        viewer.zoomTo(viewer.entities._entities);
    }, 3000);
    setTimeout(function () {
        var property = new Cesium.SampledPositionProperty();
        var start = Cesium.JulianDate.fromDate(new Date(allCaseEntitiesDateDetail[0].date));
        var planeStop = stop = Cesium.JulianDate.addSeconds(start, (allCaseEntitiesDateDetail.length - 1) * 5, new Cesium.JulianDate());

        viewer.clock.startTime = start.clone();
        viewer.clock.stopTime = stop.clone();
        viewer.clock.currentTime = start.clone();
        viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end
        viewer.clock.multiplier = 1;
        viewer.clock.shouldAnimate = true;
        for (var i = 0; i < allCaseEntitiesDateDetail.length; i++) {
            var time = Cesium.JulianDate.addSeconds(start, 5 * i, new Cesium.JulianDate());
            var position = Cesium.Cartesian3.fromDegrees(allCaseEntitiesDateDetail[i].long, allCaseEntitiesDateDetail[i].lat, 2000);
            property.addSample(time, position);
        }

        var entity = viewer.entities.add({
            // //Set the entity availability to the same interval as the simulation time.
            availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
                start: start,
                stop: stop
            })]),
            //Use our computed positions
            position: property,

            //Automatically compute orientation based on position movement.
            orientation: new Cesium.VelocityOrientationProperty(property),

            //Load the Cesium plane model to represent the entity
            model: {
                uri: '/CesiumAir/Cesium_Air.gltf',//90
                minimumPixelSize: 128
            },

        });
        var entityPosition = entity.position.getValue(viewer.clock.currentTime);

        var radians = Cesium.Ellipsoid.WGS84.cartesianToCartographic(entityPosition);
        
      Cesium.Cartesian3.fromDegrees(allCaseEntitiesDateDetail[0].long, allCaseEntitiesDateDetail[0].lat, 2000),
        entity.cylinder= {
                length: 1000,
                topRadius: 0.0,
                bottomRadius: 25.0,
                material: Cesium.Color.YELLOW
            }
     
setTimeout(function () { viewer.trackedEntity = entity; viewer.zoomTo(viewer.entities._entities); }, 3000);
}, 10000);