/** * "Open in maps" handoff — deep links to external navigation apps. * ───────────────────────────────────────────────────────────────────────────── * The in-app RouteDialog deliberately keeps routing on our own map (OSRM + * Transitous) so nothing goes to Google by default. This module is the *opt-in* * escape hatch: build links so a user who wants real turn-by-turn / offline * navigation can hand the trip to another app. * * Providers are chosen for reliable, keyless web URLs that also deep-link into * their native apps: * • Google Maps — supports driving / walking / bicycling / transit * • Apple Maps — iOS/macOS (opens the web version elsewhere) * • OpenStreetMap — car / bike / foot (no transit) * • Organic Maps — Android `geo:` intent (privacy-first, offline) * * Handing off *does* share the trip with that provider — hence opt-in. */ import { Capacitor } from '@capacitor/core'; import type { TravelMode } from '@/lib/routing'; export interface DirectionsLink { id: string; label: string; url: string; } interface Point { lat: number; lng: number; } const GOOGLE_MODE: Record = { foot: 'walking', bike: 'bicycling', moto: 'driving', car: 'driving', transit: 'transit', }; function googleUrl(from: Point, to: Point, mode: TravelMode): string { return `https://www.google.com/maps/dir/?api=1&origin=${from.lat},${from.lng}&destination=${to.lat},${to.lng}&travelmode=${GOOGLE_MODE[mode]}`; } /** Apple Maps "dirflg": w = walking, r = public transit, d = driving. */ function appleFlag(mode: TravelMode): string { if (mode === 'foot') return 'w'; if (mode === 'transit') return 'r'; return 'd'; // car / moto / bike } function appleUrl(from: Point, to: Point, mode: TravelMode): string { return `https://maps.apple.com/?saddr=${from.lat},${from.lng}&daddr=${to.lat},${to.lng}&dirflg=${appleFlag(mode)}`; } /** OSM has no public-transport routing; returns null for transit. */ function osmEngine(mode: TravelMode): string | null { if (mode === 'foot') return 'fossgis_osrm_foot'; if (mode === 'bike') return 'fossgis_osrm_bike'; if (mode === 'transit') return null; return 'fossgis_osrm_car'; // car / moto } function osmUrl(from: Point, to: Point, mode: TravelMode): string | null { const engine = osmEngine(mode); if (!engine) return null; return `https://www.openstreetmap.org/directions?engine=${engine}&route=${from.lat},${from.lng};${to.lat},${to.lng}`; } /** `geo:` intent — Android hands it to Organic Maps (or the default map app). */ function organicUrl(to: Point, label: string): string { return `geo:${to.lat},${to.lng}?q=${to.lat},${to.lng}(${encodeURIComponent(label)})`; } export function buildDirectionsLinks( from: Point, to: Point, mode: TravelMode, destinationLabel: string, ): DirectionsLink[] { const links: DirectionsLink[] = [ { id: 'google', label: 'Google Maps', url: googleUrl(from, to, mode) }, { id: 'apple', label: 'Apple Maps', url: appleUrl(from, to, mode) }, ]; const osm = osmUrl(from, to, mode); if (osm) links.push({ id: 'osm', label: 'OpenStreetMap', url: osm }); if (Capacitor.getPlatform() === 'android') { links.push({ id: 'organic', label: 'Organic Maps', url: organicUrl(to, destinationLabel) }); } return links; } /** * Open an external URL. Capacitor intercepts the `_system` target and hands the * URL to the OS (system browser / registered app); on the web it's a new tab. */ export function openExternal(url: string): void { window.open(url, '_system'); }