import axios from "axios"; const TOMTOM_API_KEY = "MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB"; export interface LatLng { latitude: number; longitude: number; } export interface RouteInfo { distance: string; duration: string; distanceMeters: number; durationSeconds: number; coordinates: LatLng[]; } export interface NavigationInstruction { instruction: string; distance: string; maneuver: string; streetName?: string; isActive: boolean; } export const maneuverTranslations: Record = { TURN_LEFT: "Tournez à gauche", TURN_RIGHT: "Tournez à droite", TURN_SLIGHT_LEFT: "Tournez légèrement à gauche", TURN_SLIGHT_RIGHT: "Tournez légèrement à droite", TURN_SHARP_LEFT: "Tournez fortement à gauche", TURN_SHARP_RIGHT: "Tournez fortement à droite", KEEP_LEFT: "Restez à gauche", KEEP_RIGHT: "Restez à droite", STRAIGHT: "Continuez tout droit", ENTER_ROUNDABOUT: "Entrez dans le rond-point", EXIT_ROUNDABOUT: "Sortez du rond-point", MOTORWAY_ENTER: "Entrez sur l'autoroute", MOTORWAY_EXIT: "Sortez de l'autoroute", ARRIVE: "Vous êtes arrivé", ARRIVE_LEFT: "Destination à gauche", ARRIVE_RIGHT: "Destination à droite", DEPART: "Départ", U_TURN: "Faites demi-tour", FOLLOW: "Suivez", WAYPOINT_REACHED: "Point de passage atteint", }; export const maneuverIcons: Record = { TURN_LEFT: "arrow-back", TURN_RIGHT: "arrow-forward", TURN_SLIGHT_LEFT: "arrow-up", TURN_SLIGHT_RIGHT: "arrow-up", TURN_SHARP_LEFT: "return-down-back", TURN_SHARP_RIGHT: "return-down-forward", KEEP_LEFT: "arrow-back", KEEP_RIGHT: "arrow-forward", STRAIGHT: "arrow-up", ENTER_ROUNDABOUT: "sync", EXIT_ROUNDABOUT: "arrow-forward", MOTORWAY_ENTER: "speedometer", MOTORWAY_EXIT: "exit-outline", ARRIVE: "flag", ARRIVE_LEFT: "flag", ARRIVE_RIGHT: "flag", DEPART: "car", U_TURN: "return-up-back", FOLLOW: "arrow-up", WAYPOINT_REACHED: "location", DEFAULT: "arrow-up", }; // ---- Helpers ---- function formatDistance(meters: number): string { if (meters < 1000) return `${Math.round(meters)} m`; return `${(meters / 1000).toFixed(1)} km`; } export async function geocodeAddress(address: string): Promise { try { const url = `https://api.tomtom.com/search/2/geocode/${encodeURIComponent(address)}.json?key=${TOMTOM_API_KEY}&limit=1`; const { data } = await axios.get(url); if (data.results && data.results.length > 0) { const pos = data.results[0].position; if (pos?.lat != null && pos?.lon != null) { return { latitude: pos.lat, longitude: pos.lon }; } } } catch { /* silent */ } return null; } export async function calculateRoute( origin: LatLng, destination: LatLng, ): Promise<{ route: RouteInfo; instructions: NavigationInstruction[] } | null> { try { const start = `${origin.latitude},${origin.longitude}`; const end = `${destination.latitude},${destination.longitude}`; const url = `https://api.tomtom.com/routing/1/calculateRoute/${start}:${end}/json?key=${TOMTOM_API_KEY}&instructionsType=text&language=fr-FR&traffic=true&travelMode=car`; const { data } = await axios.get(url); if (!data.routes || data.routes.length === 0) return null; const route = data.routes[0]; const summary = route.summary; // Extract polyline coordinates const coordinates: LatLng[] = []; if (route.legs) { for (const leg of route.legs) { if (leg.points) { for (const pt of leg.points) { coordinates.push({ latitude: pt.latitude, longitude: pt.longitude, }); } } } } // Fallback: straight line if (coordinates.length === 0) { coordinates.push(origin, destination); } const distanceKm = (summary.lengthInMeters / 1000).toFixed(1); const durationMin = Math.round(summary.travelTimeInSeconds / 60); const routeInfo: RouteInfo = { distance: `${distanceKm} km`, duration: `${durationMin} min`, distanceMeters: summary.lengthInMeters, durationSeconds: summary.travelTimeInSeconds, coordinates, }; // Extract instructions const instructions: NavigationInstruction[] = []; if (route.guidance?.instructions) { for (let i = 0; i < route.guidance.instructions.length; i++) { const inst = route.guidance.instructions[i]; const maneuver = inst.maneuver || "STRAIGHT"; const text = inst.message || maneuverTranslations[maneuver] || "Continuez"; instructions.push({ instruction: text, distance: formatDistance(inst.routeOffsetInMeters || 0), maneuver, streetName: inst.street, isActive: i === 0, }); } } if (instructions.length === 0) { instructions.push({ instruction: "Dirigez-vous vers votre destination", distance: formatDistance(summary.lengthInMeters || 0), maneuver: "DEPART", isActive: true, }); instructions.push({ instruction: "Vous êtes arrivé à destination", distance: "0 m", maneuver: "ARRIVE", isActive: false, }); } return { route: routeInfo, instructions }; } catch { /* silent */ } return null; } // ---- Bearing calculation ---- export function calculateBearing(from: LatLng, to: LatLng): number { const φ1 = (from.latitude * Math.PI) / 180; const φ2 = (to.latitude * Math.PI) / 180; const Δλ = ((to.longitude - from.longitude) * Math.PI) / 180; const y = Math.sin(Δλ) * Math.cos(φ2); const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); return ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360; }