如何在WGS84坐标系实现FOLLOW\_ROTATE相同定位展示效果
===================================

#### 问题现象

Map Kit使用GCJ02坐标系，当使用WGS84坐标图源时，使用Map Kit自带的setMyLocation设置我的位置时，我的位置图标是GCJ02坐标系，在WGS84坐标地图上会产生偏移，所以无法使用自带的我的位置相关能力。

使用WGS84坐标图源时，如何通过Marker实现MyLocationDisplayType.FOLLOW\_ROTATE相同效果，即连续定位，且将相机移动到地图中心点，定位蓝点依照设备方向旋转，并且会跟随设备移动。

#### 背景知识

[sensor.on](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-sensor#sensoron)：传感器订阅，其中[ORIENTATION](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-sensor#orientation9)表示订阅方向传感器数据。

[setRotation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/map-map-marker#section20889283323)：设置标记的旋转角度，即标记围绕标记锚点顺时针旋转的角度，旋转轴垂直于标记。

[setPosition](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/map-map-marker#section98480257324)：设置标记的位置坐标。

[geoLocationManager.on('locationChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-geolocationmanager#geolocationmanageronlocationchange)：开启位置变化订阅，并发起定位请求。

#### 解决方案

前提条件：需申请ohos.permission.LOCATION、ohos.permission.APPROXIMATELY\_LOCATION、ohos.permission.ACCELEROMETER权限。

1.  创建Marker标记。
    
    let markerOptions: mapCommon.MarkerOptions = {
      position: {
        latitude: 31.984410259206815,
        longitude: 118.76625379397866
      },
      rotation: 0,
      visible: true,
      zIndex: 0,
      alpha: 1,
      anchorU: 0.5,
      anchorV: 0.5,
      clickable: true,
      draggable: true,
      flat: false
    };
    this.marker = await this.mapController.addMarker(markerOptions);
    
2.  通过sensor.on(sensor.SensorId.ORIENTATION)监听设备方向，通过setRotation设置Marker标记的旋转角度为设备方向角度。
    
    try {
      sensor.on(sensor.SensorId.ORIENTATION, (data: sensor.OrientationResponse) => {
        let direction = Math.round(data.alpha);
        let nowOrientation = display.getDefaultDisplaySync().orientation;
        if (nowOrientation !== 0) {
          direction = direction - 90 \* nowOrientation;
          if (direction < 0) {
            direction = direction + 360;
          }
        }
        this.marker?.setRotation(direction);
      }, { interval: 100000000 });
    } catch (error) {
      console.error(\`Failed to invoke on. Code: ${error.code}, message: ${error.message}\`);
    }
    
3.  通过geoLocationManager.on('locationChange')监听位置变化，使用setPosition设置标记的位置为监听返回的坐标。注意位置监听返回的坐标是WGS84坐标系，在WGS84图源上直接使用该坐标设置Marker位置即可，无需转换为GCJ02坐标系。
    
    同时配置moveCamera接口将相机移动至marker为屏幕中心点。
    
    let requestInfo: geoLocationManager.LocationRequest = {
      'priority': geoLocationManager.LocationRequestPriority.FIRST\_FIX,
      'scenario': geoLocationManager.LocationRequestScenario.UNSET,
      'timeInterval': 1,
      'distanceInterval': 0,
      'maxAccuracy': 0
    };
    let locationChange = (location: geoLocationManager.Location): void => {
      console.info('locationChange: data: ' + JSON.stringify(location));
      _// 添加WGS84坐标Marker_
      this.marker?.setPosition(location);
      _// 并将相机移动到地图中心点_
      let cameraUpdate = map.newLatLng(location);
      this.mapController?.moveCamera(cameraUpdate);
    };
    

完整代码：

Index.ets:

import { map, mapCommon, MapComponent } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
import { geoLocationManager } from '@kit.LocationKit';
import { sensor } from '@kit.SensorServiceKit';
import { display } from '@kit.ArkUI';
import { Permissions } from '@kit.AbilityKit';
import PermissionsRequest from '../utils/PermissionsRequest';

@Entry
@Component
struct Index {
  private mapOptions?: mapCommon.MapOptions;
  private mapController?: map.MapComponentController;
  private callback?: AsyncCallback<map.MapComponentController>;
  private marker?: map.Marker;

  async aboutToAppear() {
    this.mapOptions = {
      position: {
        target: {
          latitude: 31.984410259206815,
          longitude: 118.76625379397866
        },
        zoom: 15
      }
    };

    this.callback = async (err, mapController) => {
      if (!err) {
        this.mapController = mapController;
        let permissions: Array<Permissions> = \['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY\_LOCATION'\];
        await PermissionsRequest.commonRequestPermissions(this.getUIContext(), permissions);
        let markerOptions: mapCommon.MarkerOptions = {
          position: {
            latitude: 31.984410259206815,
            longitude: 118.76625379397866
          },
          rotation: 0,
          visible: true,
          zIndex: 0,
          alpha: 1,
          anchorU: 0.5,
          anchorV: 0.5,
          clickable: true,
          draggable: true,
          flat: false
        };
        this.marker = await this.mapController.addMarker(markerOptions);
       _ // 监听设备方向，设置Marker的旋转角度_
        try {
          sensor.on(sensor.SensorId.ORIENTATION, (data: sensor.OrientationResponse) => {
            let direction = Math.round(data.alpha);
            let nowOrientation = display.getDefaultDisplaySync().orientation;
            if (nowOrientation !== 0) {
              direction = direction - 90 \* nowOrientation;
              if (direction < 0) {
                direction = direction + 360;
              }
            }
            this.marker?.setRotation(direction);
          }, { interval: 100000000 });
        } catch (error) {
          console.error(\`Failed to invoke on. Code: ${error.code}, message: ${error.message}\`);
        }

       _ // 持续监听设备位置，更新Marker点的位置_
        let requestInfo: geoLocationManager.LocationRequest = {
          'priority': geoLocationManager.LocationRequestPriority.FIRST\_FIX,
          'scenario': geoLocationManager.LocationRequestScenario.UNSET,
          'timeInterval': 1,
          'distanceInterval': 0,
          'maxAccuracy': 0
        };
        let locationChange = (location: geoLocationManager.Location): void => {
          console.info('locationChange: data: ' + JSON.stringify(location));
        _  // 添加WGS84坐标Marker_
          this.marker?.setPosition(location);
        _  // 并将相机移动到地图中心点_
          let cameraUpdate = map.newLatLng(location);
          this.mapController?.moveCamera(cameraUpdate);
        };
        try {
          geoLocationManager.on('locationChange', requestInfo, locationChange);
        } catch (err) {
          console.error(\`监听位置失败, code is: ${err.code}, message is: ${err.message}\`);
        }
      } else {
        console.error(\`地图初始化失败, code is：${err.code}, message is ${err.message}\`);
      }
    };
  }

  build() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        MapComponent({
          mapOptions: this.mapOptions,
          mapCallback: this.callback,
        })
          .width('100%')
          .height('100%');
      }.width('100%');
    }.height('100%');
  }
}

utils/PermissionsRequest.ets:

import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { hilog } from '@kit.PerformanceAnalysisKit';

class PermissionsRequest {
  _/\*\*_
   _\* 通用申请权限_
   _\*/_
  async commonRequestPermissions(context: UIContext, permissions: Array<Permissions>) {
    let isPermission: boolean = await this.checkPermissions(permissions);
    if (!isPermission) {
    _  //一次授权_
      let isDialogShown = await this.requestPermissions(context, permissions);
      if (isDialogShown !== true) {
       _ //二次授权_
        this.requestPermissionsOnSetting(context, permissions);
      }
    }
  }

 _ /\*\*_
   _\* 校验应用是否被授权_
   _\*/_
  async checkPermissions(permissions: Array<Permissions>) {
    for (let permission of permissions) {
      let grantStatus: abilityAccessCtrl.GrantStatus = await this.checkAccessToken(permission);
      if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION\_GRANTED) {
        return false;
      }
    }
    return true;
  }

  async checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION\_DENIED;

   _ // 获取应用程序的accessTokenID_
    let tokenId: number = 0;
    try {
      let bundleInfo: bundleManager.BundleInfo =
        await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET\_BUNDLE\_INFO\_WITH\_APPLICATION);
      let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
      tokenId = appInfo.accessTokenId;
      grantStatus = await atManager.checkAccessToken(tokenId, permission);
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      hilog.error(0x000, 'testTag', \`Failed to check access token  ${err.code}, message is ${err.message}\`);
    }
    return grantStatus;
  }

  _/\*\*_
   _\* 申请用户授权_
   _\*/_
  async requestPermissions(context: UIContext, permissions: Array<Permissions>): Promise<boolean | undefined> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    try {
      let data =
        await atManager.requestPermissionsFromUser(context.getHostContext() as common.UIAbilityContext, permissions);
      hilog.info(0x000, 'testTag', 'requestPermissions1 success', JSON.stringify(data));
      return data.dialogShownResults ? data.dialogShownResults\[0\] : undefined; _// 返回请求是否有弹窗_
    } catch (e) {
      hilog.error(0x000, 'testTag', \`requestPermissions1 err Code is ${e.code}, message is ${e.message}\`);
      return undefined;
    }
  }

  _/\*\*_
   _\* 2次申请用户授权_
   _\*/_
  requestPermissionsOnSetting(context: UIContext, permissions: Array<Permissions>) {
    let keyPerms = JSON.stringify(permissions);
    let store = preferences.getPreferencesSync(context.getHostContext(), { name: 'permsHasOnSetting' });
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    atManager.requestPermissionOnSetting(context.getHostContext(), permissions)
      .then((res: Array<abilityAccessCtrl.GrantStatus>) => {
        hilog.info(0x000, 'testTag', 'requestPermissions2 success', JSON.stringify(res));
        store.putSync(keyPerms, 1);
        store.flush();
      })
      .catch((err: BusinessError) => {
        hilog.info(0x000, 'testTag', 'requestPermissions2 err', JSON.stringify(err));
      });
  }
}

export default new PermissionsRequest();