Introduction
Displaying and tracking moving aircrafts on a live map is a key feature in applications like air traffic monitoring, radar simulation, and situational awareness systems.
With the ArcGIS Maps SDK for Qt, developers can create high-performance map displays that render dynamic objects—like aircrafts—moving in real time.
In this tutorial, we’ll show how to display multiple aircraft symbols on a 2D map and continuously update their position and heading using Qt C++.
Use Cases
This method is useful for:
- 🛫 Air Traffic Control simulations
- 🎯 Radar track visualization
- 🛰️ Flight path and mission monitoring tools
- 🧭 Defense or aviation system dashboards
Core Concepts
- GraphicsOverlay: Layer that holds dynamic graphic elements (e.g., aircraft icons).
- Graphic: Represents each aircraft with position and attributes.
- PictureMarkerSymbol: Custom image icon used for aircraft symbol.
- Real-time updates: Simulated using a timer to move each aircraft periodically.
Working Code Example
Header: mainwindow.h
#pragma once
#include <QMainWindow>
#include <QTimer>
#include <Map.h>
#include <MapGraphicsView.h>
#include <GraphicsOverlay.h>
#include <Graphic.h>
#include <PictureMarkerSymbol.h>
#include <Point.h>
#include <SpatialReference.h>
namespace Ui { class MainWindow; }
struct TRACK_DATA {
int ID;
double Lat;
double Lon;
double Alt;
double Heading;
double Speed;
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void updateTracks();
private:
void setupMap();
void updateTrackGraphic(const TRACK_DATA &td);
Ui::MainWindow *ui;
Esri::ArcGISRuntime::Map *m_map = nullptr;
Esri::ArcGISRuntime::MapGraphicsView *m_mapView = nullptr;
Esri::ArcGISRuntime::GraphicsOverlay *tracksLayer = nullptr;
Esri::ArcGISRuntime::PictureMarkerSymbol *m_trackSymbol = nullptr;
QTimer m_updateTimer;
QMap<int, Esri::ArcGISRuntime::Graphic*> m_trackGraphics;
QVector<TRACK_DATA> m_simTracks;
};
Source: mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <SimpleRenderer.h>
#include <QDir>
#include <QRandomGenerator>
using namespace Esri::ArcGISRuntime;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
setupMap();
// Setup timer for simulation updates
connect(&m_updateTimer, &QTimer::timeout, this, &MainWindow::updateTracks);
m_updateTimer.start(1000); // update every second
}
MainWindow::~MainWindow() { delete ui; }
void MainWindow::setupMap()
{
m_map = new Map(BasemapStyle::OsmStandard, this);
m_mapView = new MapGraphicsView(this);
m_mapView->setMap(m_map);
ui->horizontalLayout->addWidget(m_mapView);
tracksLayer = new GraphicsOverlay(this);
m_mapView->graphicsOverlays()->append(tracksLayer);
// Load aircraft symbol
QString baseDir = QCoreApplication::applicationDirPath();
QString iconPath = QDir(baseDir).filePath("../icons/aircraft.png");
m_trackSymbol = new PictureMarkerSymbol(QUrl::fromLocalFile(iconPath), this);
m_trackSymbol->setWidth(30);
m_trackSymbol->setHeight(30);
m_trackSymbol->setAngle(0);
// Initialize simulated aircrafts
for (int i = 0; i < 5; ++i)
{
TRACK_DATA td;
td.ID = i;
td.Lat = 20.0 + QRandomGenerator::global()->bounded(10.0);
td.Lon = 70.0 + QRandomGenerator::global()->bounded(10.0);
td.Alt = 10000.0;
td.Heading = QRandomGenerator::global()->bounded(360.0);
td.Speed = 200.0 + QRandomGenerator::global()->bounded(100.0);
m_simTracks.append(td);
updateTrackGraphic(td);
}
}
void MainWindow::updateTracks()
{
for (auto &td : m_simTracks)
{
// Simulate movement by changing lat/lon slightly
td.Heading += 5.0;
if (td.Heading > 360.0) td.Heading -= 360.0;
td.Lat += 0.05 * qCos(qDegreesToRadians(td.Heading));
td.Lon += 0.05 * qSin(qDegreesToRadians(td.Heading));
updateTrackGraphic(td);
}
}
void MainWindow::updateTrackGraphic(const TRACK_DATA &td)
{
Graphic *trackGraphic = m_trackGraphics.value(td.ID, nullptr);
PictureMarkerSymbol *uniqueTrackSymbol = nullptr;
if (!trackGraphic) {
trackGraphic = new Graphic(this);
uniqueTrackSymbol = static_cast<PictureMarkerSymbol*>(m_trackSymbol->clone(this));
trackGraphic->setSymbol(uniqueTrackSymbol);
tracksLayer->graphics()->append(trackGraphic);
m_trackGraphics.insert(td.ID, trackGraphic);
} else {
uniqueTrackSymbol = static_cast<PictureMarkerSymbol*>(trackGraphic->symbol());
}
// Update geometry
Point position(td.Lon, td.Lat, SpatialReference::wgs84());
trackGraphic->setGeometry(position);
// Update symbol rotation based on heading
if (uniqueTrackSymbol)
uniqueTrackSymbol->setAngle(td.Heading);
}
Explanation
- Simulation Setup
- We initialize 5 aircraft tracks with random starting positions.
- Each track updates every second using a timer.
- Symbol Creation
- A
PictureMarkerSymbol(aircraft icon) is used for each track. - Each track uses its own symbol instance so that heading changes are independent.
- A
- Position Update
- Latitude and longitude values are slightly changed on each update to simulate motion.
- The
setGeometry()andsetAngle()functions move and rotate the symbol accordingly.
Example Output
- Aircraft icons appear on the map.
- Every second, they move forward and rotate according to their heading.
- Perfect for radar or flight monitoring simulations.
Conclusion
Using the ArcGIS Maps SDK for Qt, you can easily render and animate moving aircrafts in real time.
By combining GraphicsOverlays, PictureMarkerSymbols, and timed updates, Qt developers can build interactive 2D or 3D visualizations for aerospace, defense, or transport domains.
This approach forms the foundation for advanced systems — including live ADS-B track visualization, mission playback, or real-time radar simulation.
If you’re interested in a full-fledged GIS solution, check out our flagship product PrithviGIS – a powerful QGIS-based platform for real-time geospatial visualization. PrithviGIS is an indigenous GIS development in India.
Check out our Live Aircraft Simulation using CesiumJS.

