import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'location_plugin_platform_interface.dart'; class LocationPlugin { LocationPlugin({http.Client? httpClient}) : _httpClient = httpClient; final http.Client? _httpClient; static const _reverseGeocodeHost = 'siteapi.cloud.huawei.com'; static const _reverseGeocodePath = '/mapApi/v1/siteService/reverseGeocode'; static const _defaultReverseGeocodeKey = 'DAEDAPlmscfMOlRxiAN/hCblSsGarZMJbjdbhzVnIE97zUnPB0U9x/ZN438CzDKINlrHcVDcUPPXryRsr1LJbfsKzv7Q8PXsFNypNA=='; Future getPlatformVersion() { return LocationPluginPlatform.instance.getPlatformVersion(); } Future?> getCurrentLocation() { return LocationPluginPlatform.instance.getCurrentLocation(); } /// 通过经纬度访问华为逆地理编码接口,返回城市编号与名称信息。 Future getCityInfo({ required double latitude, required double longitude, String? apiKey, Duration timeout = const Duration(seconds: 10), }) async { final client = _httpClient ?? http.Client(); final shouldCloseClient = _httpClient == null; try { final uri = Uri.https(_reverseGeocodeHost, _reverseGeocodePath, { 'key': apiKey ?? _defaultReverseGeocodeKey, }); print(uri); print(jsonEncode({'lat': latitude, 'lng': longitude})); final response = await client .post( uri, headers: const {'Content-Type': 'application/json'}, body: jsonEncode({"location":{'lat': latitude, 'lng': longitude}}), ) .timeout(timeout); if (response.statusCode != 200) { throw CityInfoLookupException( code: 'HTTP_${response.statusCode}', message: 'Reverse geocode request failed with status ${response.statusCode}.', ); } final payload = jsonDecode(response.body); if (payload is! Map) { throw CityInfoLookupException( code: 'BAD_RESPONSE', message: 'Unexpected reverse geocode response format.', ); } final returnCode = payload['returnCode']?.toString(); if (returnCode != '0') { throw CityInfoLookupException( code: 'API_$returnCode', message: payload['returnDesc']?.toString() ?? 'Reverse geocode API error.', ); } final sites = payload['sites']; if (sites is! List || sites.isEmpty) { return null; } final firstSite = sites.first; if (firstSite is! Map) { return null; } final address = firstSite['address']; if (address is! Map) { return null; } return CityInfo.fromAddress(Map.from(address)); } on TimeoutException catch (_) { throw CityInfoLookupException( code: 'TIMEOUT', message: 'Reverse geocode request timed out.', ); } on FormatException catch (error) { throw CityInfoLookupException( code: 'BAD_RESPONSE', message: 'Unable to parse reverse geocode response: ${error.message}', ); } finally { if (shouldCloseClient) { client.close(); } } } } class CityInfo { const CityInfo({ required this.code, required this.region, required this.cityName, this.rawAddress, }); final int? code; final List region; final String? cityName; final Map? rawAddress; factory CityInfo.fromAddress(Map address) { final adminCode = address['adminCode']?.toString(); final code = (adminCode != null && adminCode.length >= 6) ? int.tryParse(adminCode.substring(0, 6)) : null; final region = [ address['adminArea'], address['subAdminArea'], address['tertiaryAdminArea'], ].whereType().toList(); final cityName = address['locality']?.toString() ?? address['subAdminArea']?.toString() ?? address['adminArea']?.toString(); return CityInfo( code: code, region: region, cityName: cityName, rawAddress: address, ); } } class CityInfoLookupException implements Exception { CityInfoLookupException({required this.code, required this.message}); final String code; final String message; @override String toString() => 'CityInfoLookupException($code, $message)'; }