implemented RMV commute integration in the timetable, added Nominatim geocoding for home station lookup, created CommuteCubit for daily trip management with TTL caching, and introduced specialized timetable tiles, detail sheets, and settings for transit connections and walking buffers
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../errors/network_exception.dart';
|
||||
import '../errors/parse_exception.dart';
|
||||
import '../errors/server_exception.dart';
|
||||
import 'nominatim_result.dart';
|
||||
|
||||
/// Tiny wrapper around the public Nominatim geocoder. Only used in the
|
||||
/// commute-settings flow to look up a home address; not called from any
|
||||
/// hot path. The User-Agent header is **required** by the Nominatim usage
|
||||
/// policy — without it the service throttles/blocks the client.
|
||||
class NominatimSearch {
|
||||
static const _userAgent = 'MarianumMobile/1.0 (contact@elias-mueller.com)';
|
||||
static final Uri _base = Uri.parse('https://nominatim.openstreetmap.org/search');
|
||||
|
||||
/// Returns up to [limit] geocoded matches for the user-typed [query].
|
||||
Future<List<NominatimResult>> run(String query, {int limit = 5}) async {
|
||||
final uri = _base.replace(
|
||||
queryParameters: {
|
||||
'q': query,
|
||||
'format': 'json',
|
||||
'limit': limit.toString(),
|
||||
'addressdetails': '0',
|
||||
'accept-language': 'de',
|
||||
},
|
||||
);
|
||||
|
||||
final http.Response response;
|
||||
try {
|
||||
response = await http
|
||||
.get(uri, headers: {'User-Agent': _userAgent, 'Accept': 'application/json'})
|
||||
.timeout(const Duration(seconds: 15));
|
||||
} on SocketException catch (e) {
|
||||
throw NetworkException(technicalDetails: 'nominatim: ${e.message}');
|
||||
} on TimeoutException catch (e) {
|
||||
throw NetworkException.timeout(technicalDetails: 'nominatim: $e');
|
||||
} on http.ClientException catch (e) {
|
||||
throw NetworkException(technicalDetails: 'nominatim: ${e.message}');
|
||||
}
|
||||
|
||||
if (response.statusCode > 299) {
|
||||
throw ServerException(
|
||||
statusCode: response.statusCode,
|
||||
technicalDetails: 'nominatim HTTP ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final raw = jsonDecode(utf8.decode(response.bodyBytes)) as List;
|
||||
return raw
|
||||
.map((e) => _resultFromRaw(e as Map<String, dynamic>))
|
||||
.toList(growable: false);
|
||||
} catch (e) {
|
||||
throw ParseException(technicalDetails: 'nominatim assemble: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static NominatimResult _resultFromRaw(Map<String, dynamic> json) {
|
||||
// Nominatim returns lat/lon as strings, not numbers. Normalise here.
|
||||
final lat = double.parse(json['lat'].toString());
|
||||
final lon = double.parse(json['lon'].toString());
|
||||
return NominatimResult(
|
||||
displayName: json['display_name'] as String? ?? '?',
|
||||
lat: lat,
|
||||
lon: lon,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user