I stumbled onto this great site only recently; below are some data visualisations I ended up producing. I found some of the patterns really beautiful (especially when isolating the inbound / passing/ outbound flights).

12 Hours of EU flights:

12 Hours of EU Flights

24 Hours of London (-ish) flights (inbound = blue, outbound = red, passing = green):

Flights above London - Fri 13th Oc

This was the first time I have seen the holding stacks – they are all for Heathrow: http://www.heathrow.com/noise/heathrow-operations/arrival-flight-paths

24 Hours of Turkey flights:

Flights above Turkey - Sun 15th Oc

24 Hours of Syria flights (War):

Flights above Syria - Mon 16th Oc

Here are the London flights again but broken down into in, out, passing:

UK Flights (in, out, passing)

The same for Turkey (but also including within flights):

Turkey Flights (in, out, passing)

The code for all of these was fairly short and straight-forward (mainly because the data was provided as a neat JSON):

Python Script:

import time
import requests
import json
import csv
TRY_ATTEMPT_MAX = 10


def execute_scrape(counter):
    try_count = 0
    while try_count < TRY_ATTEMPT_MAX:
        try:
            resp = requests.get(
                'https://data.flightradar24.com/zones/fcgi/feed.js?bounds=61.023257,29.791698,'
                '-13.819503,48.474506&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&'
                'estimated=1&maxage=900&gliders=1&stats=1&')
            my_json = json.loads(resp.text)
            break
        except Exception as err:
            time.sleep(5)
            print('Waiting 5 seconds ... retry: %s' % err)
            try_count += 1

    if try_count == TRY_ATTEMPT_MAX:
        raise Exception('Failed to calculate distances - possibly API LIMIT REACHED')

    for key in my_json.keys():
        if key != "full_count" and key != "version" and key != "stats":
            print([key] + [counter] + my_json[key])
            w.writerow([key] + [counter] + my_json[key])
    time.sleep(1)


if __name__ == '__main__':
    f = open(r'C:\planes_24hrs_wide_london.csv', 'w', newline='')
    w = csv.writer(f)
    # 24 hours
    start_time = time.time()
    end_time = 60 * 60 * 24
    counter = 0
    while (time.time() - start_time) <= end_time:
        counter += 1
        execute_scrape(counter)
    f.close()

Stata Script:

global path ".../Flights_London"
import delimited "C:/planes_24hrs_wide_london.csv", clear

rename v1 id
rename v2 counter
rename v4 lat
rename v5 lon
rename v14 from
rename v15 to

// Format co-ordinates
format lat %12.4f
format lon %12.4f

sort id counter

// Drop if no movement
gen drop_fl = id==id[_n+1] & lat==lat[_n+1] & lon==lon[_n+1]
drop if drop_fl
drop drop_fl

// Keep if group has more than 5 point (need 2 to draw a line)
bysort id: gen group_nr = _N
sum group_nr
tab group_nr
keep if group_nr > 5
drop group_nr

// Tag flights
replace from = trim(upper(from))
replace to = trim(upper(to))
gen from_london = 	inlist(from,"LCY","LGW","LHR","FAB") | 	inlist(from,"LTN","STN","SEN","BQH")
gen to_london = 	inlist(to,"LCY","LGW","LHR","FAB") | 	inlist(to,"LTN","STN","SEN","BQH")

gen status = ""
replace status = "inbound" if from_london == 0 & to_london == 1
replace status = "outbound" if from_london == 1 & to_london == 0
replace status = "passing" if from_london == 0 & to_london == 0
replace status = "within" if from_london == 1 & to_london == 1
replace status = "unknown" if from == "" & to == ""

// Make constant within group
gen status_no = .
replace status_no = 1 if status == "inbound"
replace status_no = 2 if status == "outbound"
replace status_no = 3 if status == "passing"
replace status_no = 4 if status == "within"
replace status_no = 5 if status == "unknown"
bysort id: egen mo_status = mode(status_no)
drop status_no

gen status_new = ""
replace status_new = "inbound" if mo_status == 1
replace status_new = "outbound" if mo_status == 2
replace status_new = "passing" if mo_status == 3
replace status_new = "within" if mo_status == 4
replace status_new = "unknown" if mo_status == 5

bysort id: gen flag = 1 - (status_new == status_new[_n+1])
bysort id: replace flag = 0 if _n == _N
assert flag == 0
drop flag

replace status = status_new
drop status_new

