Problem with entity that needs to "hold still" if using interpolated positions

847 views
Skip to first unread message

j...@intelesense.net

unread,
May 11, 2016, 2:07:01 PM5/11/16
to cesium-dev
I am using interpolation for my sampledPositionProperty in order to smooth the angle of my aircraft. However, if my points are say
Pos A Time 1
Pos B Time 2
Pos C Time 3
Pos C Time 4
Pos C Time 5
Pos D Time 6

Then, the airplane goes all haywire at point C, not understanding that it should be waiting at that point. In other words, I need to find a way to smooth out the path between a set of points (interpolation) but if the starting point and ending point are the same, I need to have the interpolated point be the same. It would also be nice if the plane doesn't change orientation from the last known velocity-based orientation, but that is less important.

Below is an example code for the plane going haywire at the fixed point. In this example, the fixed point is after the half-circle and before the straight line back. Any advice or workaround would be greatly appreciated!


var viewer = new Cesium.Viewer('cesiumContainer', {
terrainProviderViewModels : [], //Disable terrain changing
infoBox : false, //Disable InfoBox widget
selectionIndicator : false //Disable selection indicator
});

//Enable lighting based on sun/moon positions
viewer.scene.globe.enableLighting = true;

//Use STK World Terrain
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
url : 'https://assets.agi.com/stk-terrain/world',
requestWaterMask : true,
requestVertexNormals : true
});

//Enable depth testing so things behind the terrain disappear.
viewer.scene.globe.depthTestAgainstTerrain = true;

//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);

//Set bounds of our simulation time
var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
var stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());

//Make sure viewer is at the desired time.
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 = 10;

//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);

//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius) {
var property = new Cesium.SampledPositionProperty();
var rnd=Cesium.Math.nextRandomNumber() ;
for (var i = 0; i <= 360; i += 45) {
var x = i;
if (i>180 && i<360){
x=180;
}
var radians = Cesium.Math.toRadians(x);
var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
var position = Cesium.Cartesian3.fromDegrees(lon + (radius * 1.5 * Math.cos(radians)), lat + (radius * Math.sin(radians)), rnd * 500 + 1750);
property.addSample(time, position);
console.log(position.x);
console.log(position.y);
console.log(position.z);
//Also create a point for each sample we generate.

viewer.entities.add({
position : position,
point : {
pixelSize : 8,
color : Cesium.Color.TRANSPARENT,
outlineColor : Cesium.Color.YELLOW,
outlineWidth : 3
}
});
}
return property;
}

//Compute the entity position property.
var position = computeCirclularFlight(-112.110693, 36.0994841, 0.03);

//Actually create the entity
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 : position,

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

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

//Show the path as a pink line sampled in 1 second increments.
path : {
resolution : 1,
material : new Cesium.PolylineGlowMaterialProperty({
glowPower : 0.1,
color : Cesium.Color.YELLOW
}),
width : 10
}
});

//Add button to view the path from the top down
Sandcastle.addDefaultToolbarButton('View Top Down', function() {
viewer.trackedEntity = undefined;
viewer.zoomTo(viewer.entities, new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90)));
});

//Add button to view the path from the side
Sandcastle.addToolbarButton('View Side', function() {
viewer.trackedEntity = undefined;
viewer.zoomTo(viewer.entities, new Cesium.HeadingPitchRange(Cesium.Math.toRadians(-90), Cesium.Math.toRadians(-15), 7500));
});

//Add button to track the entity as it moves
Sandcastle.addToolbarButton('View Aircraft', function() {
viewer.trackedEntity = entity;
});

//Add a combo box for selecting each interpolation mode.
Sandcastle.addToolbarMenu([{
text : 'Interpolation: Linear Approximation',
onselect : function() {
entity.position.setInterpolationOptions({
interpolationDegree : 2,
interpolationAlgorithm : Cesium.LinearApproximation
});
}
}, {
text : 'Interpolation: Lagrange Polynomial Approximation',
onselect : function() {
entity.position.setInterpolationOptions({
interpolationDegree :2,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});
}
}, {
text : 'Interpolation: Hermite Polynomial Approximation',
onselect : function() {
entity.position.setInterpolationOptions({
interpolationDegree : 2,
interpolationAlgorithm : Cesium.HermitePolynomialApproximation
});
}
}], 'interpolationMenu');

Joy Foster

unread,
May 11, 2016, 2:09:56 PM5/11/16
to cesium-dev
Adding image.  Notice that the plane made a little jot in the bottom left while waiting to go back to the origin point.

