第八篇:路线规划功能

本篇教程将学习如何使用高德地图实现驾车、步行、骑行等路线规划功能。

学习目标

  • 实现驾车路线规划
  • 实现步行路线规划
  • 实现骑行路线规划
  • 在地图上绑制规划路线
  • 显示路线详情信息

1. 路线规划类型

类型 类名 说明
驾车 DriveRouteQuery 汽车导航路线
步行 WalkRouteQuery 步行路线
骑行 RideRouteQuery 骑行路线
公交 BusRouteQuery 公共交通路线
货车 TruckRouteQuery 货车路线

2. 核心类说明

类名 说明
RouteSearch 路线搜索核心类
FromAndTo 起终点配置
LatLonPoint 坐标点
DriveRouteResult 驾车结果
WalkRouteResult 步行结果
DrivePath 驾车路径
DriveStep 驾车步骤

3. 完整代码示例

创建文件 entry/src/main/ets/pages/Demo07_Route.ets

import {
  AMap,
  MapView,
  MapViewComponent,
  MapViewManager,
  MapViewCreateCallback,
  CameraUpdateFactory,
  LatLng,
  LatLngBounds,
  Marker,
  MarkerOptions,
  Polyline,
  PolylineOptions,
  BitmapDescriptorFactory
} from '@amap/amap_lbs_map3d';
import {
  RouteSearch,
  DriveRouteQuery,
  DriveRouteResult,
  WalkRouteQuery,
  WalkRouteResult,
  RideRouteQuery,
  RideRouteResult,
  BusRouteResult,
  FromAndTo,
  LatLonPoint,
  OnRouteSearchListener,
  AMapException,
  DrivePath,
  DriveStep,
  WalkPath,
  WalkStep,
  RidePath,
  RideStep
} from '@amap/amap_lbs_search';

const MAP_VIEW_NAME = 'RouteDemo';

/**
 * 路线类型
 */
type RouteType = 'drive' | 'walk' | 'ride';

/**
 * 路线步骤信息
 */
interface RouteStepInfo {
  instruction: string;
  distance: number;
  duration: number;
}

@Entry
@Component
struct Demo07_Route {
  private mapView: MapView | undefined = undefined;
  private aMap: AMap | undefined = undefined;
  private routeSearch: RouteSearch | undefined = undefined;
  private routePolyline: Polyline | undefined = undefined;
  private startMarker: Marker | undefined = undefined;
  private endMarker: Marker | undefined = undefined;
  
  @State isMapReady: boolean = false;
  @State routeType: RouteType = 'drive';
  @State isSearching: boolean = false;
  @State routeInfo: string = '';
  @State routeSteps: RouteStepInfo[] = [];
  
  // 起终点坐标
  private startPoint: LatLonPoint = new LatLonPoint(39.942295, 116.335891);  // 北京西站
  private endPoint: LatLonPoint = new LatLonPoint(39.995576, 116.481288);    // 望京SOHO

  /**
   * 路线搜索回调
   */
  private routeSearchListener: OnRouteSearchListener = {
    // 驾车路线回调
    onDriveRouteSearched: (result: DriveRouteResult | undefined, errorCode: number) => {
      this.isSearching = false;
      
      if (errorCode === AMapException.CODE_AMAP_SUCCESS && result) {
        const paths = result.getPaths();
        if (paths && paths.length > 0) {
          const path = paths[0] as DrivePath;
          this.showDriveRoute(path, result);
        } else {
          this.routeInfo = '未找到驾车路线';
        }
      } else {
        this.routeInfo = `驾车路线查询失败: ${errorCode}`;
      }
    },
    
    // 步行路线回调
    onWalkRouteSearched: (result: WalkRouteResult | undefined, errorCode: number) => {
      this.isSearching = false;
      
      if (errorCode === AMapException.CODE_AMAP_SUCCESS && result) {
        const paths = result.getPaths();
        if (paths && paths.length > 0) {
          const path = paths[0] as WalkPath;
          this.showWalkRoute(path);
        } else {
          this.routeInfo = '未找到步行路线';
        }
      } else {
        this.routeInfo = `步行路线查询失败: ${errorCode}`;
      }
    },
    
    // 骑行路线回调
    onRideRouteSearched: (result: RideRouteResult | undefined, errorCode: number) => {
      this.isSearching = false;
      
      if (errorCode === AMapException.CODE_AMAP_SUCCESS && result) {
        const paths = result.getPaths();
        if (paths && paths.length > 0) {
          const path = paths[0] as RidePath;
          this.showRideRoute(path);
        } else {
          this.routeInfo = '未找到骑行路线';
        }
      } else {
        this.routeInfo = `骑行路线查询失败: ${errorCode}`;
      }
    },
    
    // 公交路线回调
    onBusRouteSearched: (result: BusRouteResult | undefined, errorCode: number) => {
      this.isSearching = false;
      this.routeInfo = '公交路线功能待实现';
    }
  };

