How to lift the polyline above earth surface?

Hi everyone,

In need of another help!

So I've managed to create and animate a polyline from one country (long, lat) to another (long, lat). However, I need the lines to be lifted above the earth surface so that the line isn't flat on it.

I wish to achieve something like this: http://www.peter-west.uk/project/open-data-visualisations.html

Here is my polyline code that I have so far.

var point = {
      lat: Number(sourceLatitude),
      lon: Number(sourceLongitude)
};

var finalPoint = {
      lat: Number(destLatitude),
      lon: Number(destLongitude)
};

var positions = calculatePositionSamples(point, finalPoint, start, duration, frequency);

var target = entities.add({
        name: (sourceCountry + " - " + destCountry),
        position: positions,
        orientation: new Cesium.VelocityOrientationProperty(positions),
        path: {
          resolution: 1,
          material: new Cesium.PolylineGlowMaterialProperty({
                    glowPower: 0.1,
              color: Cesium.Color.YELLOW
          }),
          width: 2,
          trailTime: duration,
          leadTime: 0
        }
});

function calculatePositionSamples(point, endPoint, startTime, duration) {
    var property = new Cesium.SampledPositionProperty();

    var positions = Cesium.PolylinePipeline.generateCartesianArc({
        positions: Cesium.Cartesian3.fromDegreesArray([
                  point.lon, point.lat
            endPoint.lon, endPoint.lat
          ])
    })

    var deltaStep = duration / positions.length
    var currentPosition;
    for (var i = 0, since = 0; i < positions.length; i +=1, since += deltaStep) {
       property.addSample(Cesium.JulianDate.addSeconds(startTime, since, new Cesium.JulianDate()),
        positions[i])
    
   }
return property;
};

Thank you in advance!

Alan

Hello Alan,

This type of curve isn’t something directly built into Cesium, but I think it can be achieved pretty easily using interpolation. Here’s the method I would use:

Add three positions to the SampledPositionProperty: The start position at height = 0, the midpoint and height = some large number, and the end position at height = 0.

Set your path interpolation to Cesium.LagrangePolynomialApproximation

Cesium should compute a nice curve from those positions.

For an example of how to use interpolation for your entity position, take a look at this demo: http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Interpolation.html&label=Showcases

Best,

Hannah

Hi Hannah,

Thanks for replying!

Just another question, how do I go about calculating the midPoint?
I wish to place it in an array such as the Point and finalPoint (which can be found at the top of my codes):

var Point = {
      lat: Number(sourceLatitude),
      lon: Number(sourceLongitude)
};
          
var finalPoint = {
      lat: Number(destLatitude),
      lon: Number(destLongitude)
};

var midPoint = {
      lat:
      lon:
};

Thanks

Alan

Hi again,

I've looked through the sandcastle link that you have provided for the interpolation. However, I'm not sure as to where I should place it in my codes.

var target = entities.add({
     name: (sourceCountry + " - " + destCountry),
     position: positions,
     orientation: new Cesium.VelocityOrientationProperty(positions),
     path: {
          resolution: 1,
          material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.1,
              color: Cesium.Color.YELLOW
          }),
          width: 5,
          trailTime: duration,
          leadTime: 0
      }
});

target.position.setInterpolationOptions({
      interpolationDegree : 5,
      interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});

Is this right?

Thanks again!

Alan

Hi Hannah,

Apologies for spamming you with emails.
This is what I have so far:

                                       var startPoint = {
                  lat: Number(sourceLatitude),
                  lon: Number(sourceLongitude)
              };
          
              var finalPoint = {
                  lat: Number(destLatitude),
                  lon: Number(destLongitude)
              };

                                        // I assume this is the way to calculate midpoint?
          var midPoint = {
            lat: Number((startPoint.lat + finalPoint.lat)/2),
            lon: Number((startPoint.lat + finalPoint.lat)/2)
          };

                                        var positions = calculatePositionSamples(startPoint, midPoint, finalPoint, start, duration, frequency);
              var target = entities.add({
            name: (sourceCountry + " - " + destCountry),
                  position: positions,
                  orientation: new Cesium.VelocityOrientationProperty(positions),
                  path: {
                    resolution: 1,
                    material: new Cesium.PolylineGlowMaterialProperty({
                        glowPower: 0.1,
                        color: Cesium.Color.YELLOW
                    }),
                    width: 10,
                    trailTime: duration,
                    leadTime: 0
                  }
              });

                                        // I assume this is where we place the LagrangePolynomialApproximation?
          target.position.setInterpolationOptions({
                      interpolationDegree : 5,
                      interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
                                         });

                      function calculatePositionSamples(startPoint, midPoint, finalPoint, startTime, duration) {
                         var property = new Cesium.SampledPositionProperty();

                         var positions = Cesium.PolylinePipeline.generateCartesianArc({
                                     positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                                          startPoint.lon, startPoint.lat, 0.000,
                                    midPoint.lon, midPoint.lat, 1000000,
                                          finalPoint.lon, finalPoint.lat, 0.000
                                       ])
                           });
        
                        var deltaStep = duration / positions.length;
                        var currentPosition;
                        for (var i = 0, since = 0; i < positions.length; i +=1, since += deltaStep)
                        {
                                    property.addSample( Cesium.JulianDate.addSeconds(startTime, since, new Cesium.JulianDate()),
                positions[i])
    
                       };
                        return property;
                     };

