mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-28 09:40:16 +01:00
Restructuration of the project folders
This commit is contained in:
181
server/traque-front/components/layer.jsx
Normal file
181
server/traque-front/components/layer.jsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Marker, Tooltip, CircleMarker, Circle, Polygon, useMap } from "react-leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import 'leaflet-polylinedecorator';
|
||||
|
||||
export function Node({position, color = 'black', display = true}) {
|
||||
const nodeSize = 5;
|
||||
const fillOpacity = 1;
|
||||
|
||||
return (
|
||||
display && position && <CircleMarker center={position} radius={nodeSize} pathOptions={{ color: color, fillColor: color, fillOpacity: fillOpacity }} />
|
||||
);
|
||||
}
|
||||
|
||||
export function Label({position, label = "", color = "black", display = true}) {
|
||||
const size = 24;
|
||||
|
||||
const labelIcon = L.divIcon({
|
||||
html: `<div style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${color};
|
||||
font-weight: bold;
|
||||
font-size: ${size}px;
|
||||
">${label}</div>`,
|
||||
className: 'custom-label-icon',
|
||||
iconSize: [size, size],
|
||||
iconAnchor: [size / 2, size / 2]
|
||||
});
|
||||
|
||||
return (
|
||||
display && position && <Marker position={position} icon={labelIcon} />
|
||||
);
|
||||
}
|
||||
|
||||
export function Tag({text = "", display = true}) {
|
||||
const offset = [0.5, -15];
|
||||
const opacity = 1;
|
||||
|
||||
return (
|
||||
display && <Tooltip permanent direction="top" offset={offset} opacity={opacity} className="custom-tooltip">{text}</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function CircleZone({circle, color = "black", display = true, children}) {
|
||||
const opacity = '0.1';
|
||||
const border = 3;
|
||||
|
||||
return (
|
||||
display && circle &&
|
||||
<Circle center={circle.center} radius={circle.radius} pathOptions={{ color: color, fillColor: color, fillOpacity: opacity, weight: border }}>
|
||||
{children}
|
||||
</Circle>
|
||||
);
|
||||
}
|
||||
|
||||
export function PolygonZone({polygon, color = "black", display = true, children}) {
|
||||
const opacity = '0.1';
|
||||
const border = 3;
|
||||
|
||||
return (
|
||||
display && polygon && polygon.length >= 3 &&
|
||||
<Polygon positions={polygon} pathOptions={{ color: color, fillColor: color, fillOpacity: opacity, weight: border }}>
|
||||
{children}
|
||||
</Polygon>
|
||||
);
|
||||
}
|
||||
|
||||
export function Position({position, color = "blue", onClick = () => {}, display = true, children}) {
|
||||
|
||||
const positionIcon = new L.Icon({
|
||||
iconUrl: `/icons/marker/${color}.png`,
|
||||
iconSize: [30, 30],
|
||||
iconAnchor: [15, 15],
|
||||
popupAnchor: [0, -15],
|
||||
shadowSize: [30, 30],
|
||||
});
|
||||
|
||||
return (
|
||||
display && position &&
|
||||
<Marker position={position} icon={positionIcon} eventHandlers={{click: onClick}}>
|
||||
{children}
|
||||
</Marker>
|
||||
);
|
||||
}
|
||||
|
||||
export function Arrow({ pos1, pos2, color = 'black', display = true }) {
|
||||
const weight = 5;
|
||||
const arrowSize = 20;
|
||||
const insetPixels = 25;
|
||||
const map = useMap();
|
||||
const [insetPositions, setInsetPositions] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const updateInsetLine = () => {
|
||||
if (!pos1 || !pos2) {
|
||||
setInsetPositions(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert lat/lng to screen coordinates
|
||||
const point1 = map.latLngToContainerPoint(pos1);
|
||||
const point2 = map.latLngToContainerPoint(pos2);
|
||||
|
||||
// Calculate direction vector
|
||||
const dx = point2.x - point1.x;
|
||||
const dy = point2.y - point1.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// If the points are too close, do not render
|
||||
if (distance <= 2*insetPixels) {
|
||||
setInsetPositions(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize direction vector
|
||||
const unitX = dx / distance;
|
||||
const unitY = dy / distance;
|
||||
|
||||
// Calculate new start and end points in screen coordinates
|
||||
const newStartPoint = {
|
||||
x: point1.x + (unitX * insetPixels),
|
||||
y: point1.y + (unitY * insetPixels)
|
||||
};
|
||||
|
||||
const newEndPoint = {
|
||||
x: point2.x - (unitX * insetPixels),
|
||||
y: point2.y - (unitY * insetPixels)
|
||||
};
|
||||
|
||||
// Convert back to lat/lng
|
||||
const newStartLatLng = map.containerPointToLatLng(newStartPoint);
|
||||
const newEndLatLng = map.containerPointToLatLng(newEndPoint);
|
||||
|
||||
setInsetPositions([[newStartLatLng.lat, newStartLatLng.lng], [newEndLatLng.lat, newEndLatLng.lng]]);
|
||||
};
|
||||
|
||||
updateInsetLine();
|
||||
|
||||
// Update when map moves or zooms
|
||||
map.on('zoom move', updateInsetLine);
|
||||
|
||||
return () => map.off('zoom move', updateInsetLine);
|
||||
}, [pos1, pos2]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!display || !insetPositions) return;
|
||||
|
||||
// Create the base polyline
|
||||
const polyline = L.polyline(insetPositions, {
|
||||
color: color,
|
||||
weight: weight
|
||||
}).addTo(map);
|
||||
|
||||
// Create the arrow decorator
|
||||
const decorator = L.polylineDecorator(polyline, {
|
||||
patterns: [{
|
||||
offset: '100%',
|
||||
repeat: 0,
|
||||
symbol: L.Symbol.arrowHead({
|
||||
pixelSize: arrowSize,
|
||||
polygon: false,
|
||||
pathOptions: {
|
||||
stroke: true,
|
||||
weight: weight,
|
||||
color: color
|
||||
}
|
||||
})
|
||||
}]
|
||||
}).addTo(map);
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
map.removeLayer(polyline);
|
||||
map.removeLayer(decorator);
|
||||
};
|
||||
}, [display, insetPositions])
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user