A printable flight record with airspace facts

A flight record is a factual account of a flight, built entirely from the aircraft's own telemetry — no manual entry. It opens as one page per flight plus a logbook table on top, ready to print or save as a PDF for a logbook, a client, or your own records.

dji-embed flightmap ./flights -f record

This writes flight-record.html next to your footage. Every figure on it carries its source and what it approximates — a missing datum is stated as a note, never filled in with a guessed number.

The logbook table shows each flight's local time over its UTC time, using --tz-offset when you state one and the auto-detected recording offset otherwise; when neither resolves, the row stays honestly UTC-only and its date cell says so. The two height columns are labelled with their datums — max above takeoff (aircraft-reported) and est. max above surface — and "entered" in the airspace section is a horizontal fact, defined right under the table.

A flight record needs exact coordinates to place a track against airspace zones honestly, so -f record refuses to run with --redact fuzz — drop --redact for a record, or use --redact fuzz for the map formats instead.

What gets fetched, and what it reveals

Building the record's airspace section, and the surface-referenced height estimate, are the only network access in this command. The logbook and the track come from the SRT telemetry already on disk; so does the takeoff-referenced height, which is aircraft-reported. The surface-referenced height is the exception — it needs a fetch from Mapterhorn's terrain tiles (see "The [terrain] extra" below).

These feeds are used:

  • US flights query the FAA's UAS Facility Map (keyless ArcGIS). The bounding box sent to the endpoint is padded and snapped outward to a 0.1° grid before it goes on the wire, so the endpoint learns no more about where you flew than a map-tile fetch already would.
  • Luxembourg, Finland and Switzerland flights fetch the country's whole ED-269 geographical-zone document, and Ireland and Sweden their published ED-318 files — the IAA's file for Ireland, LFV's dronechart file for Sweden — the feeds have no query parameter for a location at all, so nothing about the flight is sent; the entire country's zones come back regardless of where the flight was.
  • UK flights fetch NATS AIS's whole UAS flight-restrictions dataset (AIXM 5.1, refreshed each 28-day AIRAC cycle), again with no location sent. Activation hours are not in the dataset and temporary restrictions live in NOTAMs; the record says so rather than guessing.
  • Denmark flights fetch Trafikstyrelsen's whole drone-zone dataset (the file behind droneregler.dk), once more with no location sent. NOTAM-driven temporary restrictions are not part of that dataset either; the record says so.
  • Estonia flights fetch EANS's whole UAS geographical-zone file (attributed to Estonian Air Navigation Services, confirmed in writing as public data), again with no location sent. The file reflects the rules at the time of download rather than the time of the flight, and NOTAM activation hours live only as text in the zone's published message, not a machine-readable field; the record says so on both counts.
  • Slovenia flights fetch the CAA's whole published UAS geo-zones file (a KMZ export), again with no location sent. The CAA's own page says the file does not contain the populated-area limits for the Open category, and its restricted and danger areas defer to NOTAM; both caveats ride along. The zones' published exceptions, contacts and reasons appear as text, labelled published, not evaluated.
  • Belgium flights fetch the Droneguide platform's zones (the public map's own feature service, operated by skeyes for the BCAA). This is the one feed that is asked for the flight's time window: the publisher's server drops zones that had already expired when the flight began and marks, per zone, whether it was active during the flight. Nothing else about the flight is sent, and the whole country's zones come back. That activity status is the publisher's evaluation and is shown as such; a zone it marks inactive that the flight was inside is listed as not applicable, never as entered. NOTAM zones carry their dates from Droneguide's NOTAM layer; temporary zones carry none, so the publisher's status is the only currency signal for them, and the record says so. The BCAA's letter (ref G26-187) asked for four notices to stay visible, and every Belgian record carries them: the source, that this is not an official application of the BCAA or the Belgian authorities, that only the official publication channels are authoritative, and that regulatory compliance verification remains with the remote pilot and UAS operator.
  • Latvia flights fetch LGS's whole published UAS geographical-zone file (the daily UASZoneVersion export, attributed to Latvijas Gaisa Satiksme; free of charge for non-commercial use under Cabinet Regulation No. 248 §45, confirmed in writing), again with no location sent. The file's title stamp is its edition and the record states it. Validity windows are evaluated; a few zones also publish a weekday/hour schedule, which the record shows and does not evaluate; LGS's English zone message rides in the popup verbatim.
  • Spain flights query ENAIRE's documented ZGUAS service (keyless ArcGIS, attributed to ENAIRE as holder of the intellectual and industrial property rights, reuse authorised in writing). Like the FAA grid, the bounding box sent is padded and snapped outward to a 0.1° grid first, so the endpoint learns no more than a map-tile fetch would. Aerodrome and infrastructure-protection zones are fetched; the service's territory-wide urban-environment reminder is carried as a note, not drawn as a zone. The service states no edition date, only the fetch time is shown, and ENAIRE's caveat that the geometry is informative and the AIP normative rides with every Spanish record.
  • Germany flights query dipul's anonymous WFS (CC BY-ND 4.0, attribution "dipul, CC-BY-ND 4.0", use confirmed in writing), one request per category layer. As for the FAA grid and Spain, the bounding box sent is padded and snapped outward to a 0.1° grid first. Geometry and attributes pass through unchanged, as the licence requires; dipul's category, legal reference and type code ride in the popup verbatim. The § 21h LuftVO zones are shown as conditional because the paragraph permits flight in them only under its stated conditions; § 17 flight restriction areas need the authority's permission; dipul's temporary no-fly areas are prohibited for their published window. No edition date is published, so only the fetch time is shown.
  • Every flight, regardless of jurisdiction, fetches surface-height tiles from Mapterhorn (tiles.mapterhorn.com) for the surface-referenced height estimate, when the [terrain] extra is installed.

