farm-layout-model / README.md
spacedout-bits's picture
Update Gradio sdk_version to 6.14.0 to fix static asset 404s
d8aa682

A newer version of the Gradio SDK is available: 6.15.2

Upgrade
metadata
title: Farm Drip Irrigation Designer
emoji: πŸ’§
colorFrom: green
colorTo: blue
sdk: gradio
sdk_version: 6.14.0
app_file: app.py
pinned: false
short_description: Design drip irrigation layouts with deterministic geometry

Farm Drip Irrigation Designer

Overview

A conversational tool for designing drip irrigation systems for farms. Phase 1 focuses on the geometry engine: deterministic pipe layout generation using shapely and pyproj.

Key features:

  • Parse farm boundaries (geofences) from text input
  • Generate drip irrigation layouts (main lines + parallel laterals)
  • Calculate material requirements and cost estimates
  • Support for multiple crop types with optimized spacing parameters

Architecture

Phase 1: Geometry Engine (Current)

Core modules:

  • drip_engine.py – Pure geometry functions (no ML)

    • Geofence parsing and validation
    • UTM coordinate conversion (via pyproj)
    • Drip layout generation (main + laterals with clipping)
    • Bill of materials (BOM) estimation
    • Human-readable design summaries
  • app.py – Gradio UI for testing

    • Input: farm boundary (geofence), crop type, design parameters
    • Output: visualized layout, BOM (JSON), design summary
    • Example geofences for quick testing
  • test_drip_engine.py – Comprehensive test suite

    • 26 unit tests covering parsing, geometry, BOM, edge cases
    • All tests passing βœ…

Phase 2: LLM Integration (Planned)

  • llm_handler.py – Conversational interface
    • LLM as drip irrigation specialist
    • Guides users through design decisions
    • Calls geometry engine to update designs
    • Answers domain-specific questions

Phase 3: Deployment & Polish

  • Error handling and input validation
  • HF Spaces deployment with live inference
  • Documentation and user workflows

Getting Started

Install Dependencies

pip install -r requirements.txt

Run Tests

python3 -m pytest test_drip_engine.py -v

Expected: 26/26 tests passing

Run the UI

python3 app.py

Then open http://localhost:7860 in your browser.

Example: 1-hectare tomato field

  1. Default geofence: 0,0;100,0;100,100;0,100 (100m Γ— 100m = 1 ha)
  2. Select crop: "tomato" (0.5m lateral spacing)
  3. Headland: 1.0 m
  4. Click "Generate Design"

Output:

  • Visual layout (green boundary, red main line, blue laterals)
  • BOM with pipe lengths and costs
  • Design summary with emitter count and flow rate

Design Decisions

Why Geometry, Not ML?

Drip design is deterministic. Given a field shape, crop type, and spacing preferences, the layout is fully determined. ML would be:

  • Slower
  • Harder to debug
  • Less accurate
  • Overkill for a well-defined problem

The geometry engine is the source of truth. Future ML (e.g., SAM for boundary extraction) would feed into this engine, not replace it.

Why LLM in Phase 2?

Users think in intent ("I want to grow tomatoes on 3 hectares, budget $500"), not parameters ("0.5m lateral spacing, 100m mains"). The LLM:

  • Translates intent β†’ geometry parameters
  • Explains design tradeoffs
  • Answers agronomic questions
  • Iteratively refines based on feedback

Coordinate Systems

  • Input: Pixel coords (from map screenshots) or lat/lon (GPS)
  • Internal: UTM (meters) – accurate area/length calculations
  • Output: Geometry objects (shapely) + JSON specs

Extensibility

Add a New Crop

Edit drip_engine.py's CROP_DEFAULTS:

CROP_DEFAULTS = {
    "my_crop": {
        "lateral_spacing_m": 0.75,
        "emitter_spacing_m": 0.35,
        "emitter_discharge_lph": 5
    },
    # ...
}

Adjust Cost Estimates

Edit PIPE_COSTS in drip_engine.py:

