鸿蒙NEXT开发定位工具类 (WGS-84坐标系)(ArkTs)

复制代码
import geoLocationManager from '@ohos.geoLocationManager';
import { BusinessError, Callback } from '@ohos.base';
import { LogUtil } from './LogUtil';
import { PermissionUtil } from './PermissionUtil';
import { map, mapCommon } from '@kit.MapKit';
/**
 * 定位工具类 (WGS-84坐标系)
 * author: CSDN-鸿蒙布道师
 * since: 2025/04/22
 */
export class LocationUtil {
  /**
   * 判断位置服务是否已经使能(定位是否开启)。
   * @returns true 表示定位已开启,false 表示未开启。
   */
  static isLocationEnabled(): boolean {
    return geoLocationManager.isLocationEnabled();
  }
  /**
   * 申请定位权限。
   * @returns true 表示授权成功,false 表示用户拒绝授权。
   */
  static async requestLocationPermissions(): Promise<boolean> {
    const permissions: Array<string> = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
    let grant: boolean = await PermissionUtil.requestPermissions(permissions);
    if (!grant) {
      grant = await PermissionUtil.requestPermissionOnSetting(permissions);
    }
    return grant;
  }
  /**
   * 获取当前位置(简化版)。
   * @returns 当前位置信息。如果发生错误,则返回 null。
   */
  static async getCurrentLocationEasy(): Promise<geoLocationManager.Location | null> {
    return LocationUtil.getCurrentLocation();
  }
  /**
   * 获取当前位置。
   * @param request 可选的定位请求参数。
   * @returns 当前位置信息。如果发生错误,则返回 null。
   */
  static async getCurrentLocation(
    request?: geoLocationManager.CurrentLocationRequest | geoLocationManager.SingleLocationRequest
  ): Promise<geoLocationManager.Location | null> {
    try {
      return await geoLocationManager.getCurrentLocation(request);
    } catch (err) {
      LocationUtil.handleError(err, '获取当前位置失败');
      return null; // 返回 null 表示发生错误
    }
  }
  /**
   * 开启位置变化订阅,并发起定位请求(简化版)。
   * @param callBack 回调函数。
   * @returns 成功返回 0,失败返回错误码。
   */
  static onLocationChangeEasy(callBack: Callback<geoLocationManager.Location>): number {
    const defaultRequest: geoLocationManager.LocationRequest = {
      priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
      scenario: geoLocationManager.LocationRequestScenario.UNSET,
      timeInterval: 10,
      distanceInterval: 0,
      maxAccuracy: 0,
    };
    return LocationUtil.onLocationChange(defaultRequest, callBack);
  }
  /**
   * 开启位置变化订阅,并发起定位请求。
   * @param request 定位请求参数。
   * @param callBack 回调函数。
   * @returns 成功返回 0,失败返回错误码。
   */
  static onLocationChange(
    request: geoLocationManager.LocationRequest | geoLocationManager.ContinuousLocationRequest,
    callBack: Callback<geoLocationManager.Location>
  ): number {
    try {
      geoLocationManager.on('locationChange', request, callBack);
      return 0;
    } catch (err) {
      return LocationUtil.handleError(err, '开启位置变化订阅失败');
    }
  }
  /**
   * 关闭位置变化订阅,并删除对应的定位请求。
   * @param callback 不传则取消当前类型的所有订阅。
   * @returns 成功返回 0,失败返回错误码。
   */
  static offLocationChange(callback?: Callback<geoLocationManager.Location>): number {
    try {
      if (callback) {
        geoLocationManager.off('locationChange', callback);
      } else {
        geoLocationManager.off('locationChange');
      }
      return 0;
    } catch (err) {
      return LocationUtil.handleError(err, '关闭位置变化订阅失败');
    }
  }
  /**
   * 判断地理编码与逆地理编码服务是否可用。
   * @returns true 表示服务可用,false 表示不可用。
   */
  static isGeocoderAvailable(): boolean {
    return geoLocationManager.isGeocoderAvailable();
  }
  /**
   * 地理编码:将地理描述转换为具体坐标集合。
   * @param locationName 地理位置描述。
   * @param maxItems 返回结果的最大数量。
   * @returns 编码后的坐标集合。
   */
  static async getGeoAddressFromLocationName(
    locationName: string,
    maxItems: number = 1
  ): Promise<Array<geoLocationManager.GeoAddress>> {
    const geocodeRequest: geoLocationManager.GeoCodeRequest = {
      description: locationName,
      maxItems,
      locale: 'zh',
    };
    try {
      const result = await geoLocationManager.getAddressesFromLocationName(geocodeRequest);
      return result || [];
    } catch (err) {
      LocationUtil.handleError(err, '地理编码失败');
      return [];
    }
  }
  /**
   * 地理编码:将地理描述转换为具体坐标。
   * @param locationName 地理位置描述。
   * @returns 编码后的坐标对象。
   */
  static async getAddressFromLocationName(locationName: string): Promise<geoLocationManager.GeoAddress> {
    const geoAddressList = await LocationUtil.getGeoAddressFromLocationName(locationName, 1);
    return geoAddressList.length > 0 ? geoAddressList[0] : {};
  }
  /**
   * 逆地理编码:将坐标转换为地理描述集合。
   * @param latitude 纬度。
   * @param longitude 经度。
   * @param maxItems 返回结果的最大数量。
   * @returns 逆编码后的地理描述集合。
   */
  static async getGeoAddressFromLocation(
    latitude: number,
    longitude: number,
    maxItems: number = 1
  ): Promise<Array<geoLocationManager.GeoAddress>> {
    const reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
      latitude,
      longitude,
      maxItems,
      locale: 'zh',
    };
    try {
      const result = await geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest);
      return result || [];
    } catch (err) {
      LocationUtil.handleError(err, '逆地理编码失败');
      return [];
    }
  }
  /**
   * 逆地理编码:将坐标转换为地理描述。
   * @param latitude 纬度。
   * @param longitude 经度。
   * @returns 逆编码后的地理描述对象。
   */
  static async getAddressFromLocation(latitude: number, longitude: number): Promise<geoLocationManager.GeoAddress> {
    const geoAddressList = await LocationUtil.getGeoAddressFromLocation(latitude, longitude, 1);
    return geoAddressList.length > 0 ? geoAddressList[0] : {};
  }
  /**
   * 获取当前的国家码。
   * @returns 当前国家码。
   */
  static async getCountryCode(): Promise<string> {
    try {
      const result = await geoLocationManager.getCountryCode();
      return result?.country || '';
    } catch (err) {
      LocationUtil.handleError(err, '获取国家码失败');
      return '';
    }
  }
  /**
   * 根据指定的两个经纬度坐标点,计算两点间的直线距离(单位:米)。
   * @param from 起始坐标。
   * @param to 目标坐标。
   * @returns 两点间的直线距离。
   */
  static calculateDistance(from: mapCommon.LatLng, to: mapCommon.LatLng): number {
    return map.calculateDistance(from, to);
  }
  /**
   * 根据指定的两个经纬度坐标点,计算两点间的直线距离(单位:米)。
   * @param fromLat 起始纬度。
   * @param fromLng 起始经度。
   * @param toLat 目标纬度。
   * @param toLng 目标经度。
   * @returns 两点间的直线距离。
   */
  static calculateDistanceEasy(fromLat: number, fromLng: number, toLat: number, toLng: number): number {
    const fromLatLng: mapCommon.LatLng = { latitude: fromLat, longitude: fromLng };
    const toLatLng: mapCommon.LatLng = { latitude: toLat, longitude: toLng };
    return map.calculateDistance(fromLatLng, toLatLng);
  }

  /**
   * 错误处理方法。
   * @param error 错误对象,必须是 BusinessError 类型。
   * @param message 提示信息。
   * @returns 返回错误码。如果无法获取错误码,则返回默认值 -1。
   */
  private static handleError(error: BusinessError, message: string): number {
    let errorCode = -1; // 默认错误码
    let errorMessage = '未知错误';

    if (error && typeof error.code === 'number') {
      errorCode = error.code;
      errorMessage = error.message || errorMessage;
    }
    // 记录错误日志
    LogUtil.error(`${message}: code=${errorCode}, message=${errorMessage}`);
    return errorCode;
  }
  /**
   * 获取错误消息。
   * @param code 错误码。
   * @param defaultMsg 默认错误消息。
   * @returns 错误消息。
   */
  static getErrorMsg(code: number, defaultMsg: string): string {
    const errorMessages: Map<number, string> = new Map([
      [201, '权限校验失败!'],
      [202, '系统API权限校验失败!'],
      [401, '参数检查失败!'],
      [801, '该设备不支持此API!'],
      [3301000, '位置服务不可用!'],
      [3301100, '请开启位置功能开关!'],
      [3301200, '定位失败,未获取到定位结果!'],
      [3301300, '逆地理编码查询失败!'],
      [3301400, '地理编码查询失败!'],
      [3301500, '区域信息(包含国家码)查询失败!'],
      [3301600, '地理围栏操作失败!'],
    ]);

    return errorMessages.get(code) || defaultMsg;
  }
}
代码如下:
TypeScript 复制代码
import geoLocationManager from '@ohos.geoLocationManager';
import { BusinessError, Callback } from '@ohos.base';
import { LogUtil } from './LogUtil';
import { PermissionUtil } from './PermissionUtil';
import { map, mapCommon } from '@kit.MapKit';
/**
 * 定位工具类 (WGS-84坐标系)
 * author: CSDN-鸿蒙布道师
 * since: 2025/04/22
 */
