A full-stack web application that ingests drone imagery, runs OpenDroneMap / NodeODM orthomosaic jobs, and serves gallery, NDVI-driven prescription maps, and robot path planning (Fields2Cover + farm-ng style outputs) through a React UI and a FastAPI backend.
DroneBasedDevelopment is a drone imagery processing platform aimed at agricultural use cases. A React frontend (TypeScript, Tailwind, Leaflet) talks to a FastAPI backend that stages uploads, forwards jobs to NodeODM, polls for completion, downloads orthophotos and reports, and orchestrates prescription generation (R / FIELDimageR-style workflow invoked from Python) plus path preview and robot-oriented exports.
Typical flow: name a field and optionally attach a boundary shapefile, queue RGB or multispectral imagery (with optional GNSS sidecars such as .nav / .obs / .bin / .mrk), upload in chunks with per-file progress, monitor processing on the Upload tab, then use View Gallery for orthophotos and PDFs, Prescriptions for spray maps and regeneration, and Field Maps for map-centric path editing, GeoJSON export, robot file ZIP download, and deleting stored results for a task when you need to reclaim space.
The project ships a Docker Compose stack (NodeODM + API + nginx-hosted UI) so installs are repeatable on a lab or farm PC, plus an optional GitHub Container Registry (GHCR) path for teams that want pre-built images without cloning source.
- Node.js 16 or newer (the frontend pins
@types/node16.x; LTS 18 or 20 is fine) - Python 3.8+ for the FastAPI backend
- NodeODM — Docker image
opendronemap/nodeodm:stable(recommended) or any reachable NodeODM instance compatible with PyODM - Git — to clone this repository (not required for the Docker-only “application package” install if you use the compose bundle without source)
- Poetry (recommended) or pip with
requirements.txtfor Python dependencies - Docker Engine and Docker Compose v2 — for the full-stack container workflow (e.g. Docker Desktop on Windows or macOS)
Backend (exact pins in code/backend/requirements.txt; geospatial stack is heavy — use Docker or pip install -r requirements.txt after Poetry):
| Area | Packages / tools |
|---|---|
| Web stack | FastAPI, Uvicorn, Pydantic Settings, python-multipart, httpx, aiofiles |
| NodeODM | PyODM |
| Rasters & GIS | rasterio, rioxarray, xarray, geopandas, shapely, rasterstats, pyproj |
| Analysis | numpy (pinned below 2.x), pandas, matplotlib, scikit-learn |
| Robot / path | farm-ng-amiga, protobuf; Fields2Cover is wired via the Docker build (not a simple pip one-liner on all hosts) |
| Testing | pytest, pytest-asyncio |
Frontend (code/frontend/package.json):
| Area | Packages |
|---|---|
| UI | React 18, TypeScript, Tailwind CSS, react-scripts |
| Uploads | react-dropzone |
| Maps & geometry | Leaflet, react-leaflet, @turf/area |
| Reports | react-pdf, pdfjs-dist |
Poetry vs pip:
code/backend/pyproject.tomlcarries a minimal Poetry set suitable for lightweight development. For prescription, NDVI, and path-generation code paths, installrequirements.txt(or run the backend Dockerfile) so rasterio, farm-ng, and related libraries match production.
git clone <repository-url>
cd DBDOption A — Poetry (recommended), then full requirements
cd code/backend
poetry install
pip install -r requirements.txt
cp env.example .env
# Edit .env: PORT=8001, NODEODM_URL, UPLOAD_DIR, RESULTS_DIR, ALLOWED_ORIGINS (include http://localhost:8000 if you override the list)
poetry run python run.pyOption B — pip only
cd code/backend
python -m venv venv
# Windows:
# venv\Scripts\activate
# macOS / Linux:
source venv/bin/activate
pip install -r requirements.txt
cp env.example .env
python run.pyThe backend defaults to port 8001 in app/core/config.py. The bundled env.example uses PORT=8001 so it matches the frontend’s default REACT_APP_API_BASE_URL=http://localhost:8001.
cd code/frontend
npm install
npm startnpm start binds port 8000 (PORT=8000 in package.json). Set REACT_APP_API_BASE_URL if the API is not on http://localhost:8001. With DEBUG=True, the backend uses permissive CORS; if you turn DEBUG=False, ensure ALLOWED_ORIGINS lists every origin your browser uses (including http://localhost:8000).
docker run -p 3000:3000 opendronemap/nodeodm:stablePoint NODEODM_URL / NODEODM_HOST and NODEODM_PORT in .env at that service.
You do not need Git for every deployment if you ship an application package (ZIP or USB) that contains docker-compose.yml, code/, and scripts/ at the top level of one folder.
- Install Docker Engine and Docker Compose v2 on the target machine.
- Open a terminal in the folder that contains
docker-compose.yml. - Run:
./scripts/install-dbd.sh
# same as:
# docker compose up -d --buildThis starts NodeODM, the FastAPI backend, and a production build of the UI (nginx). Data persists in the named volume dbd_app_data (see comments in docker-compose.yml for backup examples using docker run … tar).
URLs when Docker runs on the same machine as the browser
| Service | URL |
|---|---|
| Web app | http://localhost:8000 |
| API / OpenAPI | http://localhost:8001/docs |
| NodeODM (optional debugging) | http://localhost:3000 |
Working offline: pull or build images while online, then use docker save and transfer the tarball (e.g. USB). On the air-gapped host, docker load then ./scripts/install-dbd.sh (use docker compose up -d without --build if you are not rebuilding).
Access from another device on the LAN: the default frontend image is built with REACT_APP_API_BASE_URL=http://localhost:8001, which only works when the browser runs on the same host as Docker. For a phone or second PC, set REACT_APP_API_BASE_URL under frontend.build.args in docker-compose.yml to http://<this-machine-LAN-IP>:8001, then docker compose up -d --build frontend.
Publishing pre-built images (maintainers): .github/workflows/publish-docker.yml builds and pushes dbd-backend and dbd-frontend to GitHub Container Registry (ghcr.io). Names follow ghcr.io/<github-owner-lowercase>/dbd-backend and ghcr.io/<github-owner-lowercase>/dbd-frontend. Pushes to main tag latest (and main); Git tags like v1.2.3 tag 1.2.3 and latest. After the first successful run, set package visibility to Public on GitHub if you want unauthenticated docker pull.
Thin GHCR install (no code/ checkout): use docker-compose.ghcr.yml and ./scripts/install-dbd-ghcr.sh (defaults often point at ghcr.io/logansutton13/...:main; adjust owner and tag as needed). The same LAN / localhost API URL caveats apply as above.
| Service | URL |
|---|---|
| Frontend (CRA) | http://localhost:8000 |
| Backend API | http://localhost:8001 |
| Swagger UI | http://localhost:8001/docs |
| ReDoc | http://localhost:8001/redoc |
| NodeODM | http://localhost:3000 |
The SPA exposes four primary areas from the sidebar:
| Tab | Purpose |
|---|---|
| Upload | Multi-step wizard: field name, optional boundary shapefile, imagery queue, chunked upload with per-file progress, NodeODM finalize, embedded processing queue (UploadQueueBoard + polling), optional path preview and save path to task |
| View Gallery | Lists processed tasks with orthophoto thumbnails; lightbox for images and in-browser PDF viewing |
| Field Maps | Tasks with prescription GeoJSON from the API; Leaflet map (optional OpenStreetMap when online), NDVI-oriented popups, path-from-task, save path, GeoJSON download, delete task results, robot ZIP |
| Prescriptions | List and detail from the same prescription API: spray levels, GPA thresholds, config edit, regenerate |
ProcessingView.tsxstill exists undercode/frontend/src/components/but is not mounted inApp.tsx. Live NodeODM status for recent uploads is handled on the Upload tab viauseProcessingQueueandUploadQueueBoard.
- Drag-and-drop and file picker, with a wizard that separates naming, boundary upload, imagery queue, and finalize.
- Chunked uploads (
init→chunk→finalize) with a default 5 MiB chunk size; XHR upload progress per file. - Supported imagery types are driven by backend MIME and extension rules (e.g. JPEG, PNG, TIFF); 100 MB default per-file cap (
MAX_FILE_SIZE). - Optional auxiliary GNSS / sensor files:
.nav,.obs,.bin,.mrk(seeALLOWED_AUXILIARY_EXTENSIONSinapp/core/config.py). - System-wide defaults via
GET/PUT /api/v1/upload/settings(NodeODM heading/grid size, robot and coverage width, prescription parameters) and scoped reset endpoints.
- Jobs are submitted to NodeODM after finalize; options such as heading and grid size flow from upload init and settings.
- The backend polls NodeODM and downloads orthophoto and report assets into per-task folders under
results/. - Large batches are supported in principle; practical limits depend on disk, NodeODM, and timeout settings (
NODEODM_TIMEOUT).
- The Upload tab shows an active queue of tasks with status and progress polling (not a separate top-level “Processing” route).
- Status comes from
GET /api/v1/upload/{task_id}/statustied to the NodeODM task.
- View Gallery loads real task rows from
GET /api/v1/results. - Orthophoto PNGs and PDF reports open in the UI; URLs are built from the API’s summary payloads.
- Field Maps lists prescriptions via
GET /api/v1/prescription, loads GeoJSON per task, and can overlay saved display paths fromGET /api/v1/results/{task_id}/display-path. - Leaflet map: optional basemap when online; blank background when offline tile fetch fails.
- Path-from-task uses pathing
POST …/from-task(and aliases); save persists the preview to the task. - Export prescription GeoJSON from the UI; download robot ZIP via
GET /api/v1/results/{task_id}/robot-files.zip(bundles whatever exists among robot path JSON, prescription GeoJSON, and prescription config — see API response headers for included vs missing files). - Delete results for a task via
DELETE /api/v1/results/{task_id}(used from Field Maps with a confirmation step).
- Prescriptions tab: list, open detail map, edit spray levels per cluster/feature, save with
PUT, adjust GPA-related config, open regenerate flow (POST …/generatereturns 202 while the R pipeline runs). - Backend merges config into stored GeoJSON where applicable (spray rates on features).
- NDVI (and related plot statistics) appear in prescription GeoJSON produced by the FIELDimageR-style R pipeline — there is no separate public HTTP API that only returns “NDVI rasters” independent of prescriptions.
- Path planning uses Fields2Cover-based generation in the backend (shapefile upload or from-task boundary), Farm-ng / Amiga-oriented track conversion, RTK base JSON under
/api/v1/pathing/rtk-base, and persistence through save and results robot-path / display-path files.
pytestincode/backend/tests/— 116 collected test functions across routes, handlers, file storage, path generator, schemas, and legacy API tests (re-runpytest --collect-onlyafter large edits)..github/workflows/backend-tests.ymlruns on push and pull request..github/workflows/publish-docker.ymlpublishes container images.
For authoritative request/response shapes, use http://localhost:8001/docs when the backend is running. In short:
| Prefix | Role |
|---|---|
/api/v1/upload |
Chunked upload, settings, boundary, status (note: list and delete upload routes return 501 today) |
/api/v1/results |
List tasks, orthophoto, PDF, robot-path, display-path, robot-files.zip, DELETE results |
/api/v1/pathing |
Path jobs (multipart shapefile or from-task), poll, result, save, RTK base |
/api/v1/prescription |
List, GeoJSON get/put, config, status, generate |
- Start services — NodeODM (e.g. Docker on port 3000), backend (
python run.pyor Poetry equivalent), frontend (npm start), or bring up Docker Compose from the repo root. - Upload — Open Upload, enter a field name, attach a boundary shapefile if you need path/prescription geometry early, add images (and optional GNSS sidecars), then run through chunked upload and finalize.
- Monitor — Stay on Upload to watch the processing queue until NodeODM completes; fix any reported errors (503 if NodeODM is down, validation errors, etc.).
- Gallery — Open View Gallery to preview orthophotos and open PDF reports.
- Prescriptions — Open Prescriptions, pick a task with a completed orthophoto pipeline, review the map, set spray levels and GPA-related settings, save, and regenerate when you change parameters.
- Field maps and paths — Use Field Maps for map-centric workflows: preview paths from task, save when satisfied, export GeoJSON, or download robot ZIP; use delete results if you must remove a task’s stored output from disk.
- Settings — Use the settings entry point in the header on Upload (increments
openSettingsTick) to adjust global defaults for NodeODM and prescription modules.
- NodeODM must be running — Finalize and status calls assume a reachable NodeODM instance; otherwise uploads fail or stall with HTTP errors (
503when the client cannot reach NodeODM is common). - Upload router gaps —
DELETE /api/v1/upload/{task_id}andGET /api/v1/upload/return 501 (not implemented). Cleaning finished artifacts is done viaDELETE /api/v1/results/{task_id}, not via cancelling an upload session in the API. - Disk usage — Failed or abandoned runs can leave data under
uploads/andpath_jobs/; operators should monitor disk; some temp cleanup helpers exist on the backend but there is no full automatic janitor for every failure mode. - CORS — If you customize
ALLOWED_ORIGINS, list every dev and production browser origin explicitly (includehttp://localhost:8000for CRA).
- No database — Task metadata lives in manifests and directory layout; there is no SQL layer for history or multi-tenant isolation.
- No built-in authentication — Treat deployments as trusted single-operator unless you add a reverse proxy or VPN.
- Fields2Cover on bare metal — Non-Docker Python environments may still need manual setup matching the Dockerfile; Compose remains the supported path for path generation.
- Frontend test depth —
npm testis configured, but component coverage is thin compared to backendpytest.
Earlier READMEs mentioned mock field maps or prescriptions; as of Sprint 6, Field Maps and Prescriptions use live prescription and results APIs. Path preview is integrated in Upload and Field Maps, not only “backend-only.”
- Sprint 1 — Drone upload UX, early processing concepts, NodeODM wiring.
- Sprint 2 — Results API, gallery backed by real orthophotos and PDFs, manifests, background polling, task naming, richer upload parameters.
- Sprint 3 — NDVI / FIELDimageR port and field analysis services, DBSCAN / soil masking concepts, GeoJSON and CSV export paths in backend tooling, field-map UI groundwork.
- Sprint 4 — Pathing API, PathGenerator (Fields2Cover), Upload boundary + preview, prescription shapefile output, meeting minutes.
- Sprint 5 — Dockerized stack, RTK endpoints, deeper path + prescription integration, CI and expanded pytest, farmer-supplied boundaries for prescriptions.
- Sprint 6 — Chunked upload + progress, prescription/field-map production UI, GPA + regenerate, robot-path / display-path / robot ZIP, delete results, GHCR install path, validation fixes — see 06_Sprint_Report.md.
- Sprint 1 · Sprint 2 · Sprint 3 · Sprint 4 · Sprint 5 · Sprint 6
- Original requirements (PDF)
- Final report (PDF) — semester / capstone write-up when present in the repo
- Meeting minutes
- External links
- Tutorials placeholder
We welcome contributions. Suggested flow:
-
Fork the repository on GitHub.
-
Clone your fork:
git clone https://github.com/your-username/DBD.git cd DBD -
Branch:
git checkout -b feature/your-feature-name
-
Environment — Follow Installation above; confirm NodeODM is reachable for integration-style manual tests.
-
Implement — Match existing TypeScript / Python style; add
pytestfor backend behavior you touch. -
Test:
cd code/backend pytest # or: poetry run pytest cd code/frontend npm test
-
Commit and push:
git commit -am "Describe the change clearly" git push origin feature/your-feature-name -
Open a pull request with a short rationale and any screenshots for UI changes.
See CONTRIBUTING.md for conventions.
- Frontend: TypeScript, functional React components, Tailwind utility classes; prefer extending
apiServiceincode/frontend/src/services/api.tsfor new endpoints. - Backend: PEP 8–style Python, FastAPI routers thin over
handlers/, Pydantic schemas for IO; add tests undercode/backend/tests/. - Docs: Update this README or
code/backend/README.mdwhen you change user-visible behavior or routes.
- Backend README — Endpoint cheat sheet, curl examples for chunked upload, configuration tables.
- Live OpenAPI —
http://localhost:8001/docs(Swagger) and/redocwhen the server is running.
This project is licensed under the MIT License — see LICENSE.
The MIT License allows commercial use, modification, distribution, and private use. Summary: https://choosealicense.com/licenses/mit/.