HarmonyOS運動開發(fā):如何集成百度地圖SDK、運動跟隨與運動公里數(shù)記錄

前言

在開發(fā)運動類應用時,集成地圖功能以及實時記錄運動軌跡和公里數(shù)是核心需求之一。本文將詳細介紹如何在 HarmonyOS 應用中集成百度地圖 SDK,實現(xiàn)運動跟隨以及運動公里數(shù)的記錄。

一、集成百度地圖 SDK

1.引入依賴

首先,需要在項目的文件中引入百度地圖相關的依賴包:

"dependencies": {
  "@bdmap/base": "1.2.6",
  "@bdmap/search": "1.2.6",
  "@bdmap/map": "1.2.6",
  "@bdmap/locsdk": "1.1.4"
}

2.初始化百度地圖

為了使用百度地圖的功能,我們需要進行初始化操作。這包括設置 API Key 和初始化定位客戶端。

MapUtil 類


export class MapUtil{

  public static initialize(context:Context){
    Initializer.getInstance().initialize("你的key");
    // 設置是否同意隱私合規(guī)政策接口
    // true,表示同意隱私合規(guī)政策
    // false,表示不同意隱私合規(guī)政策
    LocationClient.checkAuthKey("你的key", (result: string) => {
      console.debug("result = " + result); // 可打印出是否鑒權成功的結果
    });
    LocationClient.setAgreePrivacy(true);
    LocManager.getInstance().init(context);
  }

}

LocManager 類


export class LocManager {
  private client: LocationClient | null = null;

  private static instance: LocManager;

  public static getInstance(): LocManager {
    if (!LocManager.instance) {
      LocManager.instance = new LocManager();
    }
    return LocManager.instance;
  }

  constructor() {

  }

  init(context: Context) {
    if (this.client == null) {
      try {
        this.client = new LocationClient(context);
      } catch (error) {
        console.error("harmony_baidu_location error: " + error.message);
      }
    }
    if (this.client != null) {
      this.client.setLocOption(this.getDefaultLocationOption());
    }
  }

  start() {
    if (this.client != null) {
      this.client.start();
    }
  }

  stop() {
    if (this.client != null) {
      this.client.stop();
    }
  }

  requestSingleLocation() {
    if (this.client != null) {
      this.client.requestSingleLocation();
    }
  }

  registerListener(listener: BDLocationListener): boolean {
    let isSuccess: boolean = false;
    if (this.client != null && listener != null) {
      this.client.registerLocationListener(listener);
      isSuccess = true;
    }
    return isSuccess;
  }

  unRegisterListener(listener: BDLocationListener) {
    if (this.client != null && listener != null) {
      this.client.unRegisterLocationListener(listener);
    }
  }

  getSDKVersion(): string {
    let version: string = "";
    if (this.client != null) {
      version = this.client.getVersion();
    }
    return version;
  }

  enableLocInBackground(wantAgent: WantAgent) {
    if (this.client != null) {
      this.client.enableLocInBackground(wantAgent);
    }
  }

  disableLocInBackground() {
    if (this.client != null) {
      this.client.disableLocInBackground();
    }
  }

  getDefaultLocationOption() {
    let option = new LocationClientOption();
    option.setCoorType("bd09ll"); // 可選,默認為gcj02,設置返回的定位結果坐標系
    option.setTimeInterval(3); // 可選,默認1秒,設置連續(xù)定位請求的時間間隔
    option.setDistanceInterval(0); // 可選,默認0米,設置連續(xù)定位的距離間隔
    option.setIsNeedAddress(true); // 可選,設置是否需要地址信息,默認不需要
    option.setIsNeedLocationDescribe(true); // 可選,默認為false,設置是否需要地址描述
    option.setIsNeedLocationPoiList(true); // 可選,默認能為false,設置是否需要POI結果
    option.setLocationMode(LocationMode.High_Accuracy); // 可選,默認高精度,設置定位模式,高精度、低功耗、僅設備
    option.setSingleLocatingTimeout(3000); // 可選,僅針對單次定位生效,設置單次定位的超時時間

    return option;
  }

}

3.定位監(jiān)聽器

為了處理定位數(shù)據(jù),我們需要實現(xiàn)一個定位監(jiān)聽器:


