# Module: Services/GoogleMapsDownloaderV2

## Purpose
Downloads satellite imagery tiles from Google Maps. Handles session token management, concurrent download throttling, retry logic with exponential backoff, and tile deduplication.

**csproj**: `SatelliteProvider.Services.TileDownloader/GoogleMapsDownloaderV2.cs`

## Public Interface

### GoogleMapsDownloaderV2
- Constructor: `GoogleMapsDownloaderV2(ILogger, IOptions<MapConfig>, IOptions<StorageConfig>, IOptions<ProcessingConfig>, IHttpClientFactory)`
- `DownloadSingleTileAsync(double lat, double lon, int zoomLevel, CancellationToken) → Task<DownloadedTileInfoV2>`: downloads one tile at specified coordinates. Validates zoom level, creates session token, downloads image, saves to disk.
- `GetTilesWithMetadataAsync(GeoPoint center, double radiusM, int zoomLevel, IEnumerable<TileEntity> existingTiles, CancellationToken) → Task<List<DownloadedTileInfoV2>>`: downloads all tiles in a bounding box, skipping those already present in `existingTiles`. Manages session token rotation.

### DownloadedTileInfoV2 (record)
- `X`, `Y` (int), `ZoomLevel` (int), `CenterLatitude`, `CenterLongitude` (double), `FilePath` (string), `TileSizeMeters` (double)

### RateLimitException (exception)
Lives in `SatelliteProvider.Common.Exceptions` (relocated from this module in epic AZ-309 so RegionProcessing can catch it without acquiring a `ProjectReference` to TileDownloader). Thrown when Google Maps returns 429 Too Many Requests and retries are exhausted.

## Internal Logic
- **Allowed zoom levels**: 15, 16, 17, 18, 19 — throws `ArgumentException` for others
- **URL template**: `https://mt{server}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}&token={token}`
- **Session tokens**: obtained via `https://tile.googleapis.com/v1/createSession?key={apiKey}`, rotated every `SessionTokenReuseCount` tiles (default: 100)
- **Concurrency control**: `SemaphoreSlim` limits parallel downloads to `MaxConcurrentDownloads` (default: 4)
- **Deduplication**: `ConcurrentDictionary<string, Task<DownloadedTileInfoV2>>` (`_activeDownloads`) prevents duplicate concurrent downloads of the same tile
- **Retry logic**: exponential backoff (1s base, 30s max, 5 retries) for 429 and 5xx errors. Cancellation and auth errors (401, 403) propagate immediately.
- **Server selection**: `(x + y) % 4` distributes requests across `mt0`–`mt3`; single-tile downloads always use `mt0`
- **Delay between requests**: configurable via `ProcessingConfig.DelayBetweenRequestsMs`
- **Tile size calculation**: `CalculateTileSizeInMeters` uses Earth circumference × cos(latitude) / (2^zoom × 256)

## Dependencies
- `SatelliteProvider.Common.Configs` — MapConfig, StorageConfig, ProcessingConfig
- `SatelliteProvider.Common.DTO` — GeoPoint
- `SatelliteProvider.Common.Exceptions` — RateLimitException
- `SatelliteProvider.Common.Utils` — GeoUtils
- `SatelliteProvider.DataAccess.Models` — TileEntity (for existingTiles parameter)
- NuGet: `Newtonsoft.Json`, `Microsoft.Extensions.Http`, `Microsoft.Extensions.Options`

## Consumers
- `TileService` — `GetTilesWithMetadataAsync` and `DownloadSingleTileAsync` (the API endpoints reach this class only through `ITileService` post AZ-310 / AZ-311)

## Data Models
Produces `DownloadedTileInfoV2` records; accepts `TileEntity` for cache checks.

## Configuration
| Config | Key | Used For |
|--------|-----|----------|
| MapConfig | ApiKey | Session token requests |
| StorageConfig | TilesDirectory | File save paths |
| ProcessingConfig | MaxConcurrentDownloads | SemaphoreSlim capacity |
| ProcessingConfig | DelayBetweenRequestsMs | Throttle delay |
| ProcessingConfig | SessionTokenReuseCount | Token rotation threshold |

## External Integrations
| Integration | Protocol | Details |
|-------------|----------|---------|
| Google Maps Tile API | HTTPS | `mt*.google.com/vt/lyrs=s` for tiles |
| Google Maps Session API | HTTPS | `tile.googleapis.com/v1/createSession` |
| File system | Local FS | Writes JPEG tiles to `StorageConfig.TilesDirectory` |

## Security
- API key transmitted over HTTPS to Google endpoints
- User-Agent spoofs a Chrome browser to match expected Google Maps client

## Tests
No dedicated unit tests (the test file `GoogleMapsDownloaderTests.cs` contains only a dummy test).
