Suspected location exclusion zone, drawn under real nodes, and update progress bar

This commit is contained in:
stopflock
2025-10-06 22:21:05 -05:00
parent 4a44ab96d6
commit f9351ba272
8 changed files with 206 additions and 21 deletions
+43 -9
View File
@@ -355,8 +355,6 @@ class MapViewState extends State<MapView> {
// Build suspected location markers
final suspectedLocationMarkers = <Marker>[];
if (appState.suspectedLocationsEnabled && mapBounds != null) {
debugPrint('[MapView] Suspected locations enabled, getting bounds: N${mapBounds.north.toStringAsFixed(4)}, S${mapBounds.south.toStringAsFixed(4)}, E${mapBounds.east.toStringAsFixed(4)}, W${mapBounds.west.toStringAsFixed(4)}');
final suspectedLocations = appState.getSuspectedLocationsInBounds(
north: mapBounds.north,
south: mapBounds.south,
@@ -364,20 +362,21 @@ class MapViewState extends State<MapView> {
west: mapBounds.west,
);
debugPrint('[MapView] Found ${suspectedLocations.length} suspected locations in bounds');
// Filter out suspected locations that are too close to real nodes
final filteredSuspectedLocations = _filterSuspectedLocationsByProximity(
suspectedLocations: suspectedLocations,
realNodes: cameras,
minDistance: appState.suspectedLocationMinDistance,
);
suspectedLocationMarkers.addAll(
SuspectedLocationMarkersBuilder.buildSuspectedLocationMarkers(
locations: suspectedLocations,
locations: filteredSuspectedLocations,
mapController: _controller.mapController,
selectedLocationId: appState.selectedSuspectedLocation?.ticketNo,
onLocationTap: widget.onSuspectedLocationTap,
),
);
debugPrint('[MapView] Created ${suspectedLocationMarkers.length} suspected location markers');
} else {
debugPrint('[MapView] Suspected locations not enabled (${appState.suspectedLocationsEnabled}) or no mapBounds ($mapBounds)');
}
// Get current zoom level for direction cones
@@ -487,7 +486,7 @@ class MapViewState extends State<MapView> {
PolygonLayer(polygons: overlays),
if (editLines.isNotEmpty) PolylineLayer(polylines: editLines),
if (routeLines.isNotEmpty) PolylineLayer(polylines: routeLines),
MarkerLayer(markers: [...markers, ...suspectedLocationMarkers, ...centerMarkers]),
MarkerLayer(markers: [...suspectedLocationMarkers, ...markers, ...centerMarkers]),
],
);
}
@@ -633,5 +632,40 @@ class MapViewState extends State<MapView> {
return lines;
}
/// Filter suspected locations that are too close to real nodes
List<SuspectedLocation> _filterSuspectedLocationsByProximity({
required List<SuspectedLocation> suspectedLocations,
required List<OsmNode> realNodes,
required int minDistance, // in meters
}) {
if (minDistance <= 0) return suspectedLocations;
const distance = Distance();
final filteredLocations = <SuspectedLocation>[];
for (final suspected in suspectedLocations) {
bool tooClose = false;
for (final realNode in realNodes) {
final distanceMeters = distance.as(
LengthUnit.Meter,
suspected.centroid,
realNode.coord,
);
if (distanceMeters < minDistance) {
tooClose = true;
break;
}
}
if (!tooClose) {
filteredLocations.add(suspected);
}
}
return filteredLocations;
}
}
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
class SuspectedLocationProgressDialog extends StatelessWidget {
final String title;
final String message;
final double? progress; // 0.0 to 1.0, null for indeterminate
final VoidCallback? onCancel;
const SuspectedLocationProgressDialog({
super.key,
required this.title,
required this.message,
this.progress,
this.onCancel,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Row(
children: [
const Icon(Icons.help_outline, color: Colors.orange),
const SizedBox(width: 8),
Expanded(child: Text(title)),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(message),
const SizedBox(height: 16),
if (progress != null)
LinearProgressIndicator(value: progress)
else
const LinearProgressIndicator(),
const SizedBox(height: 8),
if (progress != null)
Text(
'${(progress! * 100).toInt()}%',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
actions: [
if (onCancel != null)
TextButton(
onPressed: onCancel,
child: const Text('Cancel'),
),
],
);
}
}