Files
oneapp_docs/app_car/amap_flutter_search.md
2025-09-24 14:08:54 +08:00

639 lines
18 KiB
Markdown

# AMap Flutter Search - 高德地图搜索插件
## 模块概述
`amap_flutter_search` 是 OneApp 中基于高德地图 SDK 的搜索服务插件,提供了全面的地理信息搜索功能,包括 POI 搜索、路线规划、地址解析等服务。该插件为车辆导航、位置查找、路径规划等功能提供了强大的搜索能力支持。
## 核心功能
### 1. POI 搜索
- **关键词搜索**:基于关键词的兴趣点搜索
- **周边搜索**:指定位置周边的 POI 搜索
- **分类搜索**:按类别筛选的 POI 搜索
- **详情查询**:获取 POI 的详细信息
### 2. 路线规划
- **驾车路线**:多种驾车路径规划策略
- **步行路线**:行人路径规划
- **骑行路线**:骑行路径规划
- **公交路线**:公共交通路径规划
- **货车路线**:货车专用路径规划
### 3. 地址解析
- **地理编码**:地址转换为坐标
- **逆地理编码**:坐标转换为地址
- **输入提示**:地址输入智能提示
- **行政区查询**:行政区域信息查询
### 4. 距离计算
- **直线距离**:两点间直线距离计算
- **驾车距离**:实际驾车距离和时间
- **步行距离**:步行距离和时间
- **批量计算**:批量距离矩阵计算
## 技术架构
### 架构设计
```
┌─────────────────────────────────────┐
│ 应用层 │
│ (app_navigation, app_car) │
├─────────────────────────────────────┤
│ AMap Flutter Search │
│ ┌──────────┬──────────┬──────────┐ │
│ │ POI搜索 │ 路线规划 │ 地址解析 │ │
│ ├──────────┼──────────┼──────────┤ │
│ │ 距离计算 │ 缓存管理 │ 数据处理 │ │
│ └──────────┴──────────┴──────────┘ │
├─────────────────────────────────────┤
│ 高德地图 Search SDK │
│ ┌──────────┬──────────┬──────────┐ │
│ │ Android │ iOS │ 搜索引擎 │ │
│ └──────────┴──────────┴──────────┘ │
├─────────────────────────────────────┤
│ 网络服务层 │
│ (REST API, WebService) │
└─────────────────────────────────────┘
```
### 核心组件
#### 1. POI 搜索管理器 (PoiSearchManager)
```dart
class AMapPoiSearchManager {
// 关键词搜索
Future<PoiResult> searchByKeyword(PoiKeywordSearchOption option);
// 周边搜索
Future<PoiResult> searchNearby(PoiNearbySearchOption option);
// ID 搜索
Future<PoiItem> searchById(String poiId);
// 分类搜索
Future<PoiResult> searchByCategory(PoiCategorySearchOption option);
}
```
#### 2. 路线规划管理器 (RouteSearchManager)
```dart
class AMapRouteSearchManager {
// 驾车路线规划
Future<DriveRouteResult> calculateDriveRoute(DriveRouteQuery query);
// 步行路线规划
Future<WalkRouteResult> calculateWalkRoute(WalkRouteQuery query);
// 骑行路线规划
Future<RideRouteResult> calculateRideRoute(RideRouteQuery query);
// 公交路线规划
Future<BusRouteResult> calculateBusRoute(BusRouteQuery query);
// 货车路线规划
Future<TruckRouteResult> calculateTruckRoute(TruckRouteQuery query);
}
```
#### 3. 地理编码管理器 (GeocodeManager)
```dart
class AMapGeocodeManager {
// 地理编码
Future<GeocodeResult> geocode(GeocodeQuery query);
// 逆地理编码
Future<RegeocodeResult> reverseGeocode(RegeocodeQuery query);
// 输入提示
Future<List<TipItem>> inputTips(InputTipsQuery query);
// 行政区查询
Future<DistrictResult> searchDistrict(DistrictQuery query);
}
```
#### 4. 距离计算器 (DistanceCalculator)
```dart
class AMapDistanceCalculator {
// 计算两点距离
Future<DistanceResult> calculateDistance(DistanceQuery query);
// 批量距离计算
Future<List<DistanceResult>> calculateDistances(List<DistanceQuery> queries);
// 路径距离计算
Future<RouteDistanceResult> calculateRouteDistance(RouteDistanceQuery query);
}
```
## 数据模型
### POI 模型
```dart
class PoiItem {
final String poiId; // POI ID
final String title; // POI 名称
final String snippet; // POI 描述
final LatLng latLng; // POI 坐标
final String distance; // 距离
final String tel; // 电话
final String postcode; // 邮编
final String website; // 网站
final String email; // 邮箱
final String province; // 省份
final String city; // 城市
final String district; // 区县
final String address; // 地址
final String direction; // 方向
final String businessArea; // 商圈
final String typeCode; // 类型编码
final String typeDes; // 类型描述
final List<Photo> photos; // 照片列表
final List<String> shopID; // 商铺ID
final IndoorData indoorData; // 室内数据
}
class PoiResult {
final List<PoiItem> pois;
final int pageCount;
final int totalCount;
final SearchBound searchBound;
final PoiQuery query;
}
```
### 路线模型
```dart
class DriveRouteResult {
final List<DrivePath> paths;
final RouteQuery startPos;
final RouteQuery targetPos;
final List<RouteQuery> viaPoints;
final int taxiCost;
}
class DrivePath {
final String strategy; // 策略
final int distance; // 距离(米)
final int duration; // 时长(秒)
final String tolls; // 过路费
final String tollDistance; // 收费距离
final String totalTrafficLights; // 红绿灯数
final List<DriveStep> steps; // 路径段
final List<TMC> tmcs; // 路况信息
}
class DriveStep {
final String instruction; // 行驶指示
final String orientation; // 方向
final String road; // 道路名称
final int distance; // 距离
final int duration; // 时长
final List<LatLng> polyline; // 路径坐标
final String action; // 导航动作
final String assistantAction; // 辅助动作
}
```
### 地址模型
```dart
class RegeocodeResult {
final RegeocodeAddress regeocodeAddress;
final List<PoiItem> pois;
final List<Road> roads;
final List<RoadInter> roadInters;
final List<AoiItem> aois;
}
class RegeocodeAddress {
final String formattedAddress; // 格式化地址
final String country; // 国家
final String province; // 省份
final String city; // 城市
final String district; // 区县
final String township; // 乡镇
final String neighborhood; // 社区
final String building; // 建筑
final String adcode; // 区域编码
final String citycode; // 城市编码
final List<StreetNumber> streetNumbers; // 门牌信息
}
```
## API 接口
### POI 搜索接口
```dart
abstract class PoiSearchService {
// 关键词搜索
Future<ApiResponse<PoiResult>> searchByKeyword(PoiKeywordSearchRequest request);
// 周边搜索
Future<ApiResponse<PoiResult>> searchNearby(PoiNearbySearchRequest request);
// 多边形搜索
Future<ApiResponse<PoiResult>> searchInPolygon(PoiPolygonSearchRequest request);
// POI 详情
Future<ApiResponse<PoiItem>> getPoiDetail(String poiId);
}
```
### 路线规划接口
```dart
abstract class RouteSearchService {
// 驾车路线规划
Future<ApiResponse<DriveRouteResult>> planDriveRoute(DriveRouteRequest request);
// 步行路线规划
Future<ApiResponse<WalkRouteResult>> planWalkRoute(WalkRouteRequest request);
// 骑行路线规划
Future<ApiResponse<RideRouteResult>> planRideRoute(RideRouteRequest request);
// 公交路线规划
Future<ApiResponse<BusRouteResult>> planBusRoute(BusRouteRequest request);
}
```
### 地理编码接口
```dart
abstract class GeocodeService {
// 地理编码
Future<ApiResponse<GeocodeResult>> geocode(GeocodeRequest request);
// 逆地理编码
Future<ApiResponse<RegeocodeResult>> reverseGeocode(RegeocodeRequest request);
// 输入提示
Future<ApiResponse<List<TipItem>>> getInputTips(InputTipsRequest request);
// 行政区搜索
Future<ApiResponse<DistrictResult>> searchDistrict(DistrictRequest request);
}
```
## 配置管理
### 搜索配置
```dart
class SearchConfig {
final String apiKey; // API 密钥
final int timeoutMs; // 超时时间
final int maxRetries; // 最大重试次数
final bool enableCache; // 是否启用缓存
final int cacheExpireTime; // 缓存过期时间
final String language; // 语言设置
final String region; // 区域设置
static const SearchConfig defaultConfig = SearchConfig(
apiKey: '',
timeoutMs: 30000,
maxRetries: 3,
enableCache: true,
cacheExpireTime: 3600,
language: 'zh-CN',
region: 'CN',
);
}
```
### POI 搜索配置
```dart
class PoiSearchConfig {
final int pageSize; // 每页结果数
final int maxPage; // 最大页数
final String city; // 搜索城市
final String types; // 搜索类型
final bool extensions; // 是否返回扩展信息
final String sortrule; // 排序规则
static const PoiSearchConfig defaultConfig = PoiSearchConfig(
pageSize: 20,
maxPage: 100,
city: '',
types: '',
extensions: false,
sortrule: 'distance',
);
}
```
## 使用示例
### POI 搜索示例
```dart
// 关键词搜索
final searchOption = PoiKeywordSearchOption(
keyword: '加油站',
city: '北京',
pageSize: 20,
pageNum: 1,
extensions: true,
);
final poiResult = await AMapPoiSearchManager.instance.searchByKeyword(searchOption);
for (final poi in poiResult.pois) {
print('POI 名称: ${poi.title}');
print('POI 地址: ${poi.address}');
print('POI 距离: ${poi.distance}');
print('POI 坐标: ${poi.latLng.latitude}, ${poi.latLng.longitude}');
}
```
### 周边搜索示例
```dart
// 周边搜索
final nearbyOption = PoiNearbySearchOption(
keyword: '餐厅',
center: LatLng(39.9042, 116.4074), // 搜索中心点
radius: 1000, // 搜索半径 1000 米
pageSize: 10,
pageNum: 1,
);
final nearbyResult = await AMapPoiSearchManager.instance.searchNearby(nearbyOption);
print('附近餐厅数量: ${nearbyResult.pois.length}');
for (final restaurant in nearbyResult.pois) {
print('餐厅: ${restaurant.title} - ${restaurant.distance}');
}
```
### 路线规划示例
```dart
// 驾车路线规划
final driveQuery = DriveRouteQuery(
fromPoint: RouteQuery(39.9042, 116.4074), // 起点
toPoint: RouteQuery(39.9015, 116.3974), // 终点
mode: DriveMode.fastest, // 最快路线
avoidhighspeed: false, // 不避开高速
avoidtolls: false, // 不避开收费
avoidtrafficjam: true, // 避开拥堵
);
final driveResult = await AMapRouteSearchManager.instance.calculateDriveRoute(driveQuery);
if (driveResult.paths.isNotEmpty) {
final path = driveResult.paths.first;
print('驾车距离: ${path.distance} 米');
print('预计时间: ${path.duration} 秒');
print('过路费: ${path.tolls} 元');
for (final step in path.steps) {
print('导航指示: ${step.instruction}');
print('道路: ${step.road}');
print('距离: ${step.distance} 米');
}
}
```
### 地理编码示例
```dart
// 地址转坐标
final geocodeQuery = GeocodeQuery(
locationName: '北京市朝阳区望京SOHO',
city: '北京',
);
final geocodeResult = await AMapGeocodeManager.instance.geocode(geocodeQuery);
if (geocodeResult.geocodeAddressList.isNotEmpty) {
final address = geocodeResult.geocodeAddressList.first;
print('地址坐标: ${address.latLonPoint.latitude}, ${address.latLonPoint.longitude}');
}
// 坐标转地址
final regeocodeQuery = RegeocodeQuery(
latLonPoint: LatLng(39.9915, 116.4684),
radius: 200.0,
extensions: 'all',
);
final regeocodeResult = await AMapGeocodeManager.instance.reverseGeocode(regeocodeQuery);
print('格式化地址: ${regeocodeResult.regeocodeAddress.formattedAddress}');
```
### 输入提示示例
```dart
// 地址输入提示
final tipsQuery = InputTipsQuery(
keyword: '王府井',
city: '北京',
datatype: 'all',
);
final tipsList = await AMapGeocodeManager.instance.inputTips(tipsQuery);
for (final tip in tipsList) {
print('提示: ${tip.name}');
print('地址: ${tip.address}');
if (tip.point != null) {
print('坐标: ${tip.point!.latitude}, ${tip.point!.longitude}');
}
}
```
## 测试策略
### 单元测试
```dart
group('AMapSearch Tests', () {
test('should search POI by keyword', () async {
// Given
final searchOption = PoiKeywordSearchOption(
keyword: '测试POI',
city: '北京',
);
// When
final result = await AMapPoiSearchManager.instance.searchByKeyword(searchOption);
// Then
expect(result.pois, isNotEmpty);
expect(result.pois.first.title, contains('测试POI'));
});
test('should calculate drive route', () async {
// Given
final query = DriveRouteQuery(
fromPoint: RouteQuery(39.9042, 116.4074),
toPoint: RouteQuery(39.9015, 116.3974),
);
// When
final result = await AMapRouteSearchManager.instance.calculateDriveRoute(query);
// Then
expect(result.paths, isNotEmpty);
expect(result.paths.first.distance, greaterThan(0));
});
});
```
### 集成测试
```dart
group('Search Integration Tests', () {
testWidgets('complete search flow', (tester) async {
// 1. 搜索 POI
final pois = await searchNearbyPois('加油站', currentLocation);
// 2. 选择目标 POI
final targetPoi = pois.first;
// 3. 规划路线
final route = await planRouteToDestination(currentLocation, targetPoi.latLng);
// 4. 验证结果
expect(route.paths, isNotEmpty);
expect(route.paths.first.steps, isNotEmpty);
});
});
```
## 性能优化
### 搜索优化
- **搜索缓存**:缓存常用搜索结果
- **预测搜索**:基于历史预测用户搜索
- **分页加载**:大结果集分页加载
- **搜索建议**:智能搜索建议减少请求
### 路线优化
- **路线缓存**:缓存常用路线规划结果
- **增量更新**:只更新路线变化部分
- **压缩传输**:压缩路线数据减少传输量
- **多线程处理**:并行处理多个路线请求
## 错误处理
### 搜索错误类型
```dart
enum SearchErrorType {
networkError, // 网络错误
apiKeyInvalid, // API Key 无效
quotaExceeded, // 配额超限
invalidParameter, // 参数无效
noResult, // 无搜索结果
serverError, // 服务器错误
timeout // 请求超时
}
class SearchException implements Exception {
final SearchErrorType type;
final String message;
final int? errorCode;
const SearchException(this.type, this.message, [this.errorCode]);
}
```
### 错误处理示例
```dart
try {
final result = await AMapPoiSearchManager.instance.searchByKeyword(searchOption);
// 处理搜索结果
} on SearchException catch (e) {
switch (e.type) {
case SearchErrorType.networkError:
showErrorDialog('网络连接失败,请检查网络设置');
break;
case SearchErrorType.noResult:
showInfoDialog('未找到相关结果,请尝试其他关键词');
break;
case SearchErrorType.quotaExceeded:
showErrorDialog('搜索次数已达上限,请稍后再试');
break;
default:
showErrorDialog('搜索失败: ${e.message}');
}
} catch (e) {
showErrorDialog('未知错误: $e');
}
```
## 缓存策略
### 搜索结果缓存
```dart
class SearchCache {
static const int maxCacheSize = 1000;
static const Duration cacheExpiration = Duration(hours: 1);
// 缓存 POI 搜索结果
static Future<void> cachePoiResult(String key, PoiResult result);
// 获取缓存的 POI 结果
static Future<PoiResult?> getCachedPoiResult(String key);
// 缓存路线规划结果
static Future<void> cacheRouteResult(String key, DriveRouteResult result);
// 获取缓存的路线结果
static Future<DriveRouteResult?> getCachedRouteResult(String key);
// 清理过期缓存
static Future<void> cleanExpiredCache();
}
```
## 配额管理
### API 配额监控
```dart
class QuotaManager {
// 获取当前配额使用情况
static Future<QuotaInfo> getCurrentQuota();
// 检查是否可以发起请求
static Future<bool> canMakeRequest(RequestType type);
// 记录请求使用
static Future<void> recordRequest(RequestType type);
// 获取配额重置时间
static Future<DateTime> getQuotaResetTime();
}
class QuotaInfo {
final int dailyLimit; // 日限额
final int dailyUsed; // 日使用量
final int monthlyLimit; // 月限额
final int monthlyUsed; // 月使用量
final DateTime resetTime; // 重置时间
}
```
## 版本历史
### v3.1.22+1 (当前版本)
- 支持最新的高德地图 API
- 新增货车路线规划功能
- 优化搜索性能和准确性
- 修复路线规划稳定性问题
### v3.1.21
- 支持室内 POI 搜索
- 新增批量距离计算
- 优化内存使用
- 改进错误处理机制
## 依赖关系
### 内部依赖
- `amap_flutter_base`: 高德地图基础库
- `basic_network`: 网络请求服务
- `basic_storage`: 本地存储服务
### 外部依赖
- `dio`: HTTP 客户端
- `cached_network_image`: 图片缓存
- `sqflite`: 本地数据库
## 总结
`amap_flutter_search` 作为高德地图搜索服务的 Flutter 插件,为 OneApp 提供了全面的地理信息搜索能力。通过集成 POI 搜索、路线规划、地理编码、距离计算等功能,该插件能够满足车辆导航、位置服务、路径规划等多种业务需求。
插件在设计上充分考虑了性能优化、缓存管理、错误处理、配额控制等关键因素,确保在提供高质量搜索服务的同时,保持良好的用户体验和系统稳定性。这为 OneApp 的车联网和导航服务提供了强大的技术支撑。