Skip to content

Satellite Cloud Overlay for Flight Analysis

Moved from vulture-ios/Docs/satellite-cloud-overlay.md

Status: Investigation complete, ready for implementation Date: 2026-08-12

When analyzing a flight, pilots want to see the cloud formations that were present during their flight. By scrubbing the barograph, the satellite cloud overlay on the map should update to match the time at the current graph position. This helps correlate thermals, routes, and decisions with actual weather conditions.


SkySight is already integrated in Vulture for weather forecasting. HighSight is their public-facing API for satellite imagery.

PropertyValue
EndpointGET /v1/satellite/{z}/{x}/{y}?date=YYYY/MM/DD/HHmm&key=API_KEY
Docshttps://api.highsight.dev/v1/reference
Tile formatXYZ slippy-map tiles, JPEG 512px, zoom 3-8
Historical dataFrom 2021 onward
Time resolution~10 minutes (rounded to nearest 10 min)
CoverageGlobal
PricingFree 5K tiles/mo, Standard $99/mo (100K), Pro $899/mo (2M)
AuthAPI key via query param or Authorization header

Why it’s the best fit:

  • XYZ tiles — drop-in for Mapbox (MGLRasterTileSource)
  • Historical back to 2021 at 10-min resolution — covers most flight logs
  • Same team as SkySight — can leverage existing relationship
  • Standard tile format — easy to cache as files

The app already uses SkySight satellite tiles:

  • tiles.skysight.io/sat_global/{date}/{period}/{z}/{x}/{y}.png (in SkySightOverlay+Tile.swift)
  • Unclear whether these endpoints serve historical past dates or only forecast data
  • First step: verify with SkySight whether the existing endpoint works for historical dates
SourceFormatHistoricalCoverageCostVerdict
EUMETSAT WMSWMS (not XYZ)Sept 2020+Europe/AfricaFree (non-commercial)Viable but needs WMS adapter
OpenWeatherMapXYZ tilesMarch 2019+GlobalContact salesModeled cloud %, not real satellite
NOAA GOESNetCDF (raw)2018+AmericasFreeNeeds server-side tile rendering
RainViewer————Radar only, no satellite imagery

Flight loaded → determine time range and geographic bounds
↓
Download satellite tiles for flight area at key timestamps
(e.g., every 30 min during the flight: 10:00, 10:30, 11:00, ...)
↓
Cache tiles in {NSCachesDirectory}/satellite/{date}/{time}/{z}/{x}/{y}.jpg
↓
User scrubs barograph
↓
Graph handle position → current flight time
↓
Find nearest cached satellite timestamp
↓
Swap MGLRasterTileSource URL template to that timestamp's tiles
↓
Map updates with cloud overlay for that time

Pre-download from context menu (like elevation tiles):

  1. User opens a flight → context menu shows “Download Cloud Overlay”
  2. Calculate the flight’s bounding box (with padding)
  3. Determine tile coordinates for zoom levels 3-7 within the bounding box
  4. Determine timestamps: every 30 minutes during the flight
  5. Download all tiles and cache to disk
  6. Once cached, the scrubbing overlay works offline

Estimated tile count per flight:

  • Typical flight area: ~200x200 km → ~4 tiles at z5, ~16 at z6, ~64 at z7
  • 4-hour flight at 30-min intervals → 9 timestamps
  • Total: ~750 tiles (~500 KB at ~700 bytes/tile JPEG) ≈ ~500 KB per flight

The elevation system uses a two-layer cache:

  1. File cache: {NSCachesDirectory}/elevations/{filename}.hgt
  2. Flight cache: CoreData CoreFlightData.elevations as [Int]

For satellite tiles, use a similar file cache:

  • Path: {NSCachesDirectory}/satellite/{YYYY-MM-DD}/{HHmm}/{z}/{x}/{y}.jpg
  • No CoreData cache needed — tiles are already small JPEG files
  • Mark download status on CoreFlight (boolean isSatelliteCached)

Map Integration (following SkySight pattern)

Section titled “Map Integration (following SkySight pattern)”

The existing SkySight overlay system provides the exact pattern:

  1. Create MGLRasterTileSource with a URL template pointing to cached files (or API)
  2. Add MGLRasterStyleLayer on top of the base map, below flight tracks
  3. Control opacity via existing settings.overlayOpacity
  4. When scrubbing, swap the source URL template to the new timestamp

Key file: MapViewDataSource.invalidateWeatherLayer() — shows how to add/remove tile layers.

