Fix jwt-token
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { MapContainer, TileLayer, Marker, Popup, Circle, useMap } from 'react-leaflet';
|
||||
import { Icon } from 'leaflet';
|
||||
import L from 'leaflet'; // For divIcon and other Leaflet utilities
|
||||
import { useSocket } from '../contexts/SocketContext';
|
||||
import api from '../services/api';
|
||||
import { format } from 'date-fns';
|
||||
@@ -310,75 +311,157 @@ const MapView = () => {
|
||||
})}
|
||||
|
||||
{/* RSSI-based Detection Rings around Detectors */}
|
||||
{showDroneDetections && droneDetectionHistory
|
||||
.filter(detection => {
|
||||
const hasCoords = detection.geo_lat && detection.geo_lon;
|
||||
console.log('MapView: Filtering detection:', detection, 'hasCoords:', hasCoords);
|
||||
return hasCoords;
|
||||
})
|
||||
.map(detection => {
|
||||
console.log('MapView: Rendering ring for detection:', detection);
|
||||
const opacity = getDetectionOpacity(detection);
|
||||
const age = getDetectionAge(detection);
|
||||
console.log('MapView: Detection age:', age, 'opacity:', opacity);
|
||||
|
||||
// Calculate ring radius based on RSSI (rough distance estimation)
|
||||
const getRssiRadius = (rssi) => {
|
||||
if (rssi > -40) return 100; // <100m - very close
|
||||
if (rssi > -60) return 500; // ~500m - close
|
||||
if (rssi > -70) return 1500; // ~1.5km - medium
|
||||
if (rssi > -80) return 4000; // ~4km - far
|
||||
if (rssi > -90) return 8000; // ~8km - very far
|
||||
return 15000; // ~15km - maximum range
|
||||
};
|
||||
{showDroneDetections && (() => {
|
||||
const filteredDetections = droneDetectionHistory
|
||||
.filter(detection => {
|
||||
const hasCoords = detection.geo_lat && detection.geo_lon;
|
||||
console.log('MapView: Filtering detection:', detection, 'hasCoords:', hasCoords);
|
||||
return hasCoords;
|
||||
});
|
||||
|
||||
const radius = getRssiRadius(detection.rssi);
|
||||
console.log('MapView: Ring radius:', radius, 'for RSSI:', detection.rssi);
|
||||
|
||||
// Color based on threat level and RSSI strength
|
||||
const getRingColor = (rssi, droneType) => {
|
||||
// Orlan drones (type 1) always red
|
||||
if (droneType === 1) return '#dc2626'; // red-600
|
||||
// Group detections by detector position to handle multiple drones at same detector
|
||||
const detectionsByDetector = filteredDetections.reduce((acc, detection) => {
|
||||
const key = `${detection.geo_lat}_${detection.geo_lon}`;
|
||||
if (!acc[key]) acc[key] = [];
|
||||
acc[key].push(detection);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.entries(detectionsByDetector).flatMap(([detectorKey, detections]) => {
|
||||
return detections.map((detection, droneIndex) => {
|
||||
console.log('MapView: Rendering ring for detection:', detection, 'droneIndex:', droneIndex, 'totalDrones:', detections.length);
|
||||
const opacity = getDetectionOpacity(detection);
|
||||
const age = getDetectionAge(detection);
|
||||
console.log('MapView: Detection age:', age, 'opacity:', opacity);
|
||||
|
||||
// Other drones based on RSSI
|
||||
if (rssi > -60) return '#dc2626'; // red-600 - close
|
||||
if (rssi > -70) return '#ea580c'; // orange-600 - medium
|
||||
return '#16a34a'; // green-600 - far
|
||||
};
|
||||
// Calculate ring radius based on RSSI (rough distance estimation)
|
||||
const getRssiRadius = (rssi) => {
|
||||
if (rssi > -40) return 100; // <100m - very close
|
||||
if (rssi > -60) return 500; // ~500m - close
|
||||
if (rssi > -70) return 1500; // ~1.5km - medium
|
||||
if (rssi > -80) return 4000; // ~4km - far
|
||||
if (rssi > -90) return 8000; // ~8km - very far
|
||||
return 15000; // ~15km - maximum range
|
||||
};
|
||||
|
||||
const ringColor = getRingColor(detection.rssi, detection.drone_type);
|
||||
|
||||
return (
|
||||
<React.Fragment key={detection.id}>
|
||||
{/* Detection Ring around Detector (NOT drone position) */}
|
||||
<Circle
|
||||
center={[detection.geo_lat, detection.geo_lon]} // Detector position
|
||||
radius={radius}
|
||||
pathOptions={{
|
||||
color: ringColor,
|
||||
fillColor: ringColor,
|
||||
fillOpacity: 0.05 * opacity,
|
||||
weight: detection.drone_type === 1 ? 3 : 2, // Thicker for Orlan
|
||||
opacity: opacity * 0.8,
|
||||
dashArray: detection.drone_type === 1 ? null : '5, 5' // Solid for Orlan, dashed for others
|
||||
}}
|
||||
/>
|
||||
const radius = getRssiRadius(detection.rssi);
|
||||
console.log('MapView: Ring radius:', radius, 'for RSSI:', detection.rssi);
|
||||
|
||||
// Color based on threat level and RSSI strength
|
||||
const getRingColor = (rssi, droneType) => {
|
||||
// Orlan drones (type 1) always red
|
||||
if (droneType === 1) return '#dc2626'; // red-600
|
||||
|
||||
{/* Optional: Small info marker at detector showing detection details */}
|
||||
{age < 1 && ( // Only show for very recent detections (< 1 minute)
|
||||
<Marker
|
||||
position={[detection.geo_lat, detection.geo_lon]}
|
||||
icon={createDroneIcon(detection.rssi, detection.drone_type)}
|
||||
opacity={opacity * 0.7}
|
||||
>
|
||||
<Popup>
|
||||
<DroneDetectionPopup detection={detection} age={age} droneTypes={droneTypes} />
|
||||
</Popup>
|
||||
</Marker>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
// Other drones based on RSSI
|
||||
if (rssi > -60) return '#dc2626'; // red-600 - close
|
||||
if (rssi > -70) return '#ea580c'; // orange-600 - medium
|
||||
return '#16a34a'; // green-600 - far
|
||||
};
|
||||
|
||||
const ringColor = getRingColor(detection.rssi, detection.drone_type);
|
||||
|
||||
// Different visual styles for multiple drones at same detector
|
||||
const getDashPattern = (droneType, droneIndex, totalDrones) => {
|
||||
if (droneType === 1) return null; // Orlan always solid
|
||||
|
||||
if (totalDrones === 1) return '5, 5'; // Single drone - normal dashed
|
||||
|
||||
// Multiple drones - different dash patterns
|
||||
const patterns = [
|
||||
'5, 5', // Standard dashed
|
||||
'10, 3, 3, 3', // Long dash, dot, dot
|
||||
'15, 5', // Long dashes
|
||||
'3, 3', // Short dashes
|
||||
'8, 3, 3, 3, 3, 3' // Complex pattern
|
||||
];
|
||||
return patterns[droneIndex % patterns.length];
|
||||
};
|
||||
|
||||
// Slight offset for multiple rings to make them more visible
|
||||
const getPositionOffset = (droneIndex, totalDrones) => {
|
||||
if (totalDrones === 1) return [0, 0];
|
||||
|
||||
const offsetDistance = 0.0001; // Very small offset in degrees
|
||||
const angle = (droneIndex * 360 / totalDrones) * (Math.PI / 180);
|
||||
return [
|
||||
Math.cos(angle) * offsetDistance,
|
||||
Math.sin(angle) * offsetDistance
|
||||
];
|
||||
};
|
||||
|
||||
const totalDrones = detections.length;
|
||||
const dashPattern = getDashPattern(detection.drone_type, droneIndex, totalDrones);
|
||||
const [latOffset, lonOffset] = getPositionOffset(droneIndex, totalDrones);
|
||||
|
||||
return (
|
||||
<React.Fragment key={`${detection.id}_${droneIndex}`}>
|
||||
{/* Detection Ring around Detector (NOT drone position) */}
|
||||
<Circle
|
||||
center={[
|
||||
detection.geo_lat + latOffset,
|
||||
detection.geo_lon + lonOffset
|
||||
]} // Detector position with slight offset
|
||||
radius={radius}
|
||||
pathOptions={{
|
||||
color: ringColor,
|
||||
fillColor: ringColor,
|
||||
fillOpacity: totalDrones > 1 ? 0.02 * opacity : 0.05 * opacity, // Less fill for multiple
|
||||
weight: detection.drone_type === 1 ? 3 : (totalDrones > 1 ? 3 : 2), // Thicker for multiple or Orlan
|
||||
opacity: opacity * 0.8,
|
||||
dashArray: dashPattern
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Drone ID label for multiple drones */}
|
||||
{totalDrones > 1 && age < 5 && ( // Show labels for recent detections with multiple drones
|
||||
<Marker
|
||||
position={[
|
||||
detection.geo_lat + (latOffset * 3),
|
||||
detection.geo_lon + (lonOffset * 3)
|
||||
]}
|
||||
icon={L.divIcon({
|
||||
html: `<div style="
|
||||
background: ${ringColor};
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 10px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
white-space: nowrap;
|
||||
">
|
||||
${detection.drone_id || `D${droneIndex + 1}`}
|
||||
</div>`,
|
||||
className: 'drone-id-label',
|
||||
iconSize: [30, 16],
|
||||
iconAnchor: [15, 8]
|
||||
})}
|
||||
opacity={opacity * 0.9}
|
||||
>
|
||||
<Popup>
|
||||
<DroneDetectionPopup detection={detection} age={age} droneTypes={droneTypes} />
|
||||
</Popup>
|
||||
</Marker>
|
||||
)}
|
||||
|
||||
{/* Optional: Small info marker at detector showing detection details (for single drone) */}
|
||||
{totalDrones === 1 && age < 1 && ( // Only show for very recent detections (< 1 minute) and single drone
|
||||
<Marker
|
||||
position={[detection.geo_lat, detection.geo_lon]}
|
||||
icon={createDroneIcon(detection.rssi, detection.drone_type)}
|
||||
opacity={opacity * 0.7}
|
||||
>
|
||||
<Popup>
|
||||
<DroneDetectionPopup detection={detection} age={age} droneTypes={droneTypes} />
|
||||
</Popup>
|
||||
</Marker>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
});
|
||||
})()}
|
||||
</MapContainer>
|
||||
|
||||
{/* Map Legend - Fixed positioning and visibility */}
|
||||
@@ -419,6 +502,12 @@ const MapView = () => {
|
||||
<div className="w-3 h-3 border-2 border-green-500 rounded-full bg-green-500 bg-opacity-10"></div>
|
||||
<span className="text-gray-700">Far Range (<-70dBm)</span>
|
||||
</div>
|
||||
<div className="border-t border-gray-200 mt-2 pt-2">
|
||||
<div className="text-xs text-gray-600 mb-1">Multiple Drones:</div>
|
||||
<div className="text-xs text-gray-500 mb-1">• Different dash patterns</div>
|
||||
<div className="text-xs text-gray-500 mb-1">• Drone ID labels shown</div>
|
||||
<div className="text-xs text-gray-500 mb-1">• Slight position offsets</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
Ring size = estimated distance from detector
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user