asdfasdf
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { MapContainer, TileLayer, Polygon, Marker, Circle, Rectangle, Popup, useMapEvents, useMap } from 'react-leaflet';
|
||||
import { MapContainer, TileLayer, Polygon, Marker, Circle, Rectangle, Popup, useMapEvents, useMap, Polyline } from 'react-leaflet';
|
||||
import { Icon } from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { propertiesAPI, wateringAPI, equipmentAPI } from '../../services/api';
|
||||
@@ -271,6 +271,15 @@ const Watering = () => {
|
||||
return (2*R*Math.atan2(Math.sqrt(A),Math.sqrt(1-A)))*3.28084;
|
||||
};
|
||||
|
||||
const bearingDegrees = (a,b) => {
|
||||
const toRad = d=> d*Math.PI/180, toDeg = r=> (r*180/Math.PI+360)%360;
|
||||
const lat1 = toRad(a.lat), lat2 = toRad(b.lat);
|
||||
const dLng = toRad(b.lng - a.lng);
|
||||
const y = Math.sin(dLng) * Math.cos(lat2);
|
||||
const x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLng);
|
||||
return Math.round(toDeg(Math.atan2(y,x)));
|
||||
};
|
||||
|
||||
// Build sector polygon (approx) for Leaflet
|
||||
const sectorPolygon = (center, radiusFeet, startDeg, endDeg, steps=60) => {
|
||||
const [clat, clng] = [Number(center.lat), Number(center.lng)];
|
||||
@@ -560,7 +569,12 @@ const Watering = () => {
|
||||
}}>Save</button>
|
||||
<button className="btn-secondary" onClick={async ()=>{
|
||||
await wateringAPI.deletePoint(selectedPointId);
|
||||
setPoints(prev=> prev.filter(p=> p.id!==selectedPointId));
|
||||
if (plan) {
|
||||
const rs = await wateringAPI.getPlanPoints(plan.id);
|
||||
setPoints(rs.data?.data?.points || []);
|
||||
} else {
|
||||
setPoints(prev=> prev.filter(p=> p.id!==selectedPointId));
|
||||
}
|
||||
setSelectedPointId(null); setEditForm(null);
|
||||
}}>Delete</button>
|
||||
</div>
|
||||
@@ -572,9 +586,15 @@ const Watering = () => {
|
||||
<div className="lg:col-span-3">
|
||||
<div className="card p-0" style={{height:'70vh', position:'relative'}}>
|
||||
<div className="flex items-center justify-between p-3 border-b">
|
||||
<div className="text-sm text-gray-700">{guiding && currentPos && points[guideIndex] ? (
|
||||
<>Go to point #{points[guideIndex].sequence}: {distanceFeet(currentPos, {lat: Number(points[guideIndex].lat), lng: Number(points[guideIndex].lng)}).toFixed(0)} ft away</>
|
||||
) : 'Map'}</div>
|
||||
<div className="text-sm text-gray-700">{guiding && currentPos && points[guideIndex] ? (()=>{
|
||||
const tgt = {lat:Number(points[guideIndex].lat), lng:Number(points[guideIndex].lng)};
|
||||
const dist = distanceFeet(currentPos, tgt);
|
||||
const brg = bearingDegrees(currentPos, tgt);
|
||||
if (dist <= 15) {
|
||||
return <span className="text-green-700 font-semibold">Arrived at #{points[guideIndex].sequence} • {dist.toFixed(0)} ft</span>;
|
||||
}
|
||||
return <>→ {brg}° • {dist.toFixed(0)} ft • Point #{points[guideIndex].sequence}</>;
|
||||
})() : 'Map'}</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<label className="text-xs flex items-center gap-1">
|
||||
<input type="checkbox" checked={satellite} onChange={(e)=> setSatellite(e.target.checked)} /> Satellite
|
||||
@@ -601,6 +621,16 @@ const Watering = () => {
|
||||
<Polygon key={s.id} positions={s.polygonData?.coordinates?.[0]||[]} pathOptions={{ color:'#16a34a', weight:2, fillOpacity:0.1 }} />
|
||||
))}
|
||||
{placing && <SprinklerPlacement onPlace={onPlace} />}
|
||||
{guiding && currentPos && points[guideIndex] && (
|
||||
<>
|
||||
{/* Current location marker */}
|
||||
<Marker position={[currentPos.lat, currentPos.lng]} icon={markerIcon('#0ea5e9')} />
|
||||
{/* Line to target */}
|
||||
<Polyline positions={[[currentPos.lat, currentPos.lng], [Number(points[guideIndex].lat), Number(points[guideIndex].lng)]]} pathOptions={{ color:'#0ea5e9', dashArray:'6 6' }} />
|
||||
{/* Arrival ring */}
|
||||
<Circle center={[Number(points[guideIndex].lat), Number(points[guideIndex].lng)]} radius={15*0.3048} pathOptions={{ color:'#22c55e', fillOpacity:0.15, weight:2 }} />
|
||||
</>
|
||||
)}
|
||||
{/* Legend overlay */}
|
||||
{points.length > 0 && (
|
||||
<div style={{ position:'absolute', right:10, bottom:10, zIndex:1000 }}>
|
||||
@@ -703,7 +733,15 @@ const Watering = () => {
|
||||
<div></div>
|
||||
</div>
|
||||
<div className="flex gap-2 pt-1">
|
||||
<button className="btn-secondary" onClick={async ()=>{ await wateringAPI.deletePoint(pt.id); setPoints(prev=> prev.filter(p=> p.id!==pt.id)); }}>Delete</button>
|
||||
<button className="btn-secondary" onClick={async ()=>{
|
||||
await wateringAPI.deletePoint(pt.id);
|
||||
if (plan) {
|
||||
const rs = await wateringAPI.getPlanPoints(plan.id);
|
||||
setPoints(rs.data?.data?.points || []);
|
||||
} else {
|
||||
setPoints(prev=> prev.filter(p=> p.id!==pt.id));
|
||||
}
|
||||
}}>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
|
||||
Reference in New Issue
Block a user