173 lines
4.4 KiB
Dart
173 lines
4.4 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../../core/api/api_client.dart';
|
|
|
|
/// A single route from the OSRM response.
|
|
class RouteData {
|
|
final double distance; // meters
|
|
final double duration; // seconds
|
|
final List<List<double>> geometry; // [[lon, lat], ...]
|
|
final List<RouteLeg> legs;
|
|
|
|
RouteData({
|
|
required this.distance,
|
|
required this.duration,
|
|
required this.geometry,
|
|
required this.legs,
|
|
});
|
|
|
|
String get distanceFormatted {
|
|
if (distance < 1000) {
|
|
return '${distance.round()} m';
|
|
}
|
|
return '${(distance / 1000).toStringAsFixed(1)} km';
|
|
}
|
|
|
|
String get durationFormatted {
|
|
final minutes = (duration / 60).round();
|
|
if (minutes < 60) {
|
|
return '$minutes min';
|
|
}
|
|
final hours = minutes ~/ 60;
|
|
final remainingMin = minutes % 60;
|
|
return '${hours}h ${remainingMin}min';
|
|
}
|
|
|
|
factory RouteData.fromJson(Map<String, dynamic> json) {
|
|
final geom = json['geometry'] as Map<String, dynamic>;
|
|
final coords = (geom['coordinates'] as List)
|
|
.map((c) => [(c[0] as num).toDouble(), (c[1] as num).toDouble()])
|
|
.toList();
|
|
|
|
final legs = (json['legs'] as List)
|
|
.map((l) => RouteLeg.fromJson(l as Map<String, dynamic>))
|
|
.toList();
|
|
|
|
return RouteData(
|
|
distance: (json['distance'] as num).toDouble(),
|
|
duration: (json['duration'] as num).toDouble(),
|
|
geometry: coords,
|
|
legs: legs,
|
|
);
|
|
}
|
|
}
|
|
|
|
class RouteLeg {
|
|
final double distance;
|
|
final double duration;
|
|
final String summary;
|
|
final List<RouteStep> steps;
|
|
|
|
RouteLeg({
|
|
required this.distance,
|
|
required this.duration,
|
|
required this.summary,
|
|
required this.steps,
|
|
});
|
|
|
|
factory RouteLeg.fromJson(Map<String, dynamic> json) {
|
|
final steps = (json['steps'] as List?)
|
|
?.map((s) => RouteStep.fromJson(s as Map<String, dynamic>))
|
|
.toList() ??
|
|
[];
|
|
return RouteLeg(
|
|
distance: (json['distance'] as num).toDouble(),
|
|
duration: (json['duration'] as num).toDouble(),
|
|
summary: json['summary'] as String? ?? '',
|
|
steps: steps,
|
|
);
|
|
}
|
|
}
|
|
|
|
class RouteStep {
|
|
final double distance;
|
|
final double duration;
|
|
final String name;
|
|
final Maneuver maneuver;
|
|
|
|
RouteStep({
|
|
required this.distance,
|
|
required this.duration,
|
|
required this.name,
|
|
required this.maneuver,
|
|
});
|
|
|
|
factory RouteStep.fromJson(Map<String, dynamic> json) {
|
|
return RouteStep(
|
|
distance: (json['distance'] as num).toDouble(),
|
|
duration: (json['duration'] as num).toDouble(),
|
|
name: json['name'] as String? ?? '',
|
|
maneuver:
|
|
Maneuver.fromJson(json['maneuver'] as Map<String, dynamic>),
|
|
);
|
|
}
|
|
}
|
|
|
|
class Maneuver {
|
|
final String type;
|
|
final String? modifier;
|
|
final List<double> location; // [lon, lat]
|
|
final String instruction;
|
|
|
|
Maneuver({
|
|
required this.type,
|
|
this.modifier,
|
|
required this.location,
|
|
required this.instruction,
|
|
});
|
|
|
|
factory Maneuver.fromJson(Map<String, dynamic> json) {
|
|
final loc = (json['location'] as List)
|
|
.map((e) => (e as num).toDouble())
|
|
.toList();
|
|
return Maneuver(
|
|
type: json['type'] as String,
|
|
modifier: json['modifier'] as String?,
|
|
location: loc,
|
|
instruction: json['instruction'] as String? ?? '',
|
|
);
|
|
}
|
|
}
|
|
|
|
class RoutingRepository {
|
|
final ApiClient _apiClient;
|
|
|
|
RoutingRepository(this._apiClient);
|
|
|
|
/// Calculate a route via GET /api/route/{profile}/{coordinates}.
|
|
/// [origin] and [destination] are [lat, lon] pairs.
|
|
Future<List<RouteData>> getRoute({
|
|
required String profile,
|
|
required double originLat,
|
|
required double originLon,
|
|
required double destLat,
|
|
required double destLon,
|
|
}) async {
|
|
// OSRM coordinates format: lon,lat;lon,lat
|
|
final coordinates =
|
|
'$originLon,$originLat;$destLon,$destLat';
|
|
|
|
final data = await _apiClient.get(
|
|
'/api/route/$profile/$coordinates',
|
|
queryParameters: {
|
|
'alternatives': 2,
|
|
'steps': true,
|
|
'geometries': 'geojson',
|
|
},
|
|
);
|
|
|
|
final code = data['code'] as String?;
|
|
if (code != 'Ok') {
|
|
throw Exception('Routing failed: $code');
|
|
}
|
|
|
|
final routes = (data['routes'] as List)
|
|
.map((r) => RouteData.fromJson(r as Map<String, dynamic>))
|
|
.toList();
|
|
|
|
return routes;
|
|
}
|
|
}
|
|
|
|
final routingRepositoryProvider = Provider<RoutingRepository>((ref) {
|
|
return RoutingRepository(ref.watch(apiClientProvider));
|
|
});
|