Skip to main content

Build a Flight Tracker with Cesium for Unreal

This tutorial will walk through the process of visualizing a flight from San Francisco to Copenhagen using real-world flight data.

Flying over San Francisco, CA, USA.

You’ll learn how to:

  • Import real-world flight data into Unreal Engine
  • Create a flight path using the data and USplineComponent
  • Import a model of an airplane and have the plane follow the flight path
  • Switch between different camera views in-flight

Prerequisites

  • An installed version of Unreal Engine (at least 4.26 or later). For instructions on how to install Unreal Engine, visit the Unreal Engine download page and refer to the Installing Unreal Engine guide for detailed instructions.
  • Visual Studio 2019 with Desktop Development with C++ workload
  • Cesium for Unreal version 1.7.0 or later
  • Know how to set up a basic Cesium for Unreal application. Check out our Cesium for Unreal Quickstart guide for instructions on starting with the Cesium for Unreal plugin

1Create an Unreal level

You can either start with the 01_CesiumWorld level from the Cesium for Unreal Samples on the Unreal Engine Marketplace or create a new level. If starting a new level, make sure to populate the level with at least Cesium World Terrain and some lighting, either with CesiumSunSky or your own custom lighting. Check out the Quickstart guide to learn how to set up a new level with Cesium for Unreal.

For this tutorial, the CesiumGeoreference is set to the coordinates of San Francisco International Airport (SFO), where the flight originates.

Origin Latitude = 37.61779

Origin Longitude = -122.390533

Origin Height = 0.0

2Add the PlaneTrack class

The PlaneTrack class will contain logic to process the flight data and generate position points for the spline which represents the flight path.

1Add a new C++ class by going to File —> New C++ Class… at the top left corner of the Unreal Editor. Select Actor as the parent class.
Click the Next button. On the following page, set the name of the new class to "PlaneTrack". Click the green Create Class button.
Visual Studio should automatically open the file. If not, open the project in Visual Studio by going to File —> Open Visual Studio.

Cesium for Unreal Flight tracker open visual studio

New C++ files can be found in the Source folder of the Visual Studio project.

Cesium for Unreal Flight tracker planetrack file location

2Add the following code snippet to the project .Build.cs file, which can also be found in the Source folder:

// Add Cesium for Unreal plugin dependency path
PrivateDependencyModuleNames.AddRange(new string[] { "CesiumRuntime" });

// Tell Unreal Engine to use C++17
CppStandard = CppStandardVersion.Cpp17;
Cesium for Unreal Flight tracker build cs

3Let's add some member variables to the PlaneTrack class to store the flight data, spline, and convert the data to the appropriate coordinate system. Import the necessary libraries and add the following public variables to PlaneTrack.h:

...
// Add import paths. Make sure they go above the PlaneTrack.generated.h line
#include "Components/SplineComponent.h"
#include "CesiumGeoreference.h"
#include "Engine/DataTable.h"
...

public: 
  // Spline variable to represent the plane track
  UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
    USplineComponent* SplineTrack;

  // Cesium class that contain many useful  coordinate conversion functions
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    ACesiumGeoreference* CesiumGeoreference;

  // An Unreal Engine data table to store the raw flight data
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    UDataTable* AircraftsRawDataTable;

4Navigate to PlaneTrack.cpp and initialize the SplineTrack variable in the PlaneTrack constructor as follows:

APlaneTrack::APlaneTrack()
{
  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
  PrimaryActorTick.bCanEverTick = true;

  // Initialize the track
  SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
  // This lets us visualize the spline in Play mode
  SplineTrack->SetDrawDebug(true);                                            
  // Set the color of the spline
  SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));                              
}

The ACesiumGeoreference and UDataTable variables will be set inside the Unreal Engine Editor.

Before moving on to the next step, save and compile the code. In the Unreal Engine Editor, click on Compile at the top tool panel:

Cesium for Unreal Flight tracker compileButtonInUnreal

If the code is well-formatted and correct, you will see the “Compile Complete” message at the bottom right corner of the Unreal Engine main editor. In this tutorial, compiling the code refers to this step.

3Bring in real-world flight data

