From dea6fb741bf4345da7fc9b5d07fe6ddb53a93002 Mon Sep 17 00:00:00 2001 From: Cfp Date: Wed, 25 Jun 2025 14:02:00 +0200 Subject: [PATCH] feat: add experimental landmark-based voice guidance --- src/lib/services/navigation/VoiceGuidance.ts | 83 +++++++++++++++++++ src/lib/services/navigation/routing.svelte.ts | 7 +- 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/lib/services/navigation/VoiceGuidance.ts diff --git a/src/lib/services/navigation/VoiceGuidance.ts b/src/lib/services/navigation/VoiceGuidance.ts new file mode 100644 index 0000000..e1e1542 --- /dev/null +++ b/src/lib/services/navigation/VoiceGuidance.ts @@ -0,0 +1,83 @@ +import { OVERPASS_SERVER } from "../hosts"; +import type { OverpassResult } from "../Overpass"; + +export async function generateVoiceGuidance(maneuver: Maneuver, shape: WorldLocation[]): Promise { + const landmarks = await findNearbyLandmarks(shape[maneuver.begin_shape_index]); + if(landmarks.length == 0) { + return maneuver.verbal_pre_transition_instruction; + } + console.log("Original instruction:", maneuver.verbal_pre_transition_instruction); + return `Hinter ${landmarks[0].tags.name}, ${typeToName(maneuver.type)} auf ${(maneuver.street_names || []).join(", ")}.`; +} + +function typeToName(type: number) { + switch (type) { + case 4: + return "Ziel erreicht"; + case 5: + return "Ziel auf der rechten Seite erreicht"; + case 6: + return "Ziel auf der linken Seite erreicht"; + case 8: + return "Weiterfahren"; + case 9: + return "Leicht rechts abbiegen"; + case 10: + return "Rechts abbiegen"; + case 11: + return "Scharf rechts abbiegen"; + case 12: + case 13: + return "Umdrehen"; + case 14: + return "Scharf links abbiegen"; + case 15: + return "Links abbiegen"; + case 16: + return "Leicht links abbiegen"; + case 20: + return "Rechts abfahren"; + case 21: + return "Links abfahren"; + case 22: + return "Geradeaus weiterfahren"; + case 23: + return "Rechts halten"; + case 24: + return "Links halten"; + case 25: + return "Einordnen"; + case 26: + case 27: + return "Kreisverkehr"; + case 37: + return "Rechts einordnen"; + case 38: + return "Links einordnen"; + default: + return "Unbekannt"; + } +} + +export async function findNearbyLandmarks(location: WorldLocation) { + const radius = 100; + const lat = location.lat; + const lon = location.lon; + const res = await fetch(OVERPASS_SERVER, { + method: "POST", + body: `[out:json]; + ( + node(around:${radius}, ${lat}, ${lon})["tourism"="artwork"]["name"]; + way(around:${radius}, ${lat}, ${lon})["tourism"="artwork"]["name"]; + node(around:${radius}, ${lat}, ${lon})["shop"]["name"]; + way(around:${radius}, ${lat}, ${lon})["shop"]["name"]; + ); + out center tags;`, + }).then((res) => res.json() as Promise); + // Sort by distance to the location + return res.elements.sort((a, b) => { + const distA = Math.sqrt(Math.pow(a.lat! - lat, 2) + Math.pow(a.lon! - lon, 2)); + const distB = Math.sqrt(Math.pow(b.lat! - lat, 2) + Math.pow(b.lon! - lon, 2)); + return distA - distB; + }); +} diff --git a/src/lib/services/navigation/routing.svelte.ts b/src/lib/services/navigation/routing.svelte.ts index 8c56b82..9816cf5 100644 --- a/src/lib/services/navigation/routing.svelte.ts +++ b/src/lib/services/navigation/routing.svelte.ts @@ -3,6 +3,7 @@ import { map } from "$lib/components/lnv/map.svelte"; import say from "./TTS"; import type { ValhallaRequest } from "./ValhallaRequest"; import type { LngLatBoundsLike } from "maplibre-gl"; +import { generateVoiceGuidance } from "./VoiceGuidance"; export const routing = $state({ geojson: { @@ -192,11 +193,13 @@ async function tickRoute() { ); if (distanceToEnd <= verbalDistance) { hasAnnouncedPreInstruction = true; + const instruction = await generateVoiceGuidance(currentManeuver, polyline); console.log( "[Verbal instruction] ", - currentManeuver.verbal_pre_transition_instruction, + // currentManeuver.verbal_pre_transition_instruction, + instruction ); - say(currentManeuver.verbal_pre_transition_instruction); + say(instruction); } }