feat: integrate persona D (recycling guide) and routes modules

This commit is contained in:
Alan Alonso
2026-05-23 01:00:02 -06:00
parent a38ca14f38
commit 6d1845c09d
21 changed files with 2074 additions and 99 deletions

View File

@@ -0,0 +1,111 @@
// lib/features/routes/data/repositories/routes_repository.dart
import '../datasources/routes_local_datasource.dart';
import '../../domain/entities/route_entities.dart';
import '../../domain/entities/haversine.dart';
class RoutesRepository {
final RoutesLocalDatasource _datasource;
RoutesRepository({RoutesLocalDatasource? datasource})
: _datasource = datasource ?? RoutesLocalDatasource();
/// Obtiene todas las rutas disponibles
Future<List<TruckRoute>> obtenerRutas() => _datasource.cargarRutas();
/// Obtiene una ruta por su ID
Future<TruckRoute?> obtenerRutaPorId(String routeId) async {
final rutas = await obtenerRutas();
try {
return rutas.firstWhere((r) => r.routeId == routeId);
} catch (_) {
return null;
}
}
/// Asigna la ruta más cercana a un domicilio usando Haversine.
/// Cero Google Maps — motor espacial propio.
Future<RouteAssignmentResult> asignarRuta(
double userLat,
double userLng,
) async {
final centroides = await _datasource.generarCentroides();
return Haversine.findNearestRoute(userLat, userLng, centroides);
}
/// Calcula ETA en minutos desde la posición actual del camión
/// hasta el punto más cercano al domicilio del usuario.
Future<ETAResult> calcularETA(
String routeId,
double userLat,
double userLng,
) async {
final ruta = await obtenerRutaPorId(routeId);
if (ruta == null) throw Exception('Ruta $routeId no encontrada');
// Encontrar posición más cercana al usuario en la ruta
double minDist = double.infinity;
int nearestIndex = 0;
for (int i = 0; i < ruta.positions.length - 1; i++) {
final pos = ruta.positions[i];
final d = Haversine.distanceKm(userLat, userLng, pos.lat, pos.lng);
if (d < minDist) {
minDist = d;
nearestIndex = i;
}
}
final posicionCercana = ruta.positions[nearestIndex];
final ahora = DateTime.now();
// Simular cuándo llegará el camión a esa posición
final horaLlegada = posicionCercana.timestamp;
final etaMinutos = horaLlegada.difference(ahora).inMinutes;
return ETAResult(
routeId: routeId,
routeName: ruta.name,
etaMinutos: etaMinutos.clamp(0, 999),
distanciaKm: minDist,
posicionActual: posicionCercana,
ventanaInicio: _formatHora(horaLlegada),
ventanaFin: _formatHora(horaLlegada.add(const Duration(minutes: 15))),
);
}
String _formatHora(DateTime dt) {
final hora = dt.hour > 12 ? dt.hour - 12 : dt.hour;
final minutos = dt.minute.toString().padLeft(2, '0');
final periodo = dt.hour >= 12 ? 'pm' : 'am';
return '$hora:$minutos $periodo';
}
}
class ETAResult {
final String routeId;
final String routeName;
final int etaMinutos;
final double distanciaKm;
final RoutePosition posicionActual;
final String ventanaInicio;
final String ventanaFin;
const ETAResult({
required this.routeId,
required this.routeName,
required this.etaMinutos,
required this.distanciaKm,
required this.posicionActual,
required this.ventanaInicio,
required this.ventanaFin,
});
String get ventana => '$ventanaInicio $ventanaFin';
String get mensaje {
if (etaMinutos <= 0) return 'El camión está pasando ahora. ¡Saca tu basura!';
if (etaMinutos <= 10) return 'El camión llega en ~$etaMinutos min. ¡Saca tu basura!';
return 'El camión llega entre $ventana.';
}
}