3.9 KiB
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:
# 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:
- Tile endpoint (
/tiles/{z}/{x}/{y}.png) - Reads COG tiles and returns PNG - TileJSON endpoint (
/tilejson.json) - Returns TileJSON 2.2.0 metadata - Optional viewer (
/) - app.py includes a Leaflet.js viewer
Key rio-tiler concepts:
Reader(DATA_PATH)context manager for reading COGscog.tile(x, y, z)to extract tilesimg.render()withimg_profilesfor output formattingcog.get_geographic_bounds()for bounds in geographic CRScog.minzoom/cog.maxzoomfor 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.
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:
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:
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:
- Copy the appropriate template (app.py for UInt16, app_world.py/app_mars.py for uint8)
- Update
DATA_PATHto point to your COG file indata/ - For non-Earth data, create a custom CRS/TMS following app_mars.py pattern
- Adjust rescaling if needed for UInt16 data
- Update app metadata (title, description)
- 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