You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

151 lines
4.4 KiB

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<String?> getPlatformVersion() {
return LocationPluginPlatform.instance.getPlatformVersion();
}
Future<Map<String, double>?> getCurrentLocation() {
return LocationPluginPlatform.instance.getCurrentLocation();
}
/// 通过经纬度访问华为逆地理编码接口,返回城市编号与名称信息。
Future<CityInfo?> 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<String, dynamic>) {
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<String, dynamic>.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<String> region;
final String? cityName;
final Map<String, dynamic>? rawAddress;
factory CityInfo.fromAddress(Map<String, dynamic> 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<String>().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)';
}