mowing
This commit is contained in:
@@ -112,7 +112,8 @@ const PropertyMap = ({
|
|||||||
mode = "view", // "view", "edit", "execution"
|
mode = "view", // "view", "edit", "execution"
|
||||||
gpsTrack = [],
|
gpsTrack = [],
|
||||||
currentLocation = null,
|
currentLocation = null,
|
||||||
showTrackPoints = true
|
showTrackPoints = true,
|
||||||
|
direction = null // optional mowing direction: N_S, E_W, NE_SW, NW_SE, CIRCULAR
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
@@ -413,7 +414,7 @@ const PropertyMap = ({
|
|||||||
{/* Existing sections */}
|
{/* Existing sections */}
|
||||||
{sections.map((section) => {
|
{sections.map((section) => {
|
||||||
// Handle both string and object polygon data
|
// Handle both string and object polygon data
|
||||||
let polygonData = section.polygonData;
|
let polygonData = section.polygonData || section.polygon_data;
|
||||||
if (typeof polygonData === 'string') {
|
if (typeof polygonData === 'string') {
|
||||||
try {
|
try {
|
||||||
polygonData = JSON.parse(polygonData);
|
polygonData = JSON.parse(polygonData);
|
||||||
@@ -452,9 +453,9 @@ const PropertyMap = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* GPS Tracking Elements */}
|
{/* GPS Tracking Elements */}
|
||||||
{(mode === "execution" || mode === "view") && (
|
{(mode === "execution" || mode === "view") && (
|
||||||
<>
|
<>
|
||||||
{/* GPS Track Polyline */}
|
{/* GPS Track Polyline */}
|
||||||
{gpsTrack.length > 1 && (
|
{gpsTrack.length > 1 && (
|
||||||
<Polyline
|
<Polyline
|
||||||
@@ -479,14 +480,104 @@ const PropertyMap = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Current Location - only for execution mode */}
|
{/* Current Location - only for execution mode */}
|
||||||
{currentLocation && mode === "execution" && (
|
{currentLocation && mode === "execution" && (
|
||||||
<Marker
|
<Marker
|
||||||
position={[currentLocation.lat, currentLocation.lng]}
|
position={[currentLocation.lat, currentLocation.lng]}
|
||||||
icon={currentLocationIcon}
|
icon={currentLocationIcon}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
{/* Mowing direction guide */}
|
||||||
|
{direction && sections && sections.length > 0 && (() => {
|
||||||
|
// Compute bounding box from all section polygons
|
||||||
|
let minLat = 90, maxLat = -90, minLng = 180, maxLng = -180;
|
||||||
|
sections.forEach(section => {
|
||||||
|
let polygonData = section.polygonData || section.polygon_data;
|
||||||
|
if (typeof polygonData === 'string') {
|
||||||
|
try { polygonData = JSON.parse(polygonData); } catch { return; }
|
||||||
|
}
|
||||||
|
if (!polygonData?.coordinates?.[0]) return;
|
||||||
|
polygonData.coordinates[0].forEach(([lat, lng]) => {
|
||||||
|
if (lat < minLat) minLat = lat;
|
||||||
|
if (lat > maxLat) maxLat = lat;
|
||||||
|
if (lng < minLng) minLng = lng;
|
||||||
|
if (lng > maxLng) maxLng = lng;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!(minLat < maxLat && minLng < maxLng)) return null;
|
||||||
|
const midLat = (minLat + maxLat) / 2;
|
||||||
|
const midLng = (minLng + maxLng) / 2;
|
||||||
|
|
||||||
|
// Build a main guideline polyline based on direction
|
||||||
|
let mainLine = [];
|
||||||
|
switch ((direction || '').toUpperCase()) {
|
||||||
|
case 'N_S':
|
||||||
|
mainLine = [[maxLat, midLng], [minLat, midLng]]; // North to South
|
||||||
|
break;
|
||||||
|
case 'E_W':
|
||||||
|
mainLine = [[midLat, minLng], [midLat, maxLng]]; // East to West
|
||||||
|
break;
|
||||||
|
case 'NE_SW':
|
||||||
|
mainLine = [[maxLat, maxLng], [minLat, minLng]];
|
||||||
|
break;
|
||||||
|
case 'NW_SE':
|
||||||
|
mainLine = [[maxLat, minLng], [minLat, maxLng]];
|
||||||
|
break;
|
||||||
|
case 'CIRCULAR': {
|
||||||
|
// Approximate circle around center with radius based on bbox
|
||||||
|
const latRadius = (maxLat - minLat) / 3;
|
||||||
|
const lngRadius = (maxLng - minLng) / 3;
|
||||||
|
const points = Array.from({ length: 72 }).map((_, i) => {
|
||||||
|
const t = (i / 72) * 2 * Math.PI;
|
||||||
|
return [midLat + latRadius * Math.sin(t), midLng + lngRadius * Math.cos(t)];
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Polyline positions={points} pathOptions={{ color: '#10B981', weight: 3, dashArray: '8,6', opacity: 0.8 }} />
|
||||||
|
<div className="absolute top-4 left-4 bg-white rounded shadow px-2 py-1 text-xs text-gray-700">Direction: Circular</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mainLine.length === 0) return null;
|
||||||
|
|
||||||
|
// Add a couple of parallel guide lines to improve visibility
|
||||||
|
const offsetLat = (maxLat - minLat) * 0.02; // ~2% of bbox
|
||||||
|
const offsetLng = (maxLng - minLng) * 0.02;
|
||||||
|
const parallelLines = [];
|
||||||
|
if (direction === 'N_S') {
|
||||||
|
parallelLines.push(mainLine);
|
||||||
|
parallelLines.push([[maxLat, midLng - offsetLng], [minLat, midLng - offsetLng]]);
|
||||||
|
parallelLines.push([[maxLat, midLng + offsetLng], [minLat, midLng + offsetLng]]);
|
||||||
|
} else if (direction === 'E_W') {
|
||||||
|
parallelLines.push(mainLine);
|
||||||
|
parallelLines.push([[midLat - offsetLat, minLng], [midLat - offsetLat, maxLng]]);
|
||||||
|
parallelLines.push([[midLat + offsetLat, minLng], [midLat + offsetLat, maxLng]]);
|
||||||
|
} else if (direction === 'NE_SW' || direction === 'NW_SE') {
|
||||||
|
parallelLines.push(mainLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
const directionLabelMap = {
|
||||||
|
'N_S': 'North to South',
|
||||||
|
'E_W': 'East to West',
|
||||||
|
'NE_SW': 'NE to SW',
|
||||||
|
'NW_SE': 'NW to SE',
|
||||||
|
'CIRCULAR': 'Circular'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{parallelLines.map((line, idx) => (
|
||||||
|
<Polyline key={`dir-${idx}`} positions={line} pathOptions={{ color: '#10B981', weight: idx === 0 ? 4 : 2, dashArray: '8,6', opacity: 0.9 }} />
|
||||||
|
))}
|
||||||
|
<div className="absolute top-4 left-4 bg-white rounded shadow px-2 py-1 text-xs text-gray-700">Direction: {directionLabelMap[(direction || '').toUpperCase()] || direction}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Current polygon being drawn */}
|
{/* Current polygon being drawn */}
|
||||||
{currentPolygon.length > 0 && (
|
{currentPolygon.length > 0 && (
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ const MowingExecutionModal = ({ plan, onClose, onComplete }) => {
|
|||||||
<div><span className="font-medium">Mower:</span> {plan.equipment_name}</div>
|
<div><span className="font-medium">Mower:</span> {plan.equipment_name}</div>
|
||||||
<div><span className="font-medium">Cut Height:</span> {plan.cut_height_inches}"</div>
|
<div><span className="font-medium">Cut Height:</span> {plan.cut_height_inches}"</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-80 border rounded mb-4">
|
<div className="h-80 border rounded mb-4 overflow-hidden">
|
||||||
<PropertyMap
|
<PropertyMap
|
||||||
property={null}
|
property={null}
|
||||||
sections={sections}
|
sections={sections}
|
||||||
@@ -136,6 +136,7 @@ const MowingExecutionModal = ({ plan, onClose, onComplete }) => {
|
|||||||
currentLocation={currentLocation}
|
currentLocation={currentLocation}
|
||||||
center={center || [39.8283, -98.5795]}
|
center={center || [39.8283, -98.5795]}
|
||||||
zoom={center ? 16 : 15}
|
zoom={center ? 16 : 15}
|
||||||
|
direction={plan.direction}
|
||||||
className="h-80 w-full"
|
className="h-80 w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user