227 lines
5.9 KiB
Markdown
227 lines
5.9 KiB
Markdown
# 🗺 Parcel Subdivision Tool
|
|
|
|
A full-stack GIS application for automatic land parcel generation from user-drawn site boundaries and road networks.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────┐
|
|
│ Docker Network │
|
|
│ │
|
|
│ ┌─────────────┐ ┌──────────────────────┐ │
|
|
│ │ Nginx │────▶│ FastAPI Backend │ │
|
|
│ │ (port 80) │ │ (port 8000) │ │
|
|
│ │ + Frontend │ │ Shapely/GIS engine │ │
|
|
│ └─────────────┘ └──────────────────────┘ │
|
|
└──────────────────────────────────────────────────┘
|
|
▲
|
|
│ :8080
|
|
Browser
|
|
(OpenLayers UI)
|
|
```
|
|
|
|
## Features
|
|
|
|
| Feature | Description |
|
|
|---|---|
|
|
| **Draw Boundary** | Freehand polygon drawing on map |
|
|
| **Draw Roads** | Road centerline drawing (buffered to configured width) |
|
|
| **Auto Road Grid** | Automatic internal road generation if no roads drawn |
|
|
| **Block Detection** | Remaining buildable areas become blocks |
|
|
| **Cul-de-sacs** | Auto-generated for blocks exceeding max length |
|
|
| **Parcel Subdivision** | Rectangular parcel generation respecting min frontage/depth |
|
|
| **Shape Optimization** | Bad/small parcels absorbed by neighbors |
|
|
| **Addressing** | Auto address assignment per block/plot |
|
|
| **GeoJSON Export** | Download all results |
|
|
| **Layer Controls** | Toggle parcels, roads, blocks, cul-de-sacs |
|
|
| **Hover Tooltips** | Area, frontage, depth, address per parcel |
|
|
|
|
## Default Parameters
|
|
|
|
```
|
|
min_frontage = 12 m
|
|
min_depth = 25 m
|
|
road_width = 9 m
|
|
max_block_length = 120 m
|
|
allow_culdesac = true
|
|
corner_radius = 3 m
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
- Docker & Docker Compose installed
|
|
|
|
### Run
|
|
|
|
```bash
|
|
# Clone / extract project
|
|
cd parcel-tool
|
|
|
|
# Build and start all services
|
|
docker compose up --build
|
|
|
|
# Access at:
|
|
http://localhost:8080
|
|
```
|
|
|
|
### Stop
|
|
|
|
```bash
|
|
docker compose down
|
|
```
|
|
|
|
---
|
|
|
|
## Usage Guide
|
|
|
|
### Step 1 — Draw Boundary
|
|
1. Click **Draw Boundary** in the toolbar
|
|
2. Click on the map to add polygon vertices
|
|
3. **Double-click** to finish the polygon
|
|
4. Press **Escape** to cancel
|
|
|
|
### Step 2 — (Optional) Draw Roads
|
|
1. Click **Draw Road**
|
|
2. Click to add road centerline vertices
|
|
3. Double-click to finish
|
|
4. Repeat for additional roads
|
|
|
|
> If no roads are drawn, the engine auto-generates an internal road grid based on `max_block_length`.
|
|
|
|
### Step 3 — Configure Parameters
|
|
Adjust the configuration panel:
|
|
- **Min Frontage** — minimum plot road frontage
|
|
- **Min Depth** — minimum plot depth
|
|
- **Road Width** — road right-of-way width
|
|
- **Max Block Length** — triggers cul-de-sac or cross-road insertion
|
|
- **Corner Radius** — road intersection corner rounding
|
|
- **Allow Cul-de-sacs** — toggle cul-de-sac generation
|
|
|
|
### Step 4 — Generate
|
|
Click **⚡ Generate Subdivision**
|
|
|
|
Results appear color-coded:
|
|
- 🔵 **Blue** — parcels
|
|
- ⚫ **Dark grey** — roads
|
|
- 🟢 **Green dashed** — block boundaries
|
|
- 🟡 **Yellow** — cul-de-sacs
|
|
|
|
### Step 5 — Inspect & Export
|
|
- Hover over parcels to see attributes
|
|
- Click parcels to highlight/select
|
|
- Export all data as GeoJSON
|
|
|
|
---
|
|
|
|
## API Reference
|
|
|
|
### POST `/api/subdivide`
|
|
|
|
```json
|
|
{
|
|
"boundary": { /* GeoJSON Polygon */ },
|
|
"roads": [ /* GeoJSON LineStrings */ ],
|
|
"config": {
|
|
"min_frontage": 12,
|
|
"min_depth": 25,
|
|
"road_width": 9,
|
|
"max_block_length": 120,
|
|
"allow_culdesac": true,
|
|
"corner_radius": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"parcels": [ /* GeoJSON Features with properties */ ],
|
|
"roads": [ /* GeoJSON Features */ ],
|
|
"blocks": [ /* GeoJSON Features */ ],
|
|
"culdesacs": [ /* GeoJSON Features */ ],
|
|
"stats": {
|
|
"total_parcels": 42,
|
|
"total_blocks": 4,
|
|
"avg_parcel_area_m2": 340.5,
|
|
"culdesacs": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
### GET `/api/health`
|
|
Returns `{"status": "ok"}`
|
|
|
|
### GET `/api/config/defaults`
|
|
Returns default configuration values.
|
|
|
|
---
|
|
|
|
## Subdivision Algorithm
|
|
|
|
```
|
|
1. Parse boundary polygon (EPSG:4326 → WGS84)
|
|
2. Buffer user roads → road polygons
|
|
└─ If no roads: auto-generate grid roads at max_block_length intervals
|
|
3. Subtract roads from boundary → buildable blocks
|
|
4. For each oversized block (> max_block_length):
|
|
└─ Insert cul-de-sac if allow_culdesac=true
|
|
5. For each block:
|
|
a. Detect dominant orientation via OBB
|
|
b. Determine double/single frontage layout
|
|
c. Calculate parcel columns and rows
|
|
d. Clip each parcel cell to block boundary
|
|
6. Shape QC:
|
|
- Compactness check (reject triangular/complex shapes)
|
|
- Minimum area check
|
|
- Absorb rejected parcels into neighboring plots
|
|
7. Assign addresses: "Plot N, Block B Road"
|
|
8. Return GeoJSON FeatureCollection
|
|
```
|
|
|
|
---
|
|
|
|
## Development
|
|
|
|
### Backend (Python/FastAPI)
|
|
|
|
```bash
|
|
cd backend
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload --port 8000
|
|
```
|
|
|
|
### Frontend
|
|
Pure HTML/JS — just open `frontend/index.html` in a browser (update API_BASE to `http://localhost:8000`).
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
```
|
|
parcel-tool/
|
|
├── docker-compose.yml
|
|
├── README.md
|
|
├── backend/
|
|
│ ├── Dockerfile
|
|
│ ├── requirements.txt
|
|
│ └── main.py ← Subdivision engine
|
|
├── frontend/
|
|
│ └── index.html ← OpenLayers UI
|
|
└── nginx/
|
|
└── nginx.conf ← Reverse proxy config
|
|
```
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- The map uses CartoDB Dark Matter basemap (no API key needed)
|
|
- Coordinate system: EPSG:4326 (WGS84) for I/O, EPSG:3857 for display
|
|
- Geometry engine: Shapely 2.x with GEOS backend
|
|
- For large sites (>100ha), processing may take a few seconds
|