sadfasdf
This commit is contained in:
@@ -107,7 +107,13 @@ const sanitizePolygon = (coords) => {
|
|||||||
} else { return [pts[0], pts[pts.length-1]]; }
|
} else { return [pts[0], pts[pts.length-1]]; }
|
||||||
};
|
};
|
||||||
try { filtered = rdp(filtered, EPS_DEG); } catch {}
|
try { filtered = rdp(filtered, EPS_DEG); } catch {}
|
||||||
return filtered;
|
// Ensure at least 3 unique points remain; if not, fall back to original coords
|
||||||
|
const unique = [];
|
||||||
|
for (const p of filtered) {
|
||||||
|
if (!unique.some(q => Math.abs(q[0]-p[0])<1e-9 && Math.abs(q[1]-p[1])<1e-9)) unique.push(p);
|
||||||
|
}
|
||||||
|
if (unique.length < 3) return coords;
|
||||||
|
return unique;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @route GET /api/properties
|
// @route GET /api/properties
|
||||||
|
|||||||
@@ -432,15 +432,18 @@ const PropertyDetail = () => {
|
|||||||
|
|
||||||
// Convert backend sections to frontend format
|
// Convert backend sections to frontend format
|
||||||
if (propertyData.sections && propertyData.sections.length > 0) {
|
if (propertyData.sections && propertyData.sections.length > 0) {
|
||||||
const convertedSections = propertyData.sections.map(section => ({
|
const convertedSections = propertyData.sections.map(section => {
|
||||||
|
const poly = typeof section.polygonData === 'string' ? JSON.parse(section.polygonData) : section.polygonData;
|
||||||
|
return {
|
||||||
id: section.id,
|
id: section.id,
|
||||||
name: section.name,
|
name: section.name,
|
||||||
coordinates: section.polygonData?.coordinates?.[0] || [],
|
coordinates: poly?.coordinates?.[0] || [],
|
||||||
color: section.polygonData?.color || SECTION_COLORS[0],
|
color: poly?.color || SECTION_COLORS[0],
|
||||||
area: section.area,
|
area: section.area,
|
||||||
grassType: section.grassType || '',
|
grassType: section.grassType || '',
|
||||||
grassTypes: section.grassTypes || null
|
grassTypes: section.grassTypes || null
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
setLawnSections(convertedSections);
|
setLawnSections(convertedSections);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -510,6 +513,16 @@ const PropertyDetail = () => {
|
|||||||
return [avgLat, avgLng];
|
return [avgLat, avgLng];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get the most accurate recent sample (prefer samples within 3s)
|
||||||
|
const bestRecentSample = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const recent = gpsSamples.filter(s => (now - (s.t || 0)) <= 3000);
|
||||||
|
const pool = (recent.length ? recent : gpsSamples).slice(-12);
|
||||||
|
if (pool.length === 0) return null;
|
||||||
|
const best = pool.reduce((b, s) => (s.accuracy < (b?.accuracy ?? Infinity) ? s : b), null);
|
||||||
|
return best ? [best.lat, best.lng, best.accuracy] : null;
|
||||||
|
};
|
||||||
|
|
||||||
const acceptAndNormalizePoint = (lat, lng, accuracy, currentPoints) => {
|
const acceptAndNormalizePoint = (lat, lng, accuracy, currentPoints) => {
|
||||||
if (accuracy != null && accuracy > ACCURACY_MAX_METERS) {
|
if (accuracy != null && accuracy > ACCURACY_MAX_METERS) {
|
||||||
toast("GPS accuracy too low (" + Math.round(accuracy) + "m). Waiting for better fix…");
|
toast("GPS accuracy too low (" + Math.round(accuracy) + "m). Waiting for better fix…");
|
||||||
@@ -546,16 +559,15 @@ const PropertyDetail = () => {
|
|||||||
// GPS point collection (mark-at-location)
|
// GPS point collection (mark-at-location)
|
||||||
const markCurrentPoint = () => {
|
const markCurrentPoint = () => {
|
||||||
if (!navigator.geolocation) { toast.error('GPS not available'); return; }
|
if (!navigator.geolocation) { toast.error('GPS not available'); return; }
|
||||||
navigator.geolocation.getCurrentPosition((pos) => {
|
// Prefer most accurate recent fix from warm watch; fall back to immediate read
|
||||||
const { latitude, longitude, accuracy } = pos.coords;
|
const candidate = bestRecentSample();
|
||||||
setGpsAccuracy(accuracy || null);
|
const useFix = (lat, lng, acc) => {
|
||||||
|
setGpsAccuracy(acc || null);
|
||||||
setGpsTracePoints(prev => {
|
setGpsTracePoints(prev => {
|
||||||
const [sLat, sLng] = addSampleAndSmooth(latitude, longitude, accuracy);
|
const normalized = acceptAndNormalizePoint(lat, lng, acc, prev);
|
||||||
const normalized = acceptAndNormalizePoint(sLat, sLng, accuracy, prev);
|
|
||||||
// For preview: check proximity to start even if skipping
|
|
||||||
if (prev.length >= 2) {
|
if (prev.length >= 2) {
|
||||||
const [slat, slng] = prev[0];
|
const [slat, slng] = prev[0];
|
||||||
const dStart = haversine(slat, slng, sLat, sLng);
|
const dStart = haversine(slat, slng, lat, lng);
|
||||||
setIsSnapPreview(dStart <= SNAP_METERS);
|
setIsSnapPreview(dStart <= SNAP_METERS);
|
||||||
} else { setIsSnapPreview(false); }
|
} else { setIsSnapPreview(false); }
|
||||||
if (!normalized) return prev; // skip
|
if (!normalized) return prev; // skip
|
||||||
@@ -566,10 +578,20 @@ const PropertyDetail = () => {
|
|||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
if (candidate) {
|
||||||
|
const [lat, lng, acc] = candidate;
|
||||||
|
useFix(lat, lng, acc);
|
||||||
|
} else {
|
||||||
|
navigator.geolocation.getCurrentPosition((pos) => {
|
||||||
|
const { latitude, longitude, accuracy } = pos.coords;
|
||||||
|
addSampleAndSmooth(latitude, longitude, accuracy);
|
||||||
|
useFix(latitude, longitude, accuracy);
|
||||||
}, (err)=>{
|
}, (err)=>{
|
||||||
console.warn('GPS error', err?.message);
|
console.warn('GPS error', err?.message);
|
||||||
toast.error('GPS error: ' + (err?.message || 'unknown'));
|
toast.error('GPS error: ' + (err?.message || 'unknown'));
|
||||||
}, { enableHighAccuracy: true, maximumAge: 1000, timeout: 10000 });
|
}, { enableHighAccuracy: true, maximumAge: 500, timeout: 8000 });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const undoLastPoint = () => {
|
const undoLastPoint = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user