Creating A Simple Live Flight Tracking in Python

This tutorial will take you to a journey to create a simple flight tracking with python. Knowing something live out there is always amazing, it just like having sixth sense to know something which is not directly in front of our eyes, like a position of an aircraft in the sky, but we truly believe it is somewhere over there. I was so amazed to see some flight tracking website that show hundreds of aircraft around the globe. I was thinking how they can do it? This question encouraged me to take a quite long journey till I can write this post. Days of effort and frustration have been paid, and I'd like to share the result with you.

In this tutorial we will plot aircraft position based on geographic coordinate on Open Street Map (OSM) basemap. The center of the map will be a location like an airport,  and we will request all aircraft in a range of  some km from the center point. The position will be updated every one second. At the end we will get a simple live flight tracking with python like the figure below.

Flight Tracking Python
Figure 1. Flight Tracking Python

Getting Flight Data



First of all and the most important thing in this tutorial is to get the information about the flight, where we can get it? After doing some searching and investigations, I found there are many services and mostly in API that offers flight information. Finally I chose ADS-B Exchange. ADS-B Exchange is the world largest co-op of unfiltered flight data. It provides flight data around the world that fed by worldwide community. It's free but they really appreciate a donation especially for commercial application.

Importing Libraries

In creating the flight tracker in python, we will use some libraries like urllib, json, matplotlib and cartopy. Make sure you have all libraries in your system. If not, do installation for the missing library. Then import the required libraries as the code below. Anyway for this tutorial I'm using Jupyter Notebook with Python 3.6.

%matplotlib tk
import urllib.request
import json
import matplotlib.pyplot as plt
from matplotlib import animation
import cartopy.crs as ccrs
from cartopy.io.img_tiles import OSM

Setting Up The Plot Tracking Figure

Next, we will prepare the plotting figure, set the axes into flat square projection (plate carrée), adding OSM basemap and add a center location. For this case, the John F. Kennedy International Airport in New York will be the center point with coordinate 40.639722N,73.778889W. You can change for any location as you wish.

#DEFINE FIGURE
fig, ax = plt.subplots()

#SET AXES FOR PLOTTING AREA
ax=plt.axes(projection=ccrs.PlateCarree())
ax.set_ylim(40.6051,40.6825)
ax.set_xlim(-73.8288,-73.7258)

#ADD OSM BASEMAP
osm_tiles=OSM()
ax.add_image(osm_tiles,13) #Zoom level 13

#PLOT JFK INTL AIRPORT
ax.text(-73.778889,40.639722,'JFK Intl',horizontalalignment='right',size='large')
ax.plot([-73.778889],[40.639722],'bo') #Plot a point in blue color

Before proceeding to the next step, I suggest to add plt.show() line at the end of the code above and run it. If you see a map with a blue dot as below, then everything is fine.

OSM Basemap Python Flight Tracking
Figure 2. OSM Basemap

Query Parameter to Request Flight Data

We've come to heart of this system that is creating a function to request flight data from  ADS-B Exchange API service. For that we have to send a query. Then the server will send back a response with available flight data in JSON format. Next we will parse the JSON and plot the data on the map. That's the main idea. Now let's see in more detail.

As I mentioned above, we will track all flight in range of  some km from a center point. For example 20 km from JFK International Airport. The query will be:

http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=40.639722&lng=-73.778889&fDstL=0&fDstU=20

In the url query above, can be seen some parameters like lat, lng, and fDst.
  • lat is the decimal latitude to measure distance and calculate bearings.
  • lng is the decimal longitude to measure distance and calculate bearings.
  • fDst is the distance in km. It will return a range number. Then L and U is suffix character for Lower and Upper condition
There are many query parameters available at Virtual Radar Server. Take a look the website if you are interested for more.

Sending Query

Before sending the query we need to define the request and header with build_opener method and addheaders method from urllib.request library. After sending the query, then read the response, decode and load it as JSON. At the end don't forget to close the opening request with close method. The code can be seen as follow.

