diff --git a/README.md b/README.md index 2818fd9..2709b75 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Ask an agent: *"Show the burn scar near Los Alamos, switch to satellite imagery, | [`server/`](server/) | FastAPI server — REST API, WebSocket hub, MCP server, GeoTIFF & screenshot services, auth portal | | [`sdk/`](sdk/) | `mapcontrol` — typed Python client SDK | | [`examples/`](examples/) | Runnable demo scripts (shapes, terrain, glyphs, GeoTIFFs) + sample data | -| [`docs/`](docs/) | Guides — MCP integration, LLM context block, MCP Apps field guide, map-engine comparison, Puppeteer animation skills | +| [`docs/`](docs/) | Guides — deployment, running from source (no Docker), MCP integration, LLM context block, MCP Apps field guide, map-engine comparison, Puppeteer animation skills | | [`deploy/`](deploy/) | Deployment helpers (local PyPI index for the SDK) | ## Quick start @@ -91,6 +91,8 @@ curl http://localhost:8000/docs # interactive API docs That's the whole install. Want maps that survive restarts, share links that work off your machine, or premium basemaps? See **[docs/deployment.md](docs/deployment.md)** — it walks from this minimal setup to a full production config and explains what every knob does. (Contributors: `git clone` this repo and `docker compose up --build` builds the image locally.) +**No Docker?** Run it straight from Python — see **[docs/local-development.md](docs/local-development.md)** for the full from-source setup (venv, `pip install -e ./server`, headless-screenshot browser, and running on any port). + ### 2. Drive a map from Python Install the SDK (pure Python — no GIS stack needed on the client): diff --git a/docs/deployment.md b/docs/deployment.md index 00d4464..6bb76a3 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -5,6 +5,9 @@ serves the map pages, the REST API, and the MCP endpoint on one port. This guide goes from the smallest thing that works to a full production setup, and explains what every knob does to the map. +> **Not using Docker?** To run the server directly with Python from a source +> checkout, see **[local-development.md](local-development.md)**. + ## 1. Minimal — the smallest thing that works ```yaml diff --git a/docs/local-development.md b/docs/local-development.md new file mode 100644 index 0000000..825ef85 --- /dev/null +++ b/docs/local-development.md @@ -0,0 +1,172 @@ +# Running MapControl without Docker (from source) + +This guide runs the server directly with Python — no Docker. It's the setup you +want for local development, debugging, or hosts where Docker isn't available. + +Everything the Docker image does, it does from these same steps: the image is +just Python + these dependencies + a headless Chromium. + +--- + +## Prerequisites + +- **Python ≥ 3.11** (3.11 or 3.12). Check: `python3 --version` +- **git** +- No system GDAL/GEOS needed — the `rasterio` wheels bundle their own on + Linux, macOS, and Windows. +- No Node.js and no frontend build needed — the server serves the map page + with MapLibre loaded from a CDN. (Node is only needed for the optional + [Puppeteer animation skills](puppeteer-skills/).) + +--- + +## 1. Clone + +```bash +git clone https://github.com/ESIPFed/mc2.git +cd mc2 +``` + +## 2. Create and activate a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +python -m pip install --upgrade pip +``` + +## 3. Install the server + +```bash +pip install -e ./server +``` + +This pulls FastAPI, uvicorn, rasterio/numpy/matplotlib (GeoTIFF rendering), +Pillow, the MCP SDK, and Playwright. + +## 4. Install the screenshot browser (Playwright Chromium) + +The `take_screenshot` feature renders pages with a headless Chromium. Install it +once — **skip this and everything works except screenshots** (they return a +clear "install chromium" error): + +```bash +playwright install chromium +``` + +## 5. (Optional) Install the Python SDK + +Only if you want to drive the map from Python (`from mapcontrol import +MapControl`): + +```bash +pip install -e ./sdk +``` + +--- + +## 6. Run the server + +Run it on **port 8000** to match the rest of the docs. Setting +`MAPCONTROL_PORT` matters — see [Why set the port](#why-set-the-port) below. + +From the repo root, with the venv active: + +```bash +cd server +MAPCONTROL_PORT=8000 uvicorn mapcontrol_server.main:app --host 0.0.0.0 --port 8000 +``` + +> **Windows (PowerShell):** +> ```powershell +> cd server +> $env:MAPCONTROL_PORT=8000; uvicorn mapcontrol_server.main:app --host 0.0.0.0 --port 8000 +> ``` + +**Auto-reload for development** — add `--reload`, or use the console script, +which turns reload on for you: + +```bash +cd server +MAPCONTROL_PORT=8000 mapcontrol-server +``` + +The SQLite database and uploads directory are created automatically under +`./data/` (relative to the directory you launch from — hence `cd server`, so +they land in `server/data/`, matching the container layout). To put them +elsewhere, set `MAPCONTROL_DB_PATH` and `MAPCONTROL_FILE_DIR`. + +## 7. Verify + +The first start takes ~20–30s while Python imports the geospatial stack — wait +for the `Uvicorn running on http://…:8000` line before hitting it. + +```bash +curl http://localhost:8000/docs # interactive API docs → 200 +``` + +Create a map and open it live: + +```bash +# create a map, print its id +curl -s -X POST http://localhost:8000/api/maps \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['map_id'])" +``` + +Open `http://localhost:8000/map/` in a browser. Or drive it from Python +(if you installed the SDK): + +```python +from mapcontrol import MapControl + +mc = MapControl("http://localhost:8000") +session = mc.create_map() +print(session.url) # open this in a browser +session.zoom_to_point(7.6586, 45.9763, zoom=12) # the Matterhorn +shot = session.take_screenshot() # needs the Playwright browser (step 4) +``` + +MCP clients connect at `http://localhost:8000/mcp`. + +--- + +## Why set the port + +`config.toml` ships a development default of **7777**. The server reads its port +from `config.toml` unless you override it — but uvicorn's own `--port` flag only +controls what it *binds*, not what the app *thinks* it's on. If those two +disagree, headless screenshots break: the server self-navigates Chromium to +`http://127.0.0.1:`, which won't be listening. + +Setting **`MAPCONTROL_PORT=8000`** fixes both at once — `config.toml` is +overridden to 8000 and uvicorn binds 8000 — so the bound port, the app's idea of +its port, and the URLs in these docs all agree. (This is exactly what the Docker +image does via `ENV MAPCONTROL_PORT=8000`.) + +If you'd rather just use the built-in default, run `mapcontrol-server` with **no** +`MAPCONTROL_PORT` and reach the server at `http://localhost:7777` — bound and +config agree, and screenshots work — but remember to swap `8000 → 7777` in every +command above. + +## Optional: premium basemaps + +The keyless basemaps (OpenStreetMap, Esri Satellite, Carto Dark) work out of the +box. To add the MapTiler styles (vector streets, hybrid, topo, dataviz, +satellite-dark), export a key before launching: + +```bash +export MAPTILER_API_KEY=your_key_here +``` + +--- + +## Troubleshooting + +| Symptom | Fix | +|---|---| +| `take_screenshot` fails with a browser/executable error | Run `playwright install chromium` (step 4), in the same venv. | +| Screenshots time out or hit a connection error, but the map loads in a browser | Port mismatch — launch with `MAPCONTROL_PORT` set so the bound port matches config (see [Why set the port](#why-set-the-port)). | +| `Address already in use` on 8000 | Something else holds the port (`lsof -i :8000`). Pick another: `MAPCONTROL_PORT=8010 uvicorn ... --port 8010`. | +| `ModuleNotFoundError: No module named 'mcp'` | The venv isn't active, or `pip install -e ./server` didn't run. Re-activate and reinstall. | +| Share links / screenshot URLs point at `localhost` from another machine | Set `MAPCONTROL_PUBLIC_URL` to the address clients use, e.g. `export MAPCONTROL_PUBLIC_URL=http://192.168.1.50:8000`. Keep the container/host reachable on the bound port. | +| `rasterio` fails to install | Upgrade pip (`pip install --upgrade pip`) so it fetches a prebuilt wheel; make sure Python is 3.11/3.12. |