- C++ 99%
- C 1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs/images | ||
| FieldCAD | ||
| .gitignore | ||
| CLAUDE.md | ||
| FieldCAD.sln | ||
| README.md | ||
FieldCAD
A native Windows desktop app for mapping agricultural fields — draw and measure field boundaries, exchange them with real GIS and CAD software, trace over georeferenced aerial imagery, plan full-coverage drone spray routes, and auto-design sprinkler irrigation.
Written in C++17 against raw Win32 and GDI+. No third-party libraries — the Shapefile, DXF, world-file and project-file readers and writers are all hand-rolled from the public format specifications, and every pixel of the canvas is drawn by hand.
Contents
- Draw and measure
- Work with many fields
- Drone spray routes
- Irrigation auto-design
- Building
- Self-test
- Controls
- File formats
- Architecture
- Known limitations
Draw and measure
Click to place vertices, then close the ring by clicking the first vertex again, double-clicking, or pressing Enter. The status bar reports area in both square metres and acres, plus perimeter, as soon as the field closes. Vertices stay draggable afterwards, and everything is undoable.
Snap-to-grid is on by default (G toggles it, + and - change the spacing). If you need exact numbers rather than clicks, press C and type coordinates directly.
Work with many fields
Ctrl+click to add fields to the selection, or drag a marquee across the canvas to grab everything it touches. With more than one selected, the status bar totals their combined area — handy for working out how much you are covering in one pass.
The right-hand panel lists every field and stays in sync with the canvas
selection in both directions. Collapse it with the > strip when you want the
full width for drawing.
Drone spray routes
Select the fields to treat, give a start point and a range budget per trip, and FieldCAD plans the flight. It splits the work into as many round trips as the range requires, and says so when a field cannot be reached and returned from within the budget.
Set a spray radius and it plans genuine coverage rather than a sightseeing tour: back-and-forth (boustrophedon) passes one swath wide, inset from the boundary by the spray radius so the sprayed band lands inside the field. Concave fields split into several passes per scanline automatically. The swath is drawn as a translucent band beneath the flight path, so you can see what actually gets wet.
Leave the spray radius at 0 and you get the simpler behaviour — a nearest-neighbour tour of field centres, improved by a 2-opt pass.
Pick the start point by typing coordinates, or press Pick Start on Map and click the canvas.
Irrigation auto-design
Select one field, give a sprinkler spacing and radius, and get a full layout: heads placed across the interior, and a minimum-spanning-tree pipe network running from your water source to every one of them.
- Coverage is measured, not assumed. The result reports the percentage of the field actually within reach of a sprinkler, and when gaps remain it tells you the spacing that would close them.
- Square or triangular packing. Hexagonal packing covers the same field with roughly a third fewer heads at equal radius.
- Pipes are sized, not just routed. Because the network is a tree, the flow in each run is exactly its downstream head count times the per-head flow. From that, FieldCAD reports peak system flow and the mainline diameter needed to stay under a 1.5 m/s velocity cap.
Building
Requires Visual Studio with the Desktop development with C++ workload. x64
only, C++17, /W4, toolset v145. The only dependencies are system libraries
(gdiplus, comdlg32, dwmapi, uxtheme) — no vcpkg, no NuGet, no submodules.
Open FieldCAD.sln and press F5, or from a Developer PowerShell:
msbuild FieldCAD.sln /p:Configuration=Release /p:Platform=x64
The executable lands in bin\x64\Release\FieldCAD.exe.
Self-test
There is no separate test project. A hidden console path in the same executable exercises every non-GUI subsystem — geometry, all four file formats, route planning, irrigation placement, pipe routing and hydraulics — without opening a window:
bin\x64\Debug\FieldCAD.exe --selftest
It prints PASS/FAIL for each of 112 checks and exits non-zero if any fail.
New non-GUI logic is expected to arrive with cases added to src/SelfTest.cpp.
Controls
| Action | Input |
|---|---|
| Add a vertex (starts a field if none is active) | Left-click |
| Close the field being drawn | Click the start vertex, double-click, or Enter |
| Cancel the field being drawn | Esc |
| Select a field | Left-click inside it |
| Add / remove from the selection | Ctrl+click |
| Select everything a drag touches | Drag from empty space (Ctrl to add) |
| Move a vertex | Drag it (when exactly one field is selected) |
| Delete selected field(s) | Del |
| Rename the selected field | F2, or double-click it in the list |
| Type an exact coordinate | C |
| Pick a route start or water source on the canvas | Pick ... on Map in either tool dialog, then click |
| Undo / redo | Ctrl+Z / Ctrl+Y |
| Pan | Middle-drag, or hold Space and drag |
| Zoom to cursor | Mouse wheel |
| Toggle snap-to-grid | G |
| Grow / shrink grid spacing | + / - |
| Reset the view | 0 |
Shortcuts run through a real Win32 accelerator table, so they work no matter which control has keyboard focus.
File formats
| Format | Notes |
|---|---|
Shapefile .shp .shx .dbf .prj |
Standard ESRI polygon shapefile with ID and NAME attributes; opens in QGIS and ArcGIS. The .prj declares a local planar CRS in metres so GIS tools stop asking. |
DXF .dxf |
ASCII DXF with closed POLYLINE/VERTEX entities; opens in AutoCAD, QCAD and LibreCAD. Import also reads LWPOLYLINE from other CAD software and survives malformed coordinates. |
World file .jgw .pgw .wld .tfw |
Six-line ESRI georeferencing sidecar for background imagery. All six coefficients are applied, so rotated imagery is placed correctly, not just north-up. |
Project .fcp |
Native UTF-8 text format (v2) holding fields, grid, background image placement, spray route and irrigation design. v1 files still load. Saves are atomic, so a failed write cannot destroy the previous version. |
Coordinates are a local planar system in metres with no map projection — treat the origin as a survey benchmark on the farm.
Architecture
App is a singleton owning all mutable state; Renderer is stateless and is
handed a fresh RenderState each frame. The algorithm modules depend on neither
and are tested independently.
| File | Responsibility |
|---|---|
src/main.cpp |
wWinMain, DPI awareness, --selftest entry point |
src/App.h/.cpp |
Window and message handling, tool state machine, commands, dialogs |
src/Geometry.h/.cpp |
Area, perimeter, centroid, hit-testing, snapping, rect/polygon intersection |
src/Camera.h/.cpp |
World/screen transform, pan, zoom, grid |
src/Renderer.h/.cpp |
Double-buffered GDI+ drawing of every layer plus the HUD |
src/Routing.h/.cpp |
Spray routing: nearest-neighbour + 2-opt, multi-trip, coverage passes |
src/Irrigation.h/.cpp |
Head placement, MST pipe routing, hydraulic sizing, coverage estimation |
src/Shapefile.h/.cpp |
Hand-rolled .shp/.shx/.dbf/.prj |
src/Dxf.h/.cpp |
Hand-rolled ASCII DXF |
src/WorldFile.h/.cpp |
ESRI world-file parsing and image placement |
src/ProjectFile.h/.cpp |
Native .fcp save/load |
src/DarkMode.h/.cpp |
Dark menu bar and popups via undocumented uxtheme ordinals and WM_UAH* |
src/SelfTest.h/.cpp |
The --selftest checks |
Known limitations
- The dark menu bar and popup menus rely on undocumented Windows APIs. They are resolved at runtime and fail soft: a Windows build without them shows the native light menu rather than breaking.
- Background images are referenced by path, not embedded, so moving or deleting the image leaves a project unable to restore it. The fields still open, with a warning.
- Irrigation hydraulics are a first-order sizing pass — flow per run and the diameter needed to hold a velocity cap. There is no pressure or friction-loss modelling, no valve or zoning logic, and no scheduling. Pipe runs are straight MST edges, so on a strongly concave field a run can cross outside the boundary.
- Coverage spray passes are always axis-aligned rather than rotated to a field's longest axis, so a long diagonal field takes more turns than an optimal heading would.
- Full-coverage spacing advice is the unbounded lattice bound. Heads must sit inside the boundary, so a thin margin along the edge can stay uncovered even at that spacing — triangular packing typically lands a couple of percent short where a square grid reaches 100%. The reported coverage percentage is the figure to trust.