The graph handle already publishes the current time:

  • GraphHandleView.relativePositionSubject → normalized position (0-100)
  • PanelGraphView.updateHandleRelativePosition → resolves to a Fix per flight
  • Each Fix has rawTime (seconds since midnight UTC)

When the handle moves:

  1. Get the current fix’s rawTime + flight date → absolute timestamp
  2. Round to nearest 10/30 minutes (matching cached tile timestamps)
  3. If different from current overlay timestamp → swap tile source

New files in Packages/Flights/:

  • Sources/Flights/Satellite/SatelliteRequest.swift — Cara request for HighSight tiles
  • Sources/Flights/Satellite/SatelliteCache.swift — file-based tile cache manager
  • Sources/Flights/Satellite/SatelliteTileManager.swift — coordinates download for a flight’s bounds + time range

Modified files in Modules/App/:

  • PanelFlightsViewController.swift — add “Download Cloud Overlay” to context menu
  • New download progress UI (similar to elevation download spinner)

Modified files in Modules/App/:

  • FlightMapViewDataSource.swift — add satellite overlay layer management
  • MapViewDataSource.swift — add satellite layer type
  • PanelGraphView.swift — on scrub, notify map of timestamp change
  • FlightMapViewController.swift — bridge graph timestamp to map overlay

ComponentPath
Elevation cache patternPackages/Flights/Sources/Flights/Elevation/Elevation.swift
Elevation downloadPackages/Flights/Sources/Flights/Enricher/FlightElevationEnricher.swift
SkySight overlayModules/App/Sources/Components/SkySight/Extensions/SkySightOverlay+Tile.swift
SkySight managerModules/App/Sources/Components/SkySight/SkySightManager.swift
Map tile managementModules/App/Sources/Modules/Detail Map/MapViewDataSource.swift
Graph scrubbingModules/App/Sources/Modules/Detail Map/Flight/Panel/Graph/PanelGraphView.swift
Graph handleModules/App/Sources/Modules/Detail Map/Flight/Panel/Graph/Views/GraphHandleView.swift
Flight context menuModules/App/Sources/Modules/Detail Map/Flight/Panel/Flights/PanelFlightsViewController.swift
Cache property wrapper@Cache<Value: Codable> in SkySight module

Store in CoreFlightData (synced via iCloud):

  • satelliteTimestamps: [Date] — which timestamps are available
  • satelliteBounds: Data — the bounding box that was downloaded
  • isSatelliteCached: Bool — whether tiles have been downloaded on this device

Tiles stored as files in {NSCachesDirectory}/satellite/. When a flight syncs to another device, that device sees “cloud overlay available” and can re-download the tiles on demand.

Follow the elevation reload pattern:

  • When opening a flight, check if satellite data is available but not yet cached locally
  • Show a “Download Cloud Overlay” action (like the existing elevation reload)
  • If already cached, show “Reload Cloud Overlay” to re-download (e.g., higher resolution becomes available, or tiles were evicted from cache)
  • The download/reload action lives in the flight detail context menu alongside terrain

This mirrors how elevation works: metadata travels with the flight via iCloud, tiles are downloaded locally on each device when needed.


The service says nothing about its own reach: a tile it does not have is a 404. Asked for tiles across the years and across the world, the endpoint (satellite.skysight.io/tiles, zoom 6) answered the same way on every date tried:

PeriodCoverage
before 1 January 2014nothing
1 January 2014 – 31 December 2022tiles x 28–43, y 11–47: 22.5°W to 67.5°E, about 75.5°N to 66.5°S — Europe, Africa and the Middle East
from 1 January 2023the whole world

Before 2025 the frames change every 30 minutes, not every 10; a request for a time in between answers with the frame before it.

SatelliteTileManager.hasImagery(coordinates:startDate:) holds these limits. The flight map leaves a flight without imagery out of everything the overlay does and hides the cloud button when nothing on the map has any: a download counts a 404 as a failed tile and one failed tile fails the download, so an older flight used to run its ring to the end and fall back to the download cloud with nothing to show. If the service widens its archive, the constants there are what to re-measure.

  1. Does the existing tiles.skysight.io/sat_global/ endpoint work for historical dates? If yes, no new API integration needed — just cache and scrub.
  2. HighSight pricing — 5K free tiles/month might suffice for testing; need to estimate real usage for ~1000 users.
  3. Overlay opacity — should it share the existing SkySight overlay opacity setting, or have its own control?