diff --git a/frontend/src/components/Applications/ApplicationViewModal.js b/frontend/src/components/Applications/ApplicationViewModal.js index 2aa849b..9c3c997 100644 --- a/frontend/src/components/Applications/ApplicationViewModal.js +++ b/frontend/src/components/Applications/ApplicationViewModal.js @@ -9,38 +9,55 @@ const ApplicationViewModal = ({ application, propertyDetails, onClose }) => { const [planDetails, setPlanDetails] = useState(null); const [loading, setLoading] = useState(true); + // Haversine distance between two lat/lng in meters + const haversineMeters = (lat1, lng1, lat2, lng2) => { + const R = 6371e3; + const toRad = (d) => (d * Math.PI) / 180; + const dLat = toRad(lat2 - lat1); + const dLng = toRad(lng2 - lng1); + const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2; + return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + }; + + const computeDistanceFeetFromPoints = (points = []) => { + if (!Array.isArray(points) || points.length < 2) return 0; + let meters = 0; + for (let i = 1; i < points.length; i++) { + const p1 = points[i - 1]; + const p2 = points[i]; + meters += haversineMeters(p1.lat, p1.lng, p2.lat, p2.lng); + } + return meters * 3.28084; // feet + }; + + const distanceFeet = (log) => { + if (!log?.gpsTrack) return 0; + const stored = log.gpsTrack.totalDistance; + if (typeof stored === 'number' && stored > 0) { + // stored value is meters (from execution modal). Convert to feet. + return stored * 3.28084; + } + return computeDistanceFeetFromPoints(log.gpsTrack.points); + }; + // Calculate coverage percentage based on GPS tracking and equipment specifications const calculateCoverage = (application, log) => { - if (!log?.gpsTrack?.points || log.gpsTrack.points.length < 2) { - return 0; // No movement = no coverage - } + if (!log?.gpsTrack?.points || log.gpsTrack.points.length < 2) return 0; - const totalDistance = log.gpsTrack.totalDistance || 0; + const totalDistanceFeet = distanceFeet(log); const plannedArea = application.totalSectionArea || 0; - - if (totalDistance === 0 || plannedArea === 0) { - return 0; - } + if (totalDistanceFeet === 0 || plannedArea === 0) return 0; - // Estimate equipment width based on type - let equipmentWidth = 4; // Default 4 feet for unknown equipment - + // Estimate equipment width based on type (feet) + let equipmentWidth = 4; const equipmentName = application.equipmentName?.toLowerCase() || ''; - if (equipmentName.includes('spreader')) { - equipmentWidth = 12; // Spreaders typically 8-16 feet wide - } else if (equipmentName.includes('sprayer')) { - equipmentWidth = 20; // Sprayers typically 15-30 feet wide - } else if (equipmentName.includes('mower')) { - equipmentWidth = 6; // Mowers typically 4-8 feet wide - } + if (equipmentName.includes('spreader')) equipmentWidth = 12; + else if (equipmentName.includes('sprayer')) equipmentWidth = 20; + else if (equipmentName.includes('mower')) equipmentWidth = 6; - // Calculate theoretical coverage area - // Distance (feet) * Width (feet) = Coverage area (sq ft) - const theoreticalCoverageArea = totalDistance * equipmentWidth; - - // Calculate coverage percentage (capped at 100%) + // Distance (ft) * Width (ft) = Area (sq ft) + const theoreticalCoverageArea = totalDistanceFeet * equipmentWidth; const coveragePercentage = Math.min((theoreticalCoverageArea / plannedArea) * 100, 100); - return Math.round(coveragePercentage); }; @@ -231,7 +248,7 @@ const ApplicationViewModal = ({ application, propertyDetails, onClose }) => {
Distance
- {applicationLog.gpsTrack?.totalDistance?.toFixed(0) || 0} ft + {Math.round(distanceFeet(applicationLog))} ft
@@ -286,4 +303,4 @@ const ApplicationViewModal = ({ application, propertyDetails, onClose }) => { ); }; -export default ApplicationViewModal; \ No newline at end of file +export default ApplicationViewModal;