鸿蒙实现滴滴出行项目之线路规划图

目录:

    • 一、核心流程概览
    • 二、实现步骤
      • [Step 1:申请地图WebService API密钥](#Step 1:申请地图WebService API密钥)
      • [Step 2:封装线路规划API调用](#Step 2:封装线路规划API调用)
      • [Step 3:创建线路规划页面(输入起点终点)](#Step 3:创建线路规划页面(输入起点终点))
      • [Step 4:在地图页渲染线路(WebView实现)](#Step 4:在地图页渲染线路(WebView实现))
      • [Step 5:创建本地H5地图文件](#Step 5:创建本地H5地图文件)
    • 三、关键功能解析
      • [1. 高德线路规划API参数与返回值](#1. 高德线路规划API参数与返回值)
      • [2. 线路渲染核心JS代码](#2. 线路渲染核心JS代码)
      • [3. 距离与时间格式化](#3. 距离与时间格式化)
    • 四、避坑指南
      • [1. 地址转经纬度(坐标解析)](#1. 地址转经纬度(坐标解析))
      • [2. 坐标系一致性](#2. 坐标系一致性)
      • [3. WebView本地H5文件路径](#3. WebView本地H5文件路径)
      • [4. API Key权限](#4. API Key权限)
    • 五、效果对比
    • 六、完整项目结构

一、核心流程概览

二、实现步骤

Step 1:申请地图WebService API密钥

以 高德地图 为例(百度地图流程类似):

登录 高德开放平台 → 创建应用 → 添加 "Web服务" 类型Key,获取 API Key(需记录)。

开通 "路径规划" 服务(免费额度满足开发需求)。

Step 2:封装线路规划API调用

创建 RoutePlanService.ets,调用高德地图 驾车路线规划接口(支持起点终点经纬度/地址):

typescript 复制代码
// services/RoutePlanService.ets
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';

export class RoutePlanService {
  private apiKey: string = '你的高德WebService API Key'; // 替换为实际Key
  private 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. 拼接请求URL
      const 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,实现用户输入起点终点并触发线路规划:

typescript 复制代码
// 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地图 并渲染线路:

typescript 复制代码
// 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):

typescript 复制代码
<!-- 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=躲避拥堵)。

返回数据(简化):

typescript 复制代码
  {
    "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 转换为经纬度:

typescript 复制代码
// 地理编码接口(高德):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。

相关推荐
我是华为OD~HR~栗栗呀4 小时前
23届考研-Java面经(华为OD)
java·c++·python·华为od·华为·面试
路很长OoO5 小时前
Flutter 插件开发实战:桥接原生 SDK
前端·flutter·harmonyos
2501_919749037 小时前
鸿蒙:使用Rating组件实现五角星打分评价
华为·harmonyos
2501_9197490311 小时前
鸿蒙:实现滑动选择日期操作
华为·harmonyos
程序员潘Sir14 小时前
鸿蒙应用开发从入门到实战(十九):样式复用案例
harmonyos·鸿蒙
std8602116 小时前
华为 Mate80 要来了,或搭载最新麒麟芯片
华为
2501_9197490316 小时前
鸿蒙:使用@Reusable实现组件的复用,提升性能
华为·harmonyos
高工智能汽车1 天前
棱镜观察|极氪销量遇阻?千里智驾左手服务吉利、右手对标华为
人工智能·华为