199 lines
12 KiB
Markdown
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. |