Despite setting the midpoint's height as 1000000, it still appears to be flat.
Hope you are able to point of what went wrong!
Thank you.

Alan

Hello Alan,

You were most of the way there! Here’s the full working example:

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var sourceLongitude = -125;
var sourceLatitude = 39;
var destinationLongitude = -115;
var destinationLatitude = 30;
var start = Cesium.JulianDate.now();
var duration = 5;

var startPoint = Cesium.Cartographic.fromDegrees(sourceLongitude, sourceLatitude);
var finalPoint = Cesium.Cartographic.fromDegrees(destinationLongitude, destinationLatitude);
var geodesic = new Cesium.EllipsoidGeodesic(startPoint, finalPoint);
var midPoint = geodesic.interpolateUsingFraction(0.5, new Cesium.Cartographic());
midPoint.height = 1000000;
var positions = computePositions(startPoint, midPoint, finalPoint, start, duration);

var target = viewer.entities.add({
position: positions,
orientation: new Cesium.VelocityOrientationProperty(positions),
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW
}),
width: 10,
trailTime: duration,
leadTime: 0
}
});

target.position.setInterpolationOptions({
interpolationDegree : 5,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});

function computePositions(startPoint, midPoint, finalpoint, startTime, duration) {
var property = new Cesium.SampledPositionProperty();
var startPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(startPoint);
property.addSample(startTime, startPosition);

var midTime = Cesium.JulianDate.addSeconds(startTime, duration/2, new Cesium.JulianDate());
var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(midPoint);
property.addSample(midTime, midPosition);

var endTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());
var endPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(finalPoint);
property.addSample(endTime, endPosition);
return property;

}

``

Depending on how far away your start and end points are, you might want to create a function to compute the height you want the midpoint at based on the distance. You can get the distance from the EllipsoidGeodesic using
var distance = geodesic.surfaceDistance

``

Best,

Hannah

Hi Hannah,

It's working perfectly, thank you so much for your guidance!

Alan

Hi everyone,

So I've managed to create the animated polyline above the earth surface.
However, I wish edit a few things but I'm not sure where to look at. I want to do the following:

1. Prevent the polyline from leaving the startPoint after reaching the finalPoint
  1.a. Current situation:
    - Polyline starts moving from startPoint to finalPoint (this is perfect)
    - Once polyline reaches finalPoint, polyline leaves the startPoint too

  1.b. What I want to edit:
    - I still want the polyline animation to move from startPoint to finalPoint but
    - Is there any way to prevent the polyline from leaving the startPoint once the polyline reaches the finalPoint?
    - Basically I don't want the 'end result' of the whole animated polyline to disappear

2. The polyline does not look smooth
  - Is there any way to resolve this issue?

This was the code that Hannah has helped me to animate the polyline from startPoint to finalPoint:

  var startPoint = Cesium.Cartographic.fromDegrees(sourceLongitude, sourceLatitude);
  var finalPoint = Cesium.Cartographic.fromDegrees(destLongitude, destLatitude);
  var geodesic = new Cesium.EllipsoidGeodesic(startPoint, finalPoint);

  // Calculate the midPoint to form the arc out of the earth surface
  var midPoint = geodesic.interpolateUsingFraction(0.5, new Cesium.Cartographic());
          
  // Altitude (Height of the midpoint)
  midPoint.height = 2000000;

  var positions = computePositions(startPoint, midPoint, finalPoint, start, duration);

  var target = viewer.entities.add({
    name: (sourceCountry + " - " + destCountry),
    position: positions,
    orientation: new Cesium.VelocityOrientationProperty(positions),
    path: {
      resolution: 1,
      material: new Cesium.PolylineGlowMaterialProperty({
        glowPower: 0.1,
        color: Cesium.Color.RED
      }),
      width: 10,
      trailTime: duration,
      leadTime: 0
    }
  });

  target.position.setInterpolationOptions({
    interpolationDegree: 5,
    interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
  });

  function computePositions(startPoint, midPoint, finalPoint, startTime, duration){
    var property = new Cesium.SampledPositionProperty();

    var startPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(startPoint);
    property.addSample(startTime, startPosition);
        
    var midTime = Cesium.JulianDate.addSeconds(startTime, duration/2, new Cesium.JulianDate());
    var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(midPoint);
    property.addSample(midTime, midPosition);

    var endTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());
    var endPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(finalPoint);

    property.addSample(endTime, endPosition);
        
    return property;
  };

