188 lines
11 KiB
Markdown
188 lines
11 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.232025, longitude 4.784498.
|
|
Default northeast corner: latitude 52.253985, longitude 4.863525.
|
|
Command-line bounds use longitude first: 4.784498 52.232025 4.863525 52.253985.
|
|
- 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-height 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 height above the map's average ground elevation.
|
|
- 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
|
|
covering the requested map bounds, excluding the surrounding collection area.
|
|
Use an area-weighted mean or an equivalent equal-area sampling method.
|
|
- Calculate each track's average height as its average aircraft altitude over
|
|
the portion inside the requested map bounds minus the map's average ground
|
|
elevation. Outside-map extensions must not affect this calculation.
|
|
- Use compatible units and a common vertical reference for aircraft altitude
|
|
and terrain elevation, applying conversions where needed. Do not subtract
|
|
terrain elevation directly from uncorrected barometric pressure altitude.
|
|
- Label the result explicitly as average height above the map's average ground
|
|
level, with units. This is not height above the terrain directly beneath each
|
|
aircraft position and must not be presented as terrain clearance.
|
|
- Indicate when the height cannot be calculated because compatible aircraft
|
|
altitude or sufficient terrain data is unavailable; do not silently substitute
|
|
altitude above sea level or assume zero ground elevation.
|
|
- 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 ground elevation, vertical-reference compatibility, average-height
|
|
calculation, missing elevation data, 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 heights in metres.
|
|
- Accept local terrain GeoTIFFs with a declared vertical reference. For
|
|
orthometric terrain, accept a matching geoid-separation raster for converting
|
|
ellipsoidal aircraft heights. Require explicit confirmation of the geometric
|
|
altitude interpretation before calculating heights; otherwise label them
|
|
unavailable. Never silently substitute barometric altitude.
|
|
- Estimate average ground elevation using a 64 by 64 equal-area sampling grid.
|
|
Any missing terrain sample makes the average unavailable.
|
|
- Use time-weighted aircraft 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. |