Search code examples
datasourcekmlcesiumjs

How do I set the view from a KML camera in Cesiumjs?


I have a simple KML file that specifies a camera.

How do I get Cesium to load the KML then fly to the specified camera view? Also the KML will be updated regularly so how do I get the data source to update and Cesium to fly to the new view on an interval basis?

I have Cesium running on a local web server so it can read the KML from the local file system, and I know the camera has to be rotated from Google's coordinate frame to Cesiums'.

Here's the KML file:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
    <Camera>
        <altitudeMode>absolute</altitudeMode>
        <longitude>-80.215317</longitude>
        <latitude>26.843521</latitude>
        <altitude>306.388825</altitude>
        <heading>48.980423</heading>
        <roll>0.062101</roll>
        <tilt>75.090492</tilt>
    </Camera>
</Document>
</kml>

and here's my code so far:

<script>

    var My_Altitude;
    var My_Heading;
    var My_Latitude;
    var My_Longitude;
    var My_Roll;
    var My_Tilt;
    var Update_Interval = 60;

    var viewer = new Cesium.Viewer('cesiumContainer');
    var options = {
        camera : viewer.scene.camera,
        canvas : viewer.scene.canvas
    };

    viewer.dataSources.add(Cesium.KmlDataSource.load('./My_Camera.kml', options)).then(function(dataSource){

        My_Altitude = dataSource.entities.getById('altitude');
        My_Heading = dataSource.entities.getById('heading');
        My_Latitude = dataSource.entities.getById('latitude');
        My_Longitude = dataSource.entities.getById('longitude');
        My_Roll = dataSource.entities.getById('roll');
        My_Tilt = dataSource.entities.getById('tilt');

        viewer.camera.flyTo({
            destination : Cesium.Cartesian3.fromDegrees( My_Longitude, My_Latitude, My_Altitude ),
            orientation : {
                heading : Cesium.Math.toRadians( My_Heading ),
                pitch   : Cesium.Math.toRadians( My_Tilt - 90.0 ),
                roll    : Cesium.Math.toRadians( My_Roll )
            },
            duration : Update_Interval
        });
    });

</script>

Any help is greatly appreciated.

:-)


Solution

  • Accessing KML Camera element in the KmlDataSource won't work because the Camera/LookAt elements are not yet supported in Cesium and not present. (See issue 873). If you look at the Object for the data source in web console/debugger you will see entities array is of length 0.

    My_Tilt = dataSource.entities.getById('tilt'); // returns undefined
    

    You need to fetch the KML content and parse the Camera element manually.

    var x = new XMLHttpRequest();
    x.open("GET", "./My_Camera.kml", true);                
    x.onreadystatechange = function () {
      if (x.readyState == 4 && x.status == 200)
      {
         var xmlDoc = x.responseXML;
         var camera = xmlDoc.getElementsByTagName("Camera")[0];
         var My_Tilt = camera.getElementsByTagName("tilt")[0].childNodes[0].nodeValue;
         // ...
      }
    };
    x.send();
    

    The conversion of tilt - 90 to pitch is correct, however, you need to reverse the sign for roll to convert from Google Earth to Cesium's representation.

    Here is the updated flyTo code:

     viewer.camera.flyTo({
                destination : Cesium.Cartesian3.fromDegrees( My_Longitude, My_Latitude, My_Altitude ),
                orientation : {
                    heading : Cesium.Math.toRadians( My_Heading ),
                    pitch   : Cesium.Math.toRadians( My_Tilt - 90.0 ),
                    roll    : -Cesium.Math.toRadians( My_Roll )
                },
    

    If you want to update the KML on an interval then you can create a timer in javascript and re-parse the KML in the timer action.