Actualizacion del programa
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
@@ -16,17 +17,28 @@ List<LatLng> _smooth(List<LatLng> pts) {
|
||||
res.add(p1);
|
||||
for (int j = 1; j <= 8; j++) {
|
||||
final t = j / 9.0;
|
||||
res.add(LatLng(_cr(p0.latitude, p1.latitude, p2.latitude, p3.latitude, t),
|
||||
_cr(p0.longitude, p1.longitude, p2.longitude, p3.longitude, t)));
|
||||
res.add(LatLng(_cr(p0.latitude,p1.latitude,p2.latitude,p3.latitude,t),
|
||||
_cr(p0.longitude,p1.longitude,p2.longitude,p3.longitude,t)));
|
||||
}
|
||||
}
|
||||
res.add(pts.last);
|
||||
return res;
|
||||
}
|
||||
|
||||
double _cr(double a, double b, double c, double d, double t) => 0.5 * (
|
||||
(2*b) + (-a+c)*t + (2*a-5*b+4*c-d)*t*t + (-a+3*b-3*c+d)*t*t*t);
|
||||
double _cr(double a,double b,double c,double d,double t) => 0.5*(
|
||||
(2*b)+(-a+c)*t+(2*a-5*b+4*c-d)*t*t+(-a+3*b-3*c+d)*t*t*t);
|
||||
|
||||
// Calcular bearing entre dos puntos
|
||||
double _bearing(LatLng from, LatLng to) {
|
||||
final lat1 = from.latitude * math.pi / 180;
|
||||
final lat2 = to.latitude * math.pi / 180;
|
||||
final dLng = (to.longitude - from.longitude) * math.pi / 180;
|
||||
final y = math.sin(dLng) * math.cos(lat2);
|
||||
final x = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(dLng);
|
||||
return (math.atan2(y, x) * 180 / math.pi + 360) % 360;
|
||||
}
|
||||
|
||||
// ── Mapa ciudadano ────────────────────────────────────────────────────────
|
||||
class RouteMapWidget extends StatelessWidget {
|
||||
final RouteModel route;
|
||||
final RouteSimulatorService simulator;
|
||||
@@ -43,44 +55,65 @@ class RouteMapWidget extends StatelessWidget {
|
||||
final gps = simulator.isGpsActive(route.routeId);
|
||||
final all = _smooth(route.polylinePoints);
|
||||
final done = posIdx > 0
|
||||
? _smooth(route.positions.take(posIdx+1).map((p) => p.latLng).toList())
|
||||
? _smooth(route.positions.take(posIdx+1).map((p)=>p.latLng).toList())
|
||||
: <LatLng>[];
|
||||
|
||||
return ClipRRect(borderRadius: BorderRadius.circular(12),
|
||||
// Calcular bearing hacia siguiente punto
|
||||
double bear = 0;
|
||||
if (posIdx < route.positions.length - 1) {
|
||||
bear = _bearing(cur, route.positions[posIdx+1].latLng);
|
||||
}
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: SizedBox(height: height,
|
||||
child: FlutterMap(
|
||||
options: MapOptions(initialCenter: cur, initialZoom: 14),
|
||||
options: MapOptions(
|
||||
initialCenter: cur, initialZoom: 14.5,
|
||||
initialRotation: -bear, // Rotar mapa según dirección del camión
|
||||
),
|
||||
children: [
|
||||
TileLayer(urlTemplate:'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName:'com.example.celaya_limpia'),
|
||||
// Ruta completa (gris punteada)
|
||||
PolylineLayer(polylines:[
|
||||
Polyline(points:all, color:Colors.grey.withOpacity(0.4), strokeWidth:4,
|
||||
Polyline(points:all, color:Colors.grey.shade400, strokeWidth:4,
|
||||
borderColor:Colors.white54, borderStrokeWidth:1),
|
||||
]),
|
||||
// Tramo recorrido (guinda)
|
||||
if (done.isNotEmpty) PolylineLayer(polylines:[
|
||||
Polyline(points:done, color:AppColors.guindaPrimary, strokeWidth:5,
|
||||
borderColor:AppColors.guindaDark, borderStrokeWidth:1),
|
||||
]),
|
||||
MarkerLayer(markers:[
|
||||
Marker(point:cur, width:52, height:52,
|
||||
child:Container(decoration:BoxDecoration(
|
||||
color:gps?AppColors.guindaPrimary:Colors.grey, shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2.5),
|
||||
boxShadow:[BoxShadow(color:Colors.black38,blurRadius:6)]),
|
||||
child:Icon(gps?Icons.local_shipping:Icons.gps_off,color:Colors.white,size:24))),
|
||||
Marker(point:route.positions.first.latLng, width:28, height:28,
|
||||
child:Container(decoration:BoxDecoration(color:AppColors.verdeExito,shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2)),
|
||||
child:const Icon(Icons.circle,color:Colors.white,size:10))),
|
||||
Marker(point:route.positions.last.latLng, width:32, height:32,
|
||||
child:Container(decoration:BoxDecoration(color:AppColors.rojoError,shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2)),
|
||||
child:const Icon(Icons.flag,color:Colors.white,size:16))),
|
||||
// Camión con orientación
|
||||
Marker(point:cur, width:56, height:56,
|
||||
child:Transform.rotate(
|
||||
angle: bear * math.pi / 180,
|
||||
child:Container(
|
||||
decoration:BoxDecoration(
|
||||
color:gps?AppColors.guindaPrimary:Colors.grey,
|
||||
shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2.5),
|
||||
boxShadow:[BoxShadow(color:Colors.black38,blurRadius:6)]),
|
||||
child:Icon(gps?Icons.navigation:Icons.gps_off,
|
||||
color:Colors.white,size:26)))),
|
||||
// Origen
|
||||
Marker(point:route.positions.first.latLng, width:24, height:24,
|
||||
child:Container(decoration:BoxDecoration(color:AppColors.verdeExito,
|
||||
shape:BoxShape.circle,border:Border.all(color:Colors.white,width:2)),
|
||||
child:const Icon(Icons.circle,color:Colors.white,size:8))),
|
||||
// Destino
|
||||
Marker(point:route.positions.last.latLng, width:28, height:28,
|
||||
child:Container(decoration:BoxDecoration(color:AppColors.rojoError,
|
||||
shape:BoxShape.circle,border:Border.all(color:Colors.white,width:2)),
|
||||
child:const Icon(Icons.flag,color:Colors.white,size:14))),
|
||||
]),
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mapa Admin (todas las rutas) ──────────────────────────────────────────
|
||||
class AdminMapWidget extends StatefulWidget {
|
||||
final List<RouteModel> routes;
|
||||
final RouteSimulatorService simulator;
|
||||
@@ -91,9 +124,9 @@ class AdminMapWidget extends StatefulWidget {
|
||||
class _AdminMapWidgetState extends State<AdminMapWidget> {
|
||||
String? _sel;
|
||||
static const _colors = [
|
||||
Colors.blue, Colors.green, Colors.orange, Colors.purple, Colors.teal,
|
||||
Colors.red, Colors.indigo, Colors.brown, Colors.cyan, Colors.pink,
|
||||
Colors.amber, Colors.lime, Colors.deepOrange, Colors.lightBlue, Colors.deepPurple,
|
||||
Colors.blue,Colors.green,Colors.orange,Colors.purple,Colors.teal,
|
||||
Colors.red,Colors.indigo,Colors.brown,Colors.cyan,Colors.pink,
|
||||
Colors.amber,Colors.lime,Colors.deepOrange,Colors.lightBlue,Colors.deepPurple,
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -105,7 +138,7 @@ class _AdminMapWidgetState extends State<AdminMapWidget> {
|
||||
TileLayer(urlTemplate:'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName:'com.example.celaya_limpia'),
|
||||
PolylineLayer(polylines: widget.routes.asMap().entries.map((e){
|
||||
final c = _colors[e.key % _colors.length];
|
||||
final c = _colors[e.key%_colors.length];
|
||||
final isS = _sel==null||_sel==e.value.routeId;
|
||||
return Polyline(points:_smooth(e.value.polylinePoints),
|
||||
color:c.withOpacity(isS?0.85:0.2), strokeWidth:isS?4:1.5,
|
||||
@@ -115,32 +148,86 @@ class _AdminMapWidgetState extends State<AdminMapWidget> {
|
||||
final r = e.value;
|
||||
final idx = widget.simulator.getPositionIndex(r.routeId);
|
||||
final pos = idx < r.positions.length ? r.positions[idx].latLng : r.positions.last.latLng;
|
||||
final c = _colors[e.key % _colors.length];
|
||||
final c = _colors[e.key%_colors.length];
|
||||
final gps = widget.simulator.isGpsActive(r.routeId);
|
||||
return Marker(point:pos, width:46, height:46,
|
||||
double bear = 0;
|
||||
if (idx < r.positions.length - 1) bear = _bearing(pos, r.positions[idx+1].latLng);
|
||||
return Marker(point:pos, width:44, height:44,
|
||||
child:GestureDetector(
|
||||
onTap:()=>setState(()=>_sel=_sel==r.routeId?null:r.routeId),
|
||||
child:Container(decoration:BoxDecoration(color:gps?c:Colors.grey,
|
||||
shape:BoxShape.circle, border:Border.all(color:Colors.white,width:2),
|
||||
boxShadow:[BoxShadow(color:Colors.black26,blurRadius:4)]),
|
||||
child:Center(child:Text(r.truckId.toString().substring(1),
|
||||
style:const TextStyle(color:Colors.white,fontSize:11,fontWeight:FontWeight.bold))))));
|
||||
child:Transform.rotate(angle: bear*math.pi/180,
|
||||
child:Container(decoration:BoxDecoration(
|
||||
color:gps?c:Colors.grey, shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2),
|
||||
boxShadow:[BoxShadow(color:Colors.black26,blurRadius:4)]),
|
||||
child:Center(child:Text(r.truckId.toString().substring(1),
|
||||
style:const TextStyle(color:Colors.white,fontSize:11,fontWeight:FontWeight.bold)))))));
|
||||
}).toList()),
|
||||
],
|
||||
)),
|
||||
if (_sel!=null) ...[
|
||||
const Divider(height:1),
|
||||
Container(padding:const EdgeInsets.symmetric(horizontal:16,vertical:10),
|
||||
color:AppColors.guindaPrimary,
|
||||
child:Row(children:[
|
||||
const Icon(Icons.local_shipping,color:Colors.white,size:16),
|
||||
const SizedBox(width:8),
|
||||
Expanded(child:Text(widget.routes.firstWhere((r)=>r.routeId==_sel).name,
|
||||
style:const TextStyle(color:Colors.white,fontWeight:FontWeight.bold,fontSize:13))),
|
||||
Text('Pos ${widget.simulator.getPositionIndex(_sel!)}/7',
|
||||
style:const TextStyle(color:AppColors.dorado,fontSize:12)),
|
||||
])),
|
||||
],
|
||||
])),
|
||||
if (_sel!=null) Container(
|
||||
padding:const EdgeInsets.symmetric(horizontal:16,vertical:10),
|
||||
color:AppColors.guindaPrimary,
|
||||
child:Row(children:[
|
||||
const Icon(Icons.local_shipping,color:Colors.white,size:16),
|
||||
const SizedBox(width:8),
|
||||
Expanded(child:Text(widget.routes.firstWhere((r)=>r.routeId==_sel).name,
|
||||
style:const TextStyle(color:Colors.white,fontWeight:FontWeight.bold,fontSize:13))),
|
||||
Text('Pos ${widget.simulator.getPositionIndex(_sel!)}/7',
|
||||
style:const TextStyle(color:AppColors.dorado,fontSize:12)),
|
||||
])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mapa conductor (con bearing) ──────────────────────────────────────────
|
||||
class DriverRouteMap extends StatelessWidget {
|
||||
final RouteModel route;
|
||||
final RouteSimulatorService simulator;
|
||||
const DriverRouteMap({super.key, required this.route, required this.simulator});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final idx = simulator.getPositionIndex(route.routeId);
|
||||
final gps = simulator.isGpsActive(route.routeId);
|
||||
final positions = route.positions;
|
||||
final cur = idx < positions.length ? positions[idx].latLng : positions.last.latLng;
|
||||
double bear = 0;
|
||||
if (idx < positions.length - 1) bear = _bearing(cur, positions[idx+1].latLng);
|
||||
|
||||
final done = idx > 0
|
||||
? _smooth(positions.take(idx+1).map((p)=>p.latLng).toList())
|
||||
: <LatLng>[];
|
||||
final pending = _smooth(positions.skip(idx).map((p)=>p.latLng).toList());
|
||||
|
||||
return FlutterMap(
|
||||
options: MapOptions(initialCenter:cur, initialZoom:15, initialRotation:-bear),
|
||||
children:[
|
||||
TileLayer(urlTemplate:'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName:'com.example.celaya_limpia'),
|
||||
PolylineLayer(polylines:[
|
||||
Polyline(points:pending, color:Colors.grey.shade400, strokeWidth:5,
|
||||
borderColor:Colors.white54, borderStrokeWidth:1),
|
||||
if (done.isNotEmpty)
|
||||
Polyline(points:done, color:AppColors.moradoConductor, strokeWidth:6,
|
||||
borderColor:AppColors.moradoConductor.withOpacity(0.4), borderStrokeWidth:2),
|
||||
]),
|
||||
MarkerLayer(markers:[
|
||||
// Waypoints pendientes
|
||||
...positions.skip(idx+1).take(4).toList().asMap().entries.map((e)=>
|
||||
Marker(point:e.value.latLng, width:22, height:22,
|
||||
child:Container(decoration:BoxDecoration(color:Colors.amber,shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:1.5)),
|
||||
child:Center(child:Text('${idx+2+e.key}',
|
||||
style:const TextStyle(color:Colors.white,fontSize:9,fontWeight:FontWeight.bold)))))),
|
||||
// Camión orientado
|
||||
Marker(point:cur, width:56, height:56,
|
||||
child:Transform.rotate(angle:bear*math.pi/180,
|
||||
child:Container(decoration:BoxDecoration(
|
||||
color:gps?AppColors.moradoConductor:Colors.grey, shape:BoxShape.circle,
|
||||
border:Border.all(color:Colors.white,width:2.5),
|
||||
boxShadow:[BoxShadow(color:Colors.black38,blurRadius:8)]),
|
||||
child:Icon(gps?Icons.navigation:Icons.gps_off,color:Colors.white,size:28)))),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user