背景:项目中如果仅仅只使用一套地图API,那是非常舒服的,也推荐这样做;但是如果技术选型完成之后,工作进入开发阶段,这时突然来了一个新需求,需要接入第三方而且会用到第三方地图,恰巧第三方地图的坐标系与当前项目使用的坐标系不是同一套,那么恭喜你,捡到宝了,这篇文档对你有很大的帮助。

    抛出问题:WGS84(华为地图采用的坐标系,项目采用)与GCJ02(高德地图采用的坐标系,第三方采用)的相同经纬度,对应的地点信息有偏差,大概3公里左右。

    话不多说直接上菜!

一.华为地图正常调用API,根据经纬度获取位置信息

async getLocalInfo(){
  let cLocation = await geoLocationManager.getCurrentLocation();  //获取当前位置信息,包含经纬度信息

  if (!cLocation?.latitude) {
      return;
  }

  let info: geoLocationManager.ReverseGeoCodeRequest = {  //逆地理编码对象
      // 获取当前位置的纬度
      latitude: cLocation.latitude,
      // 获取当前位置的经度
      longitude: cLocation.longitude,
      // 获取次数大于10次以便获得cityCode
      maxItems: 1
  }

  //调用逆地理编码服务,根据经纬度获取位置信息,拿到位置信息,可以在原生应用进行使用了
  let address = await geoLocationManager.getAddressesFromLocation(info) || [];  
  if (address.length === 0) {
      resolve('');
  }
  return address
}

二、需求是:第三方需要原生应用传入一个GCJ02的经纬度,根据经纬度来显示具体的定位信息

遇到的问题:第三方调用getLocalInfo()函数,得到的却是WGS84坐标系下的经纬度,就会出现定位偏差的问题。
目标是:调用该函数 得到高德使用的GCJ02坐标系的经纬度,那么就需要将WGS84下的经纬度转化为GCJ02坐标系下的经纬度。

解决方法:

一、使用鸿蒙内置的API 

map.convertCoordinateSync()可以将WGS84转化为GCJ02,如果业务只有输出没有输入的话,推荐使用这种方式

缺点:GCJ02转WGS84没有直接的API,转化起来会比较麻烦

二、封装一个坐标系相互转化的工具,封装成一个ets即可

const x_PI = (3.14159265358979324 * 3000.0) / 180.0;
const PI = 3.1415926535897932384626;
const a = 6378245.0; //卫星椭球坐标投影到平面地图坐标系的投影因子。
const ee = 0.00669342162296594323; //椭球的偏心率。


//判断是否在国内,在中国国内的经纬度才需要做偏移
function out_of_china(lng:number, lat:number) {
  return (
    lng < 72.004 ||
      lng > 137.8347 ||
      (lat < 0.8293 || lat > 55.8271 || false)
  );
}

//转化经度
function transformlng(lng:number, lat:number) {
  let ret =
    300.0 +
      lng +
      2.0 * lat +
      0.1 * lng * lng +
      0.1 * lng * lat +
      0.1 * Math.sqrt(Math.abs(lng));
  ret +=
  ((20.0 * Math.sin(6.0 * lng * PI) +
    20.0 * Math.sin(2.0 * lng * PI)) *
    2.0) /
    3.0;
  ret +=
  ((20.0 * Math.sin(lng * PI) +
    40.0 * Math.sin((lng / 3.0) * PI)) *
    2.0) /
    3.0;
  ret +=
  ((150.0 * Math.sin((lng / 12.0) * PI) +
    300.0 * Math.sin((lng / 30.0) * PI)) *
    2.0) /
    3.0;
  return ret;
}

//转化纬度
function transformlat(lng:number, lat:number) {
  let ret =
    -100.0 +
      2.0 * lng +
      3.0 * lat +
      0.2 * lat * lat +
      0.1 * lng * lat +
      0.2 * Math.sqrt(Math.abs(lng));
  ret +=
  ((20.0 * Math.sin(6.0 * lng * PI) +
    20.0 * Math.sin(2.0 * lng * PI)) *
    2.0) /
    3.0;
  ret +=
  ((20.0 * Math.sin(lat * PI) +
    40.0 * Math.sin((lat / 3.0) * PI)) *
    2.0) /
    3.0;
  ret +=
  ((160.0 * Math.sin((lat / 12.0) * PI) +
    320 * Math.sin((lat * PI) / 30.0)) *
    2.0) /
    3.0;
  return ret;
}

//wgs84 to gcj02   地球坐标系 转 火星坐标系
export function wgs84_to_gcj02(lng:number, lat:number) {
  if (out_of_china(lng, lat)) {
    return [lng, lat];
  } else {
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    let radlat = (lat / 180.0) * PI;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    let sqrtmagic = Math.sqrt(magic);
    dlat =
      (dlat * 180.0) /
        (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
    dlng =
      (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
    let mglat = lat + dlat;
    let mglng = lng + dlng;

    return [mglng, mglat];
  }
}


//gcj02 to wgs84  火星坐标系 转 地球坐标系
export function gcj02_to_wgs84(lng:number, lat:number) {
  if (out_of_china(lng, lat)) {
    return [lng, lat]
  }
  else {
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    let radlat = lat / 180.0 * PI;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    let sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
    let mglat = lat + dlat;
    let mglng = lng + dlng;
    return [lng * 2 - mglng, lat * 2 - mglat]
  }
}

三、使用工具转化坐标系

async getLocalInfo(){
    //xxx 步骤一的代码,此处暂时省略

    
  //待转换经纬度
  const waitTransformLocation: ESObject = {} 
  waitTransformLocation.latitude = cLocation.latitude
  waitTransformLocation.longitude = cLocation.longitude

  //转换坐标  WGS84转GCJ02
  const convertedLocation = wgs84_to_gcj02(waitTransformLocation.longitude,waitTransformLocation.latitude)
  let addressInfo: PositionDto = {
      latitude: convertedLocation.latitude,  //更新经纬度 
      longitude: convertedLocation.longitude,  //更新经纬度
      address: address[0].placeName,
      country: address[0].countryName,
      province: address[0].administrativeArea,
      city: address[0].subAdministrativeArea,
      district: address[0].subLocality,
      street: address[0].roadName || '' + address[0].subRoadName || '',
      cityCode: address[0].descriptions && address[0].descriptions.length ?
      address[0].descriptions[0] : '',
      adcode: address[0].descriptions && address[0].descriptions.length ?
      address[0].descriptions[1].slice(0, 6) : '',
  }

  if (!addressInfo.cityCode && addressInfo.province) {
    //  addressInfo.cityCode = AreaDataUtil.getCodeByName(addressInfo.province, addressInfo.city);
  }

return addressInfo

}

这样处理之后,就可以将正确的位置信息和GCJ的经纬度传递给第三方了

如果需要接收第三方的GCJ02经纬度并进行处理显示的话,就需要使用工具函数中的gcj02_to_wgs84方法,方法同理

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