Many thanks!

Alan

additionally, my code now presents 1 height currently: midPoint.height = 2000000;
Is there any way to enable the different polylines to have different heights whenever they are animated because I have some data coming and going to the same coordinates.

Hello,

I believe if you have trailTime: undefined the path will draw from the start point to the end point and stay visible.

For your other question, you can alter the computePositions function so that it determines the height of the midpoint based on the distance between the start and end points.

-Hannah

Hello Everyone,
I have a slight problem… i want the curvy way to be a little lower. To grasp a better example, can you please look at my attachment, originally, the arrow is yellow in color and travel in a higher curvy way. I would rather want the red curvy way than the yellow curvy way.

So what i have done it:

1.i designed the starting point and destination point to allow the arrow to travel from point to point. then the arrow travel from point to point in a curvy way.

I apologize if my English is Bad…

My code is found below…

var startPoint = Cesium.Cartographic.fromDegrees(sourceLongitude, sourceLatitude);

var finalPoint = Cesium.Cartographic.fromDegrees(destLongitude, destLatitude);

// Calculate the midPoint to form the arc out of the earth surface

//Initializes a geodesic on the ellipsoid connecting the two provided planetodetic points.

var geodesic = new Cesium.EllipsoidGeodesic(startPoint, finalPoint);

var midPoint = geodesic.interpolateUsingFraction(0.5, new Cesium.Cartographic());

// Different heights for each attack polyline (1000000 ~ 5000000)

//var rand = [Math.floor((Math.random() * 700000) + 500000)];

var rand = [Math.floor((Math.random() * 700000) + 500000)];

midPoint.height = rand;

var positions = computePositions(startPoint, midPoint, finalPoint, start, duration);

var target = viewer.entities.add({

name: (sourceCountry + " - " + destCountry),

position: positions,

orientation: new Cesium.VelocityOrientationProperty(positions),

path: {

resolution: 1,

/*material: new Cesium.PolylineGlowMaterialProperty({

glowPower: 0.1,

color: Cesium.Color.YELLOW

}),*/

//material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED), // by tschia attackArrowColour

material: attackArrowColour(devicetype,alert),

width: 20,

//trailTime: 12460*60,

trailTime: duration, // original is duration

leadTime: 0

},

});

//console.log(“TSDebug after init target\n”);

target.position.setInterpolationOptions({

interpolationDegree: 0.5,

interpolationAlgorithm: Cesium.LagrangePolynomialApproximation

//Cesium.LagrangePolynomialApproximation

});

/*var inip ="<?php echo $InternalIP; ?>";

*/

//console.log(“TSDebug devicetype at src [”+devicetype+"]\n");

//console.log(“TSDebug sourceIP [”+sourceIP+"]\n");

var sourceColour =setIntIPSourceColour(sourceIP,devicetype);

//console.log(“TSDebug sourceColour [” + sourceColour+"] for devicetype["+devicetype+"]\n" );

var srcPoint = Cesium.Cartographic.fromDegrees(sourceLongitude, sourceLatitude);

var srcPosition = compSrcDstPosition(srcPoint,srcPoint,start, duration);

var sourcePoint = viewer.entities.add({

name: sourceCountry,

//position : Cesium.Cartesian3.fromDegrees(sourceLongitude, sourceLatitude),

position : srcPosition,

point : {

pixelSize : 10,

/*color : new Cesium.CallbackProperty(

function(time, result){

//return Cesium.Color.fromAlpha(Cesium.Color.BLUE,

return Cesium.Color.fromAlpha(sourceColour,

(new Date(time).getTime() % 1000) / 1000,

result);

}, false),*/

color : sourceColour

//outlineColor: Cesium.Color.BLACK,

//outlineWidth: 2

},

});

//console.log("devicetype at dest "+devicetype);

//console.log("destIP "+destIP + " destCountry "+destCountry );

