Plane spotting with Node-RED

Just been working on a "fun" project for my IoT students.

It makes use of the 'flightradar24' and 'worldmap' nodes to display aircraft movements.
It also uses an iFrame with Start and Stop actions in a simple dashboard.

Here's a link to the tutorial guide and 'json' flow.

Tutorial Guide
Node-RED flow


Nice !
PS - the latest version of Worldmap (2.1.2) includes a Dashboard widget to make life easier...


Thanks Dave @dceejay I'll update the flow when I have a spare moment !!!

Running on macOS
Node-RED version: v0.20.8
Node.js version: v10.15.3
Worldmap version 2.1.0
Dashboard version 2.16.3
node-red-contrib-flightradar24 v0.0.6

I'm getting an error when trying to run this
22 Sep 10:25:04 - [error] [flightradar24:5db8cd0e.6b6c0c] TypeError: geolib.isPointInCircle is not a function
Digging in I see that there was a similar issue with the geofence node ( because a new version of geolib (v3.0.x) changes the name of the isPointInCircle() function to isPointInRadius() NOTE: that name is wrong the new function is isPointWithinRadius().

Changing the code and it works.

I've opened an issue about this

Thanks Paul for the heads-up on that.
It's great you have a Mac to try out the flow and detect any incompatibilites.

@dceejay I just upgraded to Node-RED version: v1.0.0-beta.4 with no other changes. The planes do not appear on the map. Data is coming from the flightradar24 node:

Which version of worldmap node ?

Hi Paul,
The default setting is the flow is turned-off.
You need to click the FreeRun button on the dashboard to get things started.

Oh - forget this as my answer doesn't match your question - sorry.

Worldmap version 2.1.0 Wait, now i'm seeing one plane where with NR v0.20.8 I had a lot.
This is with NR v1.0.0-beta.4

and this is with NR v0.20.8:

yeah - sorry - bug in that - upgrade to 2.1.2

Dave - here is my version with that new ui widget node

[{"id":"c6ff8646.d9daa8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"80c7036c.15267","type":"ui_group","z":"","name":"Display area","tab":"34110ee3.2d9172","order":2,"disp":false,"width":"21","collapse":false},{"id":"c5e6fe6e.4dbb6","type":"ui_group","z":"","name":"Controls","tab":"34110ee3.2d9172","order":1,"disp":false,"width":9,"collapse":false},{"id":"34110ee3.2d9172","type":"ui_tab","z":"c6ff8646.d9daa8","name":"Flight Radar","icon":"language","order":1,"disabled":false,"hidden":false},{"id":"872838f5.5a8ba8","type":"ui_base","theme":{"name":"theme-light","lightTheme":{"default":"#0094CE","baseColor":"#0094CE","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":true,"reset":false},"darkTheme":{"default":"#097479","baseColor":"#097479","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":false},"customTheme":{"name":"Untitled Theme 1","default":"#4B7930","baseColor":"#4B7930","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"},"themeState":{"base-color":{"default":"#0094CE","value":"#0094CE","edited":false},"page-titlebar-backgroundColor":{"value":"#0094CE","edited":false},"page-backgroundColor":{"value":"#fafafa","edited":false},"page-sidebar-backgroundColor":{"value":"#ffffff","edited":false},"group-textColor":{"value":"#1bbfff","edited":false},"group-borderColor":{"value":"#ffffff","edited":false},"group-backgroundColor":{"value":"#ffffff","edited":false},"widget-textColor":{"value":"#111111","edited":false},"widget-backgroundColor":{"value":"#0094ce","edited":false},"widget-borderColor":{"value":"#ffffff","edited":false},"base-font":{"value":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"}},"angularTheme":{"primary":"indigo","accents":"blue","warn":"red","background":"grey"}},"site":{"name":"Node-RED Dashboard","hideToolbar":"false","allowSwipe":"false","lockMenu":"false","allowTempTheme":"true","dateFormat":"DD/MM/YYYY","sizes":{"sx":48,"sy":48,"gx":6,"gy":6,"cx":6,"cy":6,"px":0,"py":0}}},{"id":"4de77778.d41688","type":"inject","z":"c6ff8646.d9daa8","name":"","topic":"","payload":"","payloadType":"date","repeat":"2","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":100,"wires":[["e8288ec.ce6697"]]},{"id":"d66fd95f.5cc878","type":"debug","z":"c6ff8646.d9daa8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":670,"y":260,"wires":[]},{"id":"c5ac497c.3f9b18","type":"flightradar24","z":"c6ff8646.d9daa8","lat":"51.74","latType":"num","lon":"-0.4543","lonType":"num","name":"","x":490,"y":200,"wires":[["d66fd95f.5cc878","90cff72c.79cee8"]]},{"id":"b1d7d052.6792b","type":"ui_button","z":"c6ff8646.d9daa8","name":"Free Run","group":"c5e6fe6e.4dbb6","order":1,"width":3,"height":1,"passthru":false,"label":"Free Run","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":100,"y":160,"wires":[["39fd56ff.49c8fa"]]},{"id":"b2cf98f4.a377c8","type":"ui_button","z":"c6ff8646.d9daa8","name":"STOP","group":"c5e6fe6e.4dbb6","order":2,"width":3,"height":1,"passthru":false,"label":"STOP","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":90,"y":220,"wires":[["42919721.e9e468"]]},{"id":"ae626d5b.45e64","type":"ui_button","z":"c6ff8646.d9daa8","name":"Manual Update","group":"c5e6fe6e.4dbb6","order":3,"width":3,"height":1,"passthru":false,"label":"Manual Update","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":120,"y":300,"wires":[["c5ac497c.3f9b18"]]},{"id":"39fd56ff.49c8fa","type":"function","z":"c6ff8646.d9daa8","name":"B","func":"flow.set(\"status\",\"run\");\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":160,"wires":[[]]},{"id":"42919721.e9e468","type":"function","z":"c6ff8646.d9daa8","name":"C","func":"flow.set(\"status\",\"stopped\");\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":220,"wires":[[]]},{"id":"e8288ec.ce6697","type":"function","z":"c6ff8646.d9daa8","name":"A","func":"var status = flow.get(\"status\")||\"stopped\";\n\nif (status==\"run\") {\n   msg.payload = 1;\n   return msg;\n}","outputs":1,"noerr":0,"x":310,"y":100,"wires":[["c5ac497c.3f9b18"]]},{"id":"90cff72c.79cee8","type":"ui_worldmap","z":"c6ff8646.d9daa8","group":"80c7036c.15267","order":1,"width":21,"height":12,"name":"","lat":"51.37","lon":"-0.87","zoom":"10","layer":"","cluster":"","maxage":"","usermenu":"hide","layers":"hide","panit":"false","panlock":"false","zoomlock":"false","hiderightclick":"true","coords":"none","showgrid":"false","path":"/worldmap","x":660,"y":200,"wires":[]}]

That solved it, v2.1.2 and your flow in NR v1.0.0-beta.4 works like a treet.

Funny thing as I was testing it I suddenly heard the screaming of some jets and ran out to see two jets scream by at low level. They were doing a fly over by the football stadium that is about 1 1/5 by way the crow flys. Never did see them on the worldmap :grinning:

If they were military - they don't often use ADS-B unless they are "interacting" with commercial airspace... - so sometimes you see them - sometimes you don't.


Hi Dave - your new ui widget node certainly makes the flow a lot simpler.

Hiss, boo - I'm going to have to edit the tutorial and cut out a few pages.

Cheers from David.

@dceejay Dave node-red-contrib-flightradar24 has been updated to correct the issue I ran into and using the Worldmap node it works fine, BUT using the flow you provided above, the ui_worldmap does not show up :grimacing:

I also notice in the description of the worldmap node it has a link to the map but it doesn't work. It takes you to the first tab of the editor.

And, I just noticed that the ui_worldmap discription DOES take you to your-ip:1880/worldmap

Ok, now I'm confused because it is working in the ui. I wonder if I didn't clear out my browser cache...

Here is an little update you can add to the flow. If you stop the flow and click on a plane, you can see (most of the time) the destination airport' abbreviation like BOS for Boston's Logan airport of JFK for New York's John F. Kennedy airport.

If you add a switch node after the flightradar24 node and check msg. payload.destination, you can tehn send a match to a change node and set msg.payload.iconColor to different colors for different airports.

This way, when you look at the map you will know which planes are going to the airports you've chosen to color code. Here is an example, blue planes are heading for Boston, orange ones are heading for JFK and the red ones are going somewhere else.

Nice, the other one I have done in the past is to use lineColor to set the leader line colour based on rate of climb or descent.

Thanks Paul @zenofmud for that great suggestion.
I just used a simple function node as follows.

if (msg.payload.destination == "LHR") {
    msg.payload.iconColor = "blue";
else if (msg.payload.destination == "LGW"){
    msg.payload.iconColor = "orange";
else if (msg.payload.destination == "LCY"){
    msg.payload.iconColor = "black";
return msg;

I'll get one of my students to implement it this afternoon.

I've also changed the base map to OSM grey so the colours look more impressive.

You have given me some other ideas.... I could have a couple of sliders on the Dashboard that define 'min height' and 'max_height' and filter out the aircraft that are below 'min_height'
i.e. the ones that are possibly coming in the land.
And the ones above 'max_height' as they are probably overflying.

I could also have some buttons on the Dashboard to select the 'destination' airport, and again filter out the aircraft so you get an uncluttered display.

also certain squawk codes are used for emergencies etc... (don't happen often luckily) - but also other codes can be useful.

This is turning out to be a nice project for my IoT students - thanks to Paul and Dave.
I bet they'll be excited by it.