vany5921 发表于 2020-4-15 23:35:35

将TinkerNode NB-IoT的GPS真实坐标转为百度地图坐标

用NB-IoT自带的GPS可轻松获取地理编码信息。但是,得到的数据在地图上直接查看会发现偏移很大。其实,很多人忽略了一点,国家为了电子地图安全,强制要求地图服务商对地图坐标进行加密,产生随机偏移,因此我们看到的地图上标注的地理位置并不是自己的真实地理位置。GCJ-02加密标准是基本的数据加密算法,百度地图在GCJ-02的基础上又加入了自己的一套加密算法。用下面的代码可以让你把NB-IoT上获得的GPS数据转换为GCJ-02地理坐标或百度地图坐标,通过逆向地理编码你就能查到具体的位置信息。(为什么我会选择百度地图坐标,是为了接入百度鹰眼服务,目前已经可以利用封装的AT指令通过TCP方式上报鹰眼,通过鹰眼控制台进行对车辆或穿戴设备进行位置追踪和轨迹查询。)

#include "DFRobot_BC20.h"
#include <math.h>

DFRobot_BC20 myBC20;

double pi = 3.14159265358979324;      //圆周率
double ee = 0.00669342162296594323;   //椭球的偏心率
double a = 6378245.0;               //卫星椭球坐标投影到平面地图坐标系的投影因子
double x_pi = 3.14159265358979324 * 3000.0 / 180.0;//圆周率转换量
double Lat;
double Lon;

// 求弧度
double radian(double d)
{
    return d * pi / 180.0;   //角度1? = π / 180
}

double transformLat(double lat, double lon)    //纬度转化
{
   double ret = -100.0 + 2.0 * lat + 3.0 * lon + 0.2 * lon * lon + 0.1 * lat * lon + 0.2 * sqrt(abs(lat));
   ret += (20.0 * sin(6.0 * lat * pi) + 20.0 * sin(2.0 * lat * pi)) * 2.0 / 3.0;
   ret += (20.0 * sin(lon * pi) + 40.0 * sin(lon / 3.0 * pi)) * 2.0 / 3.0;
   ret += (160.0 * sin(lon / 12.0 * pi) + 320 * sin(lon * pi/ 30.0)) * 2.0 / 3.0;
   return ret;
}


double transformLon(double lat,double lon)   //经度转化
{
   double ret = 300.0 + lat + 2.0 * lon + 0.1 * lat * lat + 0.1 * lat * lon + 0.1 * sqrt(abs(lat));
   ret += (20.0 * sin(6.0 * lat * pi) + 20.0 * sin(2.0 * lat * pi)) * 2.0 / 3.0;
   ret += (20.0 * sin(lat * pi) + 40.0 * sin(lat / 3.0 * pi)) * 2.0 / 3.0;
   ret += (150.0 * sin(lat / 12.0 * pi) + 300.0 * sin(lat / 30.0 * pi)) * 2.0 / 3.0;
   return ret;
}


/*****************************************************************************
* WGS84(GPS坐标系) to 火星坐标系(GCJ-02)
*
* @param lat
* @param lon
* @return
****************************************************************************/
void GPS84_To_GCJ02(double WGS84_Lat, double WGS84_Lon,double * GCJ02_Lat, double * GCJ02_Lon)
{
double dLat;
double dLon;
double radLat;
double magic;
double sqrtMagic;

dLat = transformLat(WGS84_Lon - 105.0, WGS84_Lat - 35.0);
dLon = transformLon(WGS84_Lon - 105.0, WGS84_Lat - 35.0);
radLat = WGS84_Lat / 180.0 * pi;
magic = sin(radLat);
magic = 1 - ee * magic * magic;
sqrtMagic = sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi);
*GCJ02_Lat = WGS84_Lat + dLat;    //GCJ02_Lat是百度纬度存储变量的地址*GCJ02_Lat就是那个值
*GCJ02_Lon = WGS84_Lon + dLon;    //GCJ02_Lon是百度经度存储变量的地址*GCJ02_Lon
}



