satdetect-dev / docs /IMPLEMENTATION_PLAN_DDA.md
coderuday21's picture
Phase 0 DDA: APP_MODE, image library API, GeoTIFF ingest, dev UI
41d16b3
|
Raw
History Blame Contribute Delete
21.2 kB
# DDA Change Detection β€” Implementation Plan (Dev Space Only)
**Document:** Scope of Work β†’ Engineering Plan
**Client:** DDA (Delhi Development Authority)
**Target environment:** Hugging Face **`satdetect-dev`** only (`hf-dev` remote, `master` branch)
**Production (`satdetect`) must not receive these changes until explicit sign-off.**
---
## 1. Executive summary
The SOW transforms the current β€œupload two PNGs β†’ run detection β†’ view history” demo into a **departmental change-monitoring system** with:
- A **persistent image library** (GeoTIFF, hierarchical metadata, search)
- **Library-driven comparison** (Base T1 vs Comparison T2)
- **Async AI detection** with geo-referenced outputs
- **Reports** (browser + PDF + email)
- **Geo-navigation** and **clickable regions**
- **Human review** (Confirmed / False Positive) and **export to departmental systems**
The existing codebase is a strong **Phase-0 foundation**: AdaptFormer-based detection, region classification, overlay UI, email notifier, and history. Roughly **40% of FR-07** exists; **FR-01–FR-06 and FR-08 are largely net-new**.
**Recommended approach:** Implement behind a **`APP_MODE=dda`** feature flag on dev, extend the database and API vertically, and keep production on the current simplified flow indefinitely.
---
## 2. SOW requirements β†’ current state
| ID | Requirement | Current state | Gap severity |
|----|-------------|---------------|--------------|
| **FR-01** | Centralized image library (Zone β†’ Village β†’ Area β†’ Year or Grid) | Zone/Village are free-text dropdowns on upload; images discarded after run except thumbnails | **Large** |
| **FR-02** | GeoTIFF upload + validation + manual georef fallback | PNG/JPEG only, 20 MB, Pillow RGB load | **Large** |
| **FR-03** | Pick Base/Comparison from library or drag from tree | Local file upload only | **Medium** (depends on FR-01) |
| **FR-04** | AI detection as background job; simplified change types; lat/lng per region | Synchronous `/api/detect`; rich internal taxonomy; pixel coords only | **Medium–Large** |
| **FR-05** | In-app notification + report link; PDF export; persistent reports | Email optional; HTML modal report; no PDF; history exists | **Medium** |
| **FR-06** | Navigate to change by lat/lng; Locate action | No geospatial metadata | **Large** (depends on FR-02) |
| **FR-07** | Clickable regions auto-pan viewer; side-by-side Base/Comparison | Region row hover + compare slider partially exist | **Small–Medium** |
| **FR-08** | Confirm / False Positive; submit to dept API or export | Not implemented | **Medium** |
### SOW change-type mapping (canonical output)
Map internal `detection_engine` labels β†’ DDA report types:
| DDA type | Internal sources (examples) |
|----------|------------------------------|
| **New Construction** | New Construction/Building, Temporary Structure, Road/Pavement Change (New) |
| **Demolition** | Demolition/Clearing, Partial/Full Demolition |
| **Extension** | Expansion, Widening, Renovation |
| **Vegetation Change** | Vegetation Change (+ sub-types) |
| **Other** | Water Body, Bare Land/Soil, Unclassified |
Store both `dda_change_type` (report) and `internal_object_type` (model/debug) on each region.
---
## 3. Deployment & branching strategy
```
master (DDA features) ──push──► hf-dev β†’ coderuday21/satdetect-dev
production ──push──► hf β†’ coderuday21/satdetect (frozen)
```
### Dev-only safeguards
1. **`APP_MODE` env var** on `satdetect-dev`: `dda` enables library, GeoTIFF, jobs, RBAC, review UI. Default / production: `legacy`.
2. **Separate DB file** on dev: `data/dda_app.db` (avoid mixing guest history with library assets).
3. **All new routes** under `/api/dda/*` or gated by `APP_MODE` in `main.py`.
4. **CI checklist** before any production promote: diff must not touch DDA modules unless mode flag documented.
---
## 4. Target architecture
```mermaid
flowchart TB
subgraph ui [Frontend SPA]
LIB[Image Library Tree + Search]
CMP[Comparison Picker T1/T2]
JOB[Job Status / Notifications]
RPT[Report Viewer + PDF]
REV[Review Confirmed / FP]
end
subgraph api [FastAPI]
AUTH[RBAC Auth]
IMG[Image Library API]
DET[Detection Job API]
RPTAPI[Report + Export API]
DEPT[Department Export Adapter]
end
subgraph data [Storage]
DB[(SQLite / PostgreSQL)]
FS[File Store: GeoTIFF + pyramids + overlays]
JQ[Job Queue Table]
end
subgraph proc [Processing]
GEO[rasterio ingest + CRS normalize]
PIPE[detection_engine.run_detection]
GEOOUT[Region centroid β†’ lat/lng]
end
LIB --> IMG
CMP --> DET
DET --> JQ
JQ --> proc
proc --> DB
proc --> FS
JOB --> DET
RPT --> RPTAPI
REV --> DEPT
AUTH --> api
```
---
## 5. Data model (new & extended)
### 5.1 Hierarchy (FR-01)
Two organization modes (user selects at library root):
**A. Administrative (default for DDA Delhi)**
`Zone` β†’ `Village` β†’ `Area` β†’ `Year` β†’ `[ImageAsset…]`
**B. Grid**
`GridId` (e.g. `G-12-04`) β†’ `Year` β†’ `[ImageAsset…]`
Implement as normalized tables (not hardcoded JS):
```text
zones(id, name, mode) -- mode: admin | grid_parent
villages(id, zone_id, name) -- nullable for grid mode
areas(id, village_id, name)
library_years(id, area_id, year) -- optional grouping node; images also store year directly
```
For MVP, **Area** can be a free-text field on `ImageAsset` if DDA taxonomy is not finalized.
### 5.2 `ImageAsset` (FR-01, FR-02)
| Column | Purpose |
|--------|---------|
| `id`, `uuid` | Primary identifiers |
| `zone_id`, `village_id`, `area_name`, `year`, `grid_id` | Hierarchy |
| `capture_date` | Required metadata |
| `source` | `satellite` \| `drone` |
| `format` | `geotiff` |
| `file_path`, `thumb_path`, `preview_path` | Storage |
| `crs`, `bounds_json` | Geo metadata (WGS84 bbox) |
| `width`, `height`, `file_size_bytes` | Validation |
| `has_georef` | bool |
| `manual_location_json` | Fallback corner coords + notes if no embedded georef |
| `uploaded_by`, `created_at` | Audit |
### 5.3 `DetectionJob` (FR-04 async)
| Column | Purpose |
|--------|---------|
| `id`, `status` | `queued` \| `running` \| `completed` \| `failed` |
| `base_image_id`, `comparison_image_id` | FK β†’ ImageAsset |
| `method`, `params_json` | Detection settings |
| `run_id` | FK β†’ DetectionRun when done |
| `error_message`, `started_at`, `completed_at` | Job lifecycle |
| `notify_email`, `notify_in_app` | FR-05 |
### 5.4 Extend `DetectionRun`
Add: `base_image_id`, `comparison_image_id`, `job_id`, `report_pdf_path`, `status` (`draft` \| `published`).
### 5.5 Extend region JSON schema
Each region gains:
```json
{
"ddaChangeType": "New Construction",
"confidence": 0.87,
"areaSqM": 142.5,
"centroid": { "x": 412, "y": 288 },
"latLng": { "lat": 28.6139, "lng": 77.2090 },
"bbox": { "x": 380, "y": 260, "w": 64, "h": 56 },
"reviewStatus": "pending",
"reviewedBy": null,
"reviewedAt": null
}
```
### 5.6 `RegionReview` + export batch (FR-08)
```text
region_reviews(id, run_id, region_id, status, reviewer_id, notes, submitted_at)
export_batches(id, run_id, format, payload_path, dept_api_response, created_at)
```
### 5.7 RBAC (FR-01, FR-02)
Extend `User`:
```text
role: viewer | uploader | analyst | admin
```
| Role | View library | Upload | Run detection | Review/submit |
|------|--------------|--------|---------------|---------------|
| viewer | βœ“ | | | |
| uploader | βœ“ | βœ“ | | |
| analyst | βœ“ | βœ“ | βœ“ | βœ“ |
| admin | βœ“ | βœ“ | βœ“ | βœ“ + manage hierarchy |
**Dev Space:** Re-enable login UI when `APP_MODE=dda`. Production stays guest/no-login.
---
## 6. API design (dev / DDA mode)
### 6.1 Image library
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/dda/hierarchy` | Tree: zones β†’ villages β†’ areas β†’ years (+ counts) |
| `GET` | `/api/dda/images` | Filter: `zone`, `village`, `area`, `year`, `date_from`, `date_to`, `grid_id`, `q` |
| `POST` | `/api/dda/images/upload` | Multipart GeoTIFF + metadata form |
| `GET` | `/api/dda/images/{id}` | Metadata + thumb URL |
| `GET` | `/api/dda/images/{id}/preview` | Downsampled PNG/Web tile for viewer |
| `DELETE` | `/api/dda/images/{id}` | Admin only |
**Upload validation (FR-02):**
1. Extension `.tif` / `.tiff`
2. Max size: **500 MB dev** (configurable; DDA responsible for suitable resolution per SOW)
3. `rasterio.open()` β€” verify readable, extract CRS/transform
4. If no georef: require `manual_bounds` (SW/NE lat-lng) + `capture_date` before commit
5. Generate 512px thumbnail + 2048px preview for UI
6. Return `{ id, status: "success" | "failed", errors[] }`
### 6.2 Comparison & detection jobs
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/dda/jobs` | Body: `{ baseImageId, comparisonImageId, method?, notifyEmail? }` β†’ `{ jobId }` |
| `GET` | `/api/dda/jobs/{id}` | Status + progress + runId when complete |
| `GET` | `/api/dda/jobs` | User's recent jobs (in-app notification feed) |
| `POST` | `/api/dda/detect` | **Legacy sync path** for small previews only (keep for HF debugging) |
**Job runner logic:**
```text
POST /jobs β†’ insert DetectionJob(queued)
β†’ return 202 + jobId
Background worker thread:
1. Load ImageAsset paths via rasterio
2. Align pair (same CRS; reproject if needed)
3. Windowed read β†’ RGB numpy arrays (max 1600px for DL)
4. run_detection(...)
5. For each region: pixel centroid β†’ lat/lng via transform
6. Map object types β†’ ddaChangeType
7. Save DetectionRun + overlay + update job completed
8. Fire notification (in-app row + optional email with report link)
```
**HF timeout mitigation:** Job endpoint returns immediately; worker runs in-process with status polling. For GeoTIFFs >60s, use **pyramid preview** for detection (DDA supplies suitable resolution) and store full metadata separately. Document in UI: β€œAnalysis uses optimized resolution; full GeoTIFF retained in library.”
### 6.3 Reports (FR-05)
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/dda/reports/{run_id}` | Full report JSON (existing shape + geo fields) |
| `GET` | `/api/dda/reports/{run_id}/pdf` | Generate/download PDF |
| `POST` | `/api/dda/reports/{run_id}/notify` | Email with link to report |
**PDF engine:** `weasyprint` or `reportlab` β€” HTML template reuse from `templates/ChangeDetection.html`.
**In-app notification:** Bell icon + `/api/dda/jobs?status=completed&unseen=true`.
### 6.4 Geo-navigation (FR-06, FR-07)
No external map SDK required for MVP β€” **image viewer geo mode**:
- Report shows `lat, lng` per region
- **Locate** button: pan/zoom canvas to bbox center at 200% zoom, pulse highlight
- Optional Phase 2: Leaflet minimap with footprint rectangle
Frontend already has region hover β†’ bbox highlight; extend to **click row β†’ scroll viewer + zoom**.
Side-by-side: keep existing compare slider; add locked **T1 | T2 | Overlay** tabs when images loaded from library.
### 6.5 Review & departmental export (FR-08)
| Method | Path | Description |
|--------|------|-------------|
| `PATCH` | `/api/dda/reports/{run_id}/regions/{region_id}` | `{ reviewStatus: confirmed \| false_positive, notes? }` |
| `POST` | `/api/dda/reports/{run_id}/submit` | Submit confirmed only |
| `GET` | `/api/dda/reports/{run_id}/export.{csv\|xlsx\|pdf}` | Structured export |
**Department adapter pattern:**
```python
class DepartmentExporter(Protocol):
def submit(self, run: DetectionRun, regions: list) -> ExportResult: ...
class ApiExporter(DepartmentExporter): # DEPT_API_URL + DEPT_API_KEY
class FileExporter(DepartmentExporter): # CSV/XLSX fallback
```
False positives: persist in `region_reviews` with `status=false_positive` for future training export (`/api/dda/training/export` β€” admin only, Phase 2).
---
## 7. GeoTIFF processing pipeline (accurate logic)
### 7.1 Ingest
```python
def ingest_geotiff(path: Path) -> GeoImageMeta:
with rasterio.open(path) as src:
crs = src.crs
bounds = transform_bounds(src.crs, "EPSG:4326", *src.bounds)
has_georef = crs is not None and src.transform != rasterio.identity()
# Prefer RGB or first 3 bands; handle 1-band grayscale
preview = read_downsampled(src, max_side=2048)
return meta
```
**Dependencies to add (dev only branch):** `rasterio`, `pyproj` (GDAL wheels on Docker).
### 7.2 Pair compatibility check (before job starts)
| Check | Action |
|-------|--------|
| Same CRS or reproject T2 β†’ T1 CRS | Reproject comparison raster |
| Overlapping bounds | Warn if <50% overlap |
| Band count | Harmonize to RGB |
| Resolution ratio | Warn if >2Γ— GSD difference |
### 7.3 Pixel β†’ lat/lng
```python
def region_centroid_latlng(region_bbox, transform, crs):
cx = region_bbox.x + region_bbox.w / 2
cy = region_bbox.y + region_bbox.h / 2
lng, lat = rasterio.transform.xy(transform, cy, cx, offset="center")
if crs != EPSG:4326:
lng, lat = transform_coords(crs, 4326, lng, lat)
return lat, lng
```
### 7.4 Area in mΒ²
Use pixel area Γ— GSDΒ² from transform (for projected CRS in meters).
---
## 8. Frontend plan (dev UI)
Replace single upload card with **tabbed workflow** when `APP_MODE=dda`:
### Tab 1 β€” Image Library
- Left: collapsible tree (Zone β†’ Village β†’ Area β†’ Year)
- Right: grid of thumbnails + metadata
- Upload modal: GeoTIFF + form (hierarchy, capture date, source, manual bounds if needed)
- Search bar + filters
### Tab 2 β€” Change Detection
- Two slots: **Base (T1)** | **Comparison (T2)**
- Drag image from library OR picker dialog
- Show thumb + capture date for each
- Run β†’ job submitted β†’ progress spinner β†’ auto-open report on complete
### Tab 3 β€” Reports & Review
- List of completed runs
- Open report β†’ regions table with DDA types, lat/lng, confidence, area
- **Locate** per row; click row β†’ pan viewer
- Toggle Confirmed / False Positive
- Submit to department / Download export
### Tab 4 β€” Notifications
- Job completion feed (FR-05 in-app)
**Auth screens:** Login/register return on dev only (`APP_MODE=dda`).
---
## 9. Implementation phases
### Phase 0 β€” Foundation (3–4 days)
- [ ] `APP_MODE` flag + route gating in `main.py`
- [ ] Alembic-style migrations or existing startup migrations for new tables
- [ ] `rasterio` in `requirements.txt` + Docker GDAL base image update (dev Dockerfile branch)
- [ ] Seed script: import current `DELHI_ZONES` into DB
- [ ] Dev README section
### Phase 1 β€” Image library (FR-01, FR-02) (5–7 days)
- [ ] Models: hierarchy + ImageAsset
- [ ] Upload API + validation + thumbnail generation
- [ ] Library tree + search UI
- [ ] RBAC: uploader vs viewer
### Phase 2 β€” Library-based comparison (FR-03) (2–3 days)
- [ ] T1/T2 picker from library
- [ ] Drag-and-drop from tree to comparison slots
- [ ] Preview + capture date display
### Phase 3 β€” Async detection + geo output (FR-04, FR-06) (5–6 days)
- [ ] DetectionJob worker + polling API
- [ ] GeoTIFF β†’ RGB pipeline integration with `detection_engine`
- [ ] DDA change type mapping + lat/lng + area mΒ² on regions
- [ ] Job failure handling + user messaging
### Phase 4 β€” Reports & notifications (FR-05) (3–4 days)
- [ ] In-app notification feed
- [ ] Report page (browser)
- [ ] PDF export
- [ ] Email with report deep link
### Phase 5 β€” Interactive viewer (FR-07) (2–3 days)
- [ ] Locate button + click-to-pan/zoom
- [ ] Side-by-side T1/T2/overlay modes
- [ ] Region highlight sync (extend existing hover logic)
### Phase 6 β€” Review & export (FR-08) (4–5 days)
- [ ] Per-region review state
- [ ] CSV/XLSX/PDF export of confirmed changes
- [ ] Department API adapter (config-driven)
- [ ] False-positive archive for training
### Phase 7 β€” Hardening & UAT (3–5 days)
- [ ] Load test with sample GeoTIFFs from DDA
- [ ] Alignment QA on drone vs satellite pairs
- [ ] Security review (auth, upload limits, path traversal)
- [ ] Deploy checklist for `satdetect-dev`
**Total estimate:** ~25–35 working days (1 senior dev), assuming DDA provides sample GeoTIFFs and hierarchy taxonomy early.
---
## 10. Hugging Face dev constraints & mitigations
| Constraint | Impact | Mitigation |
|------------|--------|------------|
| ~60s HTTP timeout (cpu-basic) | Sync detect fails on large TIFF | Async jobs + immediate 202 response; detection on downsampled preview |
| Ephemeral disk | Library loss on rebuild | HF persistent storage volume; document backup to S3 optional |
| No GPU | Slow DL | Keep AdaptFormer on CPU tiles; cap image size; queue one job at a time |
| 50 GB space limit | Large GeoTIFF library | Per-file quota; admin purge; external blob store Phase 2 |
| Single worker | No true queue | SQLite job lock; one `running` job at a time |
**Dockerfile (dev):** Switch base to `osgeo/gdal:ubuntu-small` or install `libgdal` in existing image.
---
## 11. File / module layout (new code)
```text
app/
β”œβ”€β”€ dda/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ config.py # APP_MODE, limits, DEPT_API_*
β”‚ β”œβ”€β”€ models_library.py # ImageAsset, hierarchy ORMs
β”‚ β”œβ”€β”€ models_jobs.py # DetectionJob, RegionReview, ExportBatch
β”‚ β”œβ”€β”€ geotiff_io.py # rasterio ingest, reproject, preview
β”‚ β”œβ”€β”€ geo_regions.py # pixelβ†’lat/lng, area mΒ²
β”‚ β”œβ”€β”€ change_type_map.py # internal β†’ DDA types
β”‚ β”œβ”€β”€ job_runner.py # async detection worker
β”‚ β”œβ”€β”€ library_routes.py # FR-01, FR-02 API
β”‚ β”œβ”€β”€ jobs_routes.py # FR-04 API
β”‚ β”œβ”€β”€ reports_routes.py # FR-05, PDF
β”‚ β”œβ”€β”€ review_routes.py # FR-08
β”‚ └── dept_export.py # adapter pattern
β”œβ”€β”€ static/js/dda/
β”‚ β”œβ”€β”€ library.js
β”‚ β”œβ”€β”€ comparison.js
β”‚ β”œβ”€β”€ jobs.js
β”‚ └── review.js
└── templates/
β”œβ”€β”€ index.html # legacy (production)
└── index_dda.html # dev SPA shell
```
Wire routers in `main.py`:
```python
if os.getenv("APP_MODE") == "dda":
app.include_router(library_routes.router, prefix="/api/dda")
...
```
Set on **`satdetect-dev`** Space: `APP_MODE=dda`.
Leave **`satdetect`** unset β†’ legacy behavior.
---
## 12. Testing strategy
| Layer | Tests |
|-------|-------|
| GeoTIFF ingest | Unit: georef / no-georef / 1-band / 4-band samples |
| Pair validation | CRS mismatch, non-overlap |
| Job lifecycle | queued β†’ running β†’ completed/failed |
| lat/lng accuracy | Known corner coords β†’ centroid within tolerance |
| DDA type mapping | Snapshot tests on classified regions |
| RBAC | Role forbidden on upload/delete |
| PDF | Report renders all mandatory fields |
| E2E | Upload 2 TIFFs β†’ job β†’ report β†’ confirm β†’ CSV export |
Use `scripts/validate_detection.py` extended with GeoTIFF fixtures.
---
## 13. Risks & dependencies
| Risk | Owner | Mitigation |
|------|-------|------------|
| DDA GeoTIFF quality / GSD | DDA (per SOW) | Pre-upload validation messages |
| No departmental API spec | DDA IT | File export fallback (FR-08) |
| Area taxonomy not finalized | DDA | Configurable hierarchy admin UI |
| Model accuracy on drone imagery | Shared | Alignment warnings; false-positive review loop |
| HF CPU timeout | Engineering | Async jobs + downsample policy |
**Blocked until DDA provides:**
1. Sample GeoTIFF pairs (georeferenced + one without georef)
2. Zone/Village/Area master list (or confirm Delhi list)
3. Department API spec OR confirm export-only
4. Grid definition if grid mode required at launch
---
## 14. Out of scope (explicit)
- Training/fine-tuning new DL models (use existing AdaptFormer + review feedback archive for Phase 2)
- Full GIS desktop analysis (QGIS replacement)
- Real-time satellite ingestion feeds
- Mobile native apps
- Production deployment of DDA features (until UAT sign-off)
---
## 15. Immediate next steps
1. **Create `satdetect-dev` Space env:** `APP_MODE=dda`
2. **Phase 0 PR** to `master` β†’ `git push hf-dev master:main`
3. **Request from DDA:** 3 sample GeoTIFF pairs + hierarchy spreadsheet
4. **Implement Phase 1** (library + upload) β€” highest dependency for all other FRs
---
## Appendix A β€” Legacy endpoint compatibility
Keep existing endpoints working in legacy mode:
| Legacy | DDA equivalent |
|--------|----------------|
| `POST /api/detect` (multipart files) | `POST /api/dda/jobs` (library IDs) |
| `GET /api/history` | `GET /api/dda/reports` |
| Guest user | RBAC users with roles |
No breaking changes to production API contract.
---
## Appendix B β€” Sample job response
```json
{
"jobId": 42,
"status": "completed",
"runId": 108,
"reportUrl": "/api/dda/reports/108",
"summary": {
"regionsCount": 7,
"changePercentage": 2.34,
"byType": {
"New Construction": 3,
"Vegetation Change": 2,
"Other": 2
}
}
}
```
---
*Plan version 1.0 β€” derived from **Scope of Work_Change Detection-For Team.docx** (FR-01 through FR-08).*