# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a collection of FastAPI-based tile servers that use rio-tiler to serve COG (Cloud Optimized GeoTIFF) files as dynamic map tiles. The project serves imagery from different planetary bodies: - **app.py** - NJ 2020 aerial imagery (I7D16.tif) - UInt16 data with per-band rescaling - **app_world.py** - Natural Earth global basemap (HYP_HR_SR_OB_DR_cog.tif) - uint8 RGB - **app_mars.py** - Mars MGS MOLA global basemap - Uses custom Mars CRS in equidistant cylindrical projection ## Running the Servers Each app is an independent FastAPI application. Run with uvicorn: ```bash # Run NJ imagery server (default port 8000) uvicorn app:app --reload # Run world basemap server uvicorn app_world:app --port 8001 --reload # Run Mars basemap server uvicorn app_mars:app --port 8002 --reload ``` ## Architecture ### Common Pattern (app.py, app_world.py) All apps follow the same pattern using `rio_tiler.io.Reader`: 1. **Tile endpoint** (`/tiles/{z}/{x}/{y}.png`) - Reads COG tiles and returns PNG 2. **TileJSON endpoint** (`/tilejson.json`) - Returns TileJSON 2.2.0 metadata 3. **Optional viewer** (`/`) - app.py includes a Leaflet.js viewer Key rio-tiler concepts: - `Reader(DATA_PATH)` context manager for reading COGs - `cog.tile(x, y, z)` to extract tiles - `img.render()` with `img_profiles` for output formatting - `cog.get_geographic_bounds()` for bounds in geographic CRS - `cog.minzoom`/`cog.maxzoom` for zoom levels ### Mars CRS (app_mars.py) The Mars server uses a **custom TileMatrixSet** to handle Mars-specific coordinate system: - **CRS**: Simple Cylindrical Mars (equidistant cylindrical, meters) - **Sphere radius**: 3,396,190 m (vs Earth's ~6,378,137 m) - **Extent**: ±πR in x, ±πR/2 in y (calculated from Mars radius) - **matrix_scale=[2, 1]**: 2×1 tiles at zoom 0, matching Cesium's GeographicTilingScheme **Critical design decision**: TMS and TIF both use the same Mars CRS (eqc meters). Avoid using longlat (degrees) because PROJ would attempt datum transformation through WGS84, causing "PROJ: eqc: Invalid latitude" errors due to the large sphere radius difference. ```python MARS_CRS = CRS.from_proj4( "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396190 +b=3396190 +units=m +no_defs" ) MARS_TMS = TileMatrixSet.custom( crs=MARS_CRS, extent=(-_MAX_X, -_MAX_Y, _MAX_X, _MAX_Y), matrix_scale=[2, 1], ) ``` Pass custom TMS to Reader: `Reader(DATA_PATH, tms=MARS_TMS)` ## Data Handling ### Rescaling (app.py) For UInt16 imagery, use per-band rescaling based on p2/p98 statistics: ```python RESCALE = [ (6130, 40453), # Red (10641, 41211), # Green (12482, 36323), # Blue ] img.render(img_format="PNG", rescale=RESCALE, **img_profiles.get("png", {})) ``` ### uint8 Data (app_world.py, app_mars.py) No rescaling needed for pre-scaled uint8 imagery: ```python img.render(img_format="PNG", **img_profiles.get("png", {})) ``` ## Dependencies Managed via uv with `pyproject.toml`: - fastapi - Web framework - rio-tiler - COG tiling engine - rasterio - GeoTIFF I/O - uvicorn - ASGI server - pyproj, morecantile - CRS and TMS handling (for Mars app) ## Adding New Tile Servers To add a new COG tile server: 1. Copy the appropriate template (app.py for UInt16, app_world.py/app_mars.py for uint8) 2. Update `DATA_PATH` to point to your COG file in `data/` 3. For non-Earth data, create a custom CRS/TMS following app_mars.py pattern 4. Adjust rescaling if needed for UInt16 data 5. Update app metadata (title, description) 6. Run with uvicorn on a unique port ## File Structure ``` data/ # COG files (gitignored) ├── I7D16.tif ├── HYP_HR_SR_OB_DR_cog.tif └── Mars_MGS_MOLA_...tif app.py # NJ imagery server app_world.py # World basemap server app_mars.py # Mars basemap server pyproject.toml # uv dependencies ```