Files
FlightPlot/REQUIREMENTS.md
T
2026-10-21 09:24:12 +02:00

199 lines
12 KiB
Markdown

# Flight Map Requirements
## Purpose
Create a Python program that generates an SVG map of a rectangular geographic
area with recorded aircraft flight tracks overlaid, suitable for drawing with
a physical pen plotter.
## Separate collection and plotting workflow
- Provide separate command-line commands for flight-data collection and SVG
generation. Run collection first; generate maps later from the saved database.
- The collection command accepts optional opposite map corners and the SQLite
database destination. If corners are omitted, use the default map area
including Uithoorn in the Netherlands. It records flight data without
generating an SVG or requiring map or terrain downloads.
- Store a recording-session record containing a session identifier, the
normalized requested map bounds, the expanded collection bounds, the flight
data source, the collection interval, and the recording start and end times.
Preserve this metadata across interruption or restart.
- Associate every stored flight observation with its recording session so its
map area and collection context remain identifiable.
- The plotting command accepts the database, a recording-session selection,
an optional time interval within that recording, and output settings.
Use the selected session's stored map bounds rather than requiring the user
to enter the map area again.
- Plotting must not contact OpenSky or start flight-data collection. It may
obtain OpenStreetMap-derived vector data and terrain elevation data
separately as needed.
- Allow multiple maps with different time selections and output settings to be
generated from the same recording without modifying the recorded flight
observations or session metadata.
- Report missing sessions, invalid time selections, and absence of usable
observations explicitly.
- Provide a purge command requiring an exact session identifier and explicit
confirmation to permanently delete that session's observations and metadata
in one transaction. Preserve all other sessions and their observations.
- Purging a missing session or database must report an error without creating
a database. Stop collection for the selected session before purging it.
## Map input and output
- At collection time, accept the longitude and latitude of two opposite corners
of the map, validate coordinates, and normalize their order.
- Provide a predefined default rectangular map area that includes the city of
Uithoorn in the Netherlands. Explicitly supplied corners override this default.
- Default southwest corner: latitude 52.232, longitude 4.784.
Default northeast corner: latitude 52.258, longitude 4.864.
Command-line bounds use longitude first: 4.784 52.232 4.864 52.258.
- Store the actual bounds used, including when the default is selected, in the
recording session. Later plotting must use those stored bounds rather than
a potentially changed application default.
- At plotting time, load the map bounds from the selected recording session.
- Obtain OpenStreetMap-derived vector map data, either as vector tiles or
geographic feature data, following the source's usage policy and including
the required attribution. Standard raster map tiles are not suitable for
the pen-plotter output.
- Cache downloaded vector map data persistently by source, geographic bounds,
and feature query. Reuse it across plotting runs without downloading it
again. Provide a configurable cache directory and an explicit refresh option.
- Write cache entries atomically so interrupted downloads or writes do not
replace a previously usable entry. Report damaged cache entries explicitly.
- Generate an SVG using a consistent geographic projection for the map and
flight tracks.
- Handle invalid input and download failures with clear error messages.
## Pen-plotter output
- Render map features and flight tracks as vector paths or line geometry,
without embedded raster images or reliance on filled areas, shading, or
other screen-only effects.
- Render all labels and attribution as paths, using single-line stroke
lettering suitable for pen plotting rather than relying on installed fonts
or tracing filled glyph outlines.
- Resolve clipping into the exported geometry so map features stop at the map
boundary and flight-track extensions remain within the output page. Do not
rely on SVG clipping masks being supported by the plotter software.
- Specify physical page dimensions and a matching SVG viewBox so the intended
drawing size is preserved when imported into plotter software.
- Keep map geometry, flight tracks, and lettering in separate named SVG groups
for independent selection and plotting.
- Select and simplify map features to keep the drawing legible at its physical
output size. Omit minor pedestrian and cycle paths and suppress building
outlines whose shorter dimension is smaller than three pen widths.
## Flight data and local recording
- Use OpenSky Network as the initial flight-data source.
- Retrieve live aircraft state vectors for the selected geographic area.
- Query an area slightly larger than the map so recorded flight paths can
extend beyond the map boundary.
- Collect positions periodically, respecting OpenSky's current authentication
requirements, rate limits, and usage terms.
- Store observations locally in SQLite so maps can be generated later without
fetching those observations again.
- Store the aircraft's ICAO identifier, callsign when available, position
timestamp, longitude, latitude, and available altitude measurements.
- Support generating a map from locally recorded observations for a selected
time interval.
- Do not require historical data from before local recording began. Unrestricted
historical access is not assumed.
- Report recording failures explicitly and handle missing fields and gaps in
coverage without inventing observations.
- Initial use is non-commercial, subject to OpenSky's applicable terms and
attribution requirements.
## Flight-track rendering
- Overlay recorded flight paths that cross the requested map area.
- Fit smooth interpolating curves through recorded flight positions rather
than drawing angular polylines. Export flight paths as cubic Bezier geometry
suitable for a pen plotter. Two-point tracks may remain straight.
- Do not smooth across track gaps, on-ground interruptions, or flight changes.
Handle repeated positions without generating invalid curve coordinates.
- Clip the actual curves to the output bounds, not just their sample points,
and use the same curved in-map portions for time and average-altitude selection.
- Extend the visible tracks a little beyond the map boundary using actual
recorded positions from the surrounding area, not fabricated extrapolation.
- Keep the map background within its requested bounds and provide space in the
SVG for the extended tracks and their labels.
- Identify each flight track with its date, time, callsign or flight number when
available, and average reported barometric (pressure) altitude in metres.
- Do not present an OpenSky callsign as a verified passenger-facing flight
number. Callsign-to-flight-number lookup is not required for the initial
version.
- Calculate the map's average ground elevation from terrain elevation data
for landmass within the requested map bounds, excluding mapped water and the
surrounding collection area. Preserve islands in water multipolygon relations.
Use an area-weighted mean or an equivalent equal-area sampling method.
- Average valid land samples only. Report the percentage and counts of sampled
land with valid elevations, warn about missing land samples, and never classify
missing terrain as water. Show land coverage in the SVG legend and metadata.
- Obtain AHN DTM height maps automatically through PDOK during plotting for the
Netherlands. Cache validated GeoTIFF subsets persistently and atomically,
record source and NAP reference, and support explicit refresh and disabling
of automatic terrain retrieval. Explicit local terrain inputs take precedence.
- If AHN height maps are unavailable or the area is outside coverage, warn
explicitly and continue rendering with the land-elevation summary unavailable.
Reported aircraft altitude must remain independent of terrain availability.
- Calculate each track's time-weighted average reported barometric altitude
over the portion inside the requested map bounds. Outside-map extensions
must not affect this calculation.
- Do not subtract ground elevation or apply geoid, pressure, or vertical-reference
corrections to aircraft altitude. The average land elevation is a separate,
informational summary only.
- Label aircraft values explicitly as reported barometric altitude with units,
not height above ground, terrain clearance, or verified altitude above sea level.
- Missing barometric altitude on a contributing segment makes that track's
average unavailable, with an explicit warning. Do not fall back to geometric
altitude. Zero and negative reported values are valid.
- Make labels readable and indicate unavailable metadata explicitly.
## Implementation and validation
- Implement the program in Python with a command-line interface.
- Document installation, the separate collection and plotting commands,
session selection, map generation, configuration, data-source limitations,
and attribution.
- Test coordinate handling, projection, local recording, track selection,
average land elevation, average reported barometric altitude, missing altitude
and elevation data, terrain-independent flight labels, and SVG generation.
- Test persistence of session bounds and observation associations, plotting
after collection has stopped, and repeated plotting without changing the
recorded flight data. Verify that collection does not require map or terrain
services and plotting makes no OpenSky requests.
- Test that omitting map corners selects the Uithoorn default, explicit corners
override it, and the selected bounds are persisted for later plotting.
- Validate that the exported SVG contains drawable vector geometry, path-based
lettering, correct physical dimensions, and geometry clipped to its intended
bounds, with no raster images or dependence on text rendering or clipping
masks.
- Include an end-to-end test using fixed sample data without requiring live
network access.
- Test cache reuse, separate entries for different map areas, explicit refresh,
and preservation of cached data when refresh fails.
## Initial implementation defaults and limitations
- Use Overpass to obtain OpenStreetMap roads, waterways, building outlines, and
water outlines, with local Overpass JSON also accepted for offline plotting.
- Default to A4 landscape (297 by 210 mm), 12 mm page margins, a 0.3 mm pen,
and track extensions of up to 4 mm beyond the map border.
- Collect every 30 seconds within a 2,000 m margin around the saved map area.
- Display timestamps in UTC using date and 24-hour time, and altitudes in metres.
- Use PDOK AHN DTM `dtm_05m` via WCS for Dutch areas, requesting a 512 by 512
nearest-neighbour subset in EPSG:28992, with elevations in metres relative
to NAP. Accept local terrain GeoTIFFs with a declared vertical reference for
the separate land-elevation summary. No geoid or aircraft datum conversion
inputs are required or used.
- Estimate average land elevation using a 64 by 64 equal-area sampling grid.
Exclude mapped water, average available land elevations, and warn about missing
land coverage. No valid land samples makes the average unavailable.
- Use time-weighted reported barometric altitude with linear interpolation between recorded
timestamps, restricted to the smoothed path's portion inside the map. Use
centripetal Catmull-Rom interpolation converted to cubic Bezier curves for
flight paths. Split tracks at changes
in callsign, on-ground observations, or gaps exceeding 120 seconds by default.
- Reject antimeridian-crossing bounds and latitudes outside [-85, 85] explicitly.