2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00
2026-03-03 11:33:31 +03:00

🗺 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

# Clone / extract project
cd parcel-tool

# Build and start all services
docker compose up --build

# Access at:
http://localhost:8080

Stop

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

{
  "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:

{
  "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)

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
Description
A improved version. improved UI/ux
Readme 195 KiB
Languages
HTML 76%
Python 23.7%
Dockerfile 0.3%