This commit is contained in:
Jake Kasper
2025-09-02 12:27:39 -05:00
parent cdadedec3f
commit 4d52aae85b
2 changed files with 106 additions and 14 deletions

View File

@@ -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 && (

View File

@@ -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>