PIPE_COSTS = {
    "main_line_16mm": 0.60,  # Update $/meter
    # ...
}

Support Lat/Lon Input

Use latlon_to_utm() instead of treating pixel coords as UTM:

polygon_latlon = parse_geofence_to_polygon(user_input)
polygon_utm = latlon_to_utm(polygon_latlon)  # Auto-selects UTM zone
design = generate_drip_layout(polygon_utm, ...)

Testing

Test coverage includes:

  • βœ… Geofence parsing (valid, invalid, edge cases)
  • βœ… Polygon validation (self-intersecting, too small, etc.)
  • βœ… Area calculation (hectares from UTM)
  • βœ… Layout generation (rectangular, irregular, triangular fields)
  • βœ… Headland buffering
  • βœ… Crop-specific spacing
  • βœ… BOM cost breakdown
  • βœ… Edge cases (tiny fields, long strips, etc.)

REST API

In addition to the Gradio UI, the Space exposes a one-shot REST endpoint that accepts a flat farm / plots / water_sources payload β€” no Gradio /call/ two-step required.

Endpoints

  • GET /api/v1/health β†’ { "status": "ok", "schema_version": "v1" }
  • POST /api/v1/design β†’ returns { farm_name, design_summary, bom, zone_details, warnings, geojson }

Request schema

{
  "farm": {
    "name": "DakshFarm",
    "location": { "address": "...", "latitude": 20.5937, "longitude": 78.9629 },
    "size":     { "value": 2.5, "unit": "acres" },
    "crop_name": "Wheat"
  },
  "plots": [
    {
      "plot_id": "plot_1",
      "name": "Plot 1",
      "area_sq_m": 12450.0,
      "centroid":   { "latitude": 20.5938, "longitude": 78.9627 },
      "boundaries": [
        { "latitude": 20.5937, "longitude": 78.9626 },
        { "latitude": 20.5939, "longitude": 78.9626 },
        { "latitude": 20.5939, "longitude": 78.9628 },
        { "latitude": 20.5937, "longitude": 78.9628 }
      ]
    }
  ],
  "water_sources": [
    {
      "water_source_id": "ws_1",
      "type": "Motor",
      "name": "Motor-1",
      "location": { "latitude": 20.5938, "longitude": 78.9627 },
      "plot_id": "plot_1"
    }
  ],
  "pump_hp": 5.0,
  "headland_buffer_m": 1.0
}

curl example

curl -X POST 'https://uniphy-farm-layout-model.hf.space/api/v1/design' \
  -H 'Content-Type: application/json' \
  -d @samples/rest_input_example.json

Notes / current limits

  • Multiple water_sources are accepted, but only the first is used as the pump (returned in warnings).
  • All plots inherit farm.crop_name as their crop. Per-plot crop overrides aren't wired up yet.
  • If plot polygons don't overlap, the engine sees the convex hull of their union as the farm boundary.

API Reference

parse_geofence_to_polygon(geofence_input: str) β†’ Polygon

Parse text like "0,0;100,0;100,100;0,100" into a Shapely Polygon.

generate_drip_layout(polygon_utm, crop, headland_buffer_m, ...) β†’ Dict

Generate layout. Returns dict with:

  • farm_area_ha: Area in hectares
  • main_line: LineString
  • laterals: List of LineStrings
  • total_drip_tape_m: Total lateral length
  • emitter_count: Estimated emitters
  • design_params: Spacing, discharge, etc.

estimate_bom(design, unit="usd") β†’ Dict

Bill of materials with quantities and costs.

design_summary(design, bom) β†’ str

Human-readable summary for the user.

Next Steps

  1. Deploy Phase 1 to HF Spaces – test with real users
  2. Build Phase 2 – add LLM conversational layer
  3. Integrate boundary extraction – use SAM or segmentation model for auto-boundary from aerial imagery (optional)
  4. Real-world validation – test designs with actual irrigation installers

References

License

MIT (for now – update as needed)