Every fetch is announced before it happens, and the response is cached beside the output — airspace data at airspace-cache/, terrain tiles alongside it — so a re-run reuses both and stays offline. Pass --airspace-refresh to bypass the airspace cache and fetch again (does nothing without -f record; terrain tiles are unaffected by this flag).

dji-embed flightmap ./flights -f record --airspace-refresh

Coverage: US, Luxembourg, Finland, Switzerland, Ireland, the UK, Denmark, Sweden, Estonia, Slovenia, Belgium, Latvia, Spain, Germany — and an honest gap everywhere else

Airspace lookup only resolves for flights that sit clearly inside the United States, Luxembourg, Finland, Switzerland, Ireland, the UK, Denmark, Sweden, Estonia, Slovenia, Belgium, Latvia, Spain, or Germany. Everywhere else the record states the gap instead of guessing: "no supported airspace data source for this location." A flight near a jurisdiction boundary gaps the same way, deliberately, rather than borrowing a neighbouring country's rules from coordinates alone. The logbook half of the record (times, distances, heights) is unaffected — a gapped airspace section never blocks the rest. Northern Ireland resolves to the UK dataset; flights close to the Irish land border still gap deliberately, from both sides.

Widening coverage means adding and verifying another feed; it will grow, but a wrong jurisdiction guessed from coordinates would be worse than no jurisdiction at all.

Three heights, each labelled with what it means

The record prints up to three height figures per flight, and never substitutes one for another:

  • Height above takeoff point — aircraft-reported, exact, straight from telemetry.
  • Estimated height above surface — the aircraft's height above takeoff combined with a digital surface model (includes vegetation and buildings) under the flight path; requires the [terrain] extra and a network connection.
  • Altitude (AMSL) — the aircraft-reported altitude above mean sea level, where the telemetry carries one.

Height above takeoff is not the regulatory measure, in either jurisdiction covered here: both the FAA's 400 ft AGL limit and the EU's 120 m surface limit are measured above the ground directly under the aircraft, not above the point it launched from. On sloped or varied terrain the two diverge — a hover over a valley reads higher above takeoff than it is above the ground beneath it. The surface-referenced estimate is the one that approximates the regulatory measure; see the 3D map's terrain view for the same digital surface model used to drape flights over real ground.

The [terrain] extra

The surface-referenced height needs the terrain extra:

pip install 'dji-drone-metadata-embedder[terrain]'

Without it, the record still writes — the surface height row states why it's missing rather than silently disappearing. The Windows EXE and installer builds ship with [terrain] already included, so this only matters for a pip install.

Printing to PDF

The record is a single self-contained HTML file with print styles built in — open it in a browser and use Print → Save as PDF. No extra tool or export step is needed.

What this record is not

The record states facts and their sources; it does not determine whether a flight complied with any regulation. Both the FAA's Part 107 rules and the EU's Open Category rules carry exceptions — structures, obstacles, and others — that telemetry alone cannot evaluate. Read every airspace zone and height figure as a measurement, not a verdict.

The same zones can be drawn on the interactive map: dji-embed flightmap FLIGHTS --airspace overlays them on the 2D HTML map, sharing this command's cache and consent model.