Merge pull request #28 from heathdutton/issue-27-search-viewbox

Pass (rough) viewbox to search for location-biased results
This commit is contained in:
stopflock
2026-01-29 12:45:35 -06:00
committed by GitHub
6 changed files with 57 additions and 22 deletions
+5
View File
@@ -1,4 +1,9 @@
{ {
"2.4.4": {
"content": [
"• Search results now prioritize locations near your current map view"
]
},
"2.4.3": { "2.4.3": {
"content": [ "content": [
"• Fixed 360° FOV rendering - devices with full circle coverage now render as complete rings instead of having a wedge cut out or being a line", "• Fixed 360° FOV rendering - devices with full circle coverage now render as complete rings instead of having a wedge cut out or being a line",
+3 -2
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -569,8 +570,8 @@ class AppState extends ChangeNotifier {
} }
// ---------- Navigation Methods - Simplified ---------- // ---------- Navigation Methods - Simplified ----------
void enterSearchMode(LatLng mapCenter) { void enterSearchMode(LatLng mapCenter, {LatLngBounds? viewbox}) {
_navigationState.enterSearchMode(mapCenter); _navigationState.enterSearchMode(mapCenter, viewbox: viewbox);
} }
void cancelNavigation() { void cancelNavigation() {
@@ -138,7 +138,8 @@ class NavigationCoordinator {
// Enter search mode // Enter search mode
try { try {
final center = mapController.mapController.camera.center; final center = mapController.mapController.camera.center;
appState.enterSearchMode(center); final viewbox = mapController.mapController.camera.visibleBounds;
appState.enterSearchMode(center, viewbox: viewbox);
} catch (e) { } catch (e) {
debugPrint('[NavigationCoordinator] Could not get map center for search: $e'); debugPrint('[NavigationCoordinator] Could not get map center for search: $e');
// Fallback to default location // Fallback to default location
+29 -5
View File
@@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
@@ -12,7 +13,7 @@ class SearchService {
static const Duration _timeout = Duration(seconds: 10); static const Duration _timeout = Duration(seconds: 10);
/// Search for places using Nominatim geocoding service /// Search for places using Nominatim geocoding service
Future<List<SearchResult>> search(String query) async { Future<List<SearchResult>> search(String query, {LatLngBounds? viewbox}) async {
if (query.trim().isEmpty) { if (query.trim().isEmpty) {
return []; return [];
} }
@@ -24,7 +25,7 @@ class SearchService {
} }
// Otherwise, use Nominatim API // Otherwise, use Nominatim API
return await _searchNominatim(query.trim()); return await _searchNominatim(query.trim(), viewbox: viewbox);
} }
/// Try to parse various coordinate formats /// Try to parse various coordinate formats
@@ -52,14 +53,37 @@ class SearchService {
} }
/// Search using Nominatim API /// Search using Nominatim API
Future<List<SearchResult>> _searchNominatim(String query) async { Future<List<SearchResult>> _searchNominatim(String query, {LatLngBounds? viewbox}) async {
final uri = Uri.parse('$_baseUrl/search').replace(queryParameters: { final params = {
'q': query, 'q': query,
'format': 'json', 'format': 'json',
'limit': _maxResults.toString(), 'limit': _maxResults.toString(),
'addressdetails': '1', 'addressdetails': '1',
'extratags': '1', 'extratags': '1',
}); };
if (viewbox != null) {
double round1(double v) => (v * 10).round() / 10;
var west = round1(viewbox.west);
var east = round1(viewbox.east);
var south = round1(viewbox.south);
var north = round1(viewbox.north);
if (east - west < 0.5) {
final mid = (east + west) / 2;
west = mid - 0.25;
east = mid + 0.25;
}
if (north - south < 0.5) {
final mid = (north + south) / 2;
south = mid - 0.25;
north = mid + 0.25;
}
params['viewbox'] = '$west,$north,$east,$south';
}
final uri = Uri.parse('$_baseUrl/search').replace(queryParameters: params);
debugPrint('[SearchService] Searching Nominatim: $uri'); debugPrint('[SearchService] Searching Nominatim: $uri');
+6 -2
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import '../models/search_result.dart'; import '../models/search_result.dart';
@@ -31,6 +32,7 @@ class NavigationState extends ChangeNotifier {
bool _isSearchLoading = false; bool _isSearchLoading = false;
List<SearchResult> _searchResults = []; List<SearchResult> _searchResults = [];
String _lastQuery = ''; String _lastQuery = '';
LatLngBounds? _searchViewbox;
// Location state // Location state
LatLng? _provisionalPinLocation; LatLng? _provisionalPinLocation;
@@ -106,7 +108,7 @@ class NavigationState extends ChangeNotifier {
} }
/// BRUTALIST: Single entry point to search mode /// BRUTALIST: Single entry point to search mode
void enterSearchMode(LatLng mapCenter) { void enterSearchMode(LatLng mapCenter, {LatLngBounds? viewbox}) {
debugPrint('[NavigationState] enterSearchMode - current mode: $_mode'); debugPrint('[NavigationState] enterSearchMode - current mode: $_mode');
if (_mode != AppNavigationMode.normal) { if (_mode != AppNavigationMode.normal) {
@@ -117,6 +119,7 @@ class NavigationState extends ChangeNotifier {
_mode = AppNavigationMode.search; _mode = AppNavigationMode.search;
_provisionalPinLocation = mapCenter; _provisionalPinLocation = mapCenter;
_provisionalPinAddress = null; _provisionalPinAddress = null;
_searchViewbox = viewbox;
_clearSearchResults(); _clearSearchResults();
debugPrint('[NavigationState] Entered search mode'); debugPrint('[NavigationState] Entered search mode');
@@ -149,6 +152,7 @@ class NavigationState extends ChangeNotifier {
_showingOverview = false; _showingOverview = false;
_nextPointIsStart = false; _nextPointIsStart = false;
_routingError = null; _routingError = null;
_searchViewbox = null;
// Clear search // Clear search
_clearSearchResults(); _clearSearchResults();
@@ -343,7 +347,7 @@ class NavigationState extends ChangeNotifier {
_lastQuery = query.trim(); _lastQuery = query.trim();
try { try {
final results = await _searchService.search(query.trim()); final results = await _searchService.search(query.trim(), viewbox: _searchViewbox);
_searchResults = results; _searchResults = results;
debugPrint('[NavigationState] Found ${results.length} results'); debugPrint('[NavigationState] Found ${results.length} results');
} catch (e) { } catch (e) {
+1 -1
View File
@@ -1,7 +1,7 @@
name: deflockapp name: deflockapp
description: Map public surveillance infrastructure with OpenStreetMap description: Map public surveillance infrastructure with OpenStreetMap
publish_to: "none" publish_to: "none"
version: 2.4.3+41 # The thing after the + is the version code, incremented with each release version: 2.4.4+42 # The thing after the + is the version code, incremented with each release
environment: environment:
sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+ sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+