import 'dart:convert'; import 'package:http/http.dart' as http; /// Ergebnis einer Ortssuche: Koordinaten plus ein kompakter Anzeigename, /// der auch als Missionsname dient (siehe MapSearchControls). class GeocodingResult { const GeocodingResult({ required this.label, required this.lat, required this.lon, }); final String label; final double lat; final double lon; } /// Geocoding ueber Nominatim (OpenStreetMap) - analog doSearch() im HTML- /// Demonstrator. Injectable http.Client analog WindService, damit dies in /// Tests ohne echten Netzwerkzugriff ueberschrieben werden kann. class NominatimService { NominatimService({http.Client? client}) : _client = client ?? http.Client(); final http.Client _client; Future search(String query) async { final uri = Uri.https('nominatim.openstreetmap.org', '/search', { 'format': 'json', 'addressdetails': '1', 'accept-language': 'en', 'limit': '1', 'q': query, }); final response = await _client .get(uri, headers: {'User-Agent': 'dmc_app (Drone Mission Control)'}); if (response.statusCode != 200) return null; final results = jsonDecode(response.body) as List; if (results.isEmpty) return null; final first = results.first as Map; return GeocodingResult( label: _extractPlaceLabel(first), lat: double.parse(first['lat'] as String), lon: double.parse(first['lon'] as String), ); } /// Analog extractPlaceLabel() im HTML-Demonstrator: bevorzugt einen /// kompakten Ortsnamen aus den Nominatim-Adressbestandteilen, faellt sonst /// auf den ersten Abschnitt von display_name zurueck. String _extractPlaceLabel(Map result) { final address = result['address'] as Map?; final label = address?['city'] ?? address?['town'] ?? address?['village'] ?? address?['municipality'] ?? address?['suburb'] ?? address?['county']; if (label is String && label.isNotEmpty) return label; final displayName = result['display_name'] as String?; if (displayName != null && displayName.isNotEmpty) { return displayName.split(',').first; } return 'Unknown location'; } }