HarmonyOS - 鸿蒙开发百度地图案例

HarmonyOS - 鸿蒙开发百度地图案例

开发环境为:

开发工具:DevEco Studio 5.0.5 Release

API版本是:API17

本文所有代码都已使用模拟器测试成功!

1. 准备工作

我们在纯血鸿蒙系统中,如果需要调用百度SDK做一些功能的话,我们需要先做如下准备工作:

  1. 百度地图开放平台中,创建应用后,获取AK,没有这个AK,APP是无法使用百度地图的
  2. 一部纯血鸿蒙系统手机,5.0以上系统(模拟器无法调试,只能真机测试)
  3. 必须到AppGallery上申请证书等相关文件(.cer、.p12、.p7b文件,签名需要用到)
  4. DevEco Studio开发工具(SDK需要5.0以上)

以上信息准备好之后,开始进入【百度地图开放平台】官网看API进行开发,官网地址:

https://lbsyun.baidu.com/faq/api?title=harmonynextsdk/guide/create-project/engineering

参照官网一步一步配置即可

2. 实战案例编码

1. 添加SDK依赖

在【oh-package.json5】文件中添加如下依赖:

json 复制代码
"@bdmap/base": "1.2.6",
"@bdmap/search": "1.2.6",
"@bdmap/map": "1.2.6",
"@bdmap/locsdk": "1.1.4"

如下图所示:

2. 初始化

在绘制地图前初始化SDK,我这里是在【EntryAbility.ets】文件中进行初始化的,如下图所示:

图中的MAP_KEY就是百度开发平台中创建的应用的AK,上图中新增代码如下:

js 复制代码
//检查百度地图秘钥的合法性
LocationClient.checkAuthKey(MAP_KEY, (result: string) => {
    Logger.debug("百度地图秘钥合法性检测 result = " + result);
});
//初始化秘钥
Initializer.getInstance().initialize(MAP_KEY)
LocationClient.setAgreePrivacy(true) // 设置是否同意隐私政策

3. 案例实战

1. 地图打点

需求说明:

每隔1秒就在地图上标识出一个点,点的数据存在JSON文件中

效果如下所示:

地图打点演示视频

页面代码如下:

js 复制代码
interface JsonData {
  miaoshu: string;
  location: string;
}

/**
 * 地图打点
 */
@Entry
@Component
struct MapSetMarker {
  // 定位位置(维度,经度)
  startLoc = new LatLng(0, 0)
  // 控制器
  mapController: MapController | null = null;
  // 设置地图控件参数
  @State mapOpt: MapOptions = new MapOptions({
    // 默认基础地图,可通过改变satelliteMap的值加载不同的底图
    shows: { satelliteMap: SysEnum.ESatelliteLayerType.NONE },
    gestures: {
      zoom: true,
      move: true,
      rotate: true,
      overlooking: true,
    }
  });
  @State dataArray: Array<RadiationData> = []
  marker: Marker | null = null;

