import 'dart:convert'; import 'dart:math' as math; import 'package:http/http.dart' as http; import 'road_alert_models.dart'; import 'road_alert_provider_interface.dart'; import 'community_traffic_parser.dart'; class CommunityTrafficRoadAlertProvider implements RoadAlertProvider { final String _customEndpointUrl; CommunityTrafficRoadAlertProvider({String? endpointUrl}) : _customEndpointUrl = endpointUrl ?? ''; @override String get id => 'community_traffic'; @override String get name => 'Community Traffic API'; @override Set get supportedCategories => const { RoadAlertCategory.police, RoadAlertCategory.speedCamera, RoadAlertCategory.accident, RoadAlertCategory.hazard, RoadAlertCategory.roadClosure, RoadAlertCategory.construction, RoadAlertCategory.traffic, RoadAlertCategory.other, }; @override Future testConnection(String apiKey) async { if (apiKey.trim().isEmpty && _customEndpointUrl.isEmpty) { return RoadAlertProviderStatus.notConfigured; } try { final endpoint = _customEndpointUrl.isNotEmpty ? _customEndpointUrl : 'https://api.communitytraffic.org/v1/incidents'; final uri = Uri.parse('$endpoint?top=37.80&bottom=37.70&left=-122.50&right=-122.35&apiKey=$apiKey'); final response = await http.get(uri).timeout(const Duration(seconds: 6)); if (response.statusCode == 200) { return RoadAlertProviderStatus.connected; } else if (response.statusCode == 401 || response.statusCode == 403) { return RoadAlertProviderStatus.authError; } else if (response.statusCode == 429) { return RoadAlertProviderStatus.rateLimited; } return RoadAlertProviderStatus.networkError; } catch (_) { return RoadAlertProviderStatus.networkError; } } @override Future> getAlerts({ required double latitude, required double longitude, required double radiusMeters, String? apiKey, }) async { if ((apiKey == null || apiKey.trim().isEmpty) && _customEndpointUrl.isEmpty) { return []; } try { final latOffset = radiusMeters / 111000.0; final lngOffset = radiusMeters / (111000.0 * math.cos(latitude * math.pi / 180.0)); final top = (latitude + latOffset).toStringAsFixed(5); final bottom = (latitude - latOffset).toStringAsFixed(5); final left = (longitude - lngOffset).toStringAsFixed(5); final right = (longitude + lngOffset).toStringAsFixed(5); final endpoint = _customEndpointUrl.isNotEmpty ? _customEndpointUrl : 'https://api.communitytraffic.org/v1/incidents'; final uri = Uri.parse('$endpoint?top=$top&bottom=$bottom&left=$left&right=$right&apiKey=${apiKey ?? ''}'); final response = await http.get(uri).timeout(const Duration(seconds: 8)); if (response.statusCode != 200) { return []; } final Map jsonMap = json.decode(response.body); return CommunityTrafficParser.parseAlertsJson( jsonMap, providerId: id, providerName: name, ); } catch (_) { return []; } } }