A C++ header-only library for generating AB-lines and optimizing field traversal paths for agricultural robots and tractors.
Farmtrax provides sophisticated algorithms for:
- Field Processing: Convert GeoJSON polygons into workable farm fields with AB-lines (work paths)
- Spatial Partitioning: Divide fields among multiple machines for efficient parallel work
- Path Optimization: Generate optimal traversal sequences using graph theory and spatial indexing
- Agricultural Patterns: Respect real-world farming practices and field geometry
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ FARMTRAX PROCESSING PIPELINE │
└─────────────────────────────────────────────────────────────────────────────────────┘
1. INPUT: Field Polygon Points
┌─────────────────────┐
│ GeoJSON Field │ ──┐
│ Boundary Points │ │
└─────────────────────┘ │
│
2. FIELD PARTITIONING │
┌─────────────────────┐ │
│ Partitioner │◄──┘
│ • Complex shapes │
│ • Multi-part split │ ──┐
│ • Area threshold │ │
└─────────────────────┘ │
│
3. HEADLAND GENERATION │
┌─────────────────────┐ │
│ generate_headlands │◄──┘
│ • Buffer inward │
│ • Multiple rings │ ──┐
│ • Turn areas │ │
└─────────────────────┘ │
│
4. SWATH GENERATION │
┌─────────────────────┐ │
│ generate_swaths │◄──┘
│ • Parallel lines │
│ • AB-line creation │ ──┐
│ • Optimal spacing │ │
│ • Angle optimization│ │
└─────────────────────┘ │
│
5. SPATIAL INDEXING │
┌─────────────────────┐ │
│ R-tree Build │◄──┘
│ • Swath endpoints │
│ • Bounding boxes │ ──┐
│ • O(log n) queries │ │
└─────────────────────┘ │
│
6. MACHINE DIVISION │
┌─────────────────────┐ │
│ Divy │◄──┘
│ • ALTERNATE │
│ • SPATIAL_RTREE │ ──┐
│ • LENGTH_BALANCED │ │
└─────────────────────┘ │
│
7. OBSTACLE AVOIDANCE │
┌─────────────────────┐ │
│ ObstacleAvoider │◄──┘
│ • Polygon inflation │
│ • Swath cutting │ ──┐
│ • Connection paths │ │
└─────────────────────┘ │
│
8. GRAPH CONSTRUCTION │
┌─────────────────────┐ │
│ Boost Graph │◄──┘
│ • Vertex: endpoints │
│ • Edges: work/move │ ──┐
│ • Weight: distance │ │
└─────────────────────┘ │
│
9. PATH OPTIMIZATION │
┌─────────────────────┐ │
│ Nety │◄──┘
│ • Field traversal │
│ • Pattern scoring │ ──┐
│ • Spatial clusters │ │
│ • Direction aware │ │
└─────────────────────┘ │
│
10. OUTPUT: Optimized Path │
┌────────────────────┐ │
│ Ordered Swaths │◄──┘
│ • Work sequences │
│ • Connection moves │
│ • Minimal distance │
└────────────────────┘
Data Flow Detail:
Field Polygon → [Partitioner] → Parts[]
↓
Parts[] → [Headland Gen] → Parts[].headlands[]
↓
Parts[] → [Swath Gen] → Parts[].swaths[] (AB-lines)
↓
Swaths[] → [R-tree Build] → Spatial Index
↓
Swaths[] → [Divy] → Machine Assignment
↓
Machine Swaths[] → [ObstacleAvoider] → Cut/Connected Swaths[]
↓
Swaths[] → [Nety Graph] → Graph(Vertices, Edges)
↓
Graph + Start Point → [Traversal Algorithm] → Optimized Path[]
- 🚜 Agricultural AB-Line Generation: Create work paths with A and B endpoints
- 🗺️ Multi-Machine Division: Intelligent field partitioning for fleet operations
- 🌾 Field-Aware Traversal: Graph-based path optimization with spatial clustering
- 📊 R-tree Spatial Indexing: Efficient nearest-neighbor queries for large fields
- 📈 Real-time Visualization: Integration with Rerun for 3D field visualization
- ⚡ High Performance: Optimized algorithms for real-time agricultural applications
- 🛡️ Obstacle Avoidance: Dynamic path cutting and reconnection around obstacles
- 🎯 Pattern Recognition: Agricultural-aware path patterns and field geometry respect
- Load GeoJSON polygon boundaries
- Intelligent Multi-Criteria Field Partitioning: Advanced polygon splitting system
- Area-based splitting: Split fields exceeding size thresholds (default 5 hectares)
- Bridge detection: Identify and split narrow connections using morphological erosion
- Convexity analysis: Split highly concave fields (below 60% convexity ratio)
- Aspect ratio control: Split elongated fields with excessive length/width ratios
- Tooth/extension detection: Identify and separate field protrusions and concavities
- Recursive partitioning: Apply multiple criteria with configurable depth limits
- Handle irregular shapes and multiple disconnected areas
- Configurable partitioning criteria for different agricultural scenarios
- Create turning areas by buffering field boundaries inward
- Generate multiple headland rings for different vehicle sizes
- Ensure proper turning radius for agricultural machinery
- Create parallel work lines across the interior field area
- Optimize line angle for minimal number of passes
- Generate A and B endpoints for each work line
- Respect field geometry and minimize short segments
- Build efficient spatial data structures for fast queries
- Index swath endpoints, bounding boxes, and field boundaries
- Enable O(log n) nearest-neighbor searches for large fields
- ALTERNATE: Distribute swaths in alternating pattern for load balancing
- SPATIAL_RTREE: Use spatial proximity for efficient machine routing
- LENGTH_BALANCED: Balance workload by total swath length per machine
- Inflate obstacle polygons by machine safety margin
- Cut swaths that intersect obstacles into smaller segments
- Create connection paths between cut segments
- Maintain work continuity while avoiding hazards
- Build Boost Graph with vertices at each AB-line endpoint
- Create edges for both work paths (A→B) and transitions (between lines)
- Weight edges by actual distances and movement costs
- Progressive Spatial Search: Start with 15m radius, expand to 500m
- Agricultural Pattern Scoring: Prefer parallel line clusters
- Direction-Aware Connections: Minimize cross-field movements
- Field Pattern Recognition: Maintain consistent farming patterns
- Reorder swaths according to optimal traversal sequence
- Generate connection moves between work areas
- Provide complete path with distance metrics and timing
FetchContent_Declare(
farmtrax
GIT_REPOSITORY https://github.com/bresilla/farmtrax.git
GIT_TAG main
)
FetchContent_MakeAvailable(farmtrax)
target_link_libraries(<lib/bin> PRIVATE farmtrax::farmtrax)- Boost Geometry: For spatial operations and R-tree indexing
- Boost Graph: For graph algorithms (Dijkstra, traversal)
- Concord: Coordinate system transformations
- GeoSON: GeoJSON parsing and writing
- Rerun (optional): For 3D visualization
#include "farmtrax/field.hpp"
#include "farmtrax/graph.hpp"
#include "farmtrax/divy.hpp"
#include "farmtrax/avoid.hpp"
// 1. Load field from GeoJSON
concord::Polygon poly;
auto fc = geoson::ReadFeatureCollection("field.geojson");
poly = std::get<concord::Polygon>(fc.features[0].geometry);
// 2. Create field with headlands and AB-lines
concord::Datum datum{51.989, 5.658, 53.801};
farmtrax::Field field(poly, 0.1, datum, true, 0.5);
field.gen_field(4.0, 0.0, 3); // 4m spacing, 0° angle, 3 headland rings
// 3. Divide field among machines
auto fieldPtr = std::make_shared<farmtrax::Field>(field);
farmtrax::Divy divy(fieldPtr, farmtrax::DivisionType::ALTERNATE, 4);
// 4. Set up obstacle avoidance (optional)
std::vector<concord::Polygon> obstacles = {/* obstacle polygons */};
farmtrax::ObstacleAvoider avoider(obstacles, datum);
// 5. Generate optimal paths for each machine
auto& res = divy.result();
for (size_t m = 0; m < 4; ++m) {
if (res.swaths_per_machine.at(m).empty()) continue;
// Apply obstacle avoidance with 2.0m inflation distance
auto avoided_swaths = avoider.avoid(res.swaths_per_machine.at(m), 2.0f);
// Create graph-based path optimizer (filters to only work swaths)
farmtrax::Nety nety(avoided_swaths);
// Generate optimal traversal path
nety.field_traversal(); // Reorders swaths internally
std::cout << "Machine " << m << " optimized path: "
<< nety.get_swaths().size() << " work segments\n";
}// Step-by-step processing for detailed control
// 1. Field partitioning
farmtrax::Field field(polygon, 0.1, datum);
std::cout << "Field split into " << field.get_parts().size() << " parts\n";
// 2. Generate headlands first
field.gen_field(4.0, 0.0, 3);
for (const auto& part : field.get_parts()) {
std::cout << "Part has " << part.headlands.size() << " headland rings\n";
std::cout << "Part has " << part.swaths.size() << " work swaths\n";
}
// 3. Spatial indexing (automatic)
// R-trees are built automatically for efficient queries
// 4. Machine division with different strategies
farmtrax::Divy divy_alternate(fieldPtr, farmtrax::DivisionType::ALTERNATE, 4);
farmtrax::Divy divy_spatial(fieldPtr, farmtrax::DivisionType::SPATIAL_RTREE, 4);
farmtrax::Divy divy_balanced(fieldPtr, farmtrax::DivisionType::LENGTH_BALANCED, 4);
// 5. Obstacle avoidance processing
auto machine_swaths = divy_alternate.result().swaths_per_machine.at(0);
auto safe_swaths = avoider.avoid(machine_swaths, 2.0f);
// 6. Graph construction and optimization
farmtrax::Nety optimizer(safe_swaths);
optimizer.field_traversal(); // Creates optimal traversal order
// 7. Access results
const auto& optimized_swaths = optimizer.get_swaths();
for (size_t i = 0; i < optimized_swaths.size(); ++i) {
const auto& swath = optimized_swaths[i];
std::cout << "Segment " << i << ": "
<< (swath->type == farmtrax::SwathType::Swath ? "WORK" : "MOVE")
<< " from (" << swath->getHead().enu.x << "," << swath->getHead().enu.y << ")"
<< " to (" << swath->getTail().enu.x << "," << swath->getTail().enu.y << ")\n";
}Converts polygon boundaries into workable farm fields:
farmtrax::Field field(polygon, resolution, datum, add_headlands, headland_width);
field.gen_field(spacing, angle, num_headland_rings);- Polygon Processing: Handles complex field boundaries with holes
- Partitioning: Automatically splits large/complex fields into manageable parts
- Headland Generation: Creates turning areas by inward buffering
- AB-Line Generation: Creates parallel work paths across the interior field
- Spatial Indexing: Builds R-trees for efficient spatial queries
Intelligently partitions work among multiple machines:
farmtrax::Divy divy(field_ptr, farmtrax::DivisionType::ALTERNATE, num_machines);Division Types:
ALTERNATE: Alternating swath assignment for balanced workloadSPATIAL_RTREE: Spatial proximity clustering for minimal inter-machine travelLENGTH_BALANCED: Equal total work length distributionBLOCK: Contiguous blocks for minimal inter-machine conflicts
Handles dynamic obstacle avoidance in field operations:
farmtrax::ObstacleAvoider avoider(obstacles, datum);
auto safe_swaths = avoider.avoid(input_swaths, inflation_distance);Features:
- Polygon Inflation: Expands obstacles by safety margin
- Swath Cutting: Divides work lines around obstacles
- Connection Generation: Creates transition paths between cut segments
- Smart Reconnection: Maintains work flow continuity
Advanced graph-based traversal optimization using spatial algorithms:
farmtrax::Nety nety(swaths);
nety.field_traversal(); // Optimizes and reorders swathsOptimization Features:
- Field-Aware Patterns: Respects agricultural work patterns
- Spatial Clustering: Groups nearby parallel lines
- Progressive Search: Expanding radius nearest-neighbor queries
- Direction Optimization: Minimizes cross-field movements
Farmtrax includes a sophisticated multi-criteria field partitioning system that intelligently splits complex agricultural fields into manageable parts. This system goes far beyond simple area-based splitting.
The farmtrax::Partitioner class implements five distinct splitting strategies applied in priority order:
farmtrax::Partitioner::PartitionCriteria criteria;
criteria.max_area = 50000.0; // 5 hectares maximum- Splits fields exceeding area thresholds
- Uses geometric center-line cutting
- Splits along the longer dimension for optimal shapes
criteria.min_bridge_width = 20.0; // Minimum bridge width in meters
criteria.enable_bridge_detection = true;- Uses morphological erosion to detect narrow connections
- Applies negative buffer operations to identify bottlenecks
- Automatically splits at narrowest connection points
criteria.tooth_threshold = 0.3; // 30% area threshold for teeth
criteria.enable_tooth_detection = true;- Calculates convex hull difference to find concave areas
- Identifies field "teeth" and protruding extensions
- Splits across significant concavities exceeding threshold
criteria.max_aspect_ratio = 4.0; // Maximum length/width ratio
criteria.enable_aspect_splitting = true;- Prevents overly elongated field parts
- Maintains workable field proportions
- Optimizes for machinery turning patterns
criteria.min_convexity = 0.6; // Minimum 60% convexity ratio- Calculates
polygon_area / convex_hull_arearatio - Splits highly irregular, non-convex shapes
- Ensures workable field geometry
// Create partitioner with custom criteria
farmtrax::Partitioner partitioner(field_polygon, datum);
farmtrax::Partitioner::PartitionCriteria custom_criteria;
custom_criteria.max_area = 25000.0; // 2.5 hectares max
custom_criteria.min_convexity = 0.75; // Stricter convexity
custom_criteria.max_aspect_ratio = 3.0; // Prevent elongation
custom_criteria.min_bridge_width = 15.0; // Aggressive bridge detection
custom_criteria.tooth_threshold = 0.2; // Sensitive tooth detection
custom_criteria.max_recursion_depth = 6; // Deep recursive splitting
custom_criteria.enable_bridge_detection = true;
custom_criteria.enable_tooth_detection = true;
custom_criteria.enable_aspect_splitting = true;
// Apply intelligent partitioning
auto field_parts = partitioner.partition(25000.0, custom_criteria);The partitioning system is seamlessly integrated into the main Field class:
// Automatic partitioning during field creation
farmtrax::Field field(polygon, 0.1, datum, true, 0.7, 15000.0); // 1.5 hectare threshold
// Each part gets independent processing
for (const auto& part : field.get_parts()) {
// Each part has: headlands, swaths, spatial indices
std::cout << "Part: " << part.headlands.size() << " headlands, "
<< part.swaths.size() << " swaths\n";
}- Complex Field Shapes: L-shaped, T-shaped, and irregular boundaries
- Bridge Fields: Fields connected by narrow access routes
- Concave Fields: Fields with internal concavities or "bites"
- Large Fields: Automatically split oversized fields for efficient management
- Multi-Machine Operations: Create optimal work zones for multiple machines
Farmtrax features a sophisticated multi-criteria field partitioning system that intelligently splits complex agricultural fields into manageable work areas. The system goes far beyond simple area-based splitting to handle real-world field complexities.
The intelligent partitioner uses five different splitting strategies in priority order:
criteria.max_area = 50000.0; // Maximum area in square meters (5 hectares)- Ensures field parts remain within manageable size limits
- Splits along the longer dimension for optimal machinery access
- Foundation for all other splitting criteria
criteria.min_bridge_width = 20.0; // Minimum bridge width in meters
criteria.enable_bridge_detection = true;- Uses polygon erosion techniques to detect narrow connections
- Identifies bottlenecks that create inefficient machinery paths
- Automatically splits at the narrowest connection points
criteria.tooth_threshold = 0.3; // 30% area threshold for teeth
criteria.enable_tooth_detection = true;- Calculates convex hull difference to find concave areas
- Identifies field "teeth" and protruding extensions
- Splits across significant concavities exceeding threshold
criteria.max_aspect_ratio = 4.0; // Maximum length/width ratio
criteria.enable_aspect_splitting = true;- Prevents overly elongated field parts
- Maintains workable field proportions
- Optimizes for machinery turning patterns
criteria.min_convexity = 0.6; // Minimum 60% convexity ratio- Measures polygon area/convex_hull_area ratio
- Ensures field parts have reasonable geometric properties
- Fallback splitting when other criteria fail
// Create partitioner with intelligent defaults
farmtrax::Partitioner partitioner(field_polygon, datum);
// Configure custom criteria for specific agricultural needs
farmtrax::Partitioner::PartitionCriteria criteria;
criteria.max_area = 15000.0; // 1.5 hectares max
criteria.min_convexity = 0.7; // 70% convexity required
criteria.max_aspect_ratio = 3.0; // 3:1 max length/width
criteria.min_bridge_width = 15.0; // 15m minimum bridge width
criteria.tooth_threshold = 0.25; // 25% area threshold
criteria.max_recursion_depth = 6; // Deep recursive splitting
criteria.enable_bridge_detection = true;
criteria.enable_tooth_detection = true;
criteria.enable_aspect_splitting = true;
// Apply intelligent partitioning
auto field_parts = partitioner.partition(15000.0, criteria);
std::cout << "Field intelligently split into " << field_parts.size() << " parts\n";The partitioner employs sophisticated Boost Geometry algorithms:
- Buffer Operations: Erosion-based bridge detection
- Convex Hull Analysis: Shape complexity measurement
- Boolean Operations: Polygon cutting and splitting
- Centroid Calculations: Optimal split line placement
- Envelope Analysis: Bounding box-based aspect ratios
// Automatic integration during field creation
farmtrax::Field field(polygon, resolution, datum, centered, overlap, area_threshold);
// Field is automatically partitioned based on area_threshold
// Access partitioned field parts
for (const auto& part : field.get_parts()) {
std::cout << "Part: " << part.headlands.size() << " headlands, "
<< part.swaths.size() << " swaths\n";
}- Machinery Efficiency: Eliminates long traverses between disconnected areas
- Pattern Recognition: Identifies field geometry patterns for optimal work sequences
- Obstacle Integration: Works seamlessly with obstacle avoidance systems
- Scalability: Handles fields from small plots to large commercial operations
- Quality Assurance: Ensures each part is workable by agricultural machinery
The core innovation of Farmtrax is the Nety class, which combines graph theory with spatial indexing for optimal field traversal.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ AB Lines │───▶│ Nety Class │───▶│ Optimal Path │
│ (Work Paths) │ │ │ │ (Vertex List) │
└─────────────────┘ │ ┌──────────────┐ │ └─────────────────┘
│ │ R-tree │ │
│ │ Spatial │ │
│ │ Index │ │
│ └──────────────┘ │
│ ┌──────────────┐ │
│ │ Boost │ │
│ │ Graph │ │
│ └──────────────┘ │
└──────────────────┘
The R-tree provides O(log n) spatial queries for efficient nearest-neighbor searches:
using EndpointTree = boost::geometry::index::rtree<PointRTreeValue,
boost::geometry::index::quadratic<16>>;Key Benefits:
- Fast Spatial Queries: Find nearby endpoints in logarithmic time
- Progressive Search: Expanding radius search (15m → 30m → 50m → 100m → 200m → 500m)
- Spatial Clustering: Groups nearby parallel lines to minimize cross-field jumps
Each AB line creates two vertices (A and B endpoints) connected by edges:
// Graph components
using Graph = boost::adjacency_list<boost::listS, boost::vecS,
boost::undirectedS, VertexProps, EdgeProps>;
std::vector<Vertex> vertex_A_; // A endpoints for each line
std::vector<Vertex> vertex_B_; // B endpoints for each lineEdge Types:
- Work Edges: A↔B connections (actual field work)
- Transition Edges: Connections between different AB lines (movement)
The traversal algorithm combines multiple heuristics:
const std::vector<double> search_radii = {15.0, 30.0, 50.0, 100.0, 200.0, 500.0};- Starts with tight 15m radius for local clustering
- Expands progressively if no suitable candidates found
- Prevents excessive cross-field connections
double score = distance + direction_penalty - pattern_bonus;Distance Component: Base cost (shorter is better) Direction Penalty: Prefers perpendicular movement to line orientation Pattern Bonus: Rewards maintaining parallel line clusters
bool lines_have_similar_orientation(size_t line1_id, size_t line2_id) const {
// Check if lines are parallel within 15° tolerance
return angle < (15.0 * M_PI / 180.0);
}Smart connection management prevents unrealistic paths:
- Distance Threshold: Maximum 150m connection radius
- Connection Limit: At most 4 connections per endpoint
- Parallel Preference: 50% penalty for non-parallel connections
- Clustering Bonus: Rewards local field patterns
| Operation | Time Complexity | Description |
|---|---|---|
| R-tree Insert | O(log n) | Adding endpoints to spatial index |
| Nearest Neighbor | O(log n) | Finding closest unvisited endpoints |
| Graph Construction | O(n log n) | Building connectivity graph |
| Dijkstra Path | O((V + E) log V) | Shortest path calculation |
| Field Traversal | O(n² log n) | Complete field traversal |
- Reduced Travel Time: Minimizes non-productive movement between work areas
- Fuel Efficiency: Shorter transition paths reduce fuel consumption
- Field Preservation: Avoids excessive cross-field traffic
- Scalable Performance: Handles large fields with hundreds of AB lines
- Agricultural Compliance: Respects real farming practices and patterns
#include "farmtrax/field.hpp"
#include "farmtrax/graph.hpp"
#include "farmtrax/divy.hpp"
#include "farmtrax/avoid.hpp"
#include "farmtrax/utils/visualize.hpp"
int main() {
// Load and process field
auto fc = geoson::ReadFeatureCollection("field.geojson");
concord::Polygon poly = std::get<concord::Polygon>(fc.features[0].geometry);
concord::Datum datum{51.989, 5.658, 53.801};
farmtrax::Field field(poly, 0.1, datum, true, 0.5);
field.gen_field(4.0, 0.0, 3); // 4m spacing, auto angle, 3 headland rings
// Load obstacles (trees, buildings, etc.)
std::vector<concord::Polygon> obstacles;
// ... load obstacle polygons from GeoJSON or other sources
farmtrax::ObstacleAvoider avoider(obstacles, datum);
// Multi-machine division
auto fieldPtr = std::make_shared<farmtrax::Field>(field);
farmtrax::Divy divy(fieldPtr, farmtrax::DivisionType::SPATIAL_RTREE, 4);
// Set up visualization (optional)
auto rec = std::make_shared<rerun::RecordingStream>("farmtrax", "space");
rec->connect_grpc("rerun+http://0.0.0.0:9876/proxy");
farmtrax::visualize::show_field(field, rec);
// Process each machine
auto& res = divy.result();
for (size_t m = 0; m < 4; ++m) {
if (res.swaths_per_machine.at(m).empty()) continue;
std::cout << "Machine " << m << " original swaths: "
<< res.swaths_per_machine.at(m).size() << "\n";
// Apply obstacle avoidance with 2.0 meter inflation distance
auto avoided_swaths = avoider.avoid(res.swaths_per_machine.at(m), 2.0f);
std::cout << "After obstacle avoidance: " << avoided_swaths.size() << " segments\n";
// Create and optimize path (filters to only work swaths)
farmtrax::Nety nety(avoided_swaths);
nety.field_traversal(); // Reorders swaths for optimal path
// Calculate performance metrics
const auto& optimized_swaths = nety.get_swaths();
double total_work = 0.0, total_move = 0.0;
for (const auto& swath : optimized_swaths) {
double length = swath->line.length();
if (swath->type == farmtrax::SwathType::Swath) {
total_work += length;
} else {
total_move += length;
}
}
std::cout << "Machine " << m << " final path: " << optimized_swaths.size()
<< " segments (" << total_work << "m work, " << total_move << "m movement)\n";
// Visualize results
farmtrax::visualize::show_avoided_swaths(avoided_swaths, rec, m, "avoided");
farmtrax::visualize::show_swath_tour(nety, rec, m);
}
return 0;
}// Define custom obstacles
std::vector<concord::Polygon> obstacles;
// Add a circular obstacle (tree)
concord::Polygon tree;
int num_points = 16;
double radius = 5.0; // 5 meter radius
double center_x = 100.0, center_y = 200.0;
for (int i = 0; i < num_points; ++i) {
double angle = 2.0 * M_PI * i / num_points;
concord::Point pt;
pt.enu.x = center_x + radius * cos(angle);
pt.enu.y = center_y + radius * sin(angle);
tree.addPoint(pt);
}
tree.addPoint(tree.getPoints().front()); // Close polygon
obstacles.push_back(tree);
// Add a rectangular obstacle (building)
concord::Polygon building;
building.addPoint(concord::Point{concord::ENU{50, 150, 0}});
building.addPoint(concord::Point{concord::ENU{70, 150, 0}});
building.addPoint(concord::Point{concord::ENU{70, 180, 0}});
building.addPoint(concord::Point{concord::ENU{50, 180, 0}});
building.addPoint(building.getPoints().front());
obstacles.push_back(building);
// Create avoider with 3.0m safety margin
farmtrax::ObstacleAvoider avoider(obstacles, datum);
auto safe_swaths = avoider.avoid(original_swaths, 3.0f);
// Inspect results
for (const auto& swath : safe_swaths) {
if (swath->type == farmtrax::SwathType::Swath) {
std::cout << "Work segment: " << swath->line.length() << "m\n";
} else if (swath->type == farmtrax::SwathType::Around) {
std::cout << "Avoidance connection: " << swath->line.length() << "m\n";
}
}// Create optimizer with multiple start point strategies
farmtrax::Nety nety(swaths);
// Strategy 1: Field traversal from automatic start point
nety.field_traversal();
auto path1 = nety.get_swaths();
// Strategy 2: Shortest path between specific points
farmtrax::BPoint custom_start(field_boundary.min_x, field_boundary.min_y);
farmtrax::BPoint custom_end(field_boundary.max_x, field_boundary.max_y);
nety.shortest_path(custom_start, custom_end);
auto path2 = nety.get_swaths();
// Compare strategies
double distance1 = 0.0, distance2 = 0.0;
for (const auto& swath : path1) {
distance1 += swath->line.length();
}
for (const auto& swath : path2) {
distance2 += swath->line.length();
}
std::cout << "Field traversal: " << distance1 << "m total\n";
std::cout << "Shortest path: " << distance2 << "m total\n";
std::cout << "Best strategy: " << (distance1 < distance2 ? "Field traversal" : "Shortest path") << "\n";# Ubuntu/Debian
sudo apt install cmake build-essential libboost-all-dev
# macOS
brew install cmake boost
# Or use devbox (recommended)
devbox shell# Using provided build script
./run.sh b # Build
./run.sh r # Run example
# Manual CMake
mkdir build && cd build
cmake ..
make -j$(nproc)
./mainFarmtrax integrates with Rerun for real-time 3D visualization:
auto rec = std::make_shared<rerun::RecordingStream>("farmtrax", "space");
rec->connect_grpc("rerun+http://0.0.0.0:9876/proxy");
farmtrax::visualize::show_field(field, rec);
farmtrax::visualize::show_swath_tour(nety, path, rec, machine_id);Visualization Features:
- 3D field boundaries and topography
- AB line visualization with directional arrows
- Machine path visualization with color coding
- Real-time path optimization updates
- Connection quality indicators
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
