show cable lenght info
This commit is contained in:
@@ -1,12 +1,39 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Polyline, Marker, Popup, Circle, useMapEvents } from 'react-leaflet';
|
||||
import { Polyline, Marker, Popup, Circle, Tooltip, useMapEvents } from 'react-leaflet';
|
||||
import L from 'leaflet';
|
||||
import { mapItemService } from '../../services/mapItemService';
|
||||
import { CABLE_COLORS, type MapItem, type CableType } from '../../types/mapItem';
|
||||
import { ItemContextMenu } from './ItemContextMenu';
|
||||
import { useDrawingStore } from '../../stores/drawingStore';
|
||||
|
||||
// Calculate distance between two lat/lng points in meters using Haversine formula
|
||||
function calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
|
||||
const R = 6371e3; // Earth's radius in meters
|
||||
const φ1 = (lat1 * Math.PI) / 180;
|
||||
const φ2 = (lat2 * Math.PI) / 180;
|
||||
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
|
||||
const Δλ = ((lng2 - lng1) * Math.PI) / 180;
|
||||
|
||||
const a =
|
||||
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
|
||||
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
return R * c; // Distance in meters
|
||||
}
|
||||
|
||||
// Format distance for display
|
||||
function formatDistance(meters: number): string {
|
||||
if (meters < 1) {
|
||||
return `${Math.round(meters * 100)} cm`;
|
||||
} else if (meters < 1000) {
|
||||
return `${Math.round(meters * 10) / 10} m`;
|
||||
} else {
|
||||
return `${Math.round(meters / 100) / 10} km`;
|
||||
}
|
||||
}
|
||||
|
||||
interface MapItemsLayerProps {
|
||||
mapId: string;
|
||||
refreshTrigger: number;
|
||||
@@ -128,6 +155,20 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt
|
||||
const cableType = item.properties.cable_type as CableType;
|
||||
const color = CABLE_COLORS[cableType] || '#6B7280';
|
||||
|
||||
// Calculate distances
|
||||
const segmentDistances: number[] = [];
|
||||
let totalDistance = 0;
|
||||
for (let i = 0; i < positions.length - 1; i++) {
|
||||
const dist = calculateDistance(
|
||||
positions[i][0],
|
||||
positions[i][1],
|
||||
positions[i + 1][0],
|
||||
positions[i + 1][1]
|
||||
);
|
||||
segmentDistances.push(dist);
|
||||
totalDistance += dist;
|
||||
}
|
||||
|
||||
// Find connected devices
|
||||
const startDevice = item.properties.start_device_id
|
||||
? items.find(i => i.id === item.properties.start_device_id)
|
||||
@@ -160,6 +201,19 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt
|
||||
<div className="text-sm dark:bg-gray-800 dark:text-white" style={{ minWidth: '200px' }}>
|
||||
<div className="font-semibold text-gray-900 dark:text-white">{item.properties.name || 'Cable'}</div>
|
||||
<div className="text-gray-600 dark:text-gray-400">Type: {cableType}</div>
|
||||
<div className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
<span className="font-semibold">Total Length:</span> {formatDistance(totalDistance)}
|
||||
</div>
|
||||
{segmentDistances.length > 1 && (
|
||||
<div className="mt-2 pt-2 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="font-semibold text-gray-700 dark:text-gray-300 mb-1">Segment Distances:</div>
|
||||
{segmentDistances.map((dist, idx) => (
|
||||
<div key={idx} className="text-xs text-gray-600 dark:text-gray-400 ml-2">
|
||||
Segment {idx + 1}: {formatDistance(dist)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{startDevice && (
|
||||
<div className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
From: {startDevice.properties.name || startDevice.type}
|
||||
@@ -214,6 +268,14 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt
|
||||
([lng, lat]) => [lat, lng] as [number, number]
|
||||
);
|
||||
|
||||
// Calculate distance
|
||||
const distance = calculateDistance(
|
||||
positions[0][0],
|
||||
positions[0][1],
|
||||
positions[1][0],
|
||||
positions[1][1]
|
||||
);
|
||||
|
||||
// Find connected APs
|
||||
const startAp = item.properties.start_ap_id
|
||||
? items.find(i => i.id === item.properties.start_ap_id)
|
||||
@@ -246,6 +308,9 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt
|
||||
<Popup>
|
||||
<div className="text-sm dark:bg-gray-800 dark:text-white" style={{ minWidth: '200px' }}>
|
||||
<div className="font-semibold text-gray-900 dark:text-white">{item.properties.name || 'Wireless Mesh'}</div>
|
||||
<div className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
<span className="font-semibold">Distance:</span> {formatDistance(distance)}
|
||||
</div>
|
||||
{startAp && (
|
||||
<div className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
From: {startAp.properties.name || startAp.type}
|
||||
|
||||
@@ -124,3 +124,23 @@
|
||||
border-color: #F59E0B;
|
||||
color: #F59E0B;
|
||||
}
|
||||
|
||||
/* Distance labels on map */
|
||||
.distance-label {
|
||||
background-color: rgba(255, 255, 255, 0.95) !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2) !important;
|
||||
border-radius: 4px !important;
|
||||
padding: 2px 6px !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
||||
}
|
||||
|
||||
.dark .distance-label {
|
||||
background-color: rgba(31, 41, 55, 0.95) !important;
|
||||
border-color: rgba(255, 255, 255, 0.2) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.distance-label::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user