Merge pull request 'clickablePoints' (#1) from clickablePoints into main

Reviewed-on: #1
This commit is contained in:
Steev 2025-01-16 12:38:44 +01:00
commit 7af2c9a54e
21 changed files with 551181 additions and 73 deletions

3
app.py
View File

@ -49,7 +49,6 @@ try:
session.flush() session.flush()
except: except:
print("default data not pushed as already present") print("default data not pushed as already present")
session.flush()
print("initialize handlers") print("initialize handlers")
gpxHandler = GPXHandler(session) gpxHandler = GPXHandler(session)
@ -278,7 +277,7 @@ def handleVehicleRoute():
vehicleHandler.deleteVehicle(data["id"]) vehicleHandler.deleteVehicle(data["id"])
return "vehicle deleted", 200 return "vehicle deleted", 200
except Exception as e: except Exception as e:
return f"couldnt delete vehicle due to error: {e}", 500 return f"couldn't delete vehicle due to error: {e}", 500
@app.route('/upload', methods=['POST']) @app.route('/upload', methods=['POST'])

View File

@ -17,6 +17,8 @@ services:
POSTGRES_USER: example POSTGRES_USER: example
POSTGRES_PASSWORD: example POSTGRES_PASSWORD: example
POSTGRES_DB: geotrack POSTGRES_DB: geotrack
# volumes:
# - ./init.sql:/docker-entrypoint-initdb.d/init.sql
# web: # web:
# build: . # build: .
# ports: # ports:

67
init.sql Normal file
View File

@ -0,0 +1,67 @@
-- Adminer 4.8.1 PostgreSQL 16.4 (Debian 16.4-1.pgdg120+1) dump
DROP TABLE IF EXISTS "driver";
DROP SEQUENCE IF EXISTS driver_id_seq;
CREATE SEQUENCE driver_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."driver" (
"id" integer DEFAULT nextval('driver_id_seq') NOT NULL,
"name" character varying NOT NULL,
CONSTRAINT "driver_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "driver" ("id", "name") VALUES
(1, 'default');
DROP TABLE IF EXISTS "track";
DROP SEQUENCE IF EXISTS track_id_seq;
CREATE SEQUENCE track_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."track" (
"id" integer DEFAULT nextval('track_id_seq') NOT NULL,
"trackName" character varying(200),
"vehicle_id" integer NOT NULL,
"driver_id" integer NOT NULL,
"date" date,
"distance" double precision NOT NULL,
"speed" double precision NOT NULL,
CONSTRAINT "track_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
DROP TABLE IF EXISTS "vehicle";
DROP SEQUENCE IF EXISTS vehicle_id_seq;
CREATE SEQUENCE vehicle_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."vehicle" (
"id" integer DEFAULT nextval('vehicle_id_seq') NOT NULL,
"name" character varying NOT NULL,
"licenseplate" character varying,
CONSTRAINT "vehicle_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "vehicle" ("id", "name", "licenseplate") VALUES
(1, 'default', 'default');
DROP TABLE IF EXISTS "waypoint";
DROP SEQUENCE IF EXISTS waypoint_id_seq;
CREATE SEQUENCE waypoint_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."waypoint" (
"id" integer DEFAULT nextval('waypoint_id_seq') NOT NULL,
"lat" double precision NOT NULL,
"lon" double precision NOT NULL,
"ele" double precision NOT NULL,
"speed" double precision,
"time" timestamp,
"track_id" integer NOT NULL,
CONSTRAINT "waypoint_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
ALTER TABLE ONLY "public"."track" ADD CONSTRAINT "track_driver_id_fkey" FOREIGN KEY (driver_id) REFERENCES driver(id) NOT DEFERRABLE;
ALTER TABLE ONLY "public"."track" ADD CONSTRAINT "track_vehicle_id_fkey" FOREIGN KEY (vehicle_id) REFERENCES vehicle(id) NOT DEFERRABLE;
ALTER TABLE ONLY "public"."waypoint" ADD CONSTRAINT "waypoint_track_id_fkey" FOREIGN KEY (track_id) REFERENCES track(id) NOT DEFERRABLE;
-- 2025-01-16 09:13:38.732525+00

View File

@ -32,7 +32,7 @@ class GPXHandler:
for track in self.__gpx.tracks: for track in self.__gpx.tracks:
# sets track name # sets track name
# if no name is found at a track default to using date # if no name is found at a track default to using date
trackName = track.name or f"Track-{datetime.now().isoformat()}" # todo using time.now might end up being misleading and to be reworked trackName = track.name or f"Track-{datetime.now().isoformat()}"
# initializes track values # initializes track values
self.startTime = None self.startTime = None
@ -132,33 +132,6 @@ class GPXHandler:
return track_list return track_list
def getTracksInTimeWithGeoData(self, start, end):
# Alle Tracks in der Zeitspanne abfragen
tracks = self.__dbSession.query(Track).filter(Track.date.between(start, end)).all()
# Eine Liste von GeoJSON-Features für alle Tracks
features = []
# Für jedes Track-Objekt die Waypoints abfragen und die GeoJSON-Daten generieren
for track in tracks:
# Waypoints für das Track laden
waypoints = track.waypoints # track.waypoints ist bereits korrekt verknüpft
# Waypoints in GeoJSON-kompatible Koordinaten umwandeln
coordinates = [(wp.lon, wp.lat) for wp in waypoints]
# LineString Feature für das Track erstellen
feature = Feature(geometry=LineString(coordinates))
features.append(feature)
# Ein FeatureCollection erstellen, das alle Track-Features enthält
feature_collection = FeatureCollection(features)
# GeoJSON zurückgeben, das von Leaflet verarbeitet werden kann
return feature_collection
def getTracksInTime(self, start, end): def getTracksInTime(self, start, end):
tracks = self.__dbSession.query(Track).filter(Track.date.between(start, end)).all() tracks = self.__dbSession.query(Track).filter(Track.date.between(start, end)).all()

111319
uploads/AA_WITAA333_003.gpx Normal file

File diff suppressed because it is too large Load Diff

107035
uploads/AA_WITAA333_006.gpx Normal file

File diff suppressed because it is too large Load Diff

47899
uploads/AA_WITAA999_003.gpx Normal file

File diff suppressed because it is too large Load Diff

113739
uploads/AA_WITAA999_004.gpx Normal file

File diff suppressed because it is too large Load Diff

61090
uploads/AA_WITAA999_005.gpx Normal file

File diff suppressed because it is too large Load Diff

68419
uploads/AB_WITAA333_005.gpx Normal file

File diff suppressed because it is too large Load Diff

3602
uploads/AC_ENLX666_001.gpx Normal file

File diff suppressed because it is too large Load Diff

37876
uploads/BL_WITAA999_001.gpx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ export default defineComponent({
<template> <template>
<div class="appContainer"> <div class="appContainer">
<Navbar></Navbar> <Navbar></Navbar>
<cookiePrompt v-if="showCookiePrompt"></cookiePrompt> <cookiePrompt v-if="showCookiePrompt" @close="showCookiePrompt=false;" style="margin-left:20%;width:90%;"></cookiePrompt>
<RouterView></RouterView> <RouterView></RouterView>
</div> </div>
</template> </template>

View File

@ -8,10 +8,12 @@ export default defineComponent({
setup(_, { emit }: SetupContext) { setup(_, { emit }: SetupContext) {
function acceptCookies(){ function acceptCookies(){
emit("close")
localStorage.setItem("allow-data-storage", "true") localStorage.setItem("allow-data-storage", "true")
} }
function rejectCookies(){ function rejectCookies(){
emit("close")
localStorage.setItem("allow-data-storage", "false") localStorage.setItem("allow-data-storage", "false")
} }

View File

@ -81,6 +81,8 @@ export default defineComponent({
drivers.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] }) drivers.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] })
} }
} else { } else {
alert(`upload failed: ${await response.text()}`)
console.log(await response.text()) console.log(await response.text())
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${await response.statusText}`; message.value = `upload failed: ${await response.statusText}`;
@ -98,8 +100,8 @@ export default defineComponent({
headers: headers headers: headers
}) })
// make sure the request was successful
var response = await fetch(request) var response = await fetch(request)
// make sure the request was successfull
if (response.ok) { if (response.ok) {
var jsonBody = await response.json() var jsonBody = await response.json()
@ -108,6 +110,8 @@ export default defineComponent({
vehicles.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] }) vehicles.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] })
} }
} else { } else {
alert(`upload failed: ${await response.text()}`)
console.log(await response.text()) console.log(await response.text())
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${await response.text()}`; message.value = `upload failed: ${await response.text()}`;
@ -118,6 +122,8 @@ export default defineComponent({
var save = async () => { var save = async () => {
if (!file.value) { if (!file.value) {
alert("please select a file");
messageType.value = "warning"; messageType.value = "warning";
message.value = "please select a file"; message.value = "please select a file";
return; return;
@ -142,19 +148,25 @@ export default defineComponent({
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
emit("response", result); emit("response", result);
messageType.value = "success"; messageType.value = "success";
message.value = `upload successfull`; message.value = `Upload Successful`;
buttonText.value = 'upload successfull'; buttonText.value = 'Upload Successful';
setTimeout(() => { setTimeout(() => {
buttonText.value = "Upload"; buttonText.value = "Upload";
}, 2000); }, 2000);
} else { } else {
alert(`upload failed: ${await response.statusText}`)
console.error('upload failed: ', await response.statusText); console.error('upload failed: ', await response.statusText);
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${await response.statusText}`; message.value = `upload failed: ${await response.statusText}`;
buttonText.value = "Upload"; buttonText.value = "Upload";
} }
} catch (error) { } catch (error) {
alert(`upload failed: ${error}`)
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${error}`; message.value = `upload failed: ${error}`;
buttonText.value = "Upload"; buttonText.value = "Upload";

View File

@ -96,8 +96,10 @@ export default defineComponent({
} }
console.log(track.value) console.log(track.value)
} else { } else {
alert(`failed to load track meta with error: ${await response.statusText}`)
messageType.value = "error"; messageType.value = "error";
message.value = `"failed to load track meta with error: ${await response.statusText}`; message.value = `failed to load track meta with error: ${await response.statusText}`;
} }
} }
@ -122,6 +124,8 @@ export default defineComponent({
const bounds = geoJsonLayer.getBounds(); const bounds = geoJsonLayer.getBounds();
mapInstance.value.fitBounds(bounds); mapInstance.value.fitBounds(bounds);
} else { } else {
alert("Map or GeoJSON data not available.")
console.error("Map or GeoJSON data not available."); console.error("Map or GeoJSON data not available.");
messageType.value = "error"; messageType.value = "error";
message.value = `"Map or GeoJSON data not available.`; message.value = `"Map or GeoJSON data not available.`;
@ -160,7 +164,9 @@ export default defineComponent({
<tbody> <tbody>
<!-- row 1 --> <!-- row 1 -->
<tr v-if="track.id == 0" class="bg-base-200"> <tr v-if="track.id == 0" class="bg-base-200">
<th>{{ track.name }}</th> <th>
<div class="skeleton h-4 w-full"></div>
</th>
<td> <td>
<div class="skeleton h-4 w-full"></div> <div class="skeleton h-4 w-full"></div>
</td> </td>

View File

@ -25,19 +25,19 @@ export default defineComponent({
<template> <template>
<div v-if="type==='info'" role="alert" class="alert alert-info alert-soft"> <div v-if="type=='info'" role="alert" class="alert alert-info alert-soft">
<span>{{ message }}</span> <span>{{ message }}</span>
<button class="btn btn-soft btn-info" v-on:click="close">Close</button> <button class="btn btn-soft btn-info" v-on:click="close">Close</button>
</div> </div>
<div v-if="type==='success'" role="alert" class="alert alert-success alert-soft"> <div v-if="type=='success'" role="alert" class="alert alert-success alert-soft">
<span>{{ message }}</span> <span>{{ message }}</span>
<button class="btn btn-soft btn-success" v-on:click="close">Close</button> <button class="btn btn-soft btn-success" v-on:click="close">Close</button>
</div> </div>
<div v-if="type==='warning'" role="alert" class="alert alert-warning alert-soft"> <div v-if="type=='warning'" role="alert" class="alert alert-warning alert-soft">
<span>{{ message }}</span> <span>{{ message }}</span>
<button class="btn btn-soft btn-warning" v-on:click="close">Close</button> <button class="btn btn-soft btn-warning" v-on:click="close">Close</button>
</div> </div>
<div v-if="type==='error'" role="alert" class="alert error alert-soft"> <div v-if="type=='error'" role="alert" class="alert error alert-soft">
<span>{{ message }}</span> <span>{{ message }}</span>
<button class="btn btn-soft btn-error" v-on:click="close">Close</button> <button class="btn btn-soft btn-error" v-on:click="close">Close</button>
</div> </div>

View File

@ -45,6 +45,8 @@ export default defineComponent({
console.log(props) console.log(props)
if (!props.routes || props.routes.length <= 0) { if (!props.routes || props.routes.length <= 0) {
alert("no points to show")
messageType.value = "error"; messageType.value = "error";
message.value = `no points to show`; message.value = `no points to show`;
} }
@ -80,6 +82,8 @@ export default defineComponent({
legend.value[driver["id"]] = driver["name"]; legend.value[driver["id"]] = driver["name"];
} }
} else { } else {
alert(`failed to get drivers with error: ${await response.statusText}`)
console.log(await response.text()) console.log(await response.text())
messageType.value = "error"; messageType.value = "error";
message.value = `failed to get drivers with error: ${await response.statusText}`; message.value = `failed to get drivers with error: ${await response.statusText}`;
@ -253,7 +257,30 @@ export default defineComponent({
} }
} }
/**
* Handles mouse clicks to navigate to the specific track page
* @param event MouseEvent triggered on click
*/
const onMouseClick = (event: MouseEvent) => {
if (!canvasRef.value) return;
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];
console.log(point.id)
// Redirect to /track/:id using Vue Router
router.push(`/route/${point.id}`);
}
};
// Add event listeners for mouse actions
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", () => {
@ -278,7 +305,8 @@ export default defineComponent({
<template> <template>
<Message v-if="type != 'None'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'" style="z-index: -1;"></Message> <Message v-if="type != 'None'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'"
style="z-index: -1;"></Message>
<div class="pointcloud-container relative"> <div class="pointcloud-container relative">
<canvas ref="canvasRef"></canvas> <canvas ref="canvasRef"></canvas>
<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">

View File

@ -14,7 +14,7 @@ export default defineComponent({
const driverList: Ref<driver[]> = ref([]) const driverList: Ref<driver[]> = ref([])
// values for UI Information distribution // values for UI Information distribution
const messageType: Ref<string> = ref(""); const messageType: Ref<string> = ref("error");
const message: Ref<string> = ref(""); const message: Ref<string> = ref("");
// handles delete a driver // handles delete a driver
@ -33,13 +33,17 @@ export default defineComponent({
var response = await fetch(request) var response = await fetch(request)
if (!response.ok) { if (!response.ok) {
console.log(await response.text())
alert(`deleting driver failed with error: ${await response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `deleting driver failed with error: ${await response.text()}`; message.value = `deleting driver failed with error: ${await response.text()}`;
} else { } else {
console.log(await response.text())
alert("deleted driver successful")
messageType.value = "success"; messageType.value = "success";
message.value = `deleted driver`; message.value = `deleted driver`;
driverList.value = []
getDrivers();
} }
} }
@ -68,7 +72,8 @@ export default defineComponent({
driverList.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] }) driverList.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"] })
} }
} else { } else {
console.log(await response.text())
alert(`upload failed: ${response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${response.text()}`; message.value = `upload failed: ${response.text()}`;
} }
@ -95,9 +100,10 @@ export default defineComponent({
var jsonBody = await response.json() var jsonBody = await response.json()
driverList.value.push({ id: jsonBody["body"], name: driverName.value }) driverList.value.push({ id: jsonBody["body"], name: driverName.value })
} else { } else {
console.log(await response.text())
alert(`creating driver: ${response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `upload failed: ${response.text()}`; message.value = `creating driver: ${response.text()}`;
} }
} }
@ -116,8 +122,7 @@ export default defineComponent({
<template> <template>
<Message v-if="type == 'none'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'"> <Message v-if="messageType != 'None'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'"></Message>
</Message>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table"> <table class="table">
<!-- head --> <!-- head -->
@ -133,7 +138,9 @@ export default defineComponent({
<td></td> <td></td>
<td><input type="text" placeholder="Driver Name" class="input input-bordered w-full" v-model="driverName" /> <td><input type="text" placeholder="Driver Name" class="input input-bordered w-full" v-model="driverName" />
</td> </td>
<td><a class="btn btn-success w-full" v-on:click="createDriver">Create Driver</a></td> <td><a class="btn btn-success w-full" v-on:click="createDriver"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"/>
</svg> Create Driver</a></td>
</tr> </tr>
<tr v-if="driverList.length == 0"> <tr v-if="driverList.length == 0">
<th> <th>

View File

@ -1,11 +1,12 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, Ref, SetupContext } from 'vue'; import { defineComponent, ref, Ref, SetupContext, watch } from 'vue';
import PointCloud from '../components/pointcloud.vue'; import PointCloud from '../components/pointcloud.vue';
import VueDatePicker from '@vuepic/vue-datepicker'; import VueDatePicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css' import '@vuepic/vue-datepicker/dist/main.css'
import Map from '../components/map.vue'; import Map from '../components/map.vue';
import FileUpload from '../components/fileUpload.vue'; import FileUpload from '../components/fileUpload.vue';
import Message from '../components/message.vue'; import Message from '../components/message.vue';
import { useRoute } from 'vue-router'
type DriverType = { type DriverType = {
id: number id: number
@ -52,16 +53,22 @@ export default defineComponent({
const renderSearchOnMap: Ref<Boolean> = ref(false); const renderSearchOnMap: Ref<Boolean> = ref(false);
const multipleTracks: Ref<Boolean> = ref(false); const multipleTracks: Ref<Boolean> = ref(false);
const trackid: Ref<number> = ref(0); const trackid: Ref<number> = ref(0);
const isLoading:Ref<boolean> = ref(false);
// values for UI Information distribution // values for UI Information distribution
const messageType: Ref<string> = ref(""); const messageType: Ref<string> = ref("");
const message: Ref<string> = ref(""); const message: Ref<string> = ref("");
const route = useRoute()
watch(() => route.params.id, (id) => {
loadTrack(id)
})
const loadTrack = async (id: number) => { const loadTrack = async (id: number) => {
showMap.value = true;
showCloud.value = false; showCloud.value = false;
search.value = false; search.value = false;
showUpload.value = false; showUpload.value = false;
isLoading.value = true;
trackid.value = id trackid.value = id
@ -78,14 +85,20 @@ export default defineComponent({
var response = await fetch(request); var response = await fetch(request);
if (response.ok) { if (response.ok) {
// Wenn die Antwort OK ist, die Daten verarbeiten showMap.value = true;
isLoading.value = false;
mapData.value = await response.json(); mapData.value = await response.json();
} else { } else {
alert(`loading tracks failed: ${await response.text()}`)
isLoading.value = false;
console.log(await response.text()); console.log(await response.text());
messageType.value = "error"; messageType.value = "error";
message.value = `loading tracks failed: ${await response.text()}`; message.value = `loading tracks failed: ${await response.text()}`;
} }
} catch (error) { } catch (error) {
alert(`loading tracks failed with error: ${error}`)
console.error("loading tracks failed with error:", error); console.error("loading tracks failed with error:", error);
messageType.value = "error"; messageType.value = "error";
message.value = `loading tracks failed with error: ${error}`; message.value = `loading tracks failed with error: ${error}`;
@ -114,6 +127,8 @@ export default defineComponent({
message.value = `deleting track failed with error: ${await response.text()}`; message.value = `deleting track failed with error: ${await response.text()}`;
getTracks() getTracks()
} else { } else {
alert("deleted track")
console.log(await response.text()) console.log(await response.text())
messageType.value = "success"; messageType.value = "success";
message.value = `deleted track`; message.value = `deleted track`;
@ -141,6 +156,8 @@ export default defineComponent({
tracks.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"], driver: { name: "", id: 0 }, time: new Date(Date.now()) }) tracks.value.push({ id: jsonBody[i]["id"], name: jsonBody[i]["name"], driver: { name: "", id: 0 }, time: new Date(Date.now()) })
} }
} else { } else {
alert(`loading track list failed with error: ${error}`)
console.log(await response.text()) console.log(await response.text())
messageType.value = "error"; messageType.value = "error";
message.value = `loading track list failed with error: ${error}`; message.value = `loading track list failed with error: ${error}`;
@ -151,7 +168,7 @@ export default defineComponent({
const headers: Headers = new Headers() const headers: Headers = new Headers()
headers.set('Content-Type', 'application/json') headers.set('Content-Type', 'application/json')
headers.set('Accept', 'application/json') headers.set('Accept', 'application/json')
isLoading.value = true;
showMap.value = false; showMap.value = false;
search.value = false; search.value = false;
showCloud.value = true; showCloud.value = true;
@ -190,16 +207,19 @@ export default defineComponent({
time: track["time"] time: track["time"]
}) })
} }
isLoading.value = false;
showCloud.value = true; showCloud.value = true;
} else { } else {
console.log(await response.text())
alert(`searching tracks failed: ${await response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `searching tracks failed: ${await response.text()}`; message.value = `searching tracks failed: ${await response.text()}`;
} }
if (renderSearchOnMap.value) { if (renderSearchOnMap.value) {
multipleTracks.value = true; multipleTracks.value = true;
isLoading.value = true;
showMap.value = true; showMap.value = true;
showCloud.value = false; showCloud.value = false;
@ -208,6 +228,8 @@ export default defineComponent({
await loadTrack(track.id); await loadTrack(track.id);
}, 500) }, 500)
}); });
isLoading.value = false;
} }
} }
@ -232,7 +254,8 @@ export default defineComponent({
message, message,
messageType, messageType,
deleteTrack, deleteTrack,
getTracks getTracks,
isLoading
}; };
}, },
}); });
@ -242,8 +265,8 @@ export default defineComponent({
<template> <template>
<Message v-if="type == 'none'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'"> <Message v-if="type == 'none'" :type="messageType" :message="message" @close="message = ''; messageType = 'None'">
</Message> </Message>
<FileUpload v-if="showUpload" @close="showUpload = false;" @response="getTracks" :drivers="[]" <FileUpload v-if="showUpload" @close="showUpload = false;" @response="tracks=[]; getTracks();" :drivers="[]"
style="position:absolute; top: 30VH; width: 80%;"></FileUpload> style="position:absolute; top: 30VH; width: 60%; left:25%;"></FileUpload>
<div class="grid grid-flow-col auto-cols-max gap-4"> <div class="grid grid-flow-col auto-cols-max gap-4">
<div> <div>
<ul class="menu menu-xs bg-base-200 rounded-lg w-full max-w-xs overscroll-auto"> <ul class="menu menu-xs bg-base-200 rounded-lg w-full max-w-xs overscroll-auto">
@ -258,18 +281,22 @@ export default defineComponent({
<div class="skeleton h-4 w-full"></div> <div class="skeleton h-4 w-full"></div>
</li> </li>
<li v-for="track in tracks"> <li v-for="track in tracks">
<span><a v-on:click="loadTrack(track.id); multipleTracks = false;"> {{ track.name }}</a> <span><a style="line-height: 50px; height:50px;" v-on:click="loadTrack(track.id); multipleTracks = false; showMap=false;"> {{ track.name }}</a>
<button class="btn btn-error" v-on:click="deleteTrack(track.id)"> <button style="width:50px; float:right;" class="btn btn-error" v-on:click="deleteTrack(track.id); tracks=[]; getTracks();">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-trash3-fill" viewBox="0 0 16 16"> class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path <path
d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5" /> d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5" />
</svg> </svg>
</button></span> </button>
</span>
</li> </li>
</ul> </ul>
</div> </div>
<div style="width: 70%; margin-left: 5%;"> <div v-if="isLoading">
<h1 class="title text-center " style="text-align: center;"> map is loading: <span class="loading loading-spinner loading-lg"></span> </h1>
</div>
<div style="width: 65%; margin-left: 5%;">
<PointCloud :routes="searchedTracks" style="border-radius: 10px;" v-if="showCloud && !search"></PointCloud> <PointCloud :routes="searchedTracks" style="border-radius: 10px;" v-if="showCloud && !search"></PointCloud>
</div> </div>
<div v-if="!showMap && !showCloud && search" style="margin-left: 20%; display:flex;"> <div v-if="!showMap && !showCloud && search" style="margin-left: 20%; display:flex;">
@ -290,7 +317,7 @@ export default defineComponent({
</div> </div>
<div v-if="showMap && !search && !showCloud"> <div v-if="showMap && !search && !showCloud">
<Map :track="trackid" :multiple="multipleTracks" :geoJsonData="mapData" <Map :track="trackid" :multiple="multipleTracks" :geoJsonData="mapData"
style="width: 68vw; margin-left: 10%; border-radius: 10px; border: 1px solid #95a5a6;"></Map> style="width: 68vw; margin-left: 15%; border-radius: 10px; border: 1px solid #95a5a6;"></Map>
</div> </div>
</div> </div>
</template> </template>

View File

@ -35,11 +35,13 @@ export default defineComponent({
var response = await fetch(request) var response = await fetch(request)
if (!response.ok) { if (!response.ok) {
console.log(await response.text())
alert(`deleting vehicle failed with error: ${await response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `deleting vehicle failed with error: ${await response.text()}`; message.value = `deleting vehicle failed with error: ${await response.text()}`;
} else { } else {
console.log(await response.text())
alert("delete vehicle")
messageType.value = "success"; messageType.value = "success";
message.value = `deleted vehicle`; message.value = `deleted vehicle`;
} }
@ -72,7 +74,8 @@ export default defineComponent({
vehicleList.value.push({ id: vehicle["id"], name: vehicle["name"], licenseplate: plate }) vehicleList.value.push({ id: vehicle["id"], name: vehicle["name"], licenseplate: plate })
} }
} else { } else {
console.log(await response.text())
alert(`loading vehicles failed with error: ${await response.text()}`)
messageType.value = "error"; messageType.value = "error";
message.value = `loading vehicles failed with error: ${await response.text()}`; message.value = `loading vehicles failed with error: ${await response.text()}`;
} }
@ -99,7 +102,8 @@ export default defineComponent({
var jsonBody = await response.json() var jsonBody = await response.json()
vehicleList.value.push({ id: jsonBody["body"], name: vehicleName.value, licenseplate: "N/A" }) vehicleList.value.push({ id: jsonBody["body"], name: vehicleName.value, licenseplate: "N/A" })
} else { } else {
console.log(await response.text())
alert(`creating vehicle failed with error: ${error}`)
messageType.value = "error"; messageType.value = "error";
message.value = `creating vehicle failed with error: ${error}`; message.value = `creating vehicle failed with error: ${error}`;
} }
@ -140,7 +144,9 @@ export default defineComponent({
v-model="vehicleName" /></td> v-model="vehicleName" /></td>
<td><input type="text" placeholder="License Plate" class="input input-bordered w-full " <td><input type="text" placeholder="License Plate" class="input input-bordered w-full "
v-model="licensePlate" /></td> v-model="licensePlate" /></td>
<td><a class="btn btn-success w-full" v-on:click="createVehicle">Create Vehicle</a></td> <td><a class="btn btn-success w-full" v-on:click="createVehicle"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"/>
</svg> Create Vehicle</a></td>
</tr> </tr>
<tr v-if="vehicleList.length == 0"> <tr v-if="vehicleList.length == 0">
<th> <th>