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;}
}
代碼如下:
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;}
}