/*****************************************
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
*
* @param gg_lat
* @param gg_lon
*****************************************/
//传入的参数    GCJ02_To_BD09(*BD09_Lat,*BD09_Lon,BD09_Lat,BD09_Lon);
//(*BD09_Lat,*BD09_Lon)火星坐标,(BD09_Lat,BD09_Lon)是变量的地址
void GCJ02_To_BD09(double GCJ02_Lat,double GCJ02_Lon,double * BD_09_Lat,double * BD_09_Lon)
{
    double x = GCJ02_Lon, y = GCJ02_Lat;
    double z= sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
    double theta =atan2(y, x) + 0.000003 * cos(x * x_pi);
    *BD_09_Lon = z * cos(theta) + 0.0065;
    *BD_09_Lat = z * sin(theta) + 0.006;
}


/*************************************************************
函数名称:GPS_transformation(double WGS84_Lat, double WGS84_Lon,double * BD_09_Lat, double * BD_09_Lon)
函数功能:GPS坐标转百度地图坐标
输入参数:WGS84_Lat,WGS84_Lon GPS获取到真实经纬度储存得到的百度经纬度变量的地址 BD_09_Lat,BD_09_Lon指向那个变量
输出参数:
*************************************************************/
void GPS_transformation(double WGS84_Lat,double WGS84_Lon,double * BD_09_Lat,double * BD_09_Lon)
{
   GPS84_To_GCJ02(WGS84_Lat,WGS84_Lon,BD_09_Lat,BD_09_Lon);         //GPS坐标转火星坐标
   GCJ02_To_BD09(*BD_09_Lat,*BD_09_Lon,BD_09_Lat,BD_09_Lon);         //火星坐标转百度坐标
}



/*************************************************************
函数名称:double GetDistance(double lat1, double lng1, double lat2, double lng2)
函数功能:返回两个点之间的距离
输入参数:两个点的经纬度(角度)
输出参数:距离(单位:km)
*************************************************************/
double Cal_Distance(double lat1, double lng1, double lat2, double lng2)
{
    double EARTH_RADIUS = 6378.137;      //地球近似半径
    double radLat1 = radian(lat1);
    double radLat2 = radian(lat2);
    double a = radLat1 - radLat2;
    double b = radian(lng1) - radian(lng2);
   
    double dst = 2 * asin((sqrt(pow(sin(a / 2), 2) + cos(radLat1) * cos(radLat2) * pow(sin(b / 2), 2) )));    //asin()反正弦值函数
   
    dst = dst * EARTH_RADIUS;
    dst= (uint32_t)(dst * 10000.0) / 10000.0;//舍弃小数点后4位的数
    return dst;
}


void setup() {
Serial.begin(115200);
Serial.print("Starting the BC20.Please wait. . . ");
while(!myBC20.powerOn()){
      delay(1000);
      Serial.print(".");
}
Serial.println("BC20 started successfully !");
Serial.println("check OK");
if(myBC20.getQGNSSC() == OFF){
      Serial.println("open QGNSSC");
      myBC20.setQGNSSC(ON);
}
}

void loop() {
delay(2000);
myBC20.getQGNSSRD();
Serial.println("-----------------------------");
Serial.printf("%16.12f\n", sGGNS.LatitudeVal);
Serial.printf("%16.12f\n", sGGNS.LongitudeVal);
GPS_transformation(sGGNS.LatitudeVal,sGGNS.LongitudeVal, &Lat, &Lon);
Serial.printf("BD_Lat: %16.12f\n",Lat);
Serial.printf("BD_Lon: %16.12f\n",Lon);
}

如何测试计算的准不准呢,我是在百度开发者控制中心找到位置服务的接口测试进行逆向地理编码来查看解析的位置是否正确,这样可以保证我在百度系内调用任何地理数据都准确无误。

直接上视频:https://www.bilibili.com/video/BV1Q54y197XJ/





Mr Guo 发表于 2020-7-16 18:00:26

大佬,这篇文章可以在详细一点吗
页: [1]
查看完整版本: 将TinkerNode NB-IoT的GPS真实坐标转为百度地图坐标