Workflows example: Flying Airplanes

Get transponder live information from flying airplanes!


TL; DR;


In this post we’ll create a Mapify Workflow to obtain live information from an airplane transponder, check a field to determine if it is currently flying, we’ll intersect its location with a layer of the world countries to determine which country it is flying over, get the nearest features from a layer with the main world airports, and finally we’ll save the information in a dataset and display it on a map in real-time. And we’ll do it all without having to write code 🙂


If you want to dive right in the deep end, feel free to request a Mapify trial here, or click here to schedule a talk with us.



One of the most useful features of Mapify for real-time and IoT solutions are Workflows. A Mapify Workflow handles real-time messages from Data Feeds and processes those messages through a flow of activity nodes of different types, which can include spatial operations with existing Layers.


Creating a Workflow is as simple as selecting a source Data Feed and dragging and dropping activity nodes in the workflow graph placeholders.



The Flying Airplanes Example


In this post we’ll create a Mapify Workflow to demonstrate how to use several of the available nodes to obtain information regarding a currently flying airplane, through its transponder’s ICAO 24 bit address, including displaying its location on a map.


Requirements


You will be needing an active Mapify account (you can request a trial here), and an MQTT client application, such as MQTT Explorer or some other similar tool.


Let’s begin with a Data Feed!


The first step is to create the Data Feed named “airplane-feed”, which will receive the messages with the ICAO 24 bit address of the airplane we wish to locate.

Messages sent to Mapify will be a simple JSON object with an icao24 property, such as:


{
   "icao24": "12345"
}

Therefore, in order to avoid processing messages which do not comply with that format, you need to set your Data Feed message JSON schema as:



After you save your new Data Feed, you can check the MQTT connection parameters for that Data Feed below the JSON schema input box (you will need them later in this example).


Datasets — Where your data lives in Mapify


A Dataset is a structured data collection inside Mapify. It is where all your data is kept when hosted on Mapify.


You will need a Dataset to hold the airplane locations and related information, so go ahead and create an “Airplane Location” dataset from scratch, by clicking on the (+) button on the top right hand corner of the datasets list screen, selecting the “New Dataset” option. Select point as the geom field type (this means that this Dataset will hold Point features) before adding more fields.



Add a few more fields to the Dataset as illustrated below and make sure the plane_id field’s primary key checkbox is checked:



Next, let’s add two more Datasets, this time by importing:

  1. Shapefile with World Countries data

  2. GeoJSON with World Airports data

You can get World Countries polygon shapefile from the World Bank data catalog here (you’ll need to extract the files to a local folder in your machine, and compress them again without including the encompassing folder).


You can get World Airports points GeoJSON from the Humanitarian Data Exchange here.


In the Datasets list screen click again on the (+) button on the top right hand corner of the datasets list to add a new Dataset. Select the “Import File” option, make sure the correct source type is selected, and select respective file from your machine (for more information regarding uploading a shapefile or GeoJSON to Mapify, check the official documentation).



You should now have three Datasets in your datasets list screen:



Layers — Your data must be seen to make an impact


The next step is to create Layers based on the three Datasets you already created. In the Layers list screen click on the (+) button on the top right hand corner of the layers list to add a new Layer. Create the three layers as:


Airplane Location:

  • Layer Name: Airplane Location

  • Layer Type: Realtime

  • Layer Dataset: Airplane location

World — Countries:

  • Layer Name: World - Countries

  • Layer Type: GeoJSON

  • Layer Dataset: World - Countries

World — Airports:

  • Layer Name: World - Airports

  • Layer Type: GeoJSON

  • Layer Dataset: World - Airports


After all layers are created, be sure to publish the World Countries and World Airports layers by clicking on the “publish” button in each layer’s details screen. Wait until both layers have been published, and you should see something like this on your layers list screen:



You can now preview your layers (you won’t have data yet on the Airplanes Location layer, but you’ll be able to see the world countries and airports).



Workflows — This is where the magic happens!


Now the fun begins, as we will create a workflow that will handle incoming messages from the Data Feed we created earlier, process them using the Countries and Airports layers, and finally persist the information in the Airplane Location Dataset.


Let’s start by creating a new Workflow, and naming it “Airplane Flight Management”, as illustrated below:



