Files
satellite-provider/_docs/02_document/modules/services_tile_service.md
T
Oleksandr Bezdieniezhnykh 61612044fb [AZ-503] [AZ-504] Cycle 5 Steps 11-15 sync
Wrap up cycle 5 verification + documentation:
- Steps 10/11 wrap-up reports (implementation_completeness +
  implementation_report) for the AZ-503-foundation + AZ-504 batch.
- Step 12 test-spec sync: AZ-503-foundation/AZ-504 ACs appended;
  AZ-505 deferred ACs recorded.
- Step 13 update-docs: architecture, data-model, glossary, module-
  layout, uav-tile-upload contract (v1.1.0), DataAccess + Services
  + Tests module docs synced; new common_uuidv5.md module doc.
- Step 14 security audit: PASS_WITH_WARNINGS; 0 new Critical/High;
  2 new Low informational (F1 flightId provenance, F2 pgcrypto
  deploy gap).
- Step 15 performance test: PASS_WITH_INFRA_WARNINGS; PT-08
  passed twice (AZ-504 fix verified); PT-01/02 failed due to
  recurring local Docker/colima DNS cold-start (not an app
  regression). Cycle-3 perf-harness leftover stays OPEN with
  replay #5 documented.
- Autodev state moved to Step 16 (Deploy).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 18:01:27 +03:00

4.2 KiB

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<List<TileMetadata>>:
    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<TileMetadata?>: single tile lookup
  • GetTilesByRegionAsync(double lat, double lon, double sizeMeters, int zoomLevel) → Task<IEnumerable<TileMetadata>>: query tiles in a region
  • GetOrDownloadTileAsync(int z, int x, int y, CancellationToken) → Task<TileBytes> (AZ-310): cache → repository → downloader fallback for single Z/X/Y serving
  • DownloadAndStoreSingleTileAsync(double latitude, double longitude, int zoomLevel, CancellationToken) → Task<TileMetadata> (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(<jpeg bytes from disk>). 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.EnumsTileSource, 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.