1

I am creating a visualisation/map of typical flight patterns over Los Angeles, California. I am interested not in live data, or even particularly recent data; rather, I am hoping to find a data set that would help me illustrate the typical flight operations in the area over the course of a given day (a weekday, probably), irrespective of airport origin/destination. Just the planes that happen to fly over a defined area during a defined period of time, say; Los Angeles County, June 1st, 2022.

I think I've found a data source - the sample "Trace Files" on ADS-B Exchange seem best, but I admit I am stumped on how best to process this JSON. If you visit the link, you can see that the data is in this nested format, organized by the last two digits of ICAO hex code.

My goal is just to get flight path lines onto a map in QGIS. Maybe JSON -> GeoJSON -> Shapefile? But I'm not quite sure how to approach this.

EDIT - Example Data + Screenshots

    {"icao":"000000",
"noRegData":true,
"timestamp": 1651363266.406,
"trace":[ 
[0.0,33.843896,-84.601307,2300,80.0,132.1,3,null,null,"other",null,null,null,null],
[18.2,33.839287,-84.595299,106,80.0,132.7,1,null,{"type":"other","flight":"N63STZZZ","track":132.70,"squawk":"1200","category":"B0","nic":0,"rc":0,"version":1,"nic_baro":0,"nac_p":0,"sil":0,"sil_type":"unknown","alert":0,"spi":1},"other",null,null,null,null],
[40.5,33.834725,-84.589062,106,80.0,131.4,1,null,null,"other",null,null,null,null],
[58.6,33.830070,-84.582825,1925,80.0,131.9,1,null,null,"other",null,null,null,null],
[76.7,33.825508,-84.576588,1825,70.0,131.4,1,null,null,"other",null,null,null,null],
[99.0,33.820108,-84.568577,1800,10.0,129.1,1,null,{"type":"other","flight":"N63STZZZ","track":129.10,"squawk":"1200","category":"B0","nic":0,"rc":0,"version":1,"nic_baro":0,"nac_p":0,"sil":0,"sil_type":"unknown"},"other",null,null,null,null],

Image containing JSON code Directories where data is stored, named by ICAO hex

2
  • Can you add an example of your data for those of us who don't click random links
    – Ian Turton
    Aug 24, 2022 at 14:21
  • Sure, I'll add to the original post. Aug 24, 2022 at 17:58

1 Answer 1

1

I am not sure if you could do this with QGIS feature without using Python. However, if you know how to write Python, here are some examples I wrote last night how you could do it:

Create Flight Path (GeoJSON)

Using shapely and geojson:

import os
import json

from pathlib import Path

from geojson import Feature, dump
from shapely.geometry import LineString


INPUT_FILE_PATH = Path(r"c:\temp\data\trace_full_3455d8.json")
with open(INPUT_FILE_PATH, "r") as json_file:

    data = json.load(json_file)
    
    trace_points = LineString(
        [
            [lon, lat] for _, lat, lon, *_ in data["trace"]
        ]
    )

    flight_path = Feature(geometry=trace_points, properties={
        **{
            k: v for k, v in data.items() if k in [
                "icao", "noRegData", "timestamp",
            ]
        }
    })
    folder = INPUT_FILE_PATH.parent
    file_name = f"{INPUT_FILE_PATH.stem}_flight_path.geojson"

    with open(os.path.join(folder, file_name), "w") as geojson_file:

        # write flight route to geojson
        dump(flight_path, geojson_file)

Create Trace Points (GeoJSON)

Using shapely and geojson:

import os
import json

from pathlib import Path

from geojson import Feature, FeatureCollection, dump
from shapely.geometry import Point


INPUT_FILE_PATH = Path(r"c:\temp\data\trace_full_3455d8.json")
with open(INPUT_FILE_PATH, "r") as json_file:

    data = json.load(json_file)
    
    features = [
        Feature(
            # https://www.adsbexchange.com/version-2-api-wip
            geometry=Point(p[2], p[1]),
            properties= {
                "seconds": p[0],
                "altitude": p[3],
                "ground_speed": p[4],
                "track_in_degree": p[5],
                "flags": p[6],
                "vertical_rate": p[7],
                "aircraft_details": p[8],
                "type": p[9],
                "geometric_altitude": p[10],
                "geometric_vertical_rate": p[11],
                "indicated_airspeed": p[12],
                "roll_angle": p[13],
            }
        )
        for p in data.get("trace")
    ]

    trace = FeatureCollection(features, properties={
        **{
            k: v for k, v in data.items() if k in [
                "icao", "noRegData", "timestamp",
            ]
        }
    })
    folder = INPUT_FILE_PATH.parent
    file_name = f"{INPUT_FILE_PATH.stem}_trace.geojson"

    with open(os.path.join(folder, file_name), "w") as geojson_file:

        # write flight route to geojson
        dump(trace, geojson_file)

Convert GeoJSON to Shapefile

Using geopandas:

import os

from pathlib import Path

import geopandas as gpd

# INPUT_FILE_PATH = Path(r"c:\temp\data\trace_full_3455d8_trace.geojson")
INPUT_FILE_PATH = Path(r"c:\temp\data\trace_full_3455d8_flight_path.geojson")

folder = INPUT_FILE_PATH.parent
output_file_name = f"{INPUT_FILE_PATH.stem}.shp"

flight_path = gpd.read_file(INPUT_FILE_PATH)
flight_path.to_file(os.path.join(folder, output_file_name))

Alternative: Create Shapefiles from JSON input using geopandas

import os
import json

from pathlib import Path

import pandas as pd
import geopandas as gpd

from shapely.geometry import LineString


INPUT_FILE_PATH = Path(r"c:\temp\data\trace_full_3455d8.json")
with open(INPUT_FILE_PATH, "r") as json_file:

    data = json.load(json_file)

    df = pd.DataFrame(
        data["trace"],
        columns=[
            "seconds",
            "lat",
            "lon",
            "altitude",
            "ground_speed",
            "track_in_degree",
            "flags",
            "vertical_rate",
            "aircraft_details",
            "type",
            "geometric_altitude",
            "geometric_vertical_rate",
            "indicated_airspeed",
            "roll_angle",
        ]
    )

    gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.lon, df.lat))

    folder = INPUT_FILE_PATH.parent
    trace_file_name =  f"{INPUT_FILE_PATH.stem}_trace.shp"    

    gdf.to_file(os.path.join(folder, trace_file_name))
    # Create Flight Path

    gdf["group_column"] = 1
    grouped_gdf = gdf.sort_values(by=["seconds"]).groupby(["group_column"])
    gdf = grouped_gdf["geometry"].apply(lambda x: LineString(x.tolist()))

    gdf = gpd.GeoDataFrame(gdf, geometry="geometry")
    gdf[["icao", "noRegData", "timestamp"]] = [
        data.get("icao"),
        data.get("noRegData"),
        data.get("timestamp"),
    ]

    flight_path_file_name = f"{INPUT_FILE_PATH.stem}_flight_path.shp"
    
    gdf.to_file(os.path.join(folder, flight_path_file_name))
1
  • Thanks so much for this Thomas! I am not fluent in Python, but I adapted your code to crawl through by subdirectory and added exception handling. Successfully crawled through 61,000 entries! Many thanks. Aug 31, 2022 at 16:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.