var destColour = setIntIPSourceColour(destIP,devicetype);

//var destColour = setIntIPSourceColour(dstIP);

var dstPoint = Cesium.Cartographic.fromDegrees(destLongitude, destLatitude);

var dstPosition = compSrcDstPosition(dstPoint,dstPoint,start, duration);

var destPoint = viewer.entities.add({

name: destCountry,

//position : Cesium.Cartesian3.fromDegrees(destLongitude, destLatitude),

position : dstPosition,

point : {

					pixelSize : 10,

					/*color : new Cesium.CallbackProperty(

function(time, result){

//return Cesium.Color.fromAlpha(Cesium.Color.YELLOW,

return Cesium.Color.fromAlpha(destColour,

(new Date(time).getTime() % 1000) / 1000,

result);

}, true),*/

color : destColour

//outlineColor: Cesium.Color.BLACK,

//outlineWidth: 2

},

description: '

COUNTRY: ’ + destCountry + ‘

’ + '

CITY: ’ + destCity +

'

ALERT: '+ alert + ‘

’+ '

Device Type: ’ + devicetype+

});

viewer.trackEntity = target;

} // end of for loop

//console.log(“TSDebug before next i[”+i+"]\n");

}); // end of parsing json data

/*

  • FUNCTION TO HELP CREATE THE MAIN ATTACK ANIMATION LINE

*/

function computePositions(startPoint, midPoint, finalPoint, start, duration){

//console.log(“computePositions startPoint [”+startPoint+"] finalPoint["+finalPoint+"] start["+start+"]\n");

var property = new Cesium.SampledPositionProperty();

var startPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(startPoint);

property.addSample(start, startPosition);

var midTime = Cesium.JulianDate.addSeconds(start, duration/2, new Cesium.JulianDate());

var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(midPoint);

property.addSample(midTime, midPosition);

var endTime = Cesium.JulianDate.addSeconds(start, duration, new Cesium.JulianDate());

var endPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(finalPoint);

//console.log("computePositions eeeeeeeeeeeeeee " + endTime );

property.addSample(endTime,endPosition);

return property;

};

Hi Everyone, let say in my project, I have 2 entities which have random latitudes and longitudes. How am I supposed to calculate the midPoint.height…??

Hi there,

Check out EllipsoidGeodesic: http://cesiumjs.org/Cesium/Build/Documentation/EllipsoidGeodesic.html

Hope that helps,

  • Rachel

hey ,

here it works fine for the start time and stop time and repetition works fine

var viewer = new Cesium.Viewer(‘cesiumContainer’, {

shouldAnimate:true, // Enable animations

});

var start = Cesium.JulianDate.fromDate(new Date());

var stop = Cesium.JulianDate.addSeconds(start, 50, new Cesium.JulianDate());

var duration=50;

//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 = 1;

viewer.timeline.zoomTo(start, stop);

var startPoint = Cesium.Cartographic.fromDegrees(78.42967987060547,

17.236645101793957);

var finalPoint = Cesium.Cartographic.fromDegrees(83.22383880615234,

17.731028777559807);

var geodesic = new Cesium.EllipsoidGeodesic(startPoint, finalPoint);

// Calculate the midPoint to form the arc out of the earth surface

var midPoint = geodesic.interpolateUsingFraction(0.5, new Cesium.Cartographic());

// Altitude (Height of the midpoint)

midPoint.height = 2000000;

var positions = computePositions(startPoint, midPoint, finalPoint, start, duration);

var target = viewer.entities.add({

position: positions,

orientation: new Cesium.VelocityOrientationProperty(positions),

path: {

resolution: 1,

material: new Cesium.PolylineGlowMaterialProperty({

glowPower: 0.1,

color: Cesium.Color.RED

}),

width: 10,

leadTime: 0,

trailTime: 100

}

});

target.position.setInterpolationOptions({

interpolationDegree: 5,

interpolationAlgorithm: Cesium.LagrangePolynomialApproximation

});

function computePositions(startPoint, midPoint, finalPoint, startTime, duration){

var property = new Cesium.SampledPositionProperty();

var startPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(startPoint);

property.addSample(startTime, startPosition);

var midTime = Cesium.JulianDate.addSeconds(startTime, duration/2, new Cesium.JulianDate());

var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(midPoint);

property.addSample(midTime, midPosition);

var endTime = Cesium.JulianDate.addSeconds(startTime, duration, new Cesium.JulianDate());

var endPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(finalPoint);

property.addSample(endTime, endPosition);

return property;

}

viewer.zoomTo(viewer.entities);