export class MapLocationListener extends BDLocationListener {
  private callback: (location: BDLocation) => void;

  constructor(callback: (location: BDLocation) => void) {
    super();
    this.callback = callback;
  }

  onReceiveLocation(bdLocation: BDLocation): void {
    this.callback(bdLocation);
  }
}

二、頁面使用

1.權限申請

在文件中聲明所需的權限:

"requestPermissions": [
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:location_permission",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.LOCATION_IN_BACKGROUND",
        "reason": "$string:background_location_permission",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:fuzzy_location_permission",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.APP_TRACKING_CONSENT",
        "reason": "$string:get_oaid_permission",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
        "reason": "$string:keep_background_running_permission",
        "usedScene": {
          "abilities": [
            "EntryAbility1"
          ],
          "when": "inuse"
        }
      }
    ]

2.請求權限

在頁面中請求權限:

private async requestPermissions(): Promise<boolean> {
    const permissions : Permissions[]= [
      'ohos.permission.LOCATION',
      'ohos.permission.APPROXIMATELY_LOCATION',
      'ohos.permission.APP_TRACKING_CONSENT',
    ]
    return LibPermission.requestPermissions(permissions)
  }

3.頁面調用

方向感應

使用鴻蒙系統(tǒng)自帶的方向傳感器來獲取設備的朝向角度:

// 初始化方向傳感器
      sensor.on(sensor.SensorId.ORIENTATION, (data) => {
        // 獲取設備朝向角度(繞Z軸旋轉角度)
        this.currentRotation = data.alpha;
        if(this.loc){
          this.loc.location = new LatLng(this.currentLatitude, this.currentLongitude);
          this.loc.direction = this.currentRotation;
          this.loc.radius = 0;
        }
      });

// 用完記得取消監(jiān)聽
sensor.off(sensor.SensorId.ORIENTATION);

編寫定位監(jiān)聽器

private mListener: MapLocationListener = new MapLocationListener((bdLocation: BDLocation) => {
    this.currentLatitude = bdLocation.getLatitude();
    this.currentLongitude = bdLocation.getLongitude();
    this.currentRadius = bdLocation.getRadius();

    // 更新地圖位置和位置標記
    if (this.mapController) {
      // 更新地圖中心點
      this.mapController.setMapCenter({
        lat: this.currentLatitude,
        lng: this.currentLongitude
      },15);

      if(this.loc){
        // 設置定位圖標位置、指向以及范圍
        this.loc.location = new LatLng(this.currentLatitude, this.currentLongitude);
        this.loc.direction = this.currentRotation;
        // 單位米
        this.loc.radius = 0;

      }

    }
  });

啟動和關閉定位

// 啟動定位
LocManager.getInstance().registerListener(this.mListener);
LocManager.getInstance().start();

// 關閉定位
LocManager.getInstance().unRegisterListener(this.mListener);
LocManager.getInstance().stop();

百度地圖集成

在頁面中集成百度地圖:

MapComponent({ onReady: async (err, mapController:MapController) => {
          if (!err) {
            // 獲取地圖的控制器類,用來操作地圖
            this.mapController= mapController;
            let result = this.mapController.getLayerByTag(SysEnum.LayerTag.LOCATION);
            if(result){
              this.loc = result as LocationLayer;
            }

            if(this.currentLatitude!=0&&this.currentLongitude!=0){
              if(this.loc){
                // 設置定位圖標位置、指向以及范圍
                this.loc.location = new LatLng(this.currentLatitude, this.currentLongitude);
                this.loc.direction = this.currentRotation;
                // 單位米
                this.loc.radius = 0;
              }

              this.mapController.setMapCenter({
                lat: this.currentLatitude,
                lng: this.currentLongitude
              },15);

            }
          }
        }, mapOptions: this.mapOpt }).width('100%').height('100%')

三、公里數(shù)計算

在運動應用中,記錄用戶的運動軌跡并計算運動的總距離是核心功能之一。為了實現(xiàn)這一功能,我們需要設計一個數(shù)據(jù)模型來記錄運動軌跡點,并通過這些點計算總距離。

1.運動軌跡點模型

定義一個RunPoint類來表示運動軌跡中的一個點,包含緯度、經(jīng)度和時間戳:

