import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:latlong2/latlong.dart' show LatLng; // ── Routing ───────────────────────────────────────────────────────────────── /// A driving route between two points, from OSRM's public router. /// /// Replaces the Directions API call, which needed the revoked key. Same shape /// of answer — an ordered list of coordinates — so the callers' route drawing, /// distance table and camera fitting are untouched. class MilerRouter { static const String _host = 'https://router.project-osrm.org'; /// Sent with every request. OSRM's demo server, like OSM's tile server, /// blocks anonymous clients first. static const String _agent = 'com.doormile.partner'; /// The road geometry from [start] to [end], or an empty list. /// /// Never throws: a route screen with no line is a degraded screen, but a /// route screen that crashed is a rider standing in the street with nothing. /// Callers fall back to the direct line on an empty answer. static Future> route(LatLng start, LatLng end) async { final uri = Uri.parse( '$_host/route/v1/driving/' '${start.longitude},${start.latitude};${end.longitude},${end.latitude}' '?overview=full&geometries=geojson', ); try { final res = await http .get(uri, headers: const {'User-Agent': _agent}) .timeout(const Duration(seconds: 8)); if (res.statusCode != 200) { debugPrint( '[MAP] OSRM ${res.statusCode} — falling back to a direct line.', ); return const []; } final body = jsonDecode(res.body) as Map; if (body['code'] != 'Ok') { debugPrint('[MAP] OSRM said ${body['code']} — direct line instead.'); return const []; } final routes = body['routes'] as List?; if (routes == null || routes.isEmpty) return const []; final coords = (routes.first as Map)['geometry']['coordinates'] as List; // GeoJSON is [longitude, latitude] — the opposite order to everything // else in this app, and a silent way to end up routing through the sea. return [ for (final c in coords) LatLng((c[1] as num).toDouble(), (c[0] as num).toDouble()), ]; } catch (e) { debugPrint('[MAP] OSRM unreachable ($e) — direct line instead.'); return const []; } } /// Road distance in metres between two points, or null when the router /// cannot answer. /// /// Replaces a direct call to the Google Directions API in /// `PickupsController`, which was reading the same revoked key and therefore /// silently falling back to straight-line distance on every trip — the number /// riders are *paid* on. static Future roadDistanceMetres(LatLng start, LatLng end) async { final uri = Uri.parse( '$_host/route/v1/driving/' '${start.longitude},${start.latitude};${end.longitude},${end.latitude}' '?overview=false', ); try { final res = await http .get(uri, headers: const {'User-Agent': _agent}) .timeout(const Duration(seconds: 6)); if (res.statusCode != 200) return null; final body = jsonDecode(res.body) as Map; if (body['code'] != 'Ok') return null; final routes = body['routes'] as List?; if (routes == null || routes.isEmpty) return null; final d = (routes.first as Map)['distance']; return d is num ? d.toDouble() : null; } catch (e) { debugPrint('[ROUTE] OSRM distance unavailable ($e)'); return null; } } }