  private mapViewCreateCallback: MapViewCreateCallback = 
    (mapview: MapView | undefined, mapViewName: string | undefined) => {
      if (!mapview || mapViewName !== MAP_VIEW_NAME) return;

      this.mapView = mapview;
      this.mapView.onCreate();
      
      this.mapView.getMapAsync((map: AMap) => {
        this.aMap = map;
        this.isMapReady = true;
        
        // 设置初始视野
        const center = new LatLng(39.97, 116.41);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 11));
        
        // 启用控件
        map.getUiSettings()?.setZoomControlsEnabled(true);
        
        // 添加起终点标记
        this.addStartEndMarkers();
        
        // 地图长按设置终点
        map.setOnMapLongClickListener((point: LatLng) => {
          this.endPoint = new LatLonPoint(point.latitude, point.longitude);
          this.updateEndMarker();
          this.routeInfo = '终点已更新,点击搜索查看路线';
        });
      });
    };

  /**
   * 添加起终点标记
   */
  private addStartEndMarkers(): void {
    if (!this.aMap) return;
    
    // 起点标记
    const startOptions = new MarkerOptions();
    startOptions.setPosition(new LatLng(this.startPoint.getLatitude(), this.startPoint.getLongitude()));
    startOptions.setTitle('起点');
    startOptions.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
    startOptions.setAnchor(0.5, 1.0);
    this.startMarker = this.aMap.addMarker(startOptions);
    
    // 终点标记
    const endOptions = new MarkerOptions();
    endOptions.setPosition(new LatLng(this.endPoint.getLatitude(), this.endPoint.getLongitude()));
    endOptions.setTitle('终点');
    endOptions.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
    endOptions.setAnchor(0.5, 1.0);
    this.endMarker = this.aMap.addMarker(endOptions);
  }

  /**
   * 更新终点标记
   */
  private updateEndMarker(): void {
    if (this.endMarker) {
      this.endMarker.setPosition(new LatLng(this.endPoint.getLatitude(), this.endPoint.getLongitude()));
    }
  }

  /**
   * 搜索路线
   */
  private searchRoute(): void {
    if (!this.routeSearch) return;
    
    this.isSearching = true;
    this.routeInfo = '正在搜索路线...';
    this.routeSteps = [];
    
    // 清除之前的路线
    this.clearRouteLine();
    
    // 创建起终点
    const fromAndTo = new FromAndTo(this.startPoint, this.endPoint);
    
    switch (this.routeType) {
      case 'drive':
        this.searchDriveRoute(fromAndTo);
        break;
      case 'walk':
        this.searchWalkRoute(fromAndTo);
        break;
      case 'ride':
        this.searchRideRoute(fromAndTo);
        break;
    }
  }

  /**
   * 搜索驾车路线
   */
  private searchDriveRoute(fromAndTo: FromAndTo): void {
    const query = new DriveRouteQuery(
      fromAndTo,
      RouteSearch.DrivingDefault,   // 驾车策略
      undefined,                     // 途经点
      undefined,                     // 避让区域
      ''                            // 避让道路
    );
    query.setExtensions(RouteSearch.EXTENSIONS_ALL);
    
    this.routeSearch?.calculateDriveRouteAsyn(query);
    console.info('[Route] Searching drive route...');
  }

  /**
   * 搜索步行路线
   */
  private searchWalkRoute(fromAndTo: FromAndTo): void {
    const query = new WalkRouteQuery(fromAndTo);
    
    this.routeSearch?.calculateWalkRouteAsyn(query);
    console.info('[Route] Searching walk route...');
  }

  /**
   * 搜索骑行路线
   */
  private searchRideRoute(fromAndTo: FromAndTo): void {
    const query = new RideRouteQuery(fromAndTo);
    
    this.routeSearch?.calculateRideRouteAsyn(query);
    console.info('[Route] Searching ride route...');
  }

  /**
   * 显示驾车路线
   */
  private showDriveRoute(path: DrivePath, result: DriveRouteResult): void {
    const distance = path.getDistance();
    const duration = path.getDuration();
    const taxiCost = result.getTaxiCost();
    
    this.routeInfo = `驾车路线\n距离: ${this.formatDistance(distance)}\n时间: ${this.formatDuration(duration)}\n预估打车费: ¥${Math.floor(taxiCost)}`;
    
    // 解析路线步骤
    const steps = path.getSteps();
    if (steps) {
      this.routeSteps = [];
      for (let i = 0; i < steps.length; i++) {
        const step = steps[i] as DriveStep;
        this.routeSteps.push({
          instruction: step.getInstruction() || '',
          distance: step.getDistance(),
          duration: step.getDuration()
        });
      }
    }
    
    // 绘制路线
    this.drawRouteLine(path.getSteps(), '#4CAF50');
  }

  /**
   * 显示步行路线
   */
  private showWalkRoute(path: WalkPath): void {
    const distance = path.getDistance();
    const duration = path.getDuration();
    
    this.routeInfo = `步行路线\n距离: ${this.formatDistance(distance)}\n时间: ${this.formatDuration(duration)}`;
    
    // 解析步骤
    const steps = path.getSteps();
    if (steps) {
      this.routeSteps = [];
      for (let i = 0; i < steps.length; i++) {
        const step = steps[i] as WalkStep;
        this.routeSteps.push({
          instruction: step.getInstruction() || '',
          distance: step.getDistance(),
          duration: step.getDuration()
        });
      }
    }
    
    // 绘制路线
    this.drawRouteLine(steps, '#2196F3');
  }

  /**
   * 显示骑行路线
   */
  private showRideRoute(path: RidePath): void {
    const distance = path.getDistance();
    const duration = path.getDuration();
    
    this.routeInfo = `骑行路线\n距离: ${this.formatDistance(distance)}\n时间: ${this.formatDuration(duration)}`;
    
    // 解析步骤
    const steps = path.getSteps();
    if (steps) {
      this.routeSteps = [];
      for (let i = 0; i < steps.length; i++) {
        const step = steps[i] as RideStep;
        this.routeSteps.push({
          instruction: step.getInstruction() || '',
          distance: step.getDistance(),
          duration: step.getDuration()
        });
      }
    }
    
    // 绘制路线
    this.drawRouteLine(steps, '#FF9800');
  }

  /**
   * 绘制路线
   */
  private drawRouteLine(steps: Object[] | undefined, color: string): void {
    if (!this.aMap || !steps) return;
    
    // 收集所有坐标点
    const points: LatLng[] = [];
    
    for (const step of steps) {
      // 获取polyline
      const polyline = (step as DriveStep).getPolyline?.() || 
                       (step as WalkStep).getPolyline?.() ||
                       (step as RideStep).getPolyline?.();
      
      if (polyline) {
        for (const point of polyline) {
          points.push(new LatLng(point.getLatitude(), point.getLongitude()));
        }
      }
    }
    
    if (points.length > 0) {
      // 绘制折线
      const options = new PolylineOptions();
      options.setPoints(points);
      options.setWidth(12);
      options.setColor(this.parseColor(color));
      
      this.routePolyline = this.aMap.addPolyline(options);
      
      // 调整视野
      this.fitRouteBounds(points);
    }
  }

  /**
   * 清除路线
   */
  private clearRouteLine(): void {
    if (this.routePolyline) {
      this.routePolyline.remove();
      this.routePolyline = undefined;
    }
  }

  /**
   * 调整地图视野以显示完整路线
   */
  private fitRouteBounds(points: LatLng[]): void {
    if (!this.aMap || points.length === 0) return;
    
    let minLat = 90, maxLat = -90, minLng = 180, maxLng = -180;
    
    for (const point of points) {
      minLat = Math.min(minLat, point.latitude);
      maxLat = Math.max(maxLat, point.latitude);
      minLng = Math.min(minLng, point.longitude);
      maxLng = Math.max(maxLng, point.longitude);
    }
    
    const bounds = new LatLngBounds(
      new LatLng(minLat, minLng),
      new LatLng(maxLat, maxLng)
    );
    
    this.aMap.animateCamera(
      CameraUpdateFactory.newLatLngBounds(bounds, 80),
      500
    );
  }

  /**
   * 格式化距离
   */
  private formatDistance(meters: number): string {
    if (meters < 1000) {
      return `${meters}`;
    }
    return `${(meters / 1000).toFixed(1)}公里`;
  }

  /**
   * 格式化时间
   */
  private formatDuration(seconds: number): string {
    if (seconds < 60) {
      return `${seconds}`;
    }
    const minutes = Math.floor(seconds / 60);
    if (minutes < 60) {
      return `${minutes}分钟`;
    }
    const hours = Math.floor(minutes / 60);
    const remainMinutes = minutes % 60;
    return `${hours}小时${remainMinutes}分钟`;
  }

  /**
   * 解析颜色值
   */
  private parseColor(hex: string): number {
    const r = parseInt(hex.slice(1, 3), 16);
    const g = parseInt(hex.slice(3, 5), 16);
    const b = parseInt(hex.slice(5, 7), 16);
    return 0xFF000000 | (r << 16) | (g << 8) | b;
  }

  aboutToAppear(): void {
    MapViewManager.getInstance()
      .registerMapViewCreatedCallback(this.mapViewCreateCallback);
    
    // 初始化路线搜索
    const context = getContext(this);
    this.routeSearch = new RouteSearch(context);
    this.routeSearch.setRouteSearchListener(this.routeSearchListener);
  }

  aboutToDisappear(): void {
    this.clearRouteLine();
    
    MapViewManager.getInstance()
      .unregisterMapViewCreatedCallback(this.mapViewCreateCallback);
    
    if (this.mapView) {
      this.mapView.onDestroy();
      this.mapView = undefined;
      this.aMap = undefined;
    }
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('路线规划')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
      }
      .width('100%')
      .height(50)
      .padding({ left: 16 })
      .backgroundColor('#673AB7')

      // 路线类型选择
      Row() {
        ForEach(['drive', 'walk', 'ride'] as RouteType[], (type: RouteType) => {
          Button(type === 'drive' ? '驾车' : type === 'walk' ? '步行' : '骑行')
            .fontSize(13)
            .height(36)
            .layoutWeight(1)
            .margin({ right: type !== 'ride' ? 8 : 0 })
            .backgroundColor(this.routeType === type ? '#673AB7' : '#e0e0e0')
            .fontColor(this.routeType === type ? Color.White : '#333')
            .onClick(() => { this.routeType = type; })
        })
        
        Button('搜索')
          .fontSize(13)
          .height(36)
          .width(70)
          .margin({ left: 8 })
          .enabled(!this.isSearching)
          .onClick(() => this.searchRoute())
      }
      .width('100%')
      .padding(12)
      .backgroundColor('#f5f5f5')

      // 地图区域
      Stack() {
        MapViewComponent({ mapViewName: MAP_VIEW_NAME })
          .width('100%')
          .height('100%')
        
        // 路线信息
        if (this.routeInfo) {
          Column() {
            Text(this.routeInfo)
              .fontSize(12)
              .fontColor('#333')
          }
          .padding(10)
          .backgroundColor('rgba(255,255,255,0.95)')
          .borderRadius(8)
          .position({ x: 10, y: 10 })
        }
        
        // 操作提示
        Text('长按地图设置终点')
          .fontSize(11)
          .fontColor('#fff')
          .backgroundColor('rgba(0,0,0,0.6)')
          .padding(6)
          .borderRadius(4)
          .position({ x: 10, y: 100 })
      }
      .width('100%')
      .layoutWeight(1)

      // 路线步骤
      if (this.routeSteps.length > 0) {
        Column() {
          Text('路线详情')
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
            .padding(8)
            .width('100%')
            .backgroundColor('#e0e0e0')
          
          List() {
            ForEach(this.routeSteps, (step: RouteStepInfo, index: number) => {
              ListItem() {
                Row() {
                  Text(`${index + 1}`)
                    .fontSize(12)
                    .fontColor(Color.White)
                    .width(24)
                    .height(24)
                    .textAlign(TextAlign.Center)
                    .backgroundColor('#673AB7')
                    .borderRadius(12)
                  
                  Column() {
                    Text(step.instruction)
                      .fontSize(12)
                      .fontColor('#333')
                      .maxLines(2)
                    Text(`${this.formatDistance(step.distance)} | ${this.formatDuration(step.duration)}`)
                      .fontSize(10)
                      .fontColor('#999')
                      .margin({ top: 2 })
                  }
                  .layoutWeight(1)
                  .alignItems(HorizontalAlign.Start)
                  .margin({ left: 8 })
                }
                .width('100%')
                .padding(8)
              }
            })
          }
          .height(120)
          .divider({ strokeWidth: 1, color: '#eee' })
        }
        .backgroundColor(Color.White)
      }
    }
    .width('100%')
    .height('100%')
  }
}

