DEV Community

zhonghua
zhonghua

Posted on • Edited on

HarmonyOS Sports Development: How to Integrate Baidu Map SDK, Sports Tracking, and Distance Recording

HarmonyOS Sports Development: How to Integrate Baidu Map SDK, Sports Tracking, and Distance Recording

Foreword

When developing sports applications, integrating map functionality and real-time recording of sports trajectories and distances is one of the core requirements. This article will detail how to integrate the Baidu Map SDK in a HarmonyOS application to achieve sports tracking and distance recording.

I. Integrating Baidu Map SDK

  1. Adding Dependencies

First, you need to add the Baidu Map related dependency packages to your project file:

"dependencies": {
  "@bdmap/base": "1.2.6",
  "@bdmap/search": "1.2.6",
  "@bdmap/map": "1.2.6",
  "@bdmap/locsdk": "1.1.4"
}
Enter fullscreen mode Exit fullscreen mode
  1. Initializing Baidu Map

To use Baidu Map's functionality, we need to perform initialization operations. This includes setting the API Key and initializing the location client.

MapUtil Class

export class MapUtil {
  public static initialize(context: Context) {
    Initializer.getInstance().initialize("your_key");
    // Set whether to agree to the privacy compliance policy interface
    // true, indicates agreement with the privacy compliance policy
    // false, indicates disagreement with the privacy compliance policy
    LocationClient.checkAuthKey("your_key", (result: string) => {
      console.debug("result = " + result); // Can print out the result of whether authorization is successful
    });
    LocationClient.setAgreePrivacy(true);
    LocManager.getInstance().init(context);
  }
}
Enter fullscreen mode Exit fullscreen mode

LocManager Class

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"); // Optional, default is gcj02, set the coordinate system of the returned location result
    option.setTimeInterval(3); // Optional, default is 1 second, set the time interval for continuous location requests
    option.setDistanceInterval(0); // Optional, default is 0 meters, set the distance interval for continuous location
    option.setIsNeedAddress(true); // Optional, set whether address information is needed, default is no
    option.setIsNeedLocationDescribe(true); // Optional, default is false, set whether address description is needed
    option.setIsNeedLocationPoiList(true); // Optional, default is false, set whether POI results are needed
    option.setLocationMode(LocationMode.High_Accuracy); // Optional, default is high accuracy, set the location mode, high accuracy, low power consumption, only device
    option.setSingleLocatingTimeout(3000); // Optional, only effective for single location, set the timeout for single location

    return option;
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Location Listener

To handle location data, we need to implement a location listener:

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

II. Page Usage

  1. Requesting Permissions

Declare the required permissions in the file:

"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"
    }
  }
]
Enter fullscreen mode Exit fullscreen mode
  1. Requesting Permissions

Request permissions in the page:

private async requestPermissions(): Promise<boolean> {
  const permissions: Permissions[] = [
    'ohos.permission.LOCATION',
    'ohos.permission.APPROXIMATELY_LOCATION',
    'ohos.permission.APP_TRACKING_CONSENT',
  ];
  return LibPermission.requestPermissions(permissions);
}
Enter fullscreen mode Exit fullscreen mode
  1. Page Invocation

Direction Sensing

Use the built-in direction sensor of the HarmonyOS system to obtain the device's orientation angle:

// Initialize the orientation sensor
sensor.on(sensor.SensorId.ORIENTATION, (data) => {
  // Get the device's orientation angle (rotation around the Z-axis)
  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;
  }
});

// Remember to cancel the listener after use
sensor.off(sensor.SensorId.ORIENTATION);
Enter fullscreen mode Exit fullscreen mode

Writing the Location Listener

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

  // Update map location and location marker
  if (this.mapController) {
    // Update map center
    this.mapController.setMapCenter({
      lat: this.currentLatitude,
      lng: this.currentLongitude
    }, 15);

    if (this.loc) {
      // Set the location icon's position, direction, and range
      this.loc.location = new LatLng(this.currentLatitude, this.currentLongitude);
      this.loc.direction = this.currentRotation;
      // Unit: meters
      this.loc.radius = 0;
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

Starting and Stopping Location

// Start location
LocManager.getInstance().registerListener(this.mListener);
LocManager.getInstance().start();

// Stop location
LocManager.getInstance().unRegisterListener(this.mListener);
LocManager.getInstance().stop();
Enter fullscreen mode Exit fullscreen mode

Baidu Map Integration

Integrate Baidu Map in the page:

MapComponent({
  onReady: async (err, mapController: MapController) => {
    if (!err) {
      // Get the map controller class to operate the map
      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) {
          // Set the location icon's position, direction, and range
          this.loc.location = new LatLng(this.currentLatitude, this.currentLongitude);
          this.loc.direction = this.currentRotation;
          // Unit: meters
          this.loc.radius = 0;
        }

        this.mapController.setMapCenter({
          lat: this.currentLatitude,
          lng: this.currentLongitude
        }, 15);
      }
    }
  },
  mapOptions: this.mapOpt
}).width('100%').height('100%');
Enter fullscreen mode Exit fullscreen mode