  //读取JSON文件经纬度数据
  readJsonData() {
    //开始读取工程目录 rawfile 中的JSON文件
    try {
      let context = getContext() as common.UIAbilityContext;
      let rawFile = context.resourceManager.getRawFileContentSync("jingdian.json");
      let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true });
      let jsonResult: string = textDecoder.decodeToString(rawFile, { stream: false })
      // Logger.debug('jsonContent=' + jsonResult)
      this.dataArray = this.parseRadiationData(jsonResult);
    } catch (error) {
      let code = (error as BusinessError).code;
      let message = (error as BusinessError).message;
      Logger.error(`getRawFileContentSync failed, error code: ${code}, message: ${message}.`);
    }
  }

  parseRadiationData(jsonString: string): RadiationData[] {
    try {
      const rawData: Array<JsonData> = JSON.parse(jsonString);
      let dataArray: Array<RadiationData> = []
      rawData.map((item: JsonData) => {
        // 提取剂量率数值(去掉单位)
        const miaoshu = item.miaoshu.trim();
        // 解析经纬度
        const locationParts = item.location.split(' ');
        if (locationParts.length !== 2) {
          throw new Error('Invalid location format');
        }
        // 提取纬度(去掉°N)
        const lat = locationParts[0].replace('°N', '').trim();
        // 提取经度(去掉°E)
        const lng = locationParts[1].replace('°E', '').trim();
        dataArray.push({
          miaoshu: miaoshu,
          lat: Number(lat),
          lng: Number(lng)
        });
      });
      return dataArray;
    } catch (error) {
      myTools.alertMsg('JSON数据解析失败:' + JSON.stringify(error))
      console.error('数据解析失败:', error);
      return [];
    }
  }

  /**
   * 在地图上添加Marker,并显示
   * @param lat 维度
   * @param lng 经度
   */
  addMarker(lat: number, lng: number) {
    Logger.debug('设置标注点----------开始')
    let marker = new Marker({
      position: new LatLng(lat, lng),
      icon: new ImageEntity('rawfile://user_loc_icon.png')
    });
    //在地图上添加Marker,并显示
    this.mapController?.addOverlay(marker);
    Logger.debug('设置标注点----------结束')
  }

  // 创建定时器实例
  private arrayTimer: ArrayTimerModel | null = null;

  aboutToAppear(): void {
    this.readJsonData();
    Logger.debug('this.dataArray=' + JSON.stringify(this.dataArray))
    this.arrayTimer = new ArrayTimerModel(this.dataArray);
    // 设置回调函数
    this.arrayTimer.setCallback((item: RadiationData, index: number) => {
      Logger.debug('获取到第' + (index + 1) + '个元素=' + JSON.stringify(item));
      this.addMarker(item.lat, item.lng);
    });
    this.arrayTimer.start(1000); // 每秒获取一个元素
  }

  // 页面销毁时清理资源
  aboutToDisappear() {
    this.arrayTimer?.destroy();
  }

  build() {
    Column() {
      MapComponent({
        onReady: (err, mapController: MapController) => {
          if (!err) {
            this.mapController = mapController;
          }
        }, mapOptions: this.mapOpt
      })
        .height('100%')
        .width('100%')
    }
    .height('100%')
    .width('100%')
  }
}

ArrayTimerModel代码如下:

js 复制代码
export interface RadiationData {
  miaoshu: string;
  lat: number; //纬度
  lng: number; //经度
}

export class ArrayTimerModel {
  private dataArray: RadiationData[] = [];
  private currentIndex: number = 0;
  private timerId: number = -1;
  private onItemCallback: (item: RadiationData, index: number) => void = () => {};

  // 初始化数据
  constructor(data: RadiationData[]) {
    this.dataArray = data;
  }

  // 设置回调函数,每次获取元素时调用
  setCallback(callback: (item: RadiationData, index: number) => void): void {
    this.onItemCallback = callback;
  }

  // 开始定时获取元素
  start(interval: number = 1000): void {
    if (this.timerId !== -1) {
      console.log('定时器已经在运行中');
      return;
    }

    console.log('开始定时获取数组元素');

    this.timerId = setInterval(() => {
      this.getNextItem();
    }, interval);
  }

  // 获取下一个元素
  private getNextItem(): void {
    if (this.dataArray.length === 0) {
      console.log('数组为空');
      this.stop();
      return;
    }

    // 如果当前索引超出数组长度,则重置或停止
    if (this.currentIndex >= this.dataArray.length) {
      console.log('已遍历完所有元素');
      this.stop();
      return;
    }

    const item = this.dataArray[this.currentIndex];

    // 执行回调
    this.onItemCallback(item, this.currentIndex);

    // 增加索引
    this.currentIndex++;
  }

  // 停止定时器
  stop(): void {
    if (this.timerId !== -1) {
      clearInterval(this.timerId);
      this.timerId = -1;
      console.log('定时器已停止');
    }
  }