Inline image 3 
Inline image 2
Inline image 1


--
You received this message because you are subscribed to a topic in the Google Groups "cesium-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cesium-dev/CTNlM0moR68/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cesium-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Joy Foster

unread,
May 11, 2016, 2:35:49 PM5/11/16
to cesium-dev
After more research, this seems related to this issue:

However, I can not use the same recommendation (intervals) because I am not using CZML. Instead, I am adding samples to the sampledPositionProperty.

Is it possible to work around this, or add intervals, using the sampledPositionProperty directly?

Thanks,
Joy

Scott Hunter

unread,
May 11, 2016, 2:41:24 PM5/11/16
to cesiu...@googlegroups.com
Intervals in CZML end up creating CompositePositionProperty objects.  See the example code for CompositeProperty and CompositePositionProperty:



You received this message because you are subscribed to the Google Groups "cesium-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cesium-dev+...@googlegroups.com.

Joy Foster

unread,
May 11, 2016, 2:46:56 PM5/11/16
to cesium-dev
So, just to be clear, a compositePositionProperty allows you to combine a sampledPositionProperty and a constantPositionProperty?  This would mean that for this example, I could create a compositePositionProperty that had 3 intervals inside it.  The first would be for the first half of the time which would be sampledPositionProperty.  The second would be for the "holding still" part and would be a constantProperty, and the third would be for the "return home" and would be another sampledPositionProperty.  Am I on the right track?

Thanks!

Joy Foster

unread,
May 11, 2016, 3:55:13 PM5/11/16
to cesium-dev
Scott, thank you!  I just tested my clarification and it works well in sandcastle.  Just need to figure out how to get that working in my app!

Just for anyone else with this issue, I have included the final solution below:

    var start1=start;
   var start2;
    var start3;
    var end1;
    var end2;
    var end3;
   
    //Create a composite property from two previously defined properties
    //where the property is valid on August 1st, 2012 and uses a constant
    //property for the first half of the day and a sampled property for the
    //remaining half.
 
    var sproperty1 = new Cesium.SampledPositionProperty(
        
    );
        var cproperty = new Cesium.ConstantPositionProperty(
        
    );
        var sproperty2 = new Cesium.SampledPositionProperty(
        
    );

    sproperty1.setInterpolationOptions({
            interpolationDegree :2,
            interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
        });
     sproperty2.setInterpolationOptions({
            interpolationDegree :2,
            interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
        });
    var intervalStart = start;

    var rnd=Cesium.Math.nextRandomNumber() ;
    var h = rnd * 500 + 1750;
    for (var i = 0; i <= 360; i += 45) {
        var n = i;
        if (n>=180 && n<360){
            n=135;
        }
        var radians = Cesium.Math.toRadians(n);
        var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
         var x = lon + (radius * 1.5 * Math.cos(radians));
        var y = lat + (radius * Math.sin(radians));
        var z = h;
        var position = Cesium.Cartesian3.fromDegrees(x, y, z);
        if (i===135){

            end1=time;
            start2=time;
        }
        if (i===315){
            end2=time;

            start3=time;
        }
        if (i===360){
            end3=time;

        }
        if (i<=135){
            sproperty1.addSample(time,position);
        }
        if (i===135){
            cproperty.setValue(position);
        }
        if (i >=315){
           sproperty2.addSample(time,position);
        }
        

        
        
        //Also create a point for each sample we generate.
        
        viewer.entities.add({
            position : position,
            point : {
                pixelSize : 8,
                color : Cesium.Color.TRANSPARENT,
                outlineColor : Cesium.Color.YELLOW,
                outlineWidth : 3
            }
        });
    }
       var composite = new Cesium.CompositePositionProperty();
    var timeInterval1 = new Cesium.TimeInterval({
    start : start1,
    stop : end1,
    isStartIncluded : true,
    isStopIncluded : true,
    data : sproperty1
});
    var timeInterval2 = new Cesium.TimeInterval({
    start : start2,
    stop : end2,
    isStartIncluded : false,
    isStopIncluded : false,
    data : cproperty
});
    var timeInterval3 = new Cesium.TimeInterval({
    start : start3,
    stop : end3,
    isStartIncluded : true,
    isStopIncluded : true,
    data : sproperty2
});
   
    composite.intervals.addInterval(timeInterval1);
       composite.intervals.addInterval(timeInterval2);
        composite.intervals.addInterval(timeInterval3);

    return composite;

Joy Foster

unread,
May 11, 2016, 4:09:56 PM5/11/16
to cesium-dev
One last thing...