# DEFINE REQUEST AND HEADER(BEFORE THE FUNCTION)
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]

#SEND QUERY (IN THE FUNCTION)
fp=opener.open('http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=40.639722&lng=-73.778889&fDstL=0&fDstU=20')
mybyte=fp.read()
mystr=mybyte.decode("utf8")
js_str=json.loads(mystr)
fp.close()

The response of the query will look like this:

{'flgH': 20, 'totalAc': 7677, 'src': 1, 'showPic': True, 'flgW': 85, 'stm': 1543157476791, 'lastDv': '636786720907884695', 'feeds': [{'id': 1, 'polarPlot': False, 'name': 'From Cons'}], 'shtTrlSec': 65, 'showSil': True, 'srcFeed': 1, 'showFlg': True, 'acList': [{'TSecs': 25, 'Trt': 5, 'Gnd': False, 'SpdTyp': 0, 'Op': 'JetBlue Airways', 'Man': 'Airbus', 'CMsgs': 9, 'Long': -73.817566, 'Type': 'A320', 'Sig': 200, 'Trak': 276.1, 'Reg': 'N588JB', 'InHg': 29.8372135, 'FlightsCount': 0, 'Species': 1, 'Vsi': 2688, 'HasPic': False, 'Mlat': False, 'Interested': False, 'OpIcao': 'JBU', 'TAlt': 4992, 'FSeen': '/Date(1543157451601)/', 'PosTime': 1543157475146, 'Mdl': '2004 AIRBUS A320-232', 'HasSig': True, 'WTC': 2, 'Icao': 'A7946A', 'AltT': 0, 'EngMount': 0, 'Cou': 'United States', 'Sqk': '1174', 'TrkH': False, 'Mil': False, 'GAlt': 1067, 'Alt': 1150, 'Tisb': False, 'Dst': 3.31, 'Engines': '2', 'CallSus': False, 'Year': '2004', 'Bad': False, 'EngType': 3, 'Lat': 40.644928, 'VsiT': 1, 'Id': 10982506, 'CNum': '2201', 'TTrk': 241.875, 'Spd': 170.0, 'Help': False, 'Brng': 280.1, 'Rcvr': 1}]}

Parsing the JSON Response

We already get the response and the next step is to parse it to take some required data. Coordinate in latitude and longitude are the most important thing, because we need them to plot aircraft's position on the OSM map. The coordinate can be extracted from the JSON with Lat and Long key. More keys are available in the JSON response as you can see above, which contains some information about the flight such as Op for aircraft's operator, Spd: the ground speed in knots, Mdl: Aircraft's model, Man: Aircraft's manufacture, etc. The complete description can be found at ADS-B Exchange Datafields.
   
To extract the latitude and longitude of each aircraft can be done through a looping in the response JSON list. But firstly an empty latitude and longitude list must be created, which will be used to dump each latitude and longitude. Later the aircraft's position will be plotted on the OSM using the latitude and longitude list.

Update Aircraft Position


To update the aircraft's position the request will be sent every one second. So in every second we will get new response. Each response will be parsed and the position will be updated. To make it happen can be done with the animation class from matplotlib.

This is the complete code up to this step.

%matplotlib tk
import urllib.request
import json
import matplotlib.pyplot as plt
from matplotlib import animation
import cartopy.crs as ccrs
from cartopy.io.img_tiles import OSM

#SET AXES
fig, ax = plt.subplots()
ax=plt.axes(projection=ccrs.PlateCarree())
ax.set_ylim(40.6051,40.6825)
ax.set_xlim(-73.8288,-73.7258)

#ADD OSM BASEMAP
osm_tiles=OSM()
ax.add_image(osm_tiles,13) #Zoom Level 13

#PLOT JFK INTL AIRPORT
ax.text(-73.778889,40.639722,'JFK Intl',horizontalalignment='right',size='large')
ax.plot([-73.778889],[40.639722],'bo')

