🐈

HarmonyOS運動開発:百度マップSDKの統合、運動の追跡と運動距離の記録

に公開

前言

運動系アプリを開発する際、マップ機能を統合し、リアルタイムで運動の軌跡と距離を記録することは、核心的なニーズの一つです。この記事では、HarmonyOSアプリに百度マップSDKを統合し、運動の追跡と運動距離の記録を実現する方法を詳しく紹介します。

一、百度マップSDKの統合

  1. 依存関係の導入

まず、プロジェクトのファイルに百度マップ関連の依存パッケージを導入する必要があります。

"dependencies": {
  "@bdmap/base": "1.2.6",
  "@bdmap/search": "1.2.6",
  "@bdmap/map": "1.2.6",
  "@bdmap/locsdk": "1.1.4"
}
  1. 百度マップの初期化

百度マップの機能を使用するためには、初期化操作を行う必要があります。これは、API Keyの設定と定位クライアントの初期化を含みます。

MapUtil クラス

export class MapUtil{
  public static initialize(context:Context){
    Initializer.getInstance().initialize("あなたのkey");
    // プライバシーポリシーの同意設定インターフェース
    // true:同意する
    // false:同意しない
    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秒、連続定位要求の時間間隔を設定する
    option.setDistanceInterval(0); // 省略可能、デフォルトは0メートル、連続定位の距離間隔を設定する
    option.setIsNeedAddress(true); // 省略可能、アドレス情報が必要かどうかを設定する、デフォルトは不要
    option.setIsNeedLocationDescribe(true); // 省略可能、デフォルトはfalse、アドレスの説明が必要かどうかを設定する
    option.setIsNeedLocationPoiList(true); // 省略可能、デフォルトはfalse、POI結果が必要かどうかを設定する
    option.setLocationMode(LocationMode.High_Accuracy); // 省略可能、デフォルトは高精度、定位モードを設定する、高精度、低消費電力、デバイスのみ
    option.setSingleLocatingTimeout(3000); // 省略可能、単回定位のみ有効、単回定位のタイムアウト時間を設定する

    return option;
  }
}
  1. 定位リスナー

定位データを処理するためには、定位リスナーを実装する必要があります。

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"
        }
      }
    ]
  1. 権限の要求

ページで権限を要求します。

private async requestPermissions(): Promise<boolean> {
    const permissions : Permissions[]= [
      'ohos.permission.LOCATION',
      'ohos.permission.APPROXIMATELY_LOCATION',
      'ohos.permission.APP_TRACKING_CONSENT',
    ]
    return LibPermission.requestPermissions(permissions)
  }
  1. ページの呼び出し

方向センサーの使用

鴻蒙システムの組み込み方向センサーを使用して、デバイスの方向角度を取得します。

// 方向センサーの初期化
      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;
        }
      });

// 使用後はリスナーを解除することを忘れないでください
sensor.off(sensor.SensorId.ORIENTATION);

定位リスナーの作成

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%')

三、距離の計算

運動アプリでは、ユーザーの運動軌跡を記録し、運動の総距離を計算することが核心機能の一つです。この機能を実現するためには、運動軌跡のポイントを記録するデータモデルを設計し、これらのポイントから総距離を計算する必要があります。

  1. 運動軌跡のポイントモデル

RunPoint クラスを定義して、運動軌跡の1つのポイントを表します。これは、緯度、経度、タイムスタンプを含みます。

/**
 * 運動軌跡のポイントデータモデル
 */
export class RunPoint {
  // 緯度
  latitude: number;
  // 経度
  longitude: number;
  // タイムスタンプ
  timestamp: number;
  // 所属するキロメートルグループ(何キロメートル目か)
  kilometerGroup: number;

  constructor(latitude: number, longitude: number) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.timestamp = Date.now();
    this.kilometerGroup = 0; // デフォルトのグループは0
  }
}
  1. 運動軌跡の管理クラス

RunTracker クラスを作成して、運動軌跡のポイントを管理し、総距離を計算します。


/**
 * 運動軌跡の管理クラス
 */
export class RunTracker {
  // すべての軌跡ポイント
  private points: RunPoint[] = [];
  // 現在の総距離(キロメートル)
  private totalDistance: number = 0;
  // 現在のキロメートルグループ
  private currentKilometerGroup: number = 0;

  /**
   * 新しい軌跡ポイントを追加する
   * @param latitude 緯度
   * @param longitude 経度
   * @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;

      // キロメートルグループを更新する
      point.kilometerGroup = Math.floor(this.totalDistance);
      if (point.kilometerGroup > this.currentKilometerGroup) {
        this.currentKilometerGroup = point.kilometerGroup;
      }
    }

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

  /**
   * 2つのポイント間の距離(キロメートル)を計算する
   * 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;
  }

  /**
   * 指定されたキロメートルグループの軌跡ポイントを取得する
   */
  getPointsByKilometer(kilometer: number): RunPoint[] {
    return this.points.filter(point => point.kilometerGroup === kilometer);
  }

  /**
   * 軌跡データをクリアする
   */
  clear(): void {
    this.points = [];
    this.totalDistance = 0;
    this.currentKilometerGroup = 0;
  }
}
  1. ページのリスナーでキロメートル数を記録する

ページで RunTracker クラスを使用して、運動軌跡のポイントを記録し、総距離を計算します。

  private runTracker: RunTracker = new RunTracker();
リスナーに追加するコード
      const distance = this.runTracker.addPoint(this.currentLatitude, this.currentLongitude);

distance は現在の運動キロメートル数です

四、まとめ

この記事では、HarmonyOSアプリに百度マップSDKを統合し、運動の追跡と運動キロメートル数の記録を実現する方法を詳しく紹介しました。以下の手順で、機能が完整的な運動アプリを実現できます。

• 百度マップSDKの統合:

• 必要な依存パッケージを導入する。

• 百度マップを初期化し、定位オプションを設定する。

• ページの使用:

• 必要な権限を要求する。

• 定位を開始し、停止する。

• マップの位置と方向をリアルタイムで更新する。

• キロメートル数の計算:

• 運動軌跡のポイントモデルを定義する。

• Haversine公式を使用して2つのポイント間の距離を計算する。

• 運動軌跡のポイントを記録し、総距離をリアルタイムで更新する。

これらの手順を通じて、開発者は機能が強力な運動アプリを簡単に実現し、ユーザーにリアルタイムの運動データとマップの追跡機能を提供することができます。この記事の内容があなたの HarmonyOS 開発に役立つことを願っています!

Discussion