Files
satellite-provider/README.md
T
Anton Martynenko 604f0e070d documentation
2025-11-20 12:41:07 +01:00

471 lines
12 KiB
Markdown

# Satellite Provider API
A .NET 8.0 microservice for downloading, managing, and serving satellite imagery tiles from Google Maps. Supports region-based tile requests, route planning with automatic intermediate point generation, and geofencing capabilities.
## Features
- **Tile Management**: Download and cache satellite imagery tiles from Google Maps
- **Region Requests**: Request tiles for specific geographic areas (100m - 10km squares)
- **Route Planning**: Create routes with automatic intermediate point interpolation (~200m intervals)
- **Geofencing**: Define polygon boundaries to filter regions inside/outside zones
- **Image Stitching**: Combine tiles into single images for regions
- **Tile Packaging**: Create ZIP archives of route tiles (max 50MB)
- **Async Processing**: Queue-based background processing for large requests
- **Caching**: Reuse previously downloaded tiles to minimize redundant downloads
- **REST API**: OpenAPI/Swagger documented endpoints
## Prerequisites
- Docker and Docker Compose
- .NET 8.0 SDK (for local development)
- PostgreSQL (provided via Docker)
## Quick Start
### Run the Service
```bash
docker-compose up --build
```
The API will be available at `http://localhost:5100`
Swagger documentation: `http://localhost:5100/swagger`
### Run with Tests
```bash
docker-compose -f docker-compose.yml -f docker-compose.tests.yml up --build --abort-on-container-exit
```
This command:
- Builds and starts all services
- Runs integration tests
- Exits when tests complete
## Architecture
The service follows a layered architecture:
```
┌─────────────────────────────────────┐
│ API Layer (ASP.NET Core) │ HTTP endpoints
├─────────────────────────────────────┤
│ Services (Business Logic) │ Processing, validation
├─────────────────────────────────────┤
│ Data Access (Dapper + Repos) │ Database operations
├─────────────────────────────────────┤
│ PostgreSQL Database │ Persistence
└─────────────────────────────────────┘
```
### Projects
- **SatelliteProvider.Api**: Web API and endpoints
- **SatelliteProvider.Services**: Business logic and processing
- **SatelliteProvider.DataAccess**: Database access and migrations
- **SatelliteProvider.Common**: Shared DTOs, interfaces, utilities
- **SatelliteProvider.IntegrationTests**: Integration test suite
- **SatelliteProvider.Tests**: Unit tests
## API Endpoints
### Download Single Tile
```http
GET /api/satellite/tiles/latlon?Latitude={lat}&Longitude={lon}&ZoomLevel={zoom}
```
Downloads a single tile at specified coordinates and zoom level.
**Parameters:**
- `Latitude` (double): Center latitude
- `Longitude` (double): Center longitude
- `ZoomLevel` (int): Zoom level (higher = more detail, max ~20)
**Response:**
```json
{
"id": "uuid",
"zoomLevel": 18,
"latitude": 37.7749,
"longitude": -122.4194,
"tileSizeMeters": 38.2,
"tileSizePixels": 256,
"imageType": "jpg",
"mapsVersion": "downloaded_2024-11-20",
"version": 2024,
"filePath": "18/158/90.jpg",
"createdAt": "2024-11-20T10:30:00Z",
"updatedAt": "2024-11-20T10:30:00Z"
}
```
### Request Region Tiles
```http
POST /api/satellite/request
Content-Type: application/json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"latitude": 37.7749,
"longitude": -122.4194,
"sizeMeters": 500,
"zoomLevel": 18,
"stitchTiles": false
}
```
Requests tiles for a square region. Processing happens asynchronously.
**Parameters:**
- `id` (guid): Unique identifier for the region
- `latitude` (double): Center latitude
- `longitude` (double): Center longitude
- `sizeMeters` (double): Square side length (100-10000 meters)
- `zoomLevel` (int): Zoom level (default: 18)
- `stitchTiles` (bool): Create single stitched image (default: false)
**Response:**
```json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "pending",
"csvFilePath": null,
"summaryFilePath": null,
"tilesDownloaded": 0,
"tilesReused": 0,
"createdAt": "2024-11-20T10:30:00Z",
"updatedAt": "2024-11-20T10:30:00Z"
}
```
### Get Region Status
```http
GET /api/satellite/region/{id}
```
Check processing status and get file paths when complete.
**Status Values:**
- `pending`: Queued for processing
- `processing`: Currently downloading tiles
- `completed`: All tiles ready, files created
- `failed`: Processing failed
**Response (completed):**
```json
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "completed",
"csvFilePath": "./ready/region_3fa85f64-5717-4562-b3fc-2c963f66afa6_ready.csv",
"summaryFilePath": "./ready/region_3fa85f64-5717-4562-b3fc-2c963f66afa6_summary.txt",
"tilesDownloaded": 12,
"tilesReused": 4,
"createdAt": "2024-11-20T10:30:00Z",
"updatedAt": "2024-11-20T10:32:15Z"
}
```
### Create Route
```http
POST /api/satellite/route
Content-Type: application/json
{
"id": "7fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "City Tour Route",
"description": "Downtown to waterfront",
"regionSizeMeters": 200,
"zoomLevel": 18,
"points": [
{ "latitude": 37.7749, "longitude": -122.4194, "name": "Start" },
{ "latitude": 37.7849, "longitude": -122.4094, "name": "Middle" },
{ "latitude": 37.7949, "longitude": -122.3994, "name": "End" }
],
"geofences": {
"polygons": [
{
"name": "Restricted Area",
"points": [
{ "latitude": 37.775, "longitude": -122.420 },
{ "latitude": 37.776, "longitude": -122.420 },
{ "latitude": 37.776, "longitude": -122.418 },
{ "latitude": 37.775, "longitude": -122.418 }
]
}
],
"direction": "inside"
},
"requestMaps": true,
"createTilesZip": true
}
```
Creates a route with intermediate points calculated automatically.
**Parameters:**
- `id` (guid): Route identifier
- `name` (string): Route name
- `description` (string): Optional description
- `regionSizeMeters` (double): Size of region at each point
- `zoomLevel` (int): Zoom level for tiles
- `points` (array): Original route points
- `geofences` (object): Optional polygon boundaries
- `polygons`: Array of polygon definitions
- `direction`: "inside" or "outside" (filter regions)
- `requestMaps` (bool): Automatically request tiles for route regions
- `createTilesZip` (bool): Create ZIP file of all tiles
**Response:**
```json
{
"id": "7fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "City Tour Route",
"description": "Downtown to waterfront",
"regionSizeMeters": 200,
"zoomLevel": 18,
"totalDistanceMeters": 2486.5,
"totalPoints": 15,
"points": [
{
"sequenceNumber": 0,
"latitude": 37.7749,
"longitude": -122.4194,
"pointType": "original",
"segmentIndex": 0,
"distanceFromPrevious": 0
},
{
"sequenceNumber": 1,
"latitude": 37.7765,
"longitude": -122.4177,
"pointType": "intermediate",
"segmentIndex": 0,
"distanceFromPrevious": 198.3
}
],
"regions": [
"region-id-1",
"region-id-2"
],
"tilesZipPath": "./ready/route_7fa85f64-5717-4562-b3fc-2c963f66afa6_tiles.zip",
"createdAt": "2024-11-20T10:30:00Z"
}
```
### Get Route
```http
GET /api/satellite/route/{id}
```
Retrieve route details including all interpolated points.
## Configuration
Configuration is managed through `appsettings.json`:
```json
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=satelliteprovider;Username=postgres;Password=postgres"
},
"MapConfig": {
"Service": "GoogleMaps",
"ApiKey": "your-google-maps-api-key"
},
"StorageConfig": {
"TilesDirectory": "./tiles",
"ReadyDirectory": "./ready"
},
"ProcessingConfig": {
"MaxConcurrentDownloads": 4,
"MaxConcurrentRegions": 20,
"DefaultZoomLevel": 20,
"QueueCapacity": 1000,
"DelayBetweenRequestsMs": 50,
"SessionTokenReuseCount": 100
}
}
```
### Environment Variables (Docker)
Override settings via environment variables in `docker-compose.yml`:
```yaml
environment:
- ConnectionStrings__DefaultConnection=Host=db;Database=satelliteprovider;Username=postgres;Password=postgres
- MapConfig__ApiKey=your-api-key-here
```
## File Storage
### Tiles
Downloaded tiles are stored in:
```
./tiles/{zoomLevel}/{x}/{y}.jpg
```
### Output Files
**CSV File** (`region_{id}_ready.csv`):
Comma-separated list of tiles covering the region, ordered top-left to bottom-right:
```csv
latitude,longitude,filepath
37.7750,-122.4195,18/158/90.jpg
37.7750,-122.4193,18/158/91.jpg
```
**Summary File** (`region_{id}_summary.txt`):
Processing statistics:
```
Region: 3fa85f64-5717-4562-b3fc-2c963f66afa6
Size: 500m x 500m
Center: 37.7749, -122.4194
Zoom Level: 18
Tiles Downloaded: 12
Tiles Reused: 4
Total Tiles: 16
Processing Time: 3.5s
```
**Stitched Image** (`region_{id}_stitched.jpg`):
Single combined image when `stitchTiles: true`
**Route ZIP** (`route_{id}_tiles.zip`):
ZIP archive of all tiles for route when `createTilesZip: true` (max 50MB)
## Database Schema
### Tables
**tiles**: Cached satellite tiles
- Unique per (latitude, longitude, tile_size_meters, zoom_level)
- Tracks maps version for cache invalidation
**regions**: Region requests and processing status
- Status tracking: pending → processing → completed/failed
- Links to output files when complete
**routes**: Route definitions
- Stores original configuration
- Links to calculated points and regions
**route_points**: All points (original + interpolated)
- Sequenced in route order
- Type: "original" or "intermediate"
- Includes distance calculations
**route_regions**: Junction table
- Links routes to regions for tile requests
- Tracks geofence status (inside/outside)
## Development
### Building Locally
```bash
cd SatelliteProvider.Api
dotnet build
dotnet run
```
### Running Tests
**Unit Tests:**
```bash
cd SatelliteProvider.Tests
dotnet test
```
**Integration Tests:**
```bash
docker-compose -f docker-compose.yml -f docker-compose.tests.yml up --build --abort-on-container-exit
```
### Database Migrations
Migrations run automatically on startup. SQL files are located in:
```
SatelliteProvider.DataAccess/Migrations/
```
To add a new migration:
1. Create `NNN_DescriptiveName.sql` (increment number)
2. Set Build Action to `EmbeddedResource`
3. Restart application
### Logging
Logs are written to:
- Console (stdout)
- `./logs/satellite-provider-YYYYMMDD.log`
Log level can be adjusted in `appsettings.json` under `Serilog:MinimumLevel`.
## Performance Considerations
### Tile Caching
- Tiles are cached indefinitely (no expiration)
- Reused across multiple region/route requests
- Reduces redundant downloads significantly
### Concurrent Processing
- Configurable concurrent downloads (default: 4)
- Configurable concurrent regions (default: 20)
- Queue capacity: 1000 requests
### Rate Limiting
- Delay between requests: 50ms default
- Session token reuse: 100 tiles per token
- Helps avoid Google Maps rate limits
### Geofencing
- Uses point-in-polygon algorithm
- Reduces unnecessary tile downloads
- Spatial index on polygon data
## Troubleshooting
### Service won't start
- Check Docker is running
- Verify ports 5100 and 5432 are available
- Check logs: `docker-compose logs api`
### Tiles not downloading
- Verify Google Maps API key is configured
- Check internet connectivity
- Review logs for HTTP errors
- Check rate limiting settings
### Region stuck in "processing"
- Check background service is running: `docker-compose logs api`
- Verify database connection
- Check queue capacity not exceeded
- Review logs for exceptions
### Tests failing
- Ensure all containers are healthy before tests run
- Check test container logs: `docker-compose logs tests`
- Verify database migrations completed
- Check network connectivity between containers
### Out of disk space
- Tiles accumulate in `./tiles/` directory
- Implement cleanup strategy for old tiles
- Monitor disk usage
## License
This project is proprietary software.
## Support
For issues and questions, please contact the development team.