# Module: Services/TileService ## Purpose Orchestrates tile downloading and persistence. Bridges the downloader (Google Maps) with the tile repository (PostgreSQL), handling in-memory caching, entity creation, and metadata mapping. Single ownership point for all tile read/write business logic — both region-batch and single-tile API endpoints route through this service. **csproj**: `SatelliteProvider.Services.TileDownloader/TileService.cs` ## Public Interface ### TileService (implements ITileService) - `DownloadAndStoreTilesAsync(double lat, double lon, double sizeMeters, int zoomLevel, CancellationToken) → Task>`: 1. Queries existing tiles in the region from the repository — most-recent across sources per `(latitude, longitude, tile_zoom, tile_size_meters)` (AZ-484 selection rule applied by `TileRepository.GetTilesByRegionAsync` via `DISTINCT ON`) 2. Calls `ISatelliteDownloader.GetTilesWithMetadataAsync` with existing tiles to skip 3. Creates `TileEntity` for each newly downloaded tile and inserts via repository (per-source UPSERT keyed on `(latitude, longitude, tile_zoom, tile_size_meters, source)`) 4. Returns combined list of existing + new tile metadata - `GetTileAsync(Guid id) → Task`: single tile lookup - `GetTilesByRegionAsync(double lat, double lon, double sizeMeters, int zoomLevel) → Task>`: query tiles in a region - `GetOrDownloadTileAsync(int z, int x, int y, CancellationToken) → Task` (AZ-310): cache → repository → downloader fallback for single Z/X/Y serving - `DownloadAndStoreSingleTileAsync(double latitude, double longitude, int zoomLevel, CancellationToken) → Task` (AZ-311): download one tile by lat/lon, persist, return metadata ## Internal Logic - New rows write `Version = null` and `MapsVersion = null` (post-AZ-357 / AZ-373); the `version` and `maps_version` columns are retained for backward compatibility with pre-existing rows - AZ-484: `BuildTileEntity` stamps every newly downloaded row with `Source = TileSourceConverter.ToWireValue(TileSource.GoogleMaps)` (wire value `"google_maps"`) and `CapturedAt = DateTime.UtcNow`. The Google Maps download path is the only producer of `'google_maps'` rows; UAV ingestion (separate task) is the only producer of `'uav'` rows. - AZ-503: `BuildTileEntity` computes deterministic identity fields — `Id = Uuidv5.Create(Uuidv5.TileNamespace, "{z}/{x}/{y}/google_maps/00000000-0000-0000-0000-000000000000")`, `LocationHash = Uuidv5.Create(Uuidv5.TileNamespace, "{z}/{x}/{y}")`, `ContentSha256 = SHA256.HashData()`. `FlightId` is always `null` for Google Maps tiles. No `Guid.NewGuid()` remains on this path. - `MapToMetadata(TileEntity) → TileMetadata`: entity-to-DTO mapping (static helper); `MapsVersion` is no longer projected onto `TileMetadata` / `DownloadTileResponse`. `Source`, `CapturedAt`, `FlightId`, `LocationHash`, `ContentSha256` are not currently projected to the public DTO (no API contract change observable for AZ-484 or AZ-503). - `TileSizePixels` sourced from `MapConfig.TileSizePixels` (default 256, post-AZ-371); image type fixed at `"jpg"` - `IMemoryCache` keyed by `(z, x, y)` with 1h absolute / 30min sliding expiration; populated on first hit and on downloader fallback ## Dependencies - `ISatelliteDownloader` (resolved via DI; concrete is `GoogleMapsDownloaderV2`) - `ITileRepository` - `IMemoryCache` (registered by `AddTileDownloader()`) - `SatelliteProvider.Common.DTO` — GeoPoint, TileMetadata, TileBytes - `SatelliteProvider.Common.Enums` — `TileSource`, `TileSourceConverter` (AZ-484) - `SatelliteProvider.Common.Utils.Uuidv5` (AZ-503) — deterministic UUIDv5 generator + `TileNamespace` constant - `System.Security.Cryptography.SHA256` (AZ-503) — content digest - `SatelliteProvider.DataAccess.Models` — TileEntity ## Consumers - `RegionService.ProcessRegionAsync` — downloads and retrieves tiles for a region ## Data Models Transforms between `TileEntity` (persistence) and `TileMetadata` (DTO). ## Configuration None directly; relies on `GoogleMapsDownloaderV2`'s configuration. ## External Integrations Indirect: Google Maps (via downloader), PostgreSQL (via repository). ## Security None. ## Tests No dedicated tests.