  // 重置定时器(从头开始)
  reset(): void {
    this.stop();
    this.currentIndex = 0;
    console.log('定时器已重置');
  }

  // 手动获取当前索引的元素
  getCurrentItem(): RadiationData | null {
    if (this.currentIndex < this.dataArray.length) {
      return this.dataArray[this.currentIndex];
    }
    return null;
  }

  // 设置新的数据数组
  setData(newData: RadiationData[]): void {
    this.dataArray = newData;
    this.currentIndex = 0;
  }

  // 清理资源
  destroy(): void {
    this.stop();
    this.dataArray = [];
    this.currentIndex = 0;
  }
}

JSON文件放在【rawfile】目录,内容如下:

js 复制代码
[
  {
    "miaoshu": "北京故宫",
    "location": "39.916345°N 116.397155°E"
  },
  {
    "miaoshu": "上海东方明珠广播电视塔",
    "location": "31.239679°N 121.499758°E"
  },
  {
    "miaoshu": "天津之眼摩天轮",
    "location": "39.153385°N 117.182306°E"
  },
  {
    "miaoshu": "重庆洪崖洞",
    "location": "29.563010°N 106.577500°E"
  },
  {
    "miaoshu": "吉林长白山风景区",
    "location": "42.012143°N 128.062466°E"
  },
  {
    "miaoshu": "辽宁本溪水洞",
    "location": "41.296446°N 124.087891°E"
  },
  {
    "miaoshu": "甘肃敦煌莫高窟",
    "location": "40.041206°N 94.807923°E"
  },
  {
    "miaoshu": "青海青海湖",
    "location": "36.623236°N 100.215808°E"
  },
  {
    "miaoshu": "陕西华山",
    "location": "34.483727°N 110.074478°E"
  },
  {
    "miaoshu": "河南嵩山少林寺",
    "location": "34.508522°N 112.940175°E"
  },
  {
    "miaoshu": "河北承德避暑山庄",
    "location": "41.005442°N 117.935190°E"
  },
  {
    "miaoshu": "山东泰山",
    "location": "36.256944°N 117.103611°E"
  },
  {
    "miaoshu": "山西五台山",
    "location": "39.030771°N 113.590706°E"
  },
  {
    "miaoshu": "安徽黄山",
    "location": "30.132855°N 118.168859°E"
  },
  {
    "miaoshu": "湖北武汉黄鹤楼",
    "location": "30.546545°N 114.298454°E"
  },
  {
    "miaoshu": "湖南湘西凤凰古城",
    "location": "27.948230°N 109.593903°E"
  },
  {
    "miaoshu": "四川阿坝九寨沟",
    "location": "33.260108°N 103.920197°E"
  },
  {
    "miaoshu": "云南大理古城",
    "location": "25.692711°N 100.156265°E"
  },
  {
    "miaoshu": "广西桂林漓江",
    "location": "25.234479°N 110.179953°E"
  }
]

在真机上运行页面就可以出现演示视频的效果了

2. 路线规划

支持路线规划:步行和骑行

导入依赖有变化,依赖换成如下即可:

js 复制代码
"@bdmap/base": "2.0.3",
"@bdmap/map": "2.0.3",
"@bdmap/verify": "1.0.4",
"@bdmap/map_walkride_search": "2.0.3"

EntryAbility.ets文件中只需要在【onCreate】函数中添加下面代码即可:

js 复制代码
import { Initializer } from "@bdmap/map_walkride_search";

//初始化秘钥
Initializer.getInstance().initialize(MAP_KEY, this.context)

页面代码如下:

js 复制代码
import {
  MapComponent,
  MapOptions,
  BDNaviService,
  NaviType,
  RoutePlanOption,
  RouteNodeInfo,
  LatLng,
  walkRideDefaultUIPage,
  NaviMode,
  MapController,
  IRoutePlanListener,
  RoutePlanError
} from "@bdmap/map_walkride_search";

/**
 * 步行路线规划-案例
 */
