import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter/foundation.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; /// Einfacher PTV API Service mit statischen Methoden class PtvApiServiceSimple { /// Private Konstruktor PtvApiServiceSimple._(); /// Base URL der PTV API static const String _baseUrl = 'https://api.myptv.com/geocoding/v1/locations/by-position'; /// Standard Sprache static const String _defaultLanguage = 'de'; /// Timeout in Sekunden static const int _timeoutSeconds = 15; /// API Key aus .env Datei static String get _apiKey => dotenv.env['PTVE_API_KEY'] ?? ''; /// HTTP Headers static Map get _headers => { 'Accept': 'application/json', 'Content-Type': 'application/json', 'ApiKey': _apiKey, }; /// Hauptmethode: Holt Location-Daten für gegebene Koordinaten /// /// Beispiel URL: https://api.myptv.com/geocoding/v1/locations/by-position/48.208282/14.214758?language=de /// /// [latitude] - Breitengrad (z.B. 48.208282) /// [longitude] - Längengrad (z.B. 14.214758) /// [language] - Sprache für Antwort (default: 'de') /// /// Returns Map mit API-Response oder null bei Fehler static Future?> getLocationsByPosition({ required double latitude, required double longitude, String language = _defaultLanguage, }) async { try { // Validiere Koordinaten if (!_isValidCoordinate(latitude, longitude)) { if (kDebugMode) { print('❌ Ungültige Koordinaten: lat=$latitude, lng=$longitude'); } return null; } // Lade .env falls API Key leer if (_apiKey.isEmpty) { await dotenv.load(fileName: ".env"); if (_apiKey.isEmpty) { if (kDebugMode) { print('❌ PTVE_API_KEY nicht in .env gefunden'); } return null; } } // Baue URL final url = '$_baseUrl/$latitude/$longitude?language=$language'; if (kDebugMode) { print('🌐 PTV API Request: $url'); print('🔑 API Key vorhanden: ${_apiKey.isNotEmpty}'); } // HTTP GET Request final response = await http .get( Uri.parse(url), headers: _headers, ) .timeout(Duration(seconds: _timeoutSeconds)); if (kDebugMode) { print('📡 Response Status: ${response.statusCode}'); } // Verarbeite Response if (response.statusCode == 200) { final jsonData = json.decode(response.body) as Map; if (kDebugMode) { print('✅ API Request erfolgreich'); print('📍 Locations gefunden: ${(jsonData['locations'] as List?)?.length ?? 0}'); } return jsonData; } else { if (kDebugMode) { print('❌ API Fehler: ${response.statusCode}'); print('📄 Response Body: ${response.body}'); } return null; } } catch (e) { if (kDebugMode) { print('❌ Exception beim API Aufruf: $e'); } return null; } } /// Erweiterte Methode mit zusätzlichen Parametern static Future?> getLocationsByPositionAdvanced({ required double latitude, required double longitude, String language = _defaultLanguage, int? maxResults, int? radius, }) async { try { // Baue URL mit zusätzlichen Query-Parametern String url = '$_baseUrl/$latitude/$longitude?language=$language'; if (maxResults != null && maxResults > 0) { url += '&maxResults=$maxResults'; } if (radius != null && radius > 0) { url += '&radius=$radius'; } if (kDebugMode) { print('🌐 PTV API Advanced Request: $url'); } final response = await http .get( Uri.parse(url), headers: _headers, ) .timeout(Duration(seconds: _timeoutSeconds)); if (response.statusCode == 200) { return json.decode(response.body) as Map; } else { if (kDebugMode) { print('❌ Advanced API Fehler: ${response.statusCode}'); } return null; } } catch (e) { if (kDebugMode) { print('❌ Exception bei erweiterter API Abfrage: $e'); } return null; } } /// Hilfsmethoden /// Validiert GPS-Koordinaten static bool _isValidCoordinate(double latitude, double longitude) { return latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180; } /// Prüft API-Verfügbarkeit mit Test-Request static Future testApiConnection() async { try { if (kDebugMode) { print('\n🧪 PTV API CONNECTION TEST 🧪'); } // Test mit Wien Koordinaten final result = await getLocationsByPosition( latitude: 48.2082, longitude: 16.3738, ); final success = result != null; if (kDebugMode) { print(success ? '✅ API Test erfolgreich' : '❌ API Test fehlgeschlagen'); print('🧪 TEST ABGESCHLOSSEN\n'); } return success; } catch (e) { if (kDebugMode) { print('❌ Test Exception: $e'); print('🧪 TEST ABGESCHLOSSEN\n'); } return false; } } /// Debugging: Zeigt Service-Informationen static void printServiceInfo() { if (kDebugMode) { print('\n📋 PTV API SERVICE INFO 📋'); print('Base URL: $_baseUrl'); print('Default Language: $_defaultLanguage'); print('Timeout: ${_timeoutSeconds}s'); print('API Key verfügbar: ${_apiKey.isNotEmpty}'); print('API Key Length: ${_apiKey.length}'); print('📋 SERVICE INFO END 📋\n'); } } /// Demonstration der API-Verwendung static Future runDemo() async { if (kDebugMode) { print('\n🚀 PTV API DEMO START 🚀'); // Service Info printServiceInfo(); // Test Connection await testApiConnection(); // Beispiel 1: Traun, Österreich print('📍 Beispiel 1: Traun, Österreich'); final traunResult = await getLocationsByPosition( latitude: 48.208282, longitude: 14.214758, ); if (traunResult != null) { final locations = traunResult['locations'] as List?; if (locations != null && locations.isNotEmpty) { final firstLocation = locations.first; print('✅ Gefunden: ${firstLocation['formattedAddress']}'); print('📍 Koordinaten: ${firstLocation['referencePosition']['latitude']}, ${firstLocation['referencePosition']['longitude']}'); final quality = firstLocation['quality']; if (quality != null) { print('🎯 Genauigkeit: ${quality['distance']}m'); } } } else { print('❌ Keine Daten für Traun erhalten'); } // Beispiel 2: Wien mit erweiterten Parametern print('\n📍 Beispiel 2: Wien (erweitert)'); final wienResult = await getLocationsByPositionAdvanced( latitude: 48.2082, longitude: 16.3738, maxResults: 3, radius: 500, ); if (wienResult != null) { final locations = wienResult['locations'] as List?; print('✅ Wien Locations gefunden: ${locations?.length ?? 0}'); if (locations != null) { for (int i = 0; i < locations.length && i < 3; i++) { final location = locations[i]; print(' ${i + 1}. ${location['formattedAddress']}'); } } } print('🚀 DEMO ABGESCHLOSSEN 🚀\n'); } } } /// Utility-Klasse für häufige PTV-Operationen class PtvApiUtils { /// Extrahiert die beste Location aus der API-Response static Map? getBestLocation(Map apiResponse) { final locations = apiResponse['locations'] as List?; if (locations == null || locations.isEmpty) return null; // Suche Location mit geringster Distanz Map? bestLocation; double? bestDistance; for (final location in locations) { final quality = location['quality']; if (quality != null) { final distance = (quality['distance'] as num?)?.toDouble(); if (distance != null && (bestDistance == null || distance < bestDistance)) { bestDistance = distance; bestLocation = location; } } } return bestLocation ?? locations.first; } /// Extrahiert formatierte Adresse static String? getFormattedAddress(Map apiResponse) { final bestLocation = getBestLocation(apiResponse); return bestLocation?['formattedAddress'] as String?; } /// Extrahiert Koordinaten static Map? getCoordinates(Map apiResponse) { final bestLocation = getBestLocation(apiResponse); final refPos = bestLocation?['referencePosition']; if (refPos != null) { return { 'latitude': (refPos['latitude'] as num?)?.toDouble() ?? 0.0, 'longitude': (refPos['longitude'] as num?)?.toDouble() ?? 0.0, }; } return null; } /// Prüft Qualität der Location static String getQualityLevel(Map apiResponse) { final bestLocation = getBestLocation(apiResponse); final quality = bestLocation?['quality']; if (quality != null) { final distance = (quality['distance'] as num?)?.toDouble() ?? 999999; if (distance <= 10) return 'Sehr hoch'; if (distance <= 50) return 'Hoch'; if (distance <= 100) return 'Mittel'; if (distance <= 500) return 'Niedrig'; return 'Sehr niedrig'; } return 'Unbekannt'; } /// Konvertiert API-Response zu lesbarem String static String formatLocationInfo(Map apiResponse) { final bestLocation = getBestLocation(apiResponse); if (bestLocation == null) return 'Keine Location gefunden'; final address = bestLocation['formattedAddress'] ?? 'Unbekannte Adresse'; final refPos = bestLocation['referencePosition']; final quality = bestLocation['quality']; String info = 'Adresse: $address\n'; if (refPos != null) { info += 'Koordinaten: ${refPos['latitude']}, ${refPos['longitude']}\n'; } if (quality != null) { info += 'Genauigkeit: ${quality['distance']}m (${getQualityLevel(apiResponse)})\n'; } return info; } }