export class LocationUtil {
  /**
   * 判断位置服务是否已经使能(定位是否开启)。
   * @returns true 表示定位已开启,false 表示未开启。
   */
  static isLocationEnabled(): boolean {
    return geoLocationManager.isLocationEnabled();
  }
  /**
   * 申请定位权限。
   * @returns true 表示授权成功,false 表示用户拒绝授权。
   */
  static async requestLocationPermissions(): Promise<boolean> {
    const permissions: Array<string> = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
    let grant: boolean = await PermissionUtil.requestPermissions(permissions);
    if (!grant) {
      grant = await PermissionUtil.requestPermissionOnSetting(permissions);
    }
    return grant;
  }
  /**
   * 获取当前位置(简化版)。
   * @returns 当前位置信息。如果发生错误,则返回 null。
   */
  static async getCurrentLocationEasy(): Promise<geoLocationManager.Location | null> {
    return LocationUtil.getCurrentLocation();
  }
  /**
   * 获取当前位置。
   * @param request 可选的定位请求参数。
   * @returns 当前位置信息。如果发生错误,则返回 null。
   */
  static async getCurrentLocation(
    request?: geoLocationManager.CurrentLocationRequest | geoLocationManager.SingleLocationRequest
  ): Promise<geoLocationManager.Location | null> {
    try {
      return await geoLocationManager.getCurrentLocation(request);
    } catch (err) {
      LocationUtil.handleError(err, '获取当前位置失败');
      return null; // 返回 null 表示发生错误
    }
  }
  /**
   * 开启位置变化订阅,并发起定位请求(简化版)。
   * @param callBack 回调函数。
   * @returns 成功返回 0,失败返回错误码。
   */
  static onLocationChangeEasy(callBack: Callback<geoLocationManager.Location>): number {
    const defaultRequest: geoLocationManager.LocationRequest = {
      priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
      scenario: geoLocationManager.LocationRequestScenario.UNSET,
      timeInterval: 10,
      distanceInterval: 0,
      maxAccuracy: 0,
    };
    return LocationUtil.onLocationChange(defaultRequest, callBack);
  }
  /**
   * 开启位置变化订阅,并发起定位请求。
   * @param request 定位请求参数。
   * @param callBack 回调函数。
   * @returns 成功返回 0,失败返回错误码。
   */
  static onLocationChange(
    request: geoLocationManager.LocationRequest | geoLocationManager.ContinuousLocationRequest,
    callBack: Callback<geoLocationManager.Location>
  ): number {
    try {
      geoLocationManager.on('locationChange', request, callBack);
      return 0;
    } catch (err) {
      return LocationUtil.handleError(err, '开启位置变化订阅失败');
    }
  }
  /**
   * 关闭位置变化订阅,并删除对应的定位请求。
   * @param callback 不传则取消当前类型的所有订阅。
   * @returns 成功返回 0,失败返回错误码。
   */
  static offLocationChange(callback?: Callback<geoLocationManager.Location>): number {
    try {
      if (callback) {
        geoLocationManager.off('locationChange', callback);
      } else {
        geoLocationManager.off('locationChange');
      }
      return 0;
    } catch (err) {
      return LocationUtil.handleError(err, '关闭位置变化订阅失败');
    }
  }
  /**
   * 判断地理编码与逆地理编码服务是否可用。
   * @returns true 表示服务可用,false 表示不可用。
   */
  static isGeocoderAvailable(): boolean {
    return geoLocationManager.isGeocoderAvailable();
  }
  /**
   * 地理编码:将地理描述转换为具体坐标集合。
   * @param locationName 地理位置描述。
   * @param maxItems 返回结果的最大数量。
   * @returns 编码后的坐标集合。
   */
  static async getGeoAddressFromLocationName(
    locationName: string,
    maxItems: number = 1
  ): Promise<Array<geoLocationManager.GeoAddress>> {
    const geocodeRequest: geoLocationManager.GeoCodeRequest = {
      description: locationName,
      maxItems,
      locale: 'zh',
    };
    try {
      const result = await geoLocationManager.getAddressesFromLocationName(geocodeRequest);
      return result || [];
    } catch (err) {
      LocationUtil.handleError(err, '地理编码失败');
      return [];
    }
  }
  /**
   * 地理编码:将地理描述转换为具体坐标。
   * @param locationName 地理位置描述。
   * @returns 编码后的坐标对象。
   */
  static async getAddressFromLocationName(locationName: string): Promise<geoLocationManager.GeoAddress> {
    const geoAddressList = await LocationUtil.getGeoAddressFromLocationName(locationName, 1);
    return geoAddressList.length > 0 ? geoAddressList[0] : {};
  }
  /**
   * 逆地理编码:将坐标转换为地理描述集合。
   * @param latitude 纬度。
   * @param longitude 经度。
   * @param maxItems 返回结果的最大数量。
   * @returns 逆编码后的地理描述集合。
   */
  static async getGeoAddressFromLocation(
    latitude: number,
    longitude: number,
    maxItems: number = 1
  ): Promise<Array<geoLocationManager.GeoAddress>> {
    const reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
      latitude,
      longitude,
      maxItems,
      locale: 'zh',
    };
    try {
      const result = await geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest);
      return result || [];
    } catch (err) {
      LocationUtil.handleError(err, '逆地理编码失败');
      return [];
    }
  }
  /**
   * 逆地理编码:将坐标转换为地理描述。
   * @param latitude 纬度。
   * @param longitude 经度。
   * @returns 逆编码后的地理描述对象。
   */
  static async getAddressFromLocation(latitude: number, longitude: number): Promise<geoLocationManager.GeoAddress> {
    const geoAddressList = await LocationUtil.getGeoAddressFromLocation(latitude, longitude, 1);
    return geoAddressList.length > 0 ? geoAddressList[0] : {};
  }
  /**
   * 获取当前的国家码。
   * @returns 当前国家码。
   */
  static async getCountryCode(): Promise<string> {
    try {
      const result = await geoLocationManager.getCountryCode();
      return result?.country || '';
    } catch (err) {
      LocationUtil.handleError(err, '获取国家码失败');
      return '';
    }
  }
  /**
   * 根据指定的两个经纬度坐标点,计算两点间的直线距离(单位:米)。
   * @param from 起始坐标。
   * @param to 目标坐标。
   * @returns 两点间的直线距离。
   */
  static calculateDistance(from: mapCommon.LatLng, to: mapCommon.LatLng): number {
    return map.calculateDistance(from, to);
  }
  /**
   * 根据指定的两个经纬度坐标点,计算两点间的直线距离(单位:米)。
   * @param fromLat 起始纬度。
   * @param fromLng 起始经度。
   * @param toLat 目标纬度。
   * @param toLng 目标经度。
   * @returns 两点间的直线距离。
   */
  static calculateDistanceEasy(fromLat: number, fromLng: number, toLat: number, toLng: number): number {
    const fromLatLng: mapCommon.LatLng = { latitude: fromLat, longitude: fromLng };
    const toLatLng: mapCommon.LatLng = { latitude: toLat, longitude: toLng };
    return map.calculateDistance(fromLatLng, toLatLng);
  }

  /**
   * 错误处理方法。
   * @param error 错误对象,必须是 BusinessError 类型。
   * @param message 提示信息。
   * @returns 返回错误码。如果无法获取错误码,则返回默认值 -1。
   */
  private static handleError(error: BusinessError, message: string): number {
    let errorCode = -1; // 默认错误码
    let errorMessage = '未知错误';

    if (error && typeof error.code === 'number') {
      errorCode = error.code;
      errorMessage = error.message || errorMessage;
    }
    // 记录错误日志
    LogUtil.error(`${message}: code=${errorCode}, message=${errorMessage}`);
    return errorCode;
  }
  /**
   * 获取错误消息。
   * @param code 错误码。
   * @param defaultMsg 默认错误消息。
   * @returns 错误消息。
   */
  static getErrorMsg(code: number, defaultMsg: string): string {
    const errorMessages: Map<number, string> = new Map([
      [201, '权限校验失败!'],
      [202, '系统API权限校验失败!'],
      [401, '参数检查失败!'],
      [801, '该设备不支持此API!'],
      [3301000, '位置服务不可用!'],
      [3301100, '请开启位置功能开关!'],
      [3301200, '定位失败,未获取到定位结果!'],
      [3301300, '逆地理编码查询失败!'],
      [3301400, '地理编码查询失败!'],
      [3301500, '区域信息(包含国家码)查询失败!'],
      [3301600, '地理围栏操作失败!'],
    ]);

    return errorMessages.get(code) || defaultMsg;
  }
}
相关推荐
robotx2 小时前
安卓线程相关
android
RickeyBoy2 小时前
独立 App 配置阿里云 CDN 记录
ios
消失的旧时光-19432 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon3 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon3 小时前
VSYNC 信号完整流程2
android
dalancon3 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
不爱吃糖的程序媛4 小时前
OpenHarmony 工程结构剖析
harmonyos
白玉cfc4 小时前
接口与API设计
ios·objective-c
用户69371750013844 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android5 小时前
Android 刷新一帧流程trace拆解
android