mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 20:11:17 +00:00
[AZ-484] Multi-source tile storage: source + captured_at
Add per-source tile rows to support multi-provider imagery (Google Maps + future UAV). Migration 013 (transactional) introduces source/captured_at columns, backfills existing rows to (source='google_maps', captured_at=created_at), and replaces the 4-column unique index with a 5-column index that includes source. TileRepository: - ColumnList includes source + captured_at - GetByTileCoordinatesAsync returns most-recent row across sources (ORDER BY captured_at DESC, updated_at DESC, id DESC) - GetTilesByRegionAsync uses DISTINCT ON to pick the most-recent tile per cell, restoring caller-facing row order - Insert/Update upsert on the new 5-column conflict key TileSource enum lives in Common.Enums. Snake_case wire format (google_maps, uav) is enforced by a focused TileSourceTypeHandler because the generic ToLowerInvariant pattern would emit "googlemaps", violating contract v1.0.0. TileService stamps Source=GoogleMaps + CapturedAt=UtcNow on every new tile. Tile-storage contract is now frozen at v1.0.0. AC coverage 7/7. New unit + integration tests cover all ACs; existing 200 unit + 5 smoke tests preserved. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,20 +20,22 @@ The three Layer-3 service components are compile-time siblings: each only refere
|
||||
|
||||
**Architectural principles** (inferred):
|
||||
- Single-instance deployment, no horizontal scaling requirements (`inferred-from: Channel-based queue, no distributed state`)
|
||||
- Immutable tile storage with year-based versioning for cache invalidation (`inferred-from: version column + unique index`)
|
||||
- Append-by-source tile storage — multiple producers (Google Maps, UAV upload, future SatAR, …) can each persist a row per `(latitude, longitude, tile_zoom, tile_size_meters)` cell. Reads return the most-recent row across sources, ordered by `captured_at DESC` with deterministic `(updated_at DESC, id DESC)` tie-breaks. The single-row-per-cell-per-source invariant is enforced by the 5-column unique index `idx_tiles_unique_location_source` introduced in migration 013 (AZ-484). The `tiles.version` column is vestigial since AZ-357 dropped year-based cache invalidation in favour of cell-level overwrite. (`inferred-from: tiles table + AZ-484/AZ-357 migrations + tile-storage contract v1.0.0`)
|
||||
- Fire-and-forget async processing with status polling (`inferred-from: queue + background service + status endpoint`)
|
||||
- No authentication layer — designed as an internal/trusted network service (`inferred-from: no auth middleware in Program.cs`)
|
||||
|
||||
**Planned features** (confirmed by user, currently stubs):
|
||||
- MGRS endpoint — tile access via Military Grid Reference System coordinates
|
||||
- Upload endpoint — UAV nadir camera tile ingestion (Layer 2: orthogonal tiles uploaded post-flight, stored alongside Google Maps Layer 1; most recent layer returned on access)
|
||||
- Upload endpoint — UAV nadir camera tile ingestion. Writes a row with `source='uav'` for the captured cell; the storage layer accepts it alongside any existing Google Maps row, and reads return whichever has the highest `captured_at`. AZ-484 has built the multi-source storage; the upload endpoint itself (T2 — AZ-485) and any quality-gate logic remain to be implemented.
|
||||
|
||||
The N-source storage contract is authoritative in `_docs/02_document/contracts/data-access/tile-storage.md` (v1.0.0). Anything that reads or writes `tiles` MUST follow that contract rather than re-deriving the rules from prose here.
|
||||
|
||||
**Drift signals**:
|
||||
- `geofence_polygons` mentioned in AGENTS.md as a routes table column but does not exist in schema or entity — documentation drift
|
||||
|
||||
## 1. System Context
|
||||
|
||||
**Problem being solved**: A GPS-denied UAV navigation service requires satellite imagery for positioning and route planning without GPS. This service pre-downloads Google Maps satellite tiles (Layer 1) for specified regions and routes, accepts UAV-captured nadir camera imagery uploaded post-flight (Layer 2), and serves the most recent tile layer on access. Tiles are stitched into composite images and packaged for offline use.
|
||||
**Problem being solved**: A GPS-denied UAV navigation service requires satellite imagery for positioning and route planning without GPS. This service pre-downloads satellite tiles from one or more imagery sources (currently Google Maps; future sources including UAV nadir camera upload and additional providers such as SatAR) for specified regions and routes, stores them alongside each other under a per-source storage key, and serves the most-recent row across sources on access. Tiles are stitched into composite images and packaged for offline use.
|
||||
|
||||
**System boundaries**: The Satellite Provider is a self-contained backend service. It receives HTTP requests (region/route definitions), downloads tiles from Google Maps, stores them on disk and in PostgreSQL, and produces output files (images, CSVs, ZIPs).
|
||||
|
||||
@@ -41,8 +43,8 @@ The three Layer-3 service components are compile-time siblings: each only refere
|
||||
|
||||
| System | Integration Type | Direction | Purpose |
|
||||
|--------|-----------------|-----------|---------|
|
||||
| Satellite imagery provider (e.g., Google Maps) | HTTPS (tile download) | Outbound | Layer 1 satellite imagery source (provider-agnostic via `ISatelliteDownloader`) |
|
||||
| GPS-Denied Service (UAV) | REST API | Inbound | Layer 2 nadir camera tile uploads post-flight |
|
||||
| Satellite imagery provider (e.g., Google Maps) | HTTPS (tile download) | Outbound | First implementation of the multi-source `tiles` storage; provider-agnostic via `ISatelliteDownloader`. Stamps `source='google_maps'` on every persisted row. |
|
||||
| GPS-Denied Service (UAV) | REST API | Inbound | Future producer of `source='uav'` rows via the upload endpoint (T2 — AZ-485). The storage layer (AZ-484) is already in place; the endpoint itself is still a stub. |
|
||||
| PostgreSQL | TCP (Npgsql) | Both | Tile metadata, region/route state |
|
||||
| File System | Local disk | Both | Tile image storage, output artifacts |
|
||||
| HTTP Clients | REST API | Inbound | Region/route requests, tile queries |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
**Producer task**: AZ-484 — `_docs/02_tasks/todo/AZ-484_multi_source_tile_storage.md`
|
||||
**Consumer tasks**: AZ-485 (planned T2 — UAV upload endpoint); future tasks adding additional sources (e.g., SatAR)
|
||||
**Version**: 1.0.0
|
||||
**Status**: draft (frozen on AZ-484 implementation completion)
|
||||
**Status**: frozen
|
||||
**Last Updated**: 2026-05-11
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
| Geofence | A rectangular geographic boundary (NW + SE corners) used to filter which route points receive map tile coverage | components/05_route_management/description.md |
|
||||
| Zoom Level | Google Maps tile resolution level (1–20); higher = more detail, smaller ground coverage per tile | modules/common_configs.md |
|
||||
| Stitch | Compositing multiple tiles into a single larger image with optional markers/borders | modules/services_region_service.md |
|
||||
| Layer 1 | Satellite imagery from external providers (provider-agnostic; first implementation: Google Maps) | user clarification |
|
||||
| Layer 2 | UAV-captured nadir camera imagery (orthogonal tiles uploaded post-flight) | user clarification |
|
||||
| Layer 1 | Historic name for satellite imagery from external providers (provider-agnostic; first implementation: Google Maps). Generalised in AZ-484 to one of N values of `Tile Source`; the term is retained for continuity with earlier docs and tickets. | user clarification, AZ-484 |
|
||||
| Layer 2 | Historic name for UAV-captured nadir camera imagery (orthogonal tiles uploaded post-flight). Generalised in AZ-484 to the `uav` `Tile Source` value; the term is retained for continuity with earlier docs and tickets. | user clarification, AZ-484 |
|
||||
| Tile Source | The producer of a tile row, persisted in `tiles.source` as a contract-defined string (`google_maps`, `uav`, …). Each cell may have at most one row per source; reads return the most-recent across sources. Adding a new source requires a new `TileSource` enum member and a tile-storage contract version bump. | _docs/02_document/contracts/data-access/tile-storage.md (v1.0.0) |
|
||||
| Captured At | Producer-defined UTC timestamp ("the moment this tile imagery represents") persisted in `tiles.captured_at`. For Google Maps it is `DateTime.UtcNow` at download time (provider does not expose original imagery date); for UAV it is the capture timestamp supplied by the upload client. Drives the most-recent-across-sources read selection rule. | _docs/02_document/contracts/data-access/tile-storage.md (v1.0.0) |
|
||||
| Nadir Camera | Downward-facing camera on a UAV capturing ground imagery during flight | user clarification |
|
||||
| GPS-Denied Service | The consuming system: a UAV navigation service operating without GPS, using satellite/UAV imagery for positioning | user clarification |
|
||||
| Slippy Map Coordinates | Tile X/Y indices in the Web Mercator projection grid (standard for web map tile servers) | data_model.md |
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
- `SatelliteProvider.Common/Configs/ProcessingConfig.cs`
|
||||
- `SatelliteProvider.Common/Configs/DatabaseConfig.cs`
|
||||
- `SatelliteProvider.Common/DTO/*.cs` (all DTOs)
|
||||
- `SatelliteProvider.Common/Enums/RegionStatus.cs`
|
||||
- `SatelliteProvider.Common/Enums/RoutePointType.cs`
|
||||
- `SatelliteProvider.Common/Enums/TileSource.cs` (added by AZ-484; backed by the `tile-storage` v1.0.0 contract)
|
||||
- `SatelliteProvider.Common/Exceptions/RateLimitException.cs`
|
||||
- `SatelliteProvider.Common/Interfaces/*.cs` (all service interfaces)
|
||||
- `SatelliteProvider.Common/Utils/GeoUtils.cs`
|
||||
|
||||
Reference in New Issue
Block a user