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
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)';
|
|
}
|