# Refactor: consolidate Earth-geometry constants and magic 111000

**Task**: AZ-377_refactor_consolidate_earth_constants
**Name**: Single home for Earth + tile-pixel constants
**Description**: Move Earth-geometry constants (`EarthRadiusMeters`, `EarthEquatorialCircumferenceMeters`, `MetersPerDegreeLatitude`) to `GeoUtils`; move `TileSizePixels` to `MapConfig`. Replace duplicate literals at all sites.
**Complexity**: 2 points
**Dependencies**: AZ-371 (C18 — TileSizePixels move into config)
**Component**: Common + DataAccess + Services.TileDownloader
**Tracker**: AZ-377
**Epic**: AZ-350

## Problem

Three Earth-related constants drift across the codebase:
- `GeoUtils.EARTH_RADIUS = 6378137` (m).
- `GoogleMapsDownloaderV2.CalculateTileSizeInMeters: EARTH_CIRCUMFERENCE_METERS = 40075016.686`.
- `TileRepository.GetTilesByRegionAsync: EARTH_CIRCUMFERENCE_METERS = 40075016.686` (duplicate).
- `TileRepository.GetTilesByRegionAsync: 111000.0` (meters per degree latitude approximation, twice).
- `TILE_SIZE_PIXELS = 256` at three sites (`TileRepository:83`, `GoogleMapsDownloaderV2:18`, `TileService:152`).

## Outcome

- Earth constants are named `public const`s on `GeoUtils` (or a sibling `GeoConstants` class).
- Per-degree-latitude approximation has a single named source.
- `TileSizePixels` lives on `MapConfig` (per C18).
- All duplicate literal sites use the named constants.
- Numerically identical results.
- 37 unit + 5 smoke tests stay green.

## Scope

### Included
- Add `public const double EarthRadiusMeters = 6378137d;`, `public const double EarthEquatorialCircumferenceMeters = 40075016.686d;`, `public const double MetersPerDegreeLatitude = 111000d;` (or refine to a more precise value if the existing usage allows).
- Replace literal sites with the named constants.
- Move `TileSizePixels` to `MapConfig` (depend on C18).

### Excluded
- Switching to a different Earth model (e.g., WGS84 with full geodesic).
- Refining constants beyond what's already in code.

## Acceptance Criteria

**AC-1: One source per constant**
Given the post-refactor source
When grepped for `6378137`, `40075016.686`, `111000`, and (after C18) `256`
Then matches are confined to the named-constant declarations or `appsettings.json` defaults.

**AC-2: Numerically identical results**
Given the existing region/route scenarios
When the post-refactor code runs
Then computed distances and tile-size results are byte-for-byte identical (within IEEE 754 tolerance).

**AC-3: Tests stay green**
Given the post-refactor build
When `scripts/run-tests.sh --smoke` runs
Then all 37 unit + 5 smoke scenarios pass.

## Constraints

- No numerical drift — use the same literal values as before.

## Risks & Mitigation

**Risk 1: ordering with C18**
- *Risk*: `TileSizePixels` move depends on C18 landing first.
- *Mitigation*: this ticket declares C18 as a dependency.

Full change entry: `_docs/04_refactoring/03-code-quality-refactoring/list-of-changes.md` (C24).