// line_count
bysort id (counter): gen line_count = _n
	drop counter
	
keep id line_count lat lon status
tab status
//inbound 400k
//outbound 298k
//passing 218k
//unknown 67k
//within 714
export delimited "${path}/clean_planes_for_r_wide_london.csv", replace

R Script:

##############################################################################################################
## London
##############################################################################################################
library(ggplot2)
library(sp)
library(maptools)
library(rgdal)

#Remove axis
xquiet<- scale_x_continuous("", breaks=NULL)
yquiet<-scale_y_continuous("", breaks=NULL)
quiet<-list(xquiet, yquiet)

#Import countries
world_loc <- '.../World'
world <- readOGR(world_loc, layer="TM_WORLD_BORDERS-0.3") 
world <- spTransform(world, CRS("+proj=longlat +datum=WGS84"))

#London
flight_data <- read.csv('.../clean_planes_for_r_wide_london.csv', as.is = TRUE, header = TRUE)
flight_data_in <- flight_data[flight_data$status == "inbound",]
flight_data_out <- flight_data[flight_data$status == "outbound",]
flight_data_pass <- flight_data[flight_data$status == "passing",]
flight_data_within <- flight_data[flight_data$status == "within",]
flight_data_unknown <- flight_data[flight_data$status == "unknown",]

##################
# PLOTS
##################
ggplot() +
  
  geom_path(data=flight_data_pass, aes(lon, lat, group=id), color='green', alpha=0.01) +
  geom_path(data=flight_data_within, aes(lon, lat, group=id), color='purple', alpha=0.01) +
  geom_path(data=flight_data_out, aes(lon, lat, group=id), color='red', alpha=0.06) +
  geom_path(data=flight_data_in, aes(lon, lat, group=id), color='blue', alpha=0.06) +
  theme(panel.background = element_rect(fill='#2C3539',colour='#2C3539')) +
  quiet + coord_equal(ratio=122/78)

ggsave(file='H:/new_uk_all.png', width=30,height=15,dpi=360)
# Create a shape-file for use with arcGIS but decided against this
# However, code below for anyone interested
# "points_to_line" function was not written by memoryview

#points_to_line <- function(data, long, lat, id_field = NULL, sort_field = NULL) {
#  # Convert to SpatialPointsDataFrame
#  coordinates(data) <- c(long, lat)
#  # If there is a sort field...
#  if (!is.null(sort_field)) {
#    if (!is.null(id_field)) {
#      data <- data[order(data[[id_field]], data[[sort_field]]), ]
#    } else {
#      data <- data[order(data[[sort_field]]), ]
#    }
#  }
#  # If there is only one path...
#  if (is.null(id_field)) {
#    lines <- SpatialLines(list(Lines(list(Line(data)), "id")))
#    return(lines)
#    # Now, if we have multiple lines...
#  } else if (!is.null(id_field)) {  
#    # Split into a list by ID field
#    paths <- sp::split(data, data[[id_field]])
#    sp_lines <- SpatialLines(list(Lines(list(Line(paths[[1]])), "line1")))
#    for (p in 2:length(paths)) {
#      id <- paste0("line", as.character(p))
#      l <- SpatialLines(list(Lines(list(Line(paths[[p]])), id)))
#      sp_lines <- spRbind(sp_lines, l)
#    }
#    return(sp_lines)
#  }
#}
# library(foreach)
# lines <- list()
# counter = 1
# foreach (x=c("flight_data_in", "flight_data_out", "flight_data_pass")) %do% {
#   print(counter)
#   lines[[counter]] <- points_to_line(data=get(x), 
#                                      long="lon", 
#                                      lat="lat",
#                                      id_field = "id",
#                                      sort_field = "line_count")
#   # Project
#   proj4string(lines[[counter]]) <- CRS('+proj=longlat +datum=WGS84')
#   counter = counter + 1
#   
#   #   # Create shape-file to open in arcGIS (too big to plot in R)
#   #   lines_IDs <- sapply(slot(lines, "lines"), function(x) slot(x, "ID"))
#   #   lines_df <- data.frame(rep(0, length(lines_IDs)), row.names=lines_IDs)
#   #   lines_shape <- SpatialLinesDataFrame(lines,lines_df)
#   #   setwd('K:/compdata/Flights_London/')
#   #   writeOGR(lines_shape, ".",paste(x) ,driver="ESRI Shapefile")
# }