did stuff
This commit is contained in:
parent
5b476a8f93
commit
d0758fb56b
@ -1,23 +0,0 @@
|
|||||||
name: Build and Push Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Log in to DockerHub
|
|
||||||
run: echo "${{ ?!EQ{_Pt\$D}yb^}d8 }}" | docker login https://dockerreg.slpnetwork.de -u "${{ slpdocker }}" --password-stdin
|
|
||||||
|
|
||||||
- name: Build Docker Image
|
|
||||||
run: |
|
|
||||||
docker build -t dockerreg.slpnetwork.de/geotrack:latest .
|
|
||||||
|
|
||||||
- name: Push Docker Image
|
|
||||||
run: |
|
|
||||||
docker push dockerreg.slpnetwork.de/geotrack:latest
|
|
@ -18,11 +18,10 @@ export default defineComponent({
|
|||||||
const initializeMap = () => {
|
const initializeMap = () => {
|
||||||
if (mapDiv.value) {
|
if (mapDiv.value) {
|
||||||
mapInstance.value = L.map(mapDiv.value, {
|
mapInstance.value = L.map(mapDiv.value, {
|
||||||
center: [51.4819, 7.2162], // Beispielkoordinaten
|
center: [51.4819, 7.2162],
|
||||||
zoom: 13,
|
zoom: 13,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hinzufügen von OpenStreetMap-Kacheln
|
|
||||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {}).addTo(mapInstance.value);
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {}).addTo(mapInstance.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -32,12 +31,10 @@ export default defineComponent({
|
|||||||
initializeMap();
|
initializeMap();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Beobachte geoJsonData und füge sie der Karte hinzu, wenn sie sich ändert
|
|
||||||
watch(() => props.geoJsonData, (newData) => {
|
watch(() => props.geoJsonData, (newData) => {
|
||||||
if (newData) {
|
if (newData) {
|
||||||
console.log("Neue GeoJSON-Daten erhalten:", newData);
|
console.log("Neue GeoJSON-Daten erhalten:", newData);
|
||||||
|
|
||||||
// Stelle sicher, dass die Karte initialisiert wurde
|
|
||||||
if (mapInstance.value) {
|
if (mapInstance.value) {
|
||||||
const geoJsonLayer = L.geoJSON(newData).addTo(mapInstance.value);
|
const geoJsonLayer = L.geoJSON(newData).addTo(mapInstance.value);
|
||||||
|
|
||||||
@ -50,14 +47,13 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mapDiv, // Gebe mapDiv zurück, damit es im Template verwendet wird
|
mapDiv,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Das ref-Attribut gibt den Container für die Karte an -->
|
|
||||||
<div ref="mapDiv" style="width: 70vw; height: 75vh; overflow: hidden;"></div>
|
<div ref="mapDiv" style="width: 70vw; height: 75vh; overflow: hidden;"></div>
|
||||||
<br>
|
<br>
|
||||||
<div class="overflow-x-auto" style="width: 70vw;">
|
<div class="overflow-x-auto" style="width: 70vw;">
|
||||||
@ -85,16 +81,14 @@ export default defineComponent({
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Verhindert, dass die Karte über den Bildschirm hinausgeht */
|
|
||||||
#mapDiv {
|
#mapDiv {
|
||||||
width: 100vw; /* Volle Breite des Viewports */
|
width: 100vw;
|
||||||
height: 70vh; /* Volle Höhe des Viewports */
|
height: 70vh;
|
||||||
margin: 0; /* Keine Margen */
|
margin: 0;
|
||||||
padding: 0; /* Keine Auffüllung */
|
padding: 0;
|
||||||
overflow: hidden; /* Verhindert ungewolltes Überlaufen */
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optional: Globale Styles für den Body und html, um das Layout zu normalisieren */
|
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -39,15 +39,10 @@ export default defineComponent({
|
|||||||
<ul
|
<ul
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
|
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
|
||||||
<li><a>Item 1</a></li>
|
<li><RouterLink to="/">Home</RouterLink></li>
|
||||||
<li>
|
<li><RouterLink to="/driver">Driver</RouterLink></li>
|
||||||
<a>Parent</a>
|
<li><RouterLink to="/vehicle">Vehicle</RouterLink></li>
|
||||||
<ul class="p-4">
|
<li><RouterLink to="/route">Routes</RouterLink></li>
|
||||||
<li><a>Submenu 1</a></li>
|
|
||||||
<li><a>Submenu 2</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a>Item 3</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-ghost text-xl">GeoTrack</a>
|
<a class="btn btn-ghost text-xl">GeoTrack</a>
|
||||||
|
@ -8,6 +8,7 @@ type Route = {
|
|||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
time: Date;
|
time: Date;
|
||||||
|
driver: { id: number; name: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -18,6 +19,8 @@ export default defineComponent({
|
|||||||
const tooltipRef: Ref<HTMLDivElement | null> = ref(null);
|
const tooltipRef: Ref<HTMLDivElement | null> = ref(null);
|
||||||
const tooltipText = ref("");
|
const tooltipText = ref("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const legend = ref<Record<number, string>>({});
|
||||||
|
const driverColors = ref<Record<number, string>>({});
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
emit("close");
|
emit("close");
|
||||||
@ -47,26 +50,91 @@ export default defineComponent({
|
|||||||
const raycaster = new THREE.Raycaster();
|
const raycaster = new THREE.Raycaster();
|
||||||
const mouse = new THREE.Vector2();
|
const mouse = new THREE.Vector2();
|
||||||
|
|
||||||
// Beispiel-Daten (Punkte)
|
// Beispiel-Daten (Fahrer mit ID und Punkten)
|
||||||
|
const drivers = [
|
||||||
|
{ id: 1, name: "Alice" },
|
||||||
|
{ id: 2, name: "Bob" },
|
||||||
|
{ id: 3, name: "Charlie" },
|
||||||
|
{ id: 4, name: "Dave" },
|
||||||
|
{ id: 5, name: "Eve" },
|
||||||
|
];
|
||||||
|
|
||||||
const points: Route[] = Array.from({ length: 100 }, (_, i) => ({
|
const points: Route[] = Array.from({ length: 100 }, (_, i) => ({
|
||||||
name: `Route ${i + 1}`,
|
name: `Route ${i + 1}`,
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
time: new Date(Date.now() - i * 1000 * 60),
|
time: new Date(Date.now() - i * 1000 * 60),
|
||||||
|
driver: drivers[i % drivers.length], // Fahrer zuweisen
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Nach Fahrern gruppieren
|
||||||
|
const groupedPoints = points.reduce((acc, point) => {
|
||||||
|
const driverId = point.driver.id;
|
||||||
|
acc[driverId] = acc[driverId] || [];
|
||||||
|
acc[driverId].push(point);
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<number, Route[]>);
|
||||||
|
|
||||||
|
// Farben für Fahrer generieren und der Legende zuweisen
|
||||||
|
drivers.forEach((driver, index) => {
|
||||||
|
const color = new THREE.Color(`hsl(${(index * 360) / drivers.length}, 70%, 50%)`);
|
||||||
|
driverColors.value[driver.id] = color.getStyle(); // Farbwert für die Legende
|
||||||
|
legend.value[driver.id] = driver.name; // Fahrernamen für die Legende
|
||||||
|
});
|
||||||
|
|
||||||
// Punktwolke erstellen
|
// Punktwolke erstellen
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new THREE.BufferGeometry();
|
||||||
const vertices = new Float32Array(
|
const vertices: number[] = [];
|
||||||
points.flatMap((_, i) => [
|
const colors: number[] = [];
|
||||||
(Math.random() - 0.5) * 20,
|
|
||||||
(Math.random() - 0.5) * 20,
|
const totalPoints = points.length;
|
||||||
(Math.random() - 0.5) * 20,
|
const offsetXStep = 2; // Wir setzen den Abstand pro Gruppe
|
||||||
])
|
|
||||||
);
|
let offsetX = 0;
|
||||||
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
|
|
||||||
|
// Berechnungen für den Mittelpunkt der Punktwolke
|
||||||
|
let sumX = 0, sumY = 0, sumZ = 0;
|
||||||
|
|
||||||
|
Object.entries(groupedPoints).forEach(([driverIdStr, driverPoints], index) => {
|
||||||
|
const driverId = parseInt(driverIdStr, 10);
|
||||||
|
const color = new THREE.Color(driverColors.value[driverId]); // Die Farbe aus `driverColors`
|
||||||
|
|
||||||
|
// Berechnung des Abstands auf der X-Achse basierend auf der Fahrer-ID
|
||||||
|
offsetX += offsetXStep;
|
||||||
|
|
||||||
|
driverPoints.forEach(() => {
|
||||||
|
// Zufällige Position innerhalb eines Bereichs
|
||||||
|
const x = offsetX + (Math.random() - 0.5) * 2;
|
||||||
|
const y = (Math.random() - 0.5) * 6;
|
||||||
|
const z = (Math.random() - 0.5) * 6;
|
||||||
|
|
||||||
|
// Berechnungen für den Mittelpunkt
|
||||||
|
sumX += x;
|
||||||
|
sumY += y;
|
||||||
|
sumZ += z;
|
||||||
|
|
||||||
|
vertices.push(x, y, z);
|
||||||
|
colors.push(color.r, color.g, color.b);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mittelpunkt der Punktwolke berechnen
|
||||||
|
const centerX = sumX / totalPoints;
|
||||||
|
const centerY = sumY / totalPoints;
|
||||||
|
const centerZ = sumZ / totalPoints;
|
||||||
|
|
||||||
|
// Punktwolke verschieben, damit der Mittelpunkt im Ursprung ist
|
||||||
|
const offset = new THREE.Vector3(-centerX, -centerY, -centerZ);
|
||||||
|
for (let i = 0; i < vertices.length; i += 3) {
|
||||||
|
vertices[i] += offset.x;
|
||||||
|
vertices[i + 1] += offset.y;
|
||||||
|
vertices[i + 2] += offset.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
|
||||||
|
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
||||||
|
|
||||||
const material = new THREE.PointsMaterial({
|
const material = new THREE.PointsMaterial({
|
||||||
color: 0x0077ff,
|
vertexColors: true, // Farben pro Punkt verwenden
|
||||||
size: 0.3,
|
size: 0.3,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,12 +156,10 @@ export default defineComponent({
|
|||||||
const onMouseMove = (event: MouseEvent) => {
|
const onMouseMove = (event: MouseEvent) => {
|
||||||
if (!canvasRef.value || !tooltipRef.value) return;
|
if (!canvasRef.value || !tooltipRef.value) return;
|
||||||
|
|
||||||
// Mausposition berechnen (normalisiert)
|
|
||||||
const rect = canvasRef.value.getBoundingClientRect();
|
const rect = canvasRef.value.getBoundingClientRect();
|
||||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
// Raycasting
|
|
||||||
raycaster.setFromCamera(mouse, camera);
|
raycaster.setFromCamera(mouse, camera);
|
||||||
const intersects = raycaster.intersectObject(pointCloud);
|
const intersects = raycaster.intersectObject(pointCloud);
|
||||||
|
|
||||||
@ -102,31 +168,31 @@ export default defineComponent({
|
|||||||
const point = points[intersectIndex];
|
const point = points[intersectIndex];
|
||||||
|
|
||||||
tooltipRef.value.style.display = "block";
|
tooltipRef.value.style.display = "block";
|
||||||
tooltipRef.value.style.left = `${event.clientX}px`;
|
|
||||||
tooltipRef.value.style.top = `${event.clientY - 20}px`;
|
// 3D-Koordinaten des Punktes holen
|
||||||
tooltipText.value = `${point.name} (${point.time.toLocaleString()})`;
|
const positionArray = pointCloud.geometry.attributes.position.array;
|
||||||
|
const x = positionArray[intersectIndex * 3];
|
||||||
|
const y = positionArray[intersectIndex * 3 + 1];
|
||||||
|
const z = positionArray[intersectIndex * 3 + 2];
|
||||||
|
|
||||||
|
// 3D-Koordinaten in 2D Bildschirmkoordinaten umrechnen
|
||||||
|
const screenPosition = new THREE.Vector3(x, y, z);
|
||||||
|
screenPosition.project(camera);
|
||||||
|
|
||||||
|
// Umrechnung von NDC (Normalized Device Coordinates) zu Pixeln
|
||||||
|
const tooltipX = (screenPosition.x * 0.5 + 0.5) * window.innerWidth;
|
||||||
|
const tooltipY = (screenPosition.y * -0.5 + 0.5) * window.innerHeight;
|
||||||
|
|
||||||
|
tooltipRef.value.style.left = `${tooltipX + 10}px`; // 10px Abstand zum Punkt
|
||||||
|
tooltipRef.value.style.top = `${tooltipY + 10}px`; // 10px Abstand zum Punkt
|
||||||
|
|
||||||
|
tooltipText.value = `${point.driver.name}: ${point.name} (${point.time.toLocaleString()})`;
|
||||||
} else {
|
} else {
|
||||||
tooltipRef.value.style.display = "none";
|
tooltipRef.value.style.display = "none";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Punkt klicken
|
|
||||||
const onMouseClick = (event: MouseEvent) => {
|
|
||||||
if (!canvasRef.value) return;
|
|
||||||
|
|
||||||
// Raycasting
|
|
||||||
raycaster.setFromCamera(mouse, camera);
|
|
||||||
const intersects = raycaster.intersectObject(pointCloud);
|
|
||||||
|
|
||||||
if (intersects.length > 0) {
|
|
||||||
const intersectIndex = Math.floor(intersects[0].index || 0);
|
|
||||||
const point = points[intersectIndex];
|
|
||||||
router.push(`/routeViewer/${point.id}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
canvasRef.value.addEventListener("mousemove", onMouseMove);
|
canvasRef.value.addEventListener("mousemove", onMouseMove);
|
||||||
canvasRef.value.addEventListener("click", onMouseClick);
|
|
||||||
|
|
||||||
// Fenstergröße anpassen
|
// Fenstergröße anpassen
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
@ -141,6 +207,8 @@ export default defineComponent({
|
|||||||
canvasRef,
|
canvasRef,
|
||||||
tooltipRef,
|
tooltipRef,
|
||||||
tooltipText,
|
tooltipText,
|
||||||
|
legend,
|
||||||
|
driverColors,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -152,6 +220,16 @@ export default defineComponent({
|
|||||||
<div ref="tooltipRef" class="tooltip hidden fixed z-50 px-2 py-1 bg-base-200 text-base-content rounded">
|
<div ref="tooltipRef" class="tooltip hidden fixed z-50 px-2 py-1 bg-base-200 text-base-content rounded">
|
||||||
{{ tooltipText }}
|
{{ tooltipText }}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Legende -->
|
||||||
|
<div class="legend absolute top-10 left-10 bg-base-200 p-4 rounded shadow-lg z-50">
|
||||||
|
<h3 class="font-bold mb-2">Fahrer Legende</h3>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(driverName, driverId) in legend" :key="driverId" class="flex items-center mb-1">
|
||||||
|
<div class="w-4 h-4" :style="{ backgroundColor: driverColors[driverId] }"></div>
|
||||||
|
<span class="ml-2">{{ driverName }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -168,10 +246,40 @@ canvas {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.close {
|
.tooltip {
|
||||||
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
z-index: 50;
|
||||||
right: 10px;
|
padding: 2px 8px;
|
||||||
z-index: 10;
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
width: auto;
|
||||||
|
max-width: 250px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend .color-box {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -11,7 +11,7 @@ export default defineComponent({
|
|||||||
setup(_, { emit }: SetupContext) {
|
setup(_, { emit }: SetupContext) {
|
||||||
|
|
||||||
const driverName: Ref<string> = ref("")
|
const driverName: Ref<string> = ref("")
|
||||||
const driverList: Ref<driver[]> = ref([{ id: 1, name: "jeff" }])
|
const driverList: Ref<driver[]> = ref([])
|
||||||
|
|
||||||
// handles sending webrequests to the backend
|
// handles sending webrequests to the backend
|
||||||
const getDrivers = async () => {
|
const getDrivers = async () => {
|
||||||
|
@ -112,10 +112,19 @@ export default defineComponent({
|
|||||||
search.value = false;
|
search.value = false;
|
||||||
showUpload.value = false;
|
showUpload.value = false;
|
||||||
|
|
||||||
const request: RequestInfo = new Request("http://localhost:5000/track?start=" + startSearchDate.value + "&end=" + endSearchDate.value, {
|
// Formatierung der Datumsangaben
|
||||||
|
const startDate = startSearchDate.value ? new Date(startSearchDate.value).toISOString().split('T')[0] : null;
|
||||||
|
const endDate = endSearchDate.value ? new Date(endSearchDate.value).toISOString().split('T')[0] : null;
|
||||||
|
|
||||||
|
// Baue den Request-URL mit den formatierten Datumsangaben
|
||||||
|
const url = new URL("http://localhost:5000/track");
|
||||||
|
if (startDate) url.searchParams.append("start", startDate);
|
||||||
|
if (endDate) url.searchParams.append("end", endDate);
|
||||||
|
|
||||||
|
const request: RequestInfo = new Request(url.toString(), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: headers
|
headers: headers,
|
||||||
})
|
});
|
||||||
|
|
||||||
var response = await fetch(request)
|
var response = await fetch(request)
|
||||||
// make sure the request was successfull
|
// make sure the request was successfull
|
||||||
|
@ -11,7 +11,7 @@ export default defineComponent({
|
|||||||
setup(_, { emit }: SetupContext) {
|
setup(_, { emit }: SetupContext) {
|
||||||
|
|
||||||
const vehicleName:Ref<string> = ref("")
|
const vehicleName:Ref<string> = ref("")
|
||||||
const vehicleList:Ref<vehicle[]> = ref([{id:1,name:"Bike"}])
|
const vehicleList:Ref<vehicle[]> = ref([])
|
||||||
|
|
||||||
// handles getting all existing drivers
|
// handles getting all existing drivers
|
||||||
const getVehicles = async () => {
|
const getVehicles = async () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user