147 lines
4.2 KiB
Dart
147 lines
4.2 KiB
Dart
import '../map/map_provider_interface.dart';
|
|
import 'route_models.dart';
|
|
|
|
/// Result of a place/location search query.
|
|
class PlaceSearchResult {
|
|
final String name;
|
|
final String address;
|
|
final double latitude;
|
|
final double longitude;
|
|
final String? placeId;
|
|
|
|
const PlaceSearchResult({
|
|
required this.name,
|
|
this.address = '',
|
|
required this.latitude,
|
|
required this.longitude,
|
|
this.placeId,
|
|
});
|
|
|
|
AppLatLng get latLng => AppLatLng(latitude, longitude);
|
|
}
|
|
|
|
/// Result of calculating a route along ordered stops.
|
|
class RouteCalculationResult {
|
|
final double straightLineDistanceMeters;
|
|
final double? estimatedDistanceMeters;
|
|
final int? estimatedDurationSeconds;
|
|
final List<AppLatLng> geometry;
|
|
final bool isStraightLine;
|
|
final String providerName;
|
|
|
|
const RouteCalculationResult({
|
|
required this.straightLineDistanceMeters,
|
|
this.estimatedDistanceMeters,
|
|
this.estimatedDurationSeconds,
|
|
required this.geometry,
|
|
required this.isStraightLine,
|
|
required this.providerName,
|
|
});
|
|
|
|
/// Alias for geometry points.
|
|
List<AppLatLng> get points => geometry;
|
|
|
|
/// Total straight-line distance in meters.
|
|
double get totalDistanceMeters => straightLineDistanceMeters;
|
|
|
|
/// Alias for isStraightLine.
|
|
bool get isHonestStraightLine => isStraightLine;
|
|
|
|
/// Total distance in miles.
|
|
double get distanceMiles {
|
|
final meters = estimatedDistanceMeters ?? straightLineDistanceMeters;
|
|
return meters * 0.000621371;
|
|
}
|
|
|
|
/// Truthful distance description.
|
|
String get distanceLabel {
|
|
if (estimatedDistanceMeters != null) {
|
|
return '${distanceMiles.toStringAsFixed(1)} mi (Road Est.)';
|
|
}
|
|
return '${distanceMiles.toStringAsFixed(1)} mi (Straight-line)';
|
|
}
|
|
}
|
|
|
|
/// Abstract provider interface for calculating routes and searching places.
|
|
abstract class RouteProvider {
|
|
String get name;
|
|
|
|
/// Calculates route geometry and metrics between ordered stops.
|
|
Future<RouteCalculationResult> calculateRoute(List<RouteStop> stops);
|
|
|
|
/// Searches for places by textual query.
|
|
Future<List<PlaceSearchResult>> searchPlaces(
|
|
String query, {
|
|
AppLatLng? nearLocation,
|
|
});
|
|
}
|
|
|
|
/// Default geodesic straight-line preview implementation.
|
|
///
|
|
/// Truthfully connects stops with direct segments and calculates geodesic
|
|
/// distance without fabricating road paths or speculative turn ETAs.
|
|
class StraightLineRouteProvider implements RouteProvider {
|
|
const StraightLineRouteProvider();
|
|
|
|
@override
|
|
String get name => 'Straight-line Preview';
|
|
|
|
@override
|
|
Future<RouteCalculationResult> calculateRoute(List<RouteStop> stops) async {
|
|
final ordered = List<RouteStop>.from(stops);
|
|
ordered.sort((a, b) => a.order.compareTo(b.order));
|
|
|
|
final distanceMeters = MotorSignalRoute.calculateTotalDistanceMeters(ordered);
|
|
final points = ordered.map((s) => s.latLng).toList();
|
|
|
|
return RouteCalculationResult(
|
|
straightLineDistanceMeters: distanceMeters,
|
|
estimatedDistanceMeters: null,
|
|
estimatedDurationSeconds: null,
|
|
geometry: points,
|
|
isStraightLine: true,
|
|
providerName: name,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<List<PlaceSearchResult>> searchPlaces(
|
|
String query, {
|
|
AppLatLng? nearLocation,
|
|
}) async {
|
|
final clean = query.trim();
|
|
if (clean.isEmpty) return const [];
|
|
|
|
// Support direct coordinate input (e.g. "34.5378, -83.9849")
|
|
final parts = clean.split(RegExp(r'[,\s]+'));
|
|
if (parts.length == 2) {
|
|
final lat = double.tryParse(parts[0]);
|
|
final lng = double.tryParse(parts[1]);
|
|
if (lat != null && lng != null && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
|
return [
|
|
PlaceSearchResult(
|
|
name: 'Coordinate Pin',
|
|
address: '${lat.toStringAsFixed(5)}, ${lng.toStringAsFixed(5)}',
|
|
latitude: lat,
|
|
longitude: lng,
|
|
),
|
|
];
|
|
}
|
|
}
|
|
|
|
// Default fallback: return query name centered at reference location if provided
|
|
if (nearLocation != null) {
|
|
return [
|
|
PlaceSearchResult(
|
|
name: clean,
|
|
address: 'Near Current Map Location',
|
|
latitude: nearLocation.latitude,
|
|
longitude: nearLocation.longitude,
|
|
),
|
|
];
|
|
}
|
|
|
|
return const [];
|
|
}
|
|
}
|