4. 驾车策略说明

// 驾车策略常量
RouteSearch.DrivingDefault          // 默认(速度优先)
RouteSearch.DrivingNoHighway        // 不走高速
RouteSearch.DrivingNoFare           // 避免收费
RouteSearch.DrivingShortest         // 距离最短
RouteSearch.DrivingHighway          // 高速优先
RouteSearch.DrivingAvoidCongestion  // 躲避拥堵
RouteSearch.DrivingMultiStrategy    // 多策略

5. 设置途经点

// 添加途经点
const wayPoints: LatLonPoint[] = [
  new LatLonPoint(39.95, 116.40),
  new LatLonPoint(39.96, 116.42)
];

const query = new DriveRouteQuery(
  fromAndTo,
  RouteSearch.DrivingDefault,
  wayPoints,  // 途经点数组
  undefined,
  ''
);

6. 设置避让区域

// 避让区域(矩形)
const avoidArea: LatLonPoint[] = [
  new LatLonPoint(39.90, 116.38),  // 左下角
  new LatLonPoint(39.92, 116.40)   // 右上角
];

const avoidPolygons: LatLonPoint[][] = [avoidArea];

const query = new DriveRouteQuery(
  fromAndTo,
  RouteSearch.DrivingDefault,
  undefined,
  avoidPolygons,  // 避让区域
  ''
);

7. 路线绘制优化

7.1 使用路况颜色

// 根据路况设置不同颜色
private getTrafficColor(trafficStatus: number): number {
  switch (trafficStatus) {
    case 0: return 0xFF4CAF50;  // 畅通-绿色
    case 1: return 0xFFFFEB3B;  // 缓行-黄色
    case 2: return 0xFFFF9800;  // 拥堵-橙色
    case 3: return 0xFFF44336;  // 严重拥堵-红色
    default: return 0xFF2196F3; // 未知-蓝色
  }
}

7.2 绘制带方向的路线

// 使用带箭头的纹理
options.setLineTexture(arrowTexture);
options.setUseTexture(true);

本篇小结

本篇教程我们学习了:

  • ✅ 驾车路线规划实现
  • ✅ 步行路线规划实现
  • ✅ 骑行路线规划实现
  • ✅ 路线绘制和视野调整
  • ✅ 路线详情展示
  • ✅ 途经点和避让区域设置

下一篇我们将学习绑图与测距功能。
班级
https://developer.huawei.com/consumer/cn/training/classDetail/fd34ff9286174e848d34cde7f512ce22?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248

源码地址
https://gitcode.com/daleishen/gaodehmjiaocheng.git

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