Next, you are going to use real flight data from San Francisco to Copenhagen. This data is collected by FlightRadar24. The height in Cesium for Unreal is in meters relative to the WGS84 ellipsoid. The data is pre-processed to convert the heights from feet relative to mean sea level to meters relative to the ellipsoid.
You can download the converted data here.

Cesium for Unreal Flight tracker rawdataInExcel

The flight data in Microsoft Excel.

In order for the PlaneTrack class to access the data to perform coordinate conversions, you will use the Unreal Engine DataTable to store the data inside of the project. In this step, you will create a data structure to represent the structure of the flight data.

1In PlaneTrack.h, insert this snippet of code directly below the imports to define the flight database structure:

USTRUCT(BlueprintType)
struct FAircraftRawData : public FTableRowBase
{
  GENERATED_USTRUCT_BODY()

  public:
    FAircraftRawData()
      : Longitude(0.0)
      , Latitude(0.0)
      , Height(0.0)
    {}

  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Longitude;
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Latitude;
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Height;
};

The structure contains three member variables: LongitudeLatitude, and Height. These variables correspond to the column names in the raw datatable above. Also notice that the structure inherits from FTableRowBase.

Compile the code.

2Drag-and-drop the .csv data file into the Unreal Engine Content Browser. Select AircraftRawData in the Choose DataTable Row Type dropdown:

Cesium for Unreal Flight tracker dataTableOptions

Click Apply and double-click on the new UDataTable object in the Content Browser to open the data table:

Cesium for Unreal Flight tracker dataTableInUnreal
Information

Troubleshooting: if you receive a an error saying that Unreal Engine is unable to import, check to see if the data is saved within the the project folder. This error often occurs if the data file is saved elsewhere.

4Add positions to the flight track

In this step, you will add some more code to the PlaneTrack class to complete the rest of the functionality needed to create the spline path.

1Add the following imports to PlaneTrack.h, above the PlaneTrack.Generated.h line.

// Imports should be placed above the PlaneTrack.Generated.h line. 
...
#include <glm/vec3.hpp>
#include "CesiumGeospatial/Ellipsoid.h"
#include "CesiumGeospatial/Cartographic.h"

While still in PlaneTrack.h, add the following function at the end of the APlaneTrack class definition.


public:
  // Function to parse the data table and create the spline track
  UFUNCTION(BlueprintCallable, Category = "FlightTracker")
    void LoadSplineTrackPoints();

2Switch to PlaneTrack.cpp. Add the following code snippet to create the body of LoadSplineTrackPoints. This is where the majority of the computation will be done.

void APlaneTrack::LoadSplineTrackPoints()
  {
    if (this->AircraftsRawDataTable != nullptr && this->CesiumGeoreference != nullptr)
    {
      int32 PointIndex = 0;
      for (auto& row : this->AircraftsRawDataTable->GetRowMap())
      {
        FAircraftRawData* Point = (FAircraftRawData*)row.Value;
        // Get row data point in lat/long/alt and transform it into UE4 points
        double PointLatitude = Point->Latitude;
        double PointLongitude = Point->Longitude;
        double PointHeight = Point->Height;

        // Compute the position in UE coordinates
        glm::dvec3 UECoords = this->CesiumGeoreference->TransformLongitudeLatitudeHeightToUnreal(glm::dvec3(PointLongitude, PointLatitude, PointHeight));
        FVector SplinePointPosition = FVector(UECoords.x, UECoords.y, UECoords.z);
        this->SplineTrack->AddSplinePointAtIndex(SplinePointPosition, PointIndex, ESplineCoordinateSpace::World, false);

        // Get the up vector at the position to orient the aircraft
        const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
        glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));

        // Compute the up vector at each point to correctly orient the plane
        glm::dvec4 ecefUp(upVector, 0.0);
        const GeoTransforms& geoTransforms = this->CesiumGeoreference->GetGeoTransforms();
        const glm::dmat4& ecefToUnreal = geoTransforms.GetEllipsoidCenteredToAbsoluteUnrealWorldTransform();
        glm::dvec4 unrealUp = ecefToUnreal * ecefUp;
        this->SplineTrack->SetUpVectorAtSplinePoint(PointIndex, FVector(unrealUp.x, unrealUp.y, unrealUp.z), ESplineCoordinateSpace::World, false);

        PointIndex++;
      }
      this->SplineTrack->UpdateSpline();
    }
  }

Save and compile the code.

