From 31f6960d44dd76d651f5a9327605d49946e91946 Mon Sep 17 00:00:00 2001 From: stopflock Date: Tue, 2 Dec 2025 15:02:49 -0600 Subject: [PATCH] Navigation start+end too close warning --- README.md | 1 - assets/changelog.json | 4 +++- lib/app_state.dart | 1 + lib/dev_config.dart | 3 +++ lib/localizations/de.json | 1 + lib/localizations/en.json | 1 + lib/localizations/es.json | 1 + lib/localizations/fr.json | 1 + lib/state/navigation_state.dart | 12 ++++++++++++ lib/widgets/navigation_sheet.dart | 30 +++++++++++++++++++++++++++++- 10 files changed, 52 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 79a8613..259f41b 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ cp lib/keys.dart.example lib/keys.dart - Manual cleanup (cognitive load for users) - Delete the old one (also wrong answer unless user chooses intentionally) - Give multiple of these options?? -- Nav start+end too close together error (warning + disable submit button?) - Persistent cache for MY submissions: assume submissions worked, cache,clean up when we see that node appear in overpass/OSM results or when older than 24h - Dropdown on "refine tags" page to select acceptable options for camera:mount= (is this a boolean property of a profile?) - Option to pull in profiles from NSI (man_made=surveillance only?) diff --git a/assets/changelog.json b/assets/changelog.json index 91b651a..c08a437 100644 --- a/assets/changelog.json +++ b/assets/changelog.json @@ -2,7 +2,9 @@ "1.5.4": { "content": [ "• FIXED: Download area max zoom level is now limited to the currently selected tile provider's maximum zoom level (e.g., OpenTopoMap limited to Z18, Bing Maps to Z20)", - "• IMPROVED: Download area dialog properly respects tile provider capabilities and prevents downloading tiles beyond what the provider supports" + "• IMPROVED: Download area dialog properly respects tile provider capabilities and prevents downloading tiles beyond what the provider supports", + "• NEW: Navigation route planning now prevents selecting start and end locations that are too close together (configurable minimum distance)", + "• IMPROVED: 'Select Location' button is disabled and shows warning message when route points are closer than 100 meters apart" ] }, "1.5.3": { diff --git a/lib/app_state.dart b/lib/app_state.dart index 8c08314..a5a78cd 100644 --- a/lib/app_state.dart +++ b/lib/app_state.dart @@ -112,6 +112,7 @@ class AppState extends ChangeNotifier { double? get routeDistance => _navigationState.routeDistance; bool get settingRouteStart => _navigationState.settingRouteStart; bool get isSettingSecondPoint => _navigationState.isSettingSecondPoint; + bool get areRoutePointsTooClose => _navigationState.areRoutePointsTooClose; bool get isCalculating => _navigationState.isCalculating; bool get showingOverview => _navigationState.showingOverview; String? get routingError => _navigationState.routingError; diff --git a/lib/dev_config.dart b/lib/dev_config.dart index 1633591..35e0992 100644 --- a/lib/dev_config.dart +++ b/lib/dev_config.dart @@ -124,6 +124,9 @@ const Duration kProximityAlertCooldown = Duration(minutes: 10); // Cooldown betw // Node proximity warning configuration (for new/edited nodes that are too close to existing ones) const double kNodeProximityWarningDistance = 15.0; // meters - distance threshold to show warning +// Navigation route planning configuration +const double kNavigationMinRouteDistance = 100.0; // meters - minimum distance between start and end points + // Map interaction configuration const double kNodeDoubleTapZoomDelta = 1.0; // How much to zoom in when double-tapping nodes (was 1.0) const double kScrollWheelVelocity = 0.01; // Mouse scroll wheel zoom speed (default 0.005) diff --git a/lib/localizations/de.json b/lib/localizations/de.json index 5752533..4c7a328 100644 --- a/lib/localizations/de.json +++ b/lib/localizations/de.json @@ -460,6 +460,7 @@ "endSelect": "Ende (auswählen)", "distance": "Entfernung: {} km", "routeActive": "Route aktiv", + "locationsTooClose": "Start- und Endpositionen sind zu nah beieinander", "navigationSettings": "Navigation", "navigationSettingsSubtitle": "Routenplanung und Vermeidungseinstellungen", "avoidanceDistance": "Vermeidungsabstand", diff --git a/lib/localizations/en.json b/lib/localizations/en.json index 3fdbc75..ae559e8 100644 --- a/lib/localizations/en.json +++ b/lib/localizations/en.json @@ -460,6 +460,7 @@ "endSelect": "End (select)", "distance": "Distance: {} km", "routeActive": "Route active", + "locationsTooClose": "Start and end locations are too close together", "navigationSettings": "Navigation", "navigationSettingsSubtitle": "Route planning and avoidance settings", "avoidanceDistance": "Avoidance Distance", diff --git a/lib/localizations/es.json b/lib/localizations/es.json index 410665a..5f26008 100644 --- a/lib/localizations/es.json +++ b/lib/localizations/es.json @@ -460,6 +460,7 @@ "endSelect": "Fin (seleccionar)", "distance": "Distancia: {} km", "routeActive": "Ruta activa", + "locationsTooClose": "Las ubicaciones de inicio y fin están demasiado cerca", "navigationSettings": "Navegación", "navigationSettingsSubtitle": "Configuración de planificación de rutas y evitación", "avoidanceDistance": "Distancia de evitación", diff --git a/lib/localizations/fr.json b/lib/localizations/fr.json index 9d74089..bc77643 100644 --- a/lib/localizations/fr.json +++ b/lib/localizations/fr.json @@ -460,6 +460,7 @@ "endSelect": "Fin (sélectionner)", "distance": "Distance: {} km", "routeActive": "Itinéraire actif", + "locationsTooClose": "Les emplacements de départ et d'arrivée sont trop proches", "navigationSettings": "Navigation", "navigationSettingsSubtitle": "Paramètres de planification d'itinéraire et d'évitement", "avoidanceDistance": "Distance d'évitement", diff --git a/lib/state/navigation_state.dart b/lib/state/navigation_state.dart index 072fcf9..cea90d1 100644 --- a/lib/state/navigation_state.dart +++ b/lib/state/navigation_state.dart @@ -4,6 +4,7 @@ import 'package:latlong2/latlong.dart'; import '../models/search_result.dart'; import '../services/search_service.dart'; import '../services/routing_service.dart'; +import '../dev_config.dart'; /// Simplified navigation modes - brutalist approach enum AppNavigationMode { @@ -75,6 +76,17 @@ class NavigationState extends ChangeNotifier { bool get showSearchButton => _mode == AppNavigationMode.normal; bool get showRouteButton => _mode == AppNavigationMode.routeActive; + /// Check if the start and end locations are too close together + bool get areRoutePointsTooClose { + if (!_isSettingSecondPoint || _provisionalPinLocation == null) return false; + + final firstPoint = _nextPointIsStart ? _routeEnd : _routeStart; + if (firstPoint == null) return false; + + final distance = const Distance().as(LengthUnit.Meter, firstPoint, _provisionalPinLocation!); + return distance < kNavigationMinRouteDistance; + } + /// BRUTALIST: Single entry point to search mode void enterSearchMode(LatLng mapCenter) { debugPrint('[NavigationState] enterSearchMode - current mode: $_mode'); diff --git a/lib/widgets/navigation_sheet.dart b/lib/widgets/navigation_sheet.dart index d47ee37..ab41955 100644 --- a/lib/widgets/navigation_sheet.dart +++ b/lib/widgets/navigation_sheet.dart @@ -159,10 +159,38 @@ class NavigationSheet extends StatelessWidget { ), const SizedBox(height: 16), + // Show warning message if locations are too close + if (appState.areRoutePointsTooClose) ...[ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.orange.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.orange.withOpacity(0.3)), + ), + child: Row( + children: [ + Icon(Icons.warning, color: Colors.orange[700], size: 20), + const SizedBox(width: 8), + Expanded( + child: Text( + LocalizationService.instance.t('navigation.locationsTooClose'), + style: TextStyle( + fontSize: 14, + color: Colors.orange[700], + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + ], + ElevatedButton.icon( icon: const Icon(Icons.check), label: Text(LocalizationService.instance.t('navigation.selectLocation')), - onPressed: () { + onPressed: appState.areRoutePointsTooClose ? null : () { debugPrint('[NavigationSheet] Select Location button pressed'); appState.selectSecondRoutePoint(); },