# Web Mercator Utils Helper

## Interface Definition

**Interface Name**: `IWebMercatorUtils`

### Interface Methods

```python
class IWebMercatorUtils(ABC):
    @abstractmethod
    def latlon_to_tile(self, lat: float, lon: float, zoom: int) -> Tuple[int, int]:
        pass
    
    @abstractmethod
    def tile_to_latlon(self, x: int, y: int, zoom: int) -> Tuple[float, float]:
        pass
    
    @abstractmethod
    def compute_tile_bounds(self, x: int, y: int, zoom: int) -> TileBounds:
        pass
    
    @abstractmethod
    def get_zoom_gsd(self, lat: float, zoom: int) -> float:
        pass
```

## Component Description

Web Mercator projection (EPSG:3857) for tile coordinates. Used for Google Maps tiles.

## API Methods

### `latlon_to_tile(lat: float, lon: float, zoom: int) -> Tuple[int, int]`

**Description**: Converts GPS to tile coordinates.

**Formula**:
```
n = 2^zoom
x = floor((lon + 180) / 360 * n)
lat_rad = lat * π / 180
y = floor((1 - log(tan(lat_rad) + sec(lat_rad)) / π) / 2 * n)
```

**Returns**: (x, y) tile coordinates

---

### `tile_to_latlon(x: int, y: int, zoom: int) -> Tuple[float, float]`

**Description**: Converts tile coordinates to GPS (NW corner).

**Formula** (inverse of above)

---

### `compute_tile_bounds(x: int, y: int, zoom: int) -> TileBounds`

**Description**: Computes GPS bounding box of tile.

**Returns**:
```python
TileBounds:
    nw: GPSPoint  # North-West
    ne: GPSPoint  # North-East
    sw: GPSPoint  # South-West
    se: GPSPoint  # South-East
    center: GPSPoint
    gsd: float
```

---

### `get_zoom_gsd(lat: float, zoom: int) -> float`

**Description**: Gets GSD for zoom level at latitude.

**Formula**:
```
gsd = 156543.03392 * cos(lat * π/180) / 2^zoom
```

## Dependencies

**External**: numpy

## Test Cases

1. GPS to tile at zoom 19 → valid tile coords
2. Tile to GPS → inverse correct
3. Compute bounds → 4 corners valid
4. GSD at zoom 19, Ukraine → ~0.3 m/pixel

