- routing.ts: add a 'transit' travel mode via Transitous (MOTIS 2 API, keyless, open CORS), with a local precision-7 polyline decoder and per-leg parsing. Road modes stay on keyless OSRM; moto remains ≈car and honestly labelled. - RouteDialog: transit draws each leg (walk dashed grey, transit coloured by mode) and summarises depart→arrive, duration, transfers and line chips, with Transitous/OpenStreetMap attribution. - directions.ts: opt-in 'Open in' handoff to Google / Apple / OpenStreetMap (+ Organic Maps via geo: on Android), mode-aware. In-app routing stays the default; the description notes transit shares the trip with Transitous. - No scooter/moped profile (deliberate).
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
/**
|
|
* "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<TravelMode, string> = {
|
|
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');
|
|
}
|