3Navigate back to the Unreal Engine to add the flight track to the scene. In the Place Actors panel, search for "Plane Track" and drag and drop it into the viewport.

4Select the PlaneTrack actor. In the Details panel, look for the Flight Tracker category. Set the Cesium Georeference variable to the Cesium Georeference variable in your scene and set the Aircrafts Raw Data Table variable to the data table added in step 3.

Cesium for Unreal Flight tracker planeTrackerVariables

5Navigate to the Level Blueprint. Here you're going to add some Blueprint nodes to populate the spline path.

Cesium for Unreal Flight tracker levelBlueprintMenu

6Look for the Event BeginPlay node. This node is called at the very beginning in Play mode. Drag-and-drop the PlaneTrack actor from the World Outliner, drag a connection from this node, search and add the Clear Spline Points function node.

When the spline is first added to the scene, it has two points by default. These two points are arbitrary and not needed in this tutorial, so they can be cleared with Clear Spline Points.

7From the PlaneTrack object node, drag another connection and search for "Load Spline Track Points". Connect the Clear Spline Points and the Load Spline Track Points nodes, and connect the Event BeginPlay node to the Clear Splint Points node. The final Blueprint network looks like this:

Cesium for Unreal Flight tracker beginPlayLevelBlueprint

Click Compile at the top left corner of the Blueprint Editor. Since spline visualization is off by default, you can turn it on by selecting the viewport, pressing the` key (usually under the Esc key) on your keyboard and entering in the ShowFlag.Splines 1 command. To check if everything is set up correctly, click the Play button in the top panel of the main editor. You should be able to see the data points connected by a spline curve that starts at a terminal building in the San Francisco International Airport like this:

Cesium for Unreal Flight tracker flightPath

5Add the aircraft

The final step to complete the flight tracker is adding an aircraft that follows the spline path. For the aircraft mesh, you can use any model you would like. Here is a Boeing 787 aircraft model from TurboSquid that you can download for free.

The aircraft model in this tutorial is a Boeing 737 aircraft model from Sketchfab.

1In the Content Browser, import the aircraft model into the content browser by selecting Add/Import —> Import to Game/[Path]/. You can inspect the model in the Static Mesh Editor:

Cesium for Unreal Flight tracker aircraftModel

2Right-click in a blank area of the Content Browser and select Blueprint Class. When prompted to pick the Parent Class, select Actor. Name the class “BP_Aircraft” (BP stands for Blueprint). This class will contain the logic to move the aircraft along the spline path.

3Double-click on the new Blueprint class to access the Blueprint Editor. Click on the green Add Component button at the top left corner and search for “Static Mesh”. Add it to the component list and name it “AircraftMesh”.

Cesium for Unreal Flight tracker addComponent

4With this new component selected, locate the Details panel to the right side of the Blueprint Editor. Find the Static Mesh variable and in the dropdown menu, find and select the aircraft mesh you imported earlier.

5Head to the Event Graph by clicking the Event Graph tab at the top of the Blueprint Editor. Right-click anywhere in the Event Graph and search for “Custom Event”. Name this event “MoveAircraft”.

6Right-click again in the Event Graph and search for “Add Timeline”. The Timeline node will serve as a timeline to move the aircraft over time.

Cesium for Unreal Flight tracker planeMoverTimeline

7Double-click on the Timeline node to open the Timeline Editor. Create a float curve by clicking on Add Float Track button at the top left corner of the editor and give it a name. In this tutorial, the Float Track is called “Alpha”. Add keyframes to the timeline by right-clicking on the curve and select Add key to Curve. The final curve looks like this:

Cesium for Unreal Flight tracker timelineCurve

The first keypoint is at Time = 0, Value = 0 while the second keypoint is at Time = 1, Value = 1. To assign more precise values to a keypoint, select it and type in the values at the top left corner:

Cesium for Unreal Flight tracker changeKeypointPrecise

Check the checkbox next to Use Last Keyframe. If you would like the flight to loop once it ends, you can check the Loop checkbox as well.

Head back to the Event Graph and promote the Alpha output pin of the Timeline node to a variable by right-clicking on it and select Promote to variable. This will add a new variable to the left panel under Components:

Cesium for Unreal Flight tracker promoteAlphaToVariable

8Add a variable called Duration to the BP_Aircraft Blueprint class using the green “Add New” button in the My Blueprint panel. This variable will be used to determine how long the aircraft takes to fly along the path. Give it a type of float and click on the eye symbol next to it to make it public and editable in the main Unreal Engine editor.

Similarly, add the following remaining variables to the Plane Blueprint class:

  • AircraftStartOffset: type float; public visibility; used to determine where on the timeline the flight begins. The Slider Range and Value Range should be between 0 and 1, since the timeline goes between 0 and 1. These variables can be edited in the Details panel in the Blueprint Editor.
  • PlaneTrack: type PlaneTrack with Object Reference; public visibility; used to store a reference to PlaneTrack object to retrieve the positions on the spline.
Cesium for Unreal Flight tracker planetrack object reference

The final Components list looks like this:

Cesium for Unreal Flight tracker planeVariables

9Complete the rest of the Move Aircraft custom event as follows:

Cesium for Unreal Flight tracker movePlaneFunctionBlueprint

To use a variable, drag-and-drop it into the Event Graph or right-click anywhere in the graph and search for the variable’s name. To call a function that corresponds to a variable, drag a new connection from the variable node and search for the function’s name.

Compile the Blueprint.

10Next, you will add logic to connect the PlaneTrack spline to the Move Aircraft function and interpolate the position of the aircraft using the spline. In the same Event Graph, create the following node network below the Move Aircraft function:

Cesium for Unreal Flight tracker splineLerpBlueprintNetwork

11Lastly, connect the two Blueprint networks together with the Set Actor Transform node:

Cesium for Unreal Flight tracker completeMoveplaneNetwork

Compile the Blueprint.

12Navigate back to the main editor. Add your aircraft object to the scene either by drag-and-droppping the BP_Aircraft Blueprint into the scene from the Content Browser or search for “Aircraft” in the Place Actor panel.

13There are several ways to trigger the Move Aircraft event. In this tutorial, you will trigger it on a key press. Head back to the Level Blueprint. In the Event Graph, add a Keyboard node (M is used here). Connect this Keyboard node to the Move Aircraft event as follow:

Cesium for Unreal Flight tracker moveAircraftOnButtonPress

Compile the Blueprint.

14With the BP_Aircraft actor selected, head to the Details panel and initialize the public variables DurationPlaneTrack, and AirplaneOffset. Note that with Duration, the higher the value, the longer it will take the aircraft to travel the path. For Duration, a value of 100,000 works well. Modify the AirplaneOffset variable to start the flight at another point on the path. For example, since 0.0 is the start and 1.0 is the end of the flight, set AirplaneOffset to 0.9 to begin closer to the end of the flight.

Cesium for Unreal Flight tracker planeSettings

With the viewport focused on the aircraft, click the Play button and press M (or the key that you chose to connect the Move Aircraft event to) to start the flight.

Switch between different camera views (optional)

You’re going to implement some camera switching to look at the flight from different points of view. This step is optional but it can add a nice effect to your project.

1Using the Place Actor panel, add two new Camera Actors to the scene. In the Details panel on the right, drag-and-drop the Camera Actors onto the BP_Aircraft actor so that they become its children:

Cesium for Unreal Flight tracker parentChildCamera

When the aircraft moves, the cameras will also move along with it. Adjust the cameras so that one is looking at the aircraft directly from the top and the other from a side view:

Cesium for Unreal Flight tracker aircraftTopView
Cesium for Unreal Flight tracker aircraftSideView

2Once the cameras are positioned, navigate to the Level Blueprint. Add custom keyboard events for the side and top camera views:

Cesium for Unreal Flight tracker viewThroughSideCameraBlueprint

Compile the Blueprint and head back to the main editor to view the result. In Play mode, you can press the keys you specified in the Level Blueprint to test the new camera switching feature.

Complete Source Code

PlaneTrack.h:

// Copyright 2020-2021 CesiumGS, Inc. and Contributors

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SplineComponent.h"
#include "CesiumGeoreference.h"
#include "Engine/DataTable.h"
#include <glm/vec3.hpp>
#include "CesiumGeospatial/Ellipsoid.h"
#include "CesiumGeospatial/Cartographic.h"
#include "PlaneTrack.generated.h"

USTRUCT(BlueprintType)
struct FAircraftRawData : public FTableRowBase
{
  GENERATED_USTRUCT_BODY()

public:
  FAircraftRawData()
    : Longitude(0.0)
    , Latitude(0.0)
    , Height(0.0)
  {}

  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Longitude;
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Latitude;
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Height;
};

UCLASS()
class CESIUMUNREAL_API APlaneTrack : public AActor
{
  GENERATED_BODY()
  
public:  
  // Sets default values for this actor's properties
  APlaneTrack();

  // Spline variable to represent the plane track
  UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
    USplineComponent* SplineTrack;

  // A pawn from the Cesium for Unreal API that can convert between different coordinates
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    ACesiumGeoreference* CesiumGeoreference;

  // An Unreal Engine data table to store our raw flight data
  UPROPERTY(EditAnywhere, Category = "FlightTracker")
    UDataTable* AircraftsRawDataTable;

protected:
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;

public:  
  // Called every frame
  virtual void Tick(float DeltaTime) override;

  // Function to parse the data table and create the spline track
  UFUNCTION(BlueprintCallable, Category = "FlightTracker")
    void LoadSplineTrackPoints();
};

PlaneTrack.cpp:

// Copyright 2020-2021 CesiumGS, Inc. and Contributors

#include "PlaneTrack.h"

// Sets default values
APlaneTrack::APlaneTrack()
{
   // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
  PrimaryActorTick.bCanEverTick = true;

  // Initialize the track
  SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
  // This lets us visualize the spline in Play mode
  SplineTrack->SetDrawDebug(true);
  // Set the color of the spline
  SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));
}

// Called when the game starts or when spawned
void APlaneTrack::BeginPlay()
{
  Super::BeginPlay();
}

// Called every frame
void APlaneTrack::Tick(float DeltaTime)
{
  Super::Tick(DeltaTime);
}

void APlaneTrack::LoadSplineTrackPoints()
{
  if (this->AircraftsRawDataTable != nullptr && this->CesiumGeoreference != nullptr)
  {
    int32 PointIndex = 0;
    for (auto& row : this->AircraftsRawDataTable->GetRowMap())
    {
      FAircraftRawData* Point = (FAircraftRawData*)row.Value;
      // Get row data point in lat/long/alt and transform it into UE4 points
      float PointLatitude = Point->Latitude;
      float PointLongitude = Point->Longitude;
      float PointHeight = Point->Height;

      // Compute the position in UE coordinates
      glm::dvec3 UECoords = this->CesiumGeoreference->TransformLongitudeLatitudeHeightToUnreal(glm::dvec3(PointLongitude, PointLatitude, PointHeight));
      FVector SplinePointPosition = FVector(UECoords.x, UECoords.y, UECoords.z);
      this->SplineTrack->AddSplinePointAtIndex(SplinePointPosition, PointIndex, ESplineCoordinateSpace::World, false);

      // Get the up vector at the position to orient the aircraft
      const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
      glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));
      // Compute the up vector at each point to correctly orient the plane
      glm::dvec4 ecefUp(
        upVector,
        0.0
      );
      const GeoTransforms& geoTransforms = this->CesiumGeoreference->GetGeoTransforms();
      const glm::dmat4& ecefToUnreal = geoTransforms.GetEllipsoidCenteredToAbsoluteUnrealWorldTransform();
      glm::dvec4 unrealUp = ecefToUnreal * ecefUp;
      this->SplineTrack->SetUpVectorAtSplinePoint(PointIndex, FVector(unrealUp.x, unrealUp.y, unrealUp.z), ESplineCoordinateSpace::World, false);

      PointIndex++;
    }
    this->SplineTrack->UpdateSpline();
  }
}

Next Steps

Currently, the aircraft’s velocity is constant throughout the path. If your dataset has velocity or speed variable, you can use it to adjust the velocity of the aircraft depending on its position on the spline track. Similarly, you can adjust the heading, yaw, pitch of the aircraft with real-world data.

Now that you have completed this Cesium for Unreal tutorial, please visit the Community Forum to share your feedback on Cesium for Unreal and the tutorials, or tag @CesiumJS on Twitter to show the world what you’ve built.

Content and code examples at cesium.com/learn are available under the Apache 2.0 license. You can use the code examples in your commercial or non-commercial applications.