#PLOT TRACK
track, = ax.plot([], [],'ro')

opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]

#UPDATE FUNCTION
def update(self):
    #SEND QUERY
    fp=opener.open('http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=40.639722&lng=-73.778889&fDstL=0&fDstU=20')
    mybyte=fp.read()
    mystr=mybyte.decode("utf8")
    js_str=json.loads(mystr)
    fp.close()
    lat_list=[]
    long_list=[]
    
    for num,flight_data in enumerate(js_str['acList']):
        lat=flight_data['Lat']
        lon=flight_data['Long']
        lat_list.append(lat)
        long_list.append(lon)
    track.set_data(long_list,lat_list)
    return track,
                         
#UPDATING EVERY SECOND
anim = animation.FuncAnimation(fig, update,interval=1000, blit=False)

plt.show()

If the code is running, you should get a live tracking flight in red dots as in animated picture below.

Live Flight Tracking Python
Live Flight Tracking Python

Labeling Aircraft

Just seeing dots without knowing what it is exactly is not so meaningful. Therefore in this section we will give a label to those red dots. For example we want to give aircraft's operator label. To get operator data we can use "Op" key. To do that we need initiate a list that will be used to to store operator data. In each response the operator data will be extracted and store to that list. Because of that, the labeling code will be included in the update function. Here it is the complete code.

%matplotlib tk
import urllib.request
import json
import matplotlib.pyplot as plt
from matplotlib import animation
import cartopy.crs as ccrs
from cartopy.io.img_tiles import OSM

#SET AXES
fig, ax = plt.subplots()
ax=plt.axes(projection=ccrs.PlateCarree())
ax.set_ylim(40.6051,40.6825)
ax.set_xlim(-73.8288,-73.7258)

#ADD OSM BASEMAP
osm_tiles=OSM()
ax.add_image(osm_tiles,13) #Zoom Level 13

#PLOT JFK INTL AIRPORT
ax.text(-73.778889,40.639722,'JFK Intl',horizontalalignment='right',size='large')
ax.plot([-73.778889],[40.639722],'bo')

#PLOT TRACK
track, = ax.plot([], [],'ro')

opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]

#UPDATE FUNCTION
def update(self):
    #SEND QUERY
    fp=opener.open('http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=40.639722&lng=-73.778889&fDstL=0&fDstU=20')
    mybyte=fp.read()
    mystr=mybyte.decode("utf8")
    js_str=json.loads(mystr)
    fp.close()
    lat_list=[]
    long_list=[]
    op_list=[] #OPERATOR LIST
    
    for num,flight_data in enumerate(js_str['acList']):
        lat=flight_data['Lat']
        lon=flight_data['Long']
        lat_list.append(lat)
        long_list.append(lon)
        op_list.append(flight_data['Op']) #STORE OPERATOR DATA INTO LIST
        
    track.set_data(long_list,lat_list)
    
    # LABELING
    
    #REMOVE LABEL
    for num, annot in enumerate(anotation_list):
        annot.remove()
    anotation_list[:]=[]
    
    #CREATE LABEL CONTAINER
    for num,annot in enumerate(js_str['acList']):
        annotation=ax.annotate('text',xy=(0,0),size='smaller')
        anotation_list.append(annotation)
    
    # UPDATE LABEL POSITION AND OPERATOR
    for num,ano in enumerate(anotation_list):
        ano.set_position((long_list[num],lat_list[num]))
        ano.xy = (long_list[num],lat_list[num])
        txt_op=str(op_list[num])
        ano.set_text(txt_op)
        
    return track,ano,
                         
#UPDATING EVERY SECOND
anim = animation.FuncAnimation(fig, update,interval=1000, blit=False)

plt.show()

That's all the tutorial how to create a simple flight tracking with Python. Before ending this tutorial, I want to give a final note. Sometimes when the code is executed, you will get a key error. If this happen, just stay calm, close the map/figure and run it again.       

Related Posts

Disqus Comments