III. Distance Calculation

In sports applications, recording the user's sports trajectory and calculating the total distance traveled is one of the core functions. To achieve this functionality, we need to design a data model to record the sports trajectory points and calculate the total distance through these points.

  1. Sports Trajectory Point Model

Define a RunPoint class to represent a point in the sports trajectory, including latitude, longitude, and timestamp:

/**
 * Sports trajectory point data model
 */
export class RunPoint {
  // Latitude
  latitude: number;
  // Longitude
  longitude: number;
  // Timestamp
  timestamp: number;
  // Kilometer group (which kilometer)
  kilometerGroup: number;

  constructor(latitude: number, longitude: number) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.timestamp = Date.now();
    this.kilometerGroup = 0; // Default group is 0
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Sports Trajectory Management Class

Create a RunTracker class to manage the sports trajectory points and calculate the total distance:

/**
 * Sports trajectory management class
 */
export class RunTracker {
  // All trajectory points
  private points: RunPoint[] = [];
  // Current total distance (kilometers)
  private totalDistance: number = 0;
  // Current kilometer group
  private currentKilometerGroup: number = 0;

  /**
   * Add a new trajectory point
   * @param latitude Latitude
   * @param longitude Longitude
   * @returns Current total distance (kilometers)
   */
  addPoint(latitude: number, longitude: number): number {
    const point = new RunPoint(latitude, longitude);

    if (this.points.length > 0) {
      // Calculate the distance from the last point
      const lastPoint = this.points[this.points.length - 1];
      const distance = this.calculateDistance(lastPoint, point);
      this.totalDistance += distance;

      // Update kilometer group
      point.kilometerGroup = Math.floor(this.totalDistance);
      if (point.kilometerGroup > this.currentKilometerGroup) {
        this.currentKilometerGroup = point.kilometerGroup;
      }
    }

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

  /**
   * Calculate the distance between two points (kilometers)
   * Use the Haversine formula to calculate the spherical distance
   */
  private calculateDistance(point1: RunPoint, point2: RunPoint): number {
    const R = 6371; // Earth's radius (kilometers)
    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;
  }

  /**
   * Convert degrees to radians
   */
  private toRadians(degrees: number): number {
    return degrees * (Math.PI / 180);
  }

  /**
   * Get the current total distance
   */
  getTotalDistance(): number {
    return this.totalDistance;
  }

  /**
   * Get trajectory points for a specific kilometer group
   */
  getPointsByKilometer(kilometer: number): RunPoint[] {
    return this.points.filter(point => point.kilometerGroup === kilometer);
  }

  /**
   * Clear trajectory data
   */
  clear(): void {
    this.points = [];
    this.totalDistance = 0;
    this.currentKilometerGroup = 0;
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Recording Distance in the Page Listener

Use the RunTracker class in the page to record sports trajectory points and calculate the total distance:

private runTracker: RunTracker = new RunTracker();

// Listener addition code
const distance = this.runTracker.addPoint(this.currentLatitude, this.currentLongitude);

// Distance is the current sports distance in kilometers
Enter fullscreen mode Exit fullscreen mode

IV. Summary

This article has detailed how to integrate the Baidu Map SDK in a HarmonyOS application to achieve sports tracking and distance recording. By following these steps, we can implement a fully functional sports application:

  • Integrate Baidu Map SDK:

    • Add the necessary dependency packages.
    • Initialize Baidu Map and set location options.
  • Page Usage:

    • Request the necessary permissions.
    • Start and stop location.
    • Real-time update of map location and direction.
  • Distance Calculation:

    • Define the sports trajectory point model.
    • Use the Haversine formula to calculate the distance between two points.
    • Record sports trajectory points and real-time update of total distance.

By following these steps, developers can easily implement a powerful sports application that provides real-time sports data and map tracking functions. I hope the content of this article can help you achieve better results in HarmonyOS development!

Top comments (0)