The first step is to configure the Source of the Workflow, selecting the previously created “airplane-feed” Data Feed.



Next, we will need to get information regarding an airplane. The OpenSky Network is a non-profit association based in Switzerland. It aims at improving the security, reliability and efficiency of the air space usage by providing open access of real-world air traffic control data to the public.


We will be making an HTTP request to the OpenSky Network REST API using the airplane’s transponder code to obtain the latest data regarding its flight.


Drag an HTTP Response node from the Transformer nodes group to the free node under the Source node.



Name it “Get Plane Info” and configure it as a GET request to the following address:


https://opensky-network.org/api/states/all?icao24=${icao24}

Did you notice the ${icao24} parameter at the end? This is how we tell Mapify to get the value of the icao24 property from the node input message. For more details regarding Mapify message parameters, check the official documentation.



For information regarding the data returned from the OpenSky Network REST API call, check the documentation here.


The first thing we’ll do is to check the response for the value of the on_ground attribute, which tells if the airplane is currently flying or on the ground.


So, simply drag a Custom node from the Condition nodes list to the empty slot below the “Get Plane Info” node. This node allows you to define a custom JMESPath expression to be evaluated as the workflow branching condition.


Name this node “Is Flying” and in its JMESPath Expression field, type:


get_plane_info_result.states[0][8] == `false`

This will compare the value of the on_ground attribute to the ‘false’ string value and, if the expression evaluates to true, the plane is flying, otherwise is it on the ground.


So let’s have Mapify notify us by email regarding the airplane status. Drag an Email node from the Sink nodes list to the empty slot on the NO branch and another Email node to the empty slot on the YES branch.


Name the one on the NO branch as “On the Ground Email”, add your email address to the target email address list, and for the Subject type:


Airplane ${icao24} is on the ground

Copy the text below into the Body field:


<h1>Flight ${icao24}</h1>
<h2>Currently on the ground</h2>
<br>
<br>
<b>ICAO 24:</b>  ${get_plane_info_result.states[0][0]}<br>
<b>Call Sign:</b>  ${get_plane_info_result.states[0][1]}<br>
<b>Origin Country:</b>  ${get_plane_info_result.states[0][2]}<br>
<b>Position Time:</b>  ${get_plane_info_result.states[0][3]}<br>
<b>Last Contact:</b>  ${get_plane_info_result.states[0][4]}<br>
<b>Longitude:</b>  ${get_plane_info_result.states[0][5]}<br>
<b>Latitude:</b>  ${get_plane_info_result.states[0][6]}<br>
<b>Altitude(Baro):</b> ${get_plane_info_result.states[0][7]}<br>
<b>On The Ground:</b>  ${get_plane_info_result.states[0][8]}<br>
<b>Speed (m/s):</b>  ${get_plane_info_result.states[0][9]}<br>
<b>Heading (True):</b>  ${get_plane_info_result.states[0][10]}<br>
<b>Vertical Rate:</b>  ${get_plane_info_result.states[0][11]}<br>
<b>Sensors:</b>  ${get_plane_info_result.states[0][12]}<br>
<b>Altitude(Geo):</b> ${get_plane_info_result.states[0][13]}<br>
<b>Squawk:</b>   ${get_plane_info_result.states[0][14]}<br>
<b>SPI:</b>   ${get_plane_info_result.states[0][15]}<br>
<b>Position Source:</b> ${get_plane_info_result.states[0][16]} (0 = ADS-B, 1 = ASTERIX, 2 = MLAT)<br>

See how workflow message parameters are used here again to fill information in the email ? This time we’re getting fields from the result of a previous node, using ${get_plane_info_result} to access the “Get Plane Info” output message object.



Now make sure the Email node on the YES branch is named “In The Air Email”, also configured with your email address and the same Body as above, but make the subject:


Airplane ${icao24} is flying!

Your Workflow should look like this by now:



Next, let’s check which country the airplane is flying over. The response from the OpenSky Network includes coordinates for the plane location, and we have a World Countries layer… so let’s intersect the plane location with the World Countries layer!


Drag a Spatial intersection Mapify Layer node from the Transformer nodes list and place it below the “In The Air Email” node.


