354 lines
10 KiB
Dart
354 lines
10 KiB
Dart
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<String, String> 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<Map<String, dynamic>?> 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<String, dynamic>;
|
|
|
|
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<Map<String, dynamic>?> 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<String, dynamic>;
|
|
} 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<bool> 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<void> 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<String, dynamic>? getBestLocation(Map<String, dynamic> apiResponse) {
|
|
final locations = apiResponse['locations'] as List?;
|
|
if (locations == null || locations.isEmpty) return null;
|
|
|
|
// Suche Location mit geringster Distanz
|
|
Map<String, dynamic>? 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<String, dynamic> apiResponse) {
|
|
final bestLocation = getBestLocation(apiResponse);
|
|
return bestLocation?['formattedAddress'] as String?;
|
|
}
|
|
|
|
/// Extrahiert Koordinaten
|
|
static Map<String, double>? getCoordinates(Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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;
|
|
}
|
|
} |