From b5e5aff0848e805c7375ac0208d2eee127dd542a Mon Sep 17 00:00:00 2001
From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com>
Date: Thu, 10 Apr 2025 21:24:05 -0700
Subject: [PATCH] started collision handeling for label
---
.../_components/map/AircraftMarker.tsx | 239 ++++++++++++++++++
.../_components/map/ContextMenu.tsx | 9 +-
.../app/(dispatch)/_components/map/Map.tsx | 2 +
apps/dispatch/app/_store/aircraftsStore.ts | 107 ++++++++
apps/dispatch/app/_store/mapStore.ts | 10 +
apps/dispatch/app/globals.css | 4 +
grafana/grafana.db | Bin 1122304 -> 1122304 bytes
7 files changed, 363 insertions(+), 8 deletions(-)
create mode 100644 apps/dispatch/app/(dispatch)/_components/map/AircraftMarker.tsx
create mode 100644 apps/dispatch/app/_store/aircraftsStore.ts
diff --git a/apps/dispatch/app/(dispatch)/_components/map/AircraftMarker.tsx b/apps/dispatch/app/(dispatch)/_components/map/AircraftMarker.tsx
new file mode 100644
index 00000000..de40dc02
--- /dev/null
+++ b/apps/dispatch/app/(dispatch)/_components/map/AircraftMarker.tsx
@@ -0,0 +1,239 @@
+import { Aircraft, useAircraftsStore } from "_store/aircraftsStore";
+import { Marker, Popup, useMap } from "react-leaflet";
+import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet";
+import { useMapStore } from "_store/mapStore";
+import { Fragment, useEffect, useRef, useState } from "react";
+import { cn } from "helpers/cn";
+
+export const FMS_STATUS_COLORS: { [key: string]: string } = {
+ "0": "rgb(126,0,5)",
+ "1": "rgb(0,93,0)",
+ "2": "rgb(0,93,0)",
+ "3": "rgb(126,0,5)",
+ "4": "rgb(126,0,5)",
+ "5": "rgb(126,0,5)",
+ "6": "rgb(67,67,67)",
+ "7": "rgb(126,0,5)",
+ "8": "rgb(186,105,0)",
+ "9": "rgb(0,93,0)",
+};
+
+export const FMS_STATUS_TEXT_COLORS: { [key: string]: string } = {
+ "0": "rgb(255,39,57)",
+ "1": "rgb(42,217,42)",
+ "2": "rgb(42,217,42)",
+ "3": "rgb(255,39,57)",
+ "4": "rgb(255,39,57)",
+ "5": "rgb(255,39,57)",
+ "6": "rgb(211,211,211)",
+ "7": "rgb(255,39,57)",
+ "8": "rgb(255,143,0)",
+ "9": "rgb(42,217,42)",
+};
+
+export const AircraftMarker = (props: any) => {
+ const { openAircraftMarker, setOpenAircraftMarker } = useMapStore(
+ (state) => state,
+ );
+ const aircrafts = useAircraftsStore((state) => state.aircrafts);
+ const map = useMap();
+ const [markersAdjusted, setMarkersAdjusted] = useState(false);
+
+ const getMarkerHTML = (
+ aircraft: Aircraft,
+ anchor: "topleft" | "topright" | "bottomleft" | "bottomright",
+ ) => {
+ return `
+
+
+
+ ${aircraft.fmsStatus}
+
+
+ ${aircraft.bosName}
+
+
`;
+ };
+
+ return aircrafts.map((aircraft) => {
+ const markerRef = useRef(null);
+ const popupRef = useRef(null);
+
+ useEffect(() => {
+ const handleClick = () => {
+ const open = openAircraftMarker.includes(aircraft.id);
+ if (open) {
+ setOpenAircraftMarker({
+ open: [],
+ close: [aircraft.id],
+ });
+ } else {
+ setOpenAircraftMarker({
+ open: [aircraft.id],
+ close: [],
+ });
+ }
+ };
+ markerRef.current?.on("click", handleClick);
+ return () => {
+ markerRef.current?.off("click", handleClick);
+ };
+ }, [markerRef.current, aircraft.id, openAircraftMarker]);
+
+ // TODO: Get Overlapping Markers and make them opientate away from the center
+ const [anchor, setAnchor] = useState<
+ "topleft" | "topright" | "bottomleft" | "bottomright"
+ >("topleft");
+
+ useEffect(() => {
+ /* const testRef = setInterval(() => {
+ positionMarker();
+ }, 1000); */
+
+ const handleZoom = () => {
+ setAnchor("topleft");
+ setMarkersAdjusted(false);
+ };
+ map.on("zoom", handleZoom);
+
+ return () => {
+ map.off("zoom", handleZoom);
+ };
+
+ /* return () => {
+ clearInterval(testRef);
+ }; */
+ }, [map]);
+
+ useEffect(() => {
+ if (markersAdjusted) return;
+ for (let i = 0; i < 3; i++) {
+ console.log(`Iteration ${i}`);
+ const otherMarkers = document.querySelectorAll(".aircraft-marker");
+ // get markers and check if they are overlapping
+ const ownMarker = document.querySelector(
+ `#aircraft-marker-${aircraft.id}`,
+ );
+
+ if (!otherMarkers || !ownMarker) return;
+
+ const overlappingMarkers = Array.from(otherMarkers).filter((marker) => {
+ // if (marker.id === `aircraft-marker-${aircraft.id}`) return false;
+ const rect1 = (marker as HTMLElement).getBoundingClientRect();
+ const rect2 = (ownMarker as HTMLElement).getBoundingClientRect();
+
+ return !(
+ rect1.right < rect2.left ||
+ rect1.left > rect2.right ||
+ rect1.bottom < rect2.top ||
+ rect1.top > rect2.bottom
+ );
+ });
+ // get the center of all overlapping markers
+ const markersPosition = overlappingMarkers.map((marker) => {
+ const rect = (marker as HTMLElement).getBoundingClientRect();
+ return {
+ x: rect.left + rect.width / 2,
+ y: rect.top + rect.height / 2,
+ };
+ });
+ const ownMarkerBounds = (
+ ownMarker as HTMLElement
+ ).getBoundingClientRect();
+ const ownMarkerPosition = {
+ x: ownMarkerBounds.left + ownMarkerBounds.width / 2,
+ y: ownMarkerBounds.top + ownMarkerBounds.height / 2,
+ };
+
+ const centerOfOverlappingMarkers = markersPosition.reduce(
+ (acc, pos) => {
+ if (acc.x === 0 && acc.y === 0) return pos;
+ return {
+ x: (acc.x + pos.x) / 2,
+ y: (acc.y + pos.y) / 2,
+ };
+ },
+ {
+ x: 0,
+ y: 0,
+ },
+ );
+ console.log(
+ "overlapping markers",
+ centerOfOverlappingMarkers,
+ ownMarkerPosition,
+ );
+ if (overlappingMarkers.length >= 1) {
+ if (centerOfOverlappingMarkers.y > ownMarkerPosition.y) {
+ if (centerOfOverlappingMarkers.x > ownMarkerPosition.x) {
+ setAnchor("topright");
+ } else {
+ setAnchor("topleft");
+ }
+ } else {
+ if (centerOfOverlappingMarkers.x > ownMarkerPosition.x) {
+ setAnchor("bottomright");
+ } else {
+ setAnchor("bottomleft");
+ }
+ }
+ }
+ // TODO: Once markers are readjusted they dont overlap to these they did in the first iteration, but when all markers are adjusted they overlap whit ones they didnt in the first interation
+ }
+ setMarkersAdjusted(true);
+ }, [setAnchor, setMarkersAdjusted, aircraft.id, markersAdjusted]);
+
+ // EG. if 3 markers are overlapping and they are left and right
+
+ return (
+
+
+ {openAircraftMarker.includes(aircraft.id) && (
+
+ Alla
+
+ )}
+
+ );
+ });
+};
diff --git a/apps/dispatch/app/(dispatch)/_components/map/ContextMenu.tsx b/apps/dispatch/app/(dispatch)/_components/map/ContextMenu.tsx
index 53820d6a..d82c1964 100644
--- a/apps/dispatch/app/(dispatch)/_components/map/ContextMenu.tsx
+++ b/apps/dispatch/app/(dispatch)/_components/map/ContextMenu.tsx
@@ -5,14 +5,7 @@ import { Popup, useMap } from "react-leaflet";
export const ContextMenu = () => {
const map = useMap();
- const {
- contextMenu,
- setContextMenu,
- setSearchElements,
- setSearchPopup,
- setOpenMissionMarker,
- openMissionMarker,
- } = useMapStore();
+ const { contextMenu, setContextMenu, setSearchElements } = useMapStore();
useEffect(() => {
map.on("contextmenu", (e) => {
diff --git a/apps/dispatch/app/(dispatch)/_components/map/Map.tsx b/apps/dispatch/app/(dispatch)/_components/map/Map.tsx
index 155d5d5d..08ac1299 100644
--- a/apps/dispatch/app/(dispatch)/_components/map/Map.tsx
+++ b/apps/dispatch/app/(dispatch)/_components/map/Map.tsx
@@ -6,6 +6,7 @@ import { BaseMaps } from "(dispatch)/_components/map/BaseMaps";
import { ContextMenu } from "(dispatch)/_components/map/ContextMenu";
import { MissionMarkers } from "(dispatch)/_components/map/MissionMarkers";
import { SearchElements } from "(dispatch)/_components/map/SearchElements";
+import { AircraftMarker } from "(dispatch)/_components/map/AircraftMarker";
export default ({}) => {
const { map } = useMapStore();
@@ -16,6 +17,7 @@ export default ({}) => {
+
);
};
diff --git a/apps/dispatch/app/_store/aircraftsStore.ts b/apps/dispatch/app/_store/aircraftsStore.ts
new file mode 100644
index 00000000..7c9a0462
--- /dev/null
+++ b/apps/dispatch/app/_store/aircraftsStore.ts
@@ -0,0 +1,107 @@
+import { create } from "zustand";
+
+export interface Aircraft {
+ id: string;
+ bosName: string;
+ bosNameShort: string;
+ fmsStatus: string;
+ fmsLog: {
+ status: string;
+ timestamp: string;
+ user: string;
+ }[];
+ location: {
+ lat: number;
+ lon: number;
+ altitude: number;
+ speed: number;
+ };
+ locationHistory: {
+ lat: number;
+ lon: number;
+ altitude: number;
+ speed: number;
+ timestamp: string;
+ }[];
+}
+
+interface AircraftStore {
+ aircrafts: Aircraft[];
+ setAircrafts: (aircrafts: Aircraft[]) => void;
+ setAircraft: (aircraft: Aircraft) => void;
+}
+
+export const useAircraftsStore = create((set) => ({
+ aircrafts: [
+ {
+ id: "1",
+ bosName: "Aircraft 1",
+ bosNameShort: "A1",
+ fmsStatus: "1",
+ fmsLog: [],
+ location: {
+ lat: 52.546781040592776,
+ lon: 13.369535209542219,
+ altitude: 0,
+ speed: 0,
+ },
+ locationHistory: [],
+ },
+ {
+ id: "2",
+ bosName: "Aircraft 2",
+ bosNameShort: "A2",
+ fmsStatus: "2",
+ fmsLog: [],
+ location: {
+ lat: 52.54588546048977,
+ lon: 13.46470691054384,
+ altitude: 0,
+ speed: 0,
+ },
+ locationHistory: [],
+ },
+ {
+ id: "3",
+ bosName: "Aircraft 3",
+ bosNameShort: "A3",
+ fmsStatus: "3",
+ fmsLog: [],
+ location: {
+ lat: 52.497519717230155,
+ lon: 13.342040806552554,
+ altitude: 0,
+ speed: 0,
+ },
+ locationHistory: [],
+ },
+ {
+ id: "4",
+ bosName: "Aircraft 4",
+ bosNameShort: "A4",
+ fmsStatus: "6",
+ fmsLog: [],
+ location: {
+ lat: 52.50175041192073,
+ lon: 13.478628701227349,
+ altitude: 0,
+ speed: 0,
+ },
+ locationHistory: [],
+ },
+ ],
+ setAircrafts: (aircrafts) => set({ aircrafts }),
+ setAircraft: (aircraft) =>
+ set((state) => {
+ const existingAircraftIndex = state.aircrafts.findIndex(
+ (a) => a.id === aircraft.id,
+ );
+ if (existingAircraftIndex !== -1) {
+ const updatedAircrafts = [...state.aircrafts];
+ updatedAircrafts[existingAircraftIndex] = aircraft;
+ return { aircrafts: updatedAircrafts };
+ } else {
+ return { aircrafts: [...state.aircrafts, aircraft] };
+ }
+ }),
+}));
diff --git a/apps/dispatch/app/_store/mapStore.ts b/apps/dispatch/app/_store/mapStore.ts
index 38589264..075364ad 100644
--- a/apps/dispatch/app/_store/mapStore.ts
+++ b/apps/dispatch/app/_store/mapStore.ts
@@ -12,6 +12,8 @@ interface MapStore {
};
openMissionMarker: string[];
setOpenMissionMarker: (mission: { open: string[]; close: string[] }) => void;
+ openAircraftMarker: string[];
+ setOpenAircraftMarker: (mission: { open: string[]; close: string[] }) => void;
searchElements: {
id: number;
nodes: {
@@ -48,6 +50,14 @@ export const useMapStore = create((set, get) => ({
),
}));
},
+ openAircraftMarker: [],
+ setOpenAircraftMarker: ({ open, close }) => {
+ set((state) => ({
+ openAircraftMarker: [...state.openAircraftMarker, ...open].filter(
+ (id) => !close.includes(id),
+ ),
+ }));
+ },
map: {
center: [51.5, 10.5],
zoom: 6,
diff --git a/apps/dispatch/app/globals.css b/apps/dispatch/app/globals.css
index 4178859a..06986ec6 100644
--- a/apps/dispatch/app/globals.css
+++ b/apps/dispatch/app/globals.css
@@ -15,6 +15,10 @@
.leaflet-popup-content p {
margin: 0 !important;
}
+.leaflet-div-icon {
+ background: inherit !important;
+ border: inherit !important;
+}
.leaflet-popup-content-wrapper {
background: transparent !important;
diff --git a/grafana/grafana.db b/grafana/grafana.db
index 05a61d31b09180e167a6ca0ce05dec5100964ebf..72bd3457916e466ea3887be30e7458209989a35c 100644
GIT binary patch
delta 122
zcmZoT;L>owWr8&0n~5^cjBgqfS`!#s6PQ{Pm|GKAS`%1X6WCf4*taHd=<4%urT>U@
zV_;xVwB%r!-fzz#HhsN5hbkK*bNY|zV$*NwbI7x@r~gO~YyYRu0mPg@%mu{UK+FTg
RygowWr8&0(up$8j7u96S`!#s6PQ{Pm|GKAS`%1X6WCf4*taHd=<4%uq<@=y
zj)8$e(UOB@dcQq~*!1=K9I9*#2h+dJZ=HTipF^INCH-raX8S*V4j|?PVlE)&24Wr{
R<^^ItAm-oxPhWuP0{}kJEdKxi