From the list of available Mapify layers to intersect, select “World — Countries”, type “MultiPoint” in the GeometryType field, and in the Coordinates field, type the expression:


[[get_plane_info_result.states[0][5], [get_plane_info_result.states[0][6]]]

It should end up something like this:



Let’s have the information sent to us by email. Drag an Email node from the Sink nodes list to the empty slot below the node we just created.


Name it “Country Email”, add your email address, and for the Subject type:


[Spatial Intersection] Flight ${get_plane_info_result.states[0][1]} (${icao24}) is over ${which_country_result[0].country}

Copy the text below into the Body field:


Flight <b>${get_plane_info_result.states[0][1]}</b> (${icao24}) is flying over <a href="https://en.wikipedia.org/wiki/${which_country_result[0].country}">${which_country_result[0].country}</a><br>

We’re almost there ! But we still need to figure out what are the nearest five airports. Fortunately we have a World Airports layer in our Mapify account 🙂Drag a Nearest Features Mapify Layer node from the Transformer nodes list and place it below the “Country Email” node.


From the list of available Mapify layers to find the nearest features from, select “World —Airports”, type 5 as the limit of features to be returned, type “MultiPoint” in the GeometryType field, and in the Coordinates field, type the expression:


[[get_plane_info_result.states[0][5], [get_plane_info_result.states[0][6]]]


Let’s have Mapify email us those airports too!


Drag an Email node from the Sink nodes list to the empty slot below the node we just created. Name it “Email Nearest Airports”, add your email address, and for the Subject type:


[Nearest Features] Flight ${get_plane_info_result.states[0][1]} (${icao24}) Nearest Airports

Copy the text below into the Body field:


Flight <b>${get_plane_info_result.states[0][1]}</b> (${icao24}) nearest airports:<br><br>
${nearest_airports_result[0].distance} meters: <a href="${nearest_airports_result[0].properties.wikipedia}">${nearest_airports_result[0].properties.name}</a><br>
${nearest_airports_result[1].distance} meters: <a href="${nearest_airports_result[1].properties.wikipedia}">${nearest_airports_result[1].properties.name}</a><br>
${nearest_airports_result[2].distance} meters: <a href="${nearest_airports_result[2].properties.wikipedia}">${nearest_airports_result[2].properties.name}</a><br>
${nearest_airports_result[3].distance} meters: <a href="${nearest_airports_result[3].properties.wikipedia}">${nearest_airports_result[3].properties.name}</a><br>
${nearest_airports_result[4].distance} meters: <a href="${nearest_airports_result[4].properties.wikipedia}">${nearest_airports_result[4].properties.name}</a><br>


All we have to do now is to save the data into our Airplane Location Dataset!


Drag a Dataset node from the Sink nodes list to the empty slot below the “Email Nearest Airports” node we just created, and name is “Save To Dataset”. From the Dataset dropdown list box, select the “Airplane Locations” dataset. Select “Update” for the “On Duplicate ID” option, so that when a new message regarding an already existing airplane in the dataset will update the record’s values.


For the Coordinates field, make sure the update is active (if it’s pink it means it is enabled, otherwise it will be white which means it’s disabled) and type:


[[get_plane_info_result.states[0][5], [get_plane_info_result.states[0][6]]]

For the Data fields, insert the values and enabled updating as illustrated below:



Your full workflow graph should now look like this:


Wohoo !!! We’re ready to test it out !


Let’s get a flying airplane transponder code to send to our workflow. Navigate to the OpenSky Network Explorer and click on one airplane that is flying over land (our countries layer does not include the ocean or sea boundaries of countries).


From the airplane details panel, copy the value for its transponder identifier, from the Mode S Code (hex) field:



Open MQTT Explorer (or any other MQTT client), and enter the MQTT connection authentication parameters from the Data Feed your created earlier. You will also see the MQTT Topic name in those settings, to where you will send the message with the selected airplane transponder code:


{
"icao24": "4841c3"
}

NOTE: Be sure to replace the value 4841c3 above with the value of the airplane transponder identifier that you obtained from the OpenSky Network Explorer.



You should get three emails with the information obtained from the different nodes, and if you open the preview of the “Airplane Locations” layer, you will see the location where your plane is located.