@Entry
@ComponentV2
struct DriveCar {
  // 控制器
  mapController: MapController | null = null;
  // 设置地图控件参数
  @Local mapOpt: MapOptions = new MapOptions({
    gestures: {
      zoom: true,
      move: true,
      rotate: true,
      overlooking: true,
    }
  });

  //路线规划
  init2() {
    // 初始化导航服务
    const service = new BDNaviService(NaviType.RIDE);// 这里可以指定是步行还是骑行
    service.initializer().init(getContext(this), this.mapController)
    // 配置路线规划参数
    let param = new RoutePlanOption();
    let startLocation = new RouteNodeInfo();
    let endLocation = new RouteNodeInfo();

    // 设置起终点
    startLocation.location = new LatLng(30.624530, 114.261376) //汉口火车站
    endLocation.location = new LatLng(30.537494, 114.323302) //武昌火车站

    param.startNodeInfo(startLocation)
      .endNodeInfo(endLocation)
      .extraNaviMode(NaviMode.RealNavi);

    // 路线规划
    service.routePlanService().routePlanWithRouteNode(param, new Object({
      //引擎开始路线规划
      onRoutePlanStart: () => {
      },
      // 正常规划成功
      onRoutePlanSuccess: () => {
        // 展示路线
        service.routePlanService().displayRoutePlanResult()
      },
      // 正常规划失败
      onRoutePlanFail: (error: RoutePlanError) => {
        console.log('正常规划失败,error=' + JSON.stringify(error))
      }
    }) as IRoutePlanListener);

    // 开始导航
    service.navigationService().lifecycle().start();
  }

  aboutToAppear(): void {
    setTimeout(() => {
      this.init2();
    }, 3000)
  }

  build() {
    Column() {
      MapComponent({
        onReady: (err, mapController: MapController) => {
          if (!err) {
            this.mapController = mapController;
          }
        }, mapOptions: this.mapOpt
      })
        .height('100%')
        .width('100%')
    }
    .height('100%')
    .width('100%')
  }
}

演示效果如下:

路线规划演示视频

效果是:进入页面3秒后,自动规划起点到终点的路线(代码中可以设置步行或者骑行)

最后

  • 希望本文对你有所帮助!
  • 本人如有任何错误或不当之处,请留言指出,谢谢!
相关推荐
小学生波波4 小时前
HarmonyOS6 - 鸿蒙电商页面实战案例
登录页面·arkts·鸿蒙系统·电商·harmonyos6
小学生波波2 天前
最新版鸿蒙开发工具DevEco Studio保姆级安装教程
arkts·鸿蒙系统·安装教程·deveco studio·鸿蒙开发·harmonyos6
编程乐学2 天前
鸿蒙非原创--DevEcoStudio开发的奶茶点餐APP
华为·harmonyos·deveco studio·鸿蒙开发·奶茶点餐·鸿蒙大作业
小学生波波2 天前
HarmonyOS6 - 弹框选择年份和月份实战案例
鸿蒙·鸿蒙开发·弹窗组件·harmonyos6·选择年份
小学生波波2 天前
HarmonyOS6 - Progress进度条组件案例
arkts·鸿蒙系统·鸿蒙开发·progress·harmonyos6·进度条组件
小学生波波2 天前
HarmonyOS6 - WaterFlow瀑布流容器案例
鸿蒙系统·鸿蒙开发·harmonyos6·waterflow·瀑布流容器
小学生波波2 天前
HarmonyOS6 - Slider滑动条组件案例
arkts·鸿蒙·slider·鸿蒙开发·harmonyos6·滑动组件
小学生波波3 天前
HarmonyOS6 - 鸿蒙日历实战案例
arkts·鸿蒙系统·日历·deveco studio·鸿蒙开发·harmonyos6·签到日历
小学生波波3 天前
HarmonyOS6 - 鸿蒙弹窗选择省市区实战案例
arkts·鸿蒙·arkui·鸿蒙开发·harmonyos6·省市区·级联选择