I have flight data containing position (lat,lon,height) and orientation (pitch, roll, heading) in function of time.
I would like to represent the aircraft in CesiumJS.
I tried to create CZML file from the flight data. Everything worked fine for the position. But CZML format support only orientation based on a quaternion in the reference of Earth fixed axes. That means I would have to preprocess this quaternion for each position, pitch, roll, heading in order to write the CZML.
Do you think I should implement this quaternion computation (not straight forward) ?
Or should I use another solution to use cesium functions that allow me to use directly pitch, roll, heading values ?. In this case, I wonder which format I can use to transfer my flight data to Cesium.
Thank you for your advices
I found how to orientate a model based on pitch, roll, heading values in function of time. It is a pity that there is no documentation on SampledProperty used with Cesium.Quaternion in the official website. Also there is no example with in the sandCastle.
I modified a few lines of a code example to illustrate orientation possibilities in cesium (copy/paste the code in HTML and JAVASCRIPT tabs in Cesium sandCastle. Only the heading takes different values in this example but you can play with pitch and roll too.
HTML CODE :
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<div id="interpolationMenu"></div>
</div>
JAVASCRIPT CODE :
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);
var lon = 0;
var lat = 45;
var radius = 0.03;
//Generate a random circular pattern with varying heights.
var positionProperty = new Cesium.SampledPositionProperty();
var orientationProperty = new Cesium.SampledProperty(Cesium.Quaternion);
for (var i = 0; i <= 360; i += 45) {
var radians = Cesium.Math.toRadians(i);
var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
// compute positions
var position = Cesium.Cartesian3.fromDegrees(lon + (radius * 1.5 * Math.cos(radians)), lat + (radius * Math.sin(radians)), Cesium.Math.nextRandomNumber() * 500 + 1750);
positionProperty.addSample(time, position);
// compute orientations
var heading = Cesium.Math.toRadians(90+i);
var pitch = Cesium.Math.toRadians(20);
var roll = Cesium.Math.toRadians(0);
var hpRoll = new Cesium.HeadingPitchRoll(heading,pitch,roll);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position,hpRoll);
orientationProperty.addSample(time, orientation);
//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
}
});
}
//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 : positionProperty,
//Automatically compute orientation based on position movement.
orientation : orientationProperty,
//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 : 1,
interpolationAlgorithm : Cesium.LinearApproximation
});
}
}, {
text : 'Interpolation: Lagrange Polynomial Approximation',
onselect : function() {
entity.position.setInterpolationOptions({
interpolationDegree : 5,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});
}
}, {
text : 'Interpolation: Hermite Polynomial Approximation',
onselect : function() {
entity.position.setInterpolationOptions({
interpolationDegree : 2,
interpolationAlgorithm : Cesium.HermitePolynomialApproximation
});
}
}], 'interpolationMenu');
I haven't tried this myself, but it should be possible to convert an aircraft heading/pitch/roll (in local axis of known aircraft position) into an Earth-fixed Quaternion, using only math functions that ship with Cesium.
You have two transformations needed here, one is the heading-pitch-roll to quaternion, the other is local axes to Earth-fixed.
Now we need local to Earth-fixed.
Use Transforms.eastNorthUpToFixedFrame. This takes position into account, but gives you more than you need in the form of a Matrix4.
Get just the rotation from your Matrix4 using Matrix4.getMatrix3. This strips off the transform offset (Earth-to-aircraft) and yields a Matrix3 containing only the rotational offset (and scale, but that should just be the identity scale given where we obtained this matrix in the previous step, so we can safely think of this as only the rotation).
Use Quaternion.fromRotationMatrix to convert your Matrix3 into a Quaternion.
Now you have 2 quaternions, one from step 1 and another from step 4.
I hope my math is right. Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With