/**
 * 運動軌跡點數(shù)據(jù)模型
 */
export class RunPoint {
  // 緯度
  latitude: number;
  // 經(jīng)度
  longitude: number;
  // 時間戳
  timestamp: number;
  // 所屬公里數(shù)分組(第幾公里)
  kilometerGroup: number;

  constructor(latitude: number, longitude: number) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.timestamp = Date.now();
    this.kilometerGroup = 0; // 默認分組為0
  }
}

2.運動軌跡管理類

創(chuàng)建一個RunTracker類來管理運動軌跡點,并計算總距離:


/**
 * 運動軌跡管理類
 */
export class RunTracker {
  // 所有軌跡點
  private points: RunPoint[] = [];
  // 當前總距離(公里)
  private totalDistance: number = 0;
  // 當前公里數(shù)分組
  private currentKilometerGroup: number = 0;

  /**
   * 添加新的軌跡點
   * @param latitude 緯度
   * @param longitude 經(jīng)度
   * @returns 當前總距離(公里)
   */
  addPoint(latitude: number, longitude: number): number {
    const point = new RunPoint(latitude, longitude);

    if (this.points.length > 0) {
      // 計算與上一個點的距離
      const lastPoint = this.points[this.points.length - 1];
      const distance = this.calculateDistance(lastPoint, point);
      this.totalDistance += distance;

      // 更新公里數(shù)分組
      point.kilometerGroup = Math.floor(this.totalDistance);
      if (point.kilometerGroup > this.currentKilometerGroup) {
        this.currentKilometerGroup = point.kilometerGroup;
      }
    }

    this.points.push(point);
    return this.totalDistance;
  }

  /**
   * 計算兩點之間的距離(公里)
   * 使用Haversine公式計算球面距離
   */
  private calculateDistance(point1: RunPoint, point2: RunPoint): number {
    const R = 6371; // 地球半徑(公里)
    const lat1 = this.toRadians(point1.latitude);
    const lat2 = this.toRadians(point2.latitude);
    const deltaLat = this.toRadians(point2.latitude - point1.latitude);
    const deltaLon = this.toRadians(point2.longitude - point1.longitude);

    const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
              Math.cos(lat1) * Math.cos(lat2) *
              Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
  }

  /**
   * 將角度轉換為弧度
   */
  private toRadians(degrees: number): number {
    return degrees * (Math.PI / 180);
  }

  /**
   * 獲取當前總距離
   */
  getTotalDistance(): number {
    return this.totalDistance;
  }

  /**
   * 獲取指定公里數(shù)分組的軌跡點
   */
  getPointsByKilometer(kilometer: number): RunPoint[] {
    return this.points.filter(point => point.kilometerGroup === kilometer);
  }

  /**
   * 清空軌跡數(shù)據(jù)
   */
  clear(): void {
    this.points = [];
    this.totalDistance = 0;
    this.currentKilometerGroup = 0;
  }
}

3.頁面的監(jiān)聽器里記錄公里數(shù)

在頁面中使用RunTracker類來記錄運動軌跡點并計算總距離:

  private runTracker: RunTracker = new RunTracker();
監(jiān)聽器添加代碼
      const distance = this.runTracker.addPoint(this.currentLatitude, this.currentLongitude);

distance就是當前運動的公里數(shù)

四、總結

本文詳細介紹了如何在 HarmonyOS 應用中集成百度地圖 SDK,實現(xiàn)運動跟隨以及運動公里數(shù)的記錄。通過以下步驟,我們可以實現(xiàn)一個功能完整的運動應用:

? 集成百度地圖 SDK:

? 引入必要的依賴包。

? 初始化百度地圖并設置定位選項。

? 頁面使用:

? 請求必要的權限。

? 啟動和關閉定位。

? 實時更新地圖位置和方向。

? 公里數(shù)計算:

? 定義運動軌跡點模型。

? 使用 Haversine 公式計算兩點之間的距離。

? 記錄運動軌跡點并實時更新總距離。

通過這些步驟,開發(fā)者可以輕松實現(xiàn)一個功能強大的運動應用,為用戶提供實時的運動數(shù)據(jù)和地圖跟隨功能。希望本文的內容能夠幫助你在 HarmonyOS 開發(fā)中取得更好的成果!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容