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
Context
Section titled “Context”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.
API Source Research
Section titled “API Source Research”Recommended: HighSight (by SkySight)
Section titled “Recommended: HighSight (by SkySight)”SkySight is already integrated in Vulture for weather forecasting. HighSight is their public-facing API for satellite imagery.
| Property | Value |
|---|---|
| Endpoint | GET /v1/satellite/{z}/{x}/{y}?date=YYYY/MM/DD/HHmm&key=API_KEY |
| Docs | https://api.highsight.dev/v1/reference |
| Tile format | XYZ slippy-map tiles, JPEG 512px, zoom 3-8 |
| Historical data | From 2021 onward |
| Time resolution | ~10 minutes (rounded to nearest 10 min) |
| Coverage | Global |
| Pricing | Free 5K tiles/mo, Standard $99/mo (100K), Pro $899/mo (2M) |
| Auth | API 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
Existing SkySight Integration
Section titled “Existing SkySight Integration”The app already uses SkySight satellite tiles:
tiles.skysight.io/sat_global/{date}/{period}/{z}/{x}/{y}.png(inSkySightOverlay+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
Alternatives
Section titled “Alternatives”| Source | Format | Historical | Coverage | Cost | Verdict |
|---|---|---|---|---|---|
| EUMETSAT WMS | WMS (not XYZ) | Sept 2020+ | Europe/Africa | Free (non-commercial) | Viable but needs WMS adapter |
| OpenWeatherMap | XYZ tiles | March 2019+ | Global | Contact sales | Modeled cloud %, not real satellite |
| NOAA GOES | NetCDF (raw) | 2018+ | Americas | Free | Needs server-side tile rendering |
| RainViewer | — | — | — | — | Radar only, no satellite imagery |
Architecture
Section titled “Architecture”How It Would Work
Section titled “How It Would Work”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 timeDownload Strategy
Section titled “Download Strategy”Pre-download from context menu (like elevation tiles):
- User opens a flight → context menu shows “Download Cloud Overlay”
- Calculate the flight’s bounding box (with padding)
- Determine tile coordinates for zoom levels 3-7 within the bounding box
- Determine timestamps: every 30 minutes during the flight
- Download all tiles and cache to disk
- 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
Caching (following elevation pattern)
Section titled “Caching (following elevation pattern)”The elevation system uses a two-layer cache:
- File cache:
{NSCachesDirectory}/elevations/{filename}.hgt - Flight cache: CoreData
CoreFlightData.elevationsas[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:
- Create
MGLRasterTileSourcewith a URL template pointing to cached files (or API) - Add
MGLRasterStyleLayeron top of the base map, below flight tracks - Control opacity via existing
settings.overlayOpacity - When scrubbing, swap the source URL template to the new timestamp
Key file: MapViewDataSource.invalidateWeatherLayer() — shows how to add/remove tile layers.
Graph Scrubbing Integration
Section titled “Graph Scrubbing Integration”The graph handle already publishes the current time:
GraphHandleView.relativePositionSubject→ normalized position (0-100)PanelGraphView.updateHandleRelativePosition→ resolves to aFixper flight- Each
FixhasrawTime(seconds since midnight UTC)
When the handle moves:
- Get the current fix’s
rawTime+ flightdate→ absolute timestamp - Round to nearest 10/30 minutes (matching cached tile timestamps)
- If different from current overlay timestamp → swap tile source
Implementation Plan
Section titled “Implementation Plan”Phase 1: Tile Download + Cache
Section titled “Phase 1: Tile Download + Cache”New files in Packages/Flights/:
Sources/Flights/Satellite/SatelliteRequest.swift— Cara request for HighSight tilesSources/Flights/Satellite/SatelliteCache.swift— file-based tile cache managerSources/Flights/Satellite/SatelliteTileManager.swift— coordinates download for a flight’s bounds + time range
Phase 2: Context Menu + Download UI
Section titled “Phase 2: Context Menu + Download UI”Modified files in Modules/App/:
PanelFlightsViewController.swift— add “Download Cloud Overlay” to context menu- New download progress UI (similar to elevation download spinner)
Phase 3: Map Overlay + Scrubbing
Section titled “Phase 3: Map Overlay + Scrubbing”Modified files in Modules/App/:
FlightMapViewDataSource.swift— add satellite overlay layer managementMapViewDataSource.swift— add satellite layer typePanelGraphView.swift— on scrub, notify map of timestamp changeFlightMapViewController.swift— bridge graph timestamp to map overlay
Key Files Reference
Section titled “Key Files Reference”| Component | Path |
|---|---|
| Elevation cache pattern | Packages/Flights/Sources/Flights/Elevation/Elevation.swift |
| Elevation download | Packages/Flights/Sources/Flights/Enricher/FlightElevationEnricher.swift |
| SkySight overlay | Modules/App/Sources/Components/SkySight/Extensions/SkySightOverlay+Tile.swift |
| SkySight manager | Modules/App/Sources/Components/SkySight/SkySightManager.swift |
| Map tile management | Modules/App/Sources/Modules/Detail Map/MapViewDataSource.swift |
| Graph scrubbing | Modules/App/Sources/Modules/Detail Map/Flight/Panel/Graph/PanelGraphView.swift |
| Graph handle | Modules/App/Sources/Modules/Detail Map/Flight/Panel/Graph/Views/GraphHandleView.swift |
| Flight context menu | Modules/App/Sources/Modules/Detail Map/Flight/Panel/Flights/PanelFlightsViewController.swift |
| Cache property wrapper | @Cache<Value: Codable> in SkySight module |
Caching & Sync Strategy
Section titled “Caching & Sync Strategy”Metadata in CoreData, tiles in file cache
Section titled “Metadata in CoreData, tiles in file cache”Store in CoreFlightData (synced via iCloud):
satelliteTimestamps: [Date]— which timestamps are availablesatelliteBounds: Data— the bounding box that was downloadedisSatelliteCached: 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.
Reload when available
Section titled “Reload when available”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.
Archive limits (measured 2026-10-02)
Section titled “Archive limits (measured 2026-10-02)”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:
| Period | Coverage |
|---|---|
| before 1 January 2014 | nothing |
| 1 January 2014 – 31 December 2022 | tiles 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 2023 | the 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.
Open Questions
Section titled “Open Questions”- Does the existing
tiles.skysight.io/sat_global/endpoint work for historical dates? If yes, no new API integration needed — just cache and scrub. - HighSight pricing — 5K free tiles/month might suffice for testing; need to estimate real usage for ~1000 users.
- Overlay opacity — should it share the existing SkySight overlay opacity setting, or have its own control?
