鸿蒙实现滴滴出行项目之线路规划图
目录:
- 一、核心流程概览
- 二、实现步骤
- Step 1:申请地图WebService API密钥
- Step 2:封装线路规划API调用
- Step 3:创建线路规划页面(输入起点终点)
- Step 4:在地图页渲染线路(WebView实现)
- Step 5:创建本地H5地图文件
- 三、关键功能解析
- 1. 高德线路规划API参数与返回值
- 2. 线路渲染核心JS代码
- 3. 距离与时间格式化
- 四、避坑指南
- 1. 地址转经纬度(坐标解析)
- 2. 坐标系一致性
- 3. WebView本地H5文件路径
- 4. API Key权限
- 五、效果对比
- 六、完整项目结构
一、核心流程概览
二、实现步骤
Step 1:申请地图WebService API密钥
以 高德地图 为例(百度地图流程类似):
登录 高德开放平台 → 创建应用 → 添加 “Web服务” 类型Key,获取 API Key(需记录)。
开通 “路径规划” 服务(免费额度满足开发需求)。
Step 2:封装线路规划API调用
创建 RoutePlanService.ets,调用高德地图 驾车路线规划接口(支持起点终点经纬度/地址):
// services/RoutePlanService.ets
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';export class RoutePlanService {private apiKey: string = '你的高德WebService API Key'; // 替换为实际Keyprivate driveRouteUrl: string = 'https://restapi.amap.com/v3/direction/driving'; // 驾车路线接口/*** 规划驾车路线(支持地址或经纬度)* @param origin 起点(格式:"经度,纬度" 或 "地址")* @param destination 终点(格式同上)* @returns 线路数据 { path: 经纬度数组, distance: 距离(米), duration: 时间(秒) }*/async planDriveRoute(origin: string, destination: string): Promise<{path: Array<{ longitude: number; latitude: number }>; // 线路经纬度数组distance: number; // 总距离(米)duration: number; // 总时间(秒)}> {return new Promise((resolve, reject) => {// 1. 构建请求参数const params = new Map<string, string>();params.set('key', this.apiKey);params.set('origin', origin); // 起点(如"113.3245,23.1022"或"广州市广州塔")params.set('destination', destination); // 终点(同上)params.set('strategy', '0'); // 路线策略:0=最快路线// 2. 拼接请求URLconst queryString = Array.from(params.entries()).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');const requestUrl = `${this.driveRouteUrl}?${queryString}`;// 3. 发起HTTP GET请求let httpRequest = http.createHttp();httpRequest.request(requestUrl,{ method: http.RequestMethod.GET },(err: BusinessError, data: http.HttpResponse) => {httpRequest.destroy(); // 销毁请求实例if (err) {reject(`路线规划失败: ${err.message}`);return;}// 4. 解析返回数据(高德驾车路线接口格式)if (data.responseCode === 200) {const result = JSON.parse(data.result as string);if (result.status === '1' && result.route && result.route.paths.length > 0) {const pathData = result.route.paths[0]; // 取第一条路线// 解析线路经纬度(高德返回格式:"经度,纬度;经度,纬度;...")const pathArray = pathData.polyline.split(';').map((point: string) => {const [lng, lat] = point.split(',').map(Number);return { longitude: lng, latitude: lat };});resolve({path: pathArray,distance: Number(pathData.distance), // 距离(米)duration: Math.ceil(Number(pathData.duration)) // 时间(秒)});} else {reject(`路线规划无结果: ${result.info || '未知错误'}`);}} else {reject(`HTTP错误: ${data.responseCode}`);}});});}
}
Step 3:创建线路规划页面(输入起点终点)
创建 RoutePlanPage.ets,实现用户输入起点终点并触发线路规划:
// pages/RoutePlanPage.ets
import { RoutePlanService } from '../services/RoutePlanService';
import router from '@ohos.router';
import promptAction from '@ohos.promptAction';@Entry
@Component
struct RoutePlanPage {@State origin: string = ''; // 起点@State destination: string = ''; // 终点private routeService: RoutePlanService = new RoutePlanService();build() {Column() {// 1. 输入框(起点+终点)Column() {TextInput({ placeholder: '请输入起点(地址或位置)' }).width('90%').height(45).margin(5).padding(10).backgroundColor('#FFFFFF').borderRadius(5);TextInput({ placeholder: '请输入终点(地址或位置)' }).width('90%').height(45).margin(5).padding(10).backgroundColor('#FFFFFF').borderRadius(5);Button('规划路线').width('90%').height(45).margin(10).backgroundColor('#007AFF').onClick(async () => {if (!this.origin || !this.destination) {promptAction.showToast({ message: '请输入起点和终点' });return;}// 2. 调用线路规划API并跳转地图页try {const routeData = await this.routeService.planDriveRoute(this.origin, this.destination);// 跳转到地图页并传递线路数据router.pushUrl({url: 'pages/MapRoutePage',params: { routeData: routeData }});} catch (err) {promptAction.showToast({ message: `规划失败: ${err.message}` });}});}.width('100%').padding(10).backgroundColor('#F5F5F5');}.width('100%').height('100%').backgroundColor('#F5F5F5');}
}
Step 4:在地图页渲染线路(WebView实现)
创建 MapRoutePage.ets,通过 WebView加载高德H5地图 并渲染线路:
// pages/MapRoutePage.ets
import webview from '@ohos.web.webview';
import router from '@ohos.router';@Entry
@Component
struct MapRoutePage {private webController: webview.WebviewController = new webview.WebviewController();private routeData: any = null; // 从路由获取的线路数据aboutToAppear() {// 获取从规划页传递的线路数据this.routeData = router.getParams()?.routeData;if (!this.routeData) {router.back(); // 无数据时返回上一页return;}}build() {Column() {// 1. 线路信息(距离+时间)Row() {Column() {Text(`距离: ${(this.routeData.distance / 1000).toFixed(2)} 公里`).fontSize(16).color('#333333');Text(`时间: ${Math.ceil(this.routeData.duration / 60)} 分钟`).fontSize(16).color('#333333').margin({ top: 5 });}.width('100%').padding(15).backgroundColor('#FFFFFF');}.borderBottom({ width: 0.5, color: '#EEEEEE' });// 2. WebView地图(渲染线路)Web({src: $rawfile('map_route.html'), // 本地H5地图文件(需提前准备)controller: this.webController}).width('100%').height('85%').onPageEnd(() => {// 地图加载完成后,调用JS渲染线路this.renderRouteOnMap();});}.width('100%').height('100%');}/*** 调用H5地图的JS方法,渲染线路*/private renderRouteOnMap() {if (!this.routeData) return;// 1. 转换线路数据为JS数组格式const pathArray = this.routeData.path.map((p: any) => `[${p.longitude}, ${p.latitude}]`).join(',');// 2. 构造JS代码(调用高德地图API绘制线路)const jsCode = `// 初始化地图(若未初始化)if (!window.map) {window.map = new AMap.Map('mapContainer', {zoom: 14,center: [${this.routeData.path[0].longitude}, ${this.routeData.path[0].latitude}] // 起点为中心});}// 绘制线路const path = [${pathArray}]; // 线路经纬度数组new AMap.Polyline({path: path,strokeColor: '#0066FF', // 线路颜色(蓝色)strokeWeight: 6, // 线路宽度strokeOpacity: 0.8, // 透明度map: window.map});// 添加起点终点标记new AMap.Marker({ position: path[0], map: window.map, icon: '/start.png' }); // 起点标记new AMap.Marker({ position: path[path.length-1], map: window.map, icon: '/end.png' }); // 终点标记// 调整地图视野以显示完整线路window.map.setFitView();`;// 3. 在WebView中执行JS代码this.webController.executeJs({script: jsCode,callback: (result) => {if (result === null) {console.error('线路渲染JS执行失败');}}});}
}
Step 5:创建本地H5地图文件
在 main/resources/rawfile 目录下创建 map_route.html(H5地图容器,加载高德地图JS API):
<!-- rawfile/map_route.html -->
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>线路规划地图</title><!-- 引入高德地图JS API(需替换为你的Web端Key) --><script src="https://webapi.amap.com/maps?v=2.0&key=你的高德Web端API Key"></script><style>body, html { width: 100%; height: 100%; margin: 0; }#mapContainer { width: 100%; height: 100%; }</style>
</head>
<body><div id="mapContainer"></div> <!-- 地图容器 -->
</body>
</html>
三、关键功能解析
1. 高德线路规划API参数与返回值
核心参数:
- origin:起点(支持经纬度 113.3245,23.1022 或地址 广州市广州塔)。
- destination:终点(同上)。
- strategy:路线策略(0=最快路线,1=最短路线,3=躲避拥堵)。
返回数据(简化):
{"status": "1","route": {"paths": [{"polyline": "113.3245,23.1022;113.3255,23.1032;...", // 线路经纬度字符串(分号分隔)"distance": "14085", // 距离(米)"duration": "1140" // 时间(秒)}]}}
2. 线路渲染核心JS代码
在H5地图中,通过高德地图JS API绘制线路:
- Polyline组件:用于绘制折线,path 参数传入经纬度数组。
- Marker组件:添加起点终点标记(可自定义图标)。
- setFitView():自动调整地图视野,确保完整显示线路。
3. 距离与时间格式化
- 将API返回的 距离(米) 转换为公里:distance / 1000。
- 将 时间(秒) 转换为分钟:Math.ceil(duration / 60)。
四、避坑指南
1. 地址转经纬度(坐标解析)
若用户输入的是地址(如“广州塔”),需先调用 地理编码API 转换为经纬度:
// 地理编码接口(高德):https://restapi.amap.com/v3/geocode/geo
// 将地址转换为经纬度:"广州市广州塔" → "113.330943,23.113406"
2. 坐标系一致性
高德WebService API返回的是 GCJ02坐标系(火星坐标),H5地图JS API默认使用GCJ02,无需转换。若使用其他坐标系地图(如WGS84),需用 coordtransform 库转换。
3. WebView本地H5文件路径
H5文件需放在 main/resources/rawfile 目录,加载路径为 $rawfile(‘map_route.html’),确保WebView能正确访问。
4. API Key权限
- WebService Key:用于后端接口调用(如 RoutePlanService)。
- Web端JS Key:用于H5地图加载(map_route.html 中的高德JS API)。 两者需分别申请,不可混用。
五、效果对比
- 输入起点终点:用户输入“广州塔”和“广州东站”。
- 线路规划结果:API返回距离 14085米(14.085公里),时间 1140秒(19分钟)。
- 地图渲染:WebView中显示蓝色线路,起点绿色标记,终点红色标记,顶部显示距离和时间(与截图效果一致)。
六、完整项目结构
总结:
实现乘客输入起点终点后展示线路规划图的核心步骤为:
- 输入起点终点:通过TextInput获取用户输入。
- 调用线路规划API:使用高德WebService接口计算路线,获取经纬度数组、距离、时间。
- WebView渲染线路:通过H5地图JS API绘制折线、标记点,显示完整路线。
- 展示线路信息:格式化距离和时间并显示在页面顶部。
此方案低成本实现了截图中的线路规划功能,依赖地图WebService API和WebView,无需集成复杂的原生地图SDK。