I did a little more testing and found that I could use just two intervals.  The first covered the whole time (start1-end3) and the second interval covered the stopped time.  I added the stopped interval second so that it would take precedence over the first interval.

Then, I changed the code that added to the properties to the below code:
  sproperty3.addSample(time,position);
to
  sproperty1.addSample(time,position);

and removed sproperty3 and timeInterval3;

This way, the samples before and after the stop were added to the single sampledPositionProperty (which was set to the interval that covered the whole span).

The whole code is below.  

My question is first whether I can count on the system to use the last interval added if two are overlapping.  Also, I want to know if using overlapped intervals is an acceptable way to do this.  It will dramatically reduce the number of intervals if there are lots of starts and stops (about by half the number of intervals + 1 for the full interval).  However, it depends on whether the system is designed to dea welll with overlaps.  I assume that this is ok, but want to know if the performance of dealing with overlap is problematic enough that I should avoid it when necessary.

Modified code below:


  //  var start3;
  //  var end1;
    var end2;
    var end3;
   
    //Create a composite property from two previously defined properties
    //where the property is valid on August 1st, 2012 and uses a constant
    //property for the first half of the day and a sampled property for the
    //remaining half.
 
    var sproperty1 = new Cesium.SampledPositionProperty(
        
    );
        var cproperty = new Cesium.ConstantPositionProperty(
        
    );
     //   var sproperty2 = new Cesium.SampledPositionProperty(
        
   // );

    sproperty1.setInterpolationOptions({
            interpolationDegree :2,
            interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
        });
    // sproperty2.setInterpolationOptions({
     //       interpolationDegree :2,
     //       interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
      //  });
    var intervalStart = start;

    var rnd=Cesium.Math.nextRandomNumber() ;
    var h = rnd * 500 + 1750;
    for (var i = 0; i <= 360; i += 45) {
        var n = i;
        if (n>=180 && n<360){
            n=135;
        }
        var radians = Cesium.Math.toRadians(n);
        var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
         var x = lon + (radius * 1.5 * Math.cos(radians));
        var y = lat + (radius * Math.sin(radians));
        var z = h;
        var position = Cesium.Cartesian3.fromDegrees(x, y, z);
        if (i===135){

        //    end1=time;
            start2=time;
        }
        if (i===315){
            end2=time;

           // start3=time;
        }
        if (i===360){
            end3=time;

        }
        if (i<=135){
            sproperty1.addSample(time,position);
        }
        if (i===135){
            cproperty.setValue(position);
        }
        if (i >=315){
           sproperty1.addSample(time,position);
//sproperty2.addSample(time,position);
        }
        

        
        
        //Also create a point for each sample we generate.
        
        viewer.entities.add({
            position : position,
            point : {
                pixelSize : 8,
                color : Cesium.Color.TRANSPARENT,
                outlineColor : Cesium.Color.YELLOW,
                outlineWidth : 3
            }
        });
    }
       var composite = new Cesium.CompositePositionProperty();
    var timeInterval1 = new Cesium.TimeInterval({
    start : start1,
   //stop : end1,
stop : end3,
    isStartIncluded : true,
    isStopIncluded : true,
    data : sproperty1
});
    var timeInterval2 = new Cesium.TimeInterval({
    start : start2,
    stop : end2,
    isStartIncluded : false,
    isStopIncluded : false,
    data : cproperty
});
/*
    var timeInterval3 = new Cesium.TimeInterval({
    start : start3,
    stop : end3,
    isStartIncluded : true,
    isStopIncluded : true,
    data : sproperty2
});
*/
   
    composite.intervals.addInterval(timeInterval1);
       composite.intervals.addInterval(timeInterval2);
        //composite.intervals.addInterval(timeInterval3);

Scott Hunter

unread,
May 11, 2016, 4:29:19 PM5/11/16
to cesiu...@googlegroups.com
Yes, that would work.  Alternatively you could use two intervals, [start, return] and [return, end] and configure the first sampled property with forwardExtrapolationType HOLD, so it holds position of the last sample after that time (between time of arrival and time of return).

Joy Foster

unread,
May 11, 2016, 4:33:14 PM5/11/16
to cesium-dev
Great, thank you!  That makes perfect sense.

For my particular code, it will be easiest to just add in the "stopped" intervals (and convert the sampledPositionProperty to a compositePositionedProperty with the sampledPositionProperty inside).

However,  I will keep this in mind in case this alternative approach will help with something else in the future.

Reply all
Reply to author
Forward
0 new messages