# RF Propagation API HTTP API for radio visibility, terrain profiles, Fresnel/LOS checks, link budget, viewshed, and coverage calculations. This repository follows `SPEC.md`. The first implementation pass creates the full service skeleton, pure RF/math kernels, and Copernicus DEM sampling. Integrations that require PostGIS data, pycraf, ITM/P.1812, landcover/canopy rasters, or external viewshed binaries are exposed through stable interfaces and return explicit "not implemented" responses until the corresponding data pipeline is connected. ## Layout - `api/` - FastAPI service, core calculations, service orchestration, tests. - `scripts/` - data bootstrap/import entry points. - `data/` - mounted data volume for DEM, landcover, and canopy rasters. - `docker-compose.yml` - local stack with API, worker, Redis, PostGIS, optional tiler. - `API.md` - current HTTP endpoints, examples, and implementation status. ## Quick Start ```bash cp .env.example .env docker compose up --build api redis postgis ``` The API is served at `http://localhost:${API_PORT:-5603}`, with OpenAPI docs at `/docs`. ## DEM Bootstrap Download Copernicus DEM GLO-30 COG tiles for Saint Petersburg and Leningrad Oblast: ```bash python scripts/bootstrap_dem.py --bbox 27.3,58.4,35.8,61.4 --output-dir data/dem docker compose restart api worker ``` The API samples all `.tif`/`.tiff` files under `DEM_PATH` recursively. In Docker, the default `DEM_PATH=/data/dem` points to the mounted `./data/dem` directory. ## Buildings Bootstrap Download an OSM PBF extract and import building polygons into PostGIS: ```bash mkdir -p data/osm wget -O data/osm/northwestern-fed-district-latest.osm.pbf \ https://download.geofabrik.de/russia/northwestern-fed-district-latest.osm.pbf sh scripts/load_buildings.sh data/osm/northwestern-fed-district-latest.osm.pbf docker compose restart api worker ``` The script builds a local `radio-osm2pgsql:latest` image from `scripts/Dockerfile.osm2pgsql` on first run. It imports the PBF through standard `osm2pgsql` tables, then normalizes `planet_osm_polygon` into the API `buildings` table with `db/sql/normalize_buildings.sql`. The import writes a `buildings` table with `geom`, `height_m`, `levels`, `building_type`, and `source`. Heights come from `height`, then `building:levels * 3.0`, then an estimated default by building type. Check the API: ```bash curl -s -X POST http://localhost:5603/api/v1/buildings/query \ -H 'Content-Type: application/json' \ -d '{"bbox":[30.30,59.93,30.33,59.95]}' | jq '.features | length' ``` Check data availability and run a minimal integration smoke test: ```bash curl -s http://localhost:5603/api/v1/status/data | jq API_BASE=http://localhost:5603 sh scripts/smoke_api.sh ``` ## Landcover And Canopy Place ESA WorldCover GeoTIFF/COG files under `data/landcover` and optional canopy height GeoTIFF/COG files under `data/canopy`, then restart the API: ```bash python scripts/bootstrap_landcover.py --bbox 27.3,58.4,35.8,61.4 --landcover-dir data/landcover python scripts/bootstrap_canopy.py --bbox 27.3,58.4,35.8,61.4 --output-dir data/canopy docker compose restart api worker ``` Or download manually into `data/landcover` and `data/canopy`. If the canopy tile index is unavailable, pass a newline-separated list of COG URLs with `scripts/bootstrap_canopy.py --urls-file urls.txt --output-dir data/canopy`. If direct download is blocked, use a SOCKS5 proxy (`pip install PySocks`): ```bash python scripts/bootstrap_canopy.py \ --bbox 27.3,58.4,35.8,61.4 \ --output-dir data/canopy \ --proxy socks5h://user:pass@host:port ``` Use `socks5h://` (DNS via proxy), same as `curl --proxy socks5h://`. Plain `socks5://` resolves hostnames locally and may hang if local DNS is blocked. The same URL can be passed via `CANOPY_PROXY` env var instead of `--proxy`. The `/api/v1/landcover/path` endpoint samples all `.tif`/`.tiff` files under `LANDCOVER_PATH` recursively. Canopy data is optional; when it is missing, `canopy_height_m` is returned as `null`. When WorldCover is available, `/api/v1/link/budget` and `/api/v1/terrain/los` can include P.833 vegetation attenuation. Tune the coefficients with `P833_GAMMA_DB_PER_M` and `P833_MAX_ATTENUATION_DB` in `.env`. For local Python development: ```bash cd api python -m venv .venv source .venv/bin/activate pip install -e ".[dev]" python -m pytest python -m ruff check . ```