在嵌入式平台上将GPS/北斗模块获取的经纬度转换为百度地图经纬度

您所在的位置:网站首页 拾坐标地图 在嵌入式平台上将GPS/北斗模块获取的经纬度转换为百度地图经纬度

在嵌入式平台上将GPS/北斗模块获取的经纬度转换为百度地图经纬度

2024-07-12 10:58| 来源: 网络整理| 查看: 265

一、前言

  最近需要做一个嵌入式系统显示地图的项目,百度地图给我们留出了API接口可以调用。百度地图API网址为:https://lbsyun.baidu.com

  之前已经有同事做好了地图获取的程序,但是显示的位置和实际位置大概有1km的偏差,上网查阅各种资料,试过各种经纬度转换的函数,最后得到的结果都很差。最后在百度地图常见问题的一栏中见到了以下内容:

  “5、百度坐标为何有偏移?

  国际经纬度坐标标准为WGS-84,国内必须至少使用国测局制定的GCJ-02,对地理位置进行首次加密。百度坐标在此基础上,进行了BD-09二次加密措施,更加保护了个人隐私。百度对外接口的坐标系并不是GPS采集的真实经纬度,需要通过坐标转换接口进行转换。”

  根据以上信息可以知道,既然是百度地图官方加密,那么想要得到正确的百度地图坐标,就只能通过百度地图给出的坐标转换接口进行转换。其他任何民间的转换方法都不可靠,因为就算有人找到了转换方法,百度地图官方也可以改。

二、启动百度地图服务,获取转换过后的经纬度

  点击菜单栏的“开发文档”->“服务接口”->“Web服务API”,

  

 在左侧服务选择栏中点击“坐标转换”,如下图:

  

   这里就是坐标转换的服务介绍。

  在使用服务之前,要先完成“登录百度账号”->“申请成为百度开发者”->“获取服务秘钥(ak)”,在官方网页上有指引教程。

  或者登录百度账号后,在“控制台”->“应用管理”->“我的应用”中点击“创建应用”,如下图:

  

 

   在弹出的界面中应用名称名字随便取,应用类型选浏览器端,启用服务里面的”静态图”和“坐标转换”都要勾选(本次只使用坐标转换服务,以后会用到静态图),Referer白名单里写一个“*”即可,然后点击提交。

  

   这时在应用列表中就能看到刚才创建的应用了, 并且有一个AK,如下图:

  

  将这个AK复制下来,在网页中打开“坐标转换”的“服务文档”,这里有一个网址,如下图:

  

    里面的参数都有介绍,但一般不用改,只要将ak填入自己的ak,并且经纬度填入自己用GPS/北斗模块获取到的经纬度即可,然后将这一串网址复制到浏览器中打开即可。浏览器会直接显示返回结果,如下:

  {"status":0,"result":[{"x":108.99576850316559,"y":34.38357890574941}]}

  这是一个json的数据格式,status结果为0表示转换成功,result中的x表示的就是转换后的经度,y表示的就是转换后的纬度。

  有些人可能会问,为什么没有指定是东半球还是西半球,没有指定是南半球还是北半球,我只能说没必要。如果想获取境外地图,百度地图有境外地图的服务。

三、通过wireshark抓取HTTP数据包

  上节只是通过浏览器得到了转换后的坐标,如果要在linux下编程实现,需要将自己“冒充”为浏览器。

  首先,我们需要知道,浏览器到底给谁发送了什么东西,这个时候就要用到一个非常强大的网络抓包工具:wireshark。另外,在浏览器中输入的网址是以“https”开头的,这是加密过的,就算把包抓出来也看不出里面的内容。直接删掉“https”中的“s”,将删掉“s”的网址放到浏览器中照样能够得到转换后的经纬度,但是这样就是明文传输了,我们就可以看出里面的数据了。

  打开wireshark,双击正在上网的网卡(我的是WLAN 5),如下图:

  

  这时wireshark抓出来很多包,我们需要设置一下过滤规则,在过滤器中输入“http”后按回车,如下图:

  

   一下子过滤掉很多数据,这时在浏览器中输入坐标转换的网址,注意要把前面的https改为http,当浏览器得到转换结果之后停止wireshark的抓包,这时wireshark抓到很多包,如下图:

  

  从抓到的数据中很容易分辨出哪些是和百度地图相关的数据包,这样我们就知道了目标IP地址为220.181.43.101,为了再次过滤掉不必要的干扰,在过滤规则里加上ip.addr==220.181.43.101,这样就只剩和百度地图相关的数据了,如下图:

  

  任选一条,右键->追踪流->HTTP流,如下图:

   

    在弹出的界面中能够看到,红色区域是源(自己)向目标(百度地图)发出的数据,蓝色区域是目标(百度地图)向源(自己)返回的数据,其中就有我们想要的转换过后的数据,如下图:

  

   按照上图中红色部分发送的请求包编写程序(因为第1段红色部分发出去之后,百度地图就返回了转换过后的经纬度,因此不需要再发送第2段红色部分的数据了,然鹅我也不知道第二段红色部分数据发出去是为了什么)。特别注意,数据中换行的地方是真的换行符,windows中的换行符为“\r\n”,并且最后一行是个空行,空行也必须发出去。发送的内容是HTTP请求包,想知道包里各部分代表什么意义可以去搜索“HTTP请求报文格式”。

  编写的程序是将接收到的报文打印出来,具体代码先不放出来, 最后会放出完整版代码。程序运行结果如下:

  

  可以看到,接收到的数据前面部分和抓包抓出来的数据一样,最后那部分,也就是包含了我们要的经纬度数据的那部分打印出来却是乱码。这是为什么呢,首先,在wireshark中能看到原始数据,如下图:

  

   在“Server: apache\r\n”后面还有一个空行(这可以作为报文内容的起始标志),后面的数据确确实实不是在wireshark中看到的json数据。但是在前面打印出来的数据中能看到,“Content-Encoding: gzip”这一行,说明内容是用gzip编码过的。想要得到最终的数据还得用gzip解码。

四、将经纬度数据解码出来

  4.1)用Ubuntu自带的gzip命令解码

  通过编程,可以将数据内容保存到一个文件中,该文件是用gzip编码过的二进制文件,文件后缀必须为.gz,例如contet.txt.gz,之后输入命令,gzip -d contet.txt.gz,那么contet.txt.gz就会变成一个名为contet.txt的新文件,这个文件就是解码过后的文件。打开该文件就可以看到里面的json数据。接下来编写程序将文件中的内容读取出来再解析即可,如果有cJSON库可以用cJSON库解析,这里数据比较简单,用字符串解析也很容易。

  编写程序时,调用函数system("gzip -d contet.txt.gz");就相当于在命令行输入了gzip -d contet.txt.gz。

  4.2)用zlib库解码

  4.1中的方法需要在嵌入式linux平台中支持gzip命令,显然一般的平台是不支持这条命令的。原本以为自己要手撕代码,但幸运的是在网上找到一篇博客,上面的代码可以直接使用,博客链接如下:https://blog.csdn.net/weixin_28607671/article/details/116988589

  将这份代码移植过来之后可以直接使用,我在上面的基础上稍微进行了一些改动,原代码解码之后会少两个字节,我改动后不会少字节了。

 五、完整代码 1 /** 2 * filename: bdmap_coord.c 3 * author: Suzkfly 4 * date: 2021-08-21 5 * platform: linux 6 * 将GPS/北斗模块的经纬度转换为百度地图经纬度。编译时要加-lz参数,第26行的AK要改为自己的AK 7 */ 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 #include 18 #include 19 #include 20 21 #define SER_PORT 80 /* HTTP请求端口固定为80 */ 22 #define SER_ADDR "220.181.43.101" /* 百度地图服务IP地址 */ 23 24 #define ORIGINAL_LON "108.9844475" /* 原始经度 */ 25 #define ORIGINAL_LAT "34.37899" /* 原始纬度 */ 26 #define AK "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /* AK,填入自己的AK */ 27 //https://lbsyun.baidu.com/ 28 29 /** 30 * \brief gzip解压 31 * 32 * \param[in] pSrc:需要解压的数据首地址 33 * \param[in] srcSize:需要解压的数据长度 34 * \param[out]:pOutDest:存放解压后的数据的二级指针 35 * \param[out]:pOutBufSize:解压后的数据长度 36 * 37 * \retval 成功返回0,失败返回-1 38 * 39 * \note gzip和zip解压大致相同,但是他们的头数据大小不一样,这个得注意,用inflateInit2(&d_stream,47) 40 * \note pOutBufSize可以传入NULL 41 */ 42 int vidpeek_uncompressGzip (unsigned char* pSrc, unsigned int srcSize, char**pOutDest, unsigned int* pOutBufSize) 43 { 44 char* pBuf = pSrc + (srcSize - 1); 45 unsigned int len = *pBuf; 46 int uncompressResult; 47 z_stream d_stream; 48 int i = 0; 49 50 if ((pSrc == NULL ) || (pOutDest == NULL) || (*pOutDest == NULL)) { 51 return -1; 52 } 53 54 //printf("#############pSrc 0x%x 0x%x 0x%x 0x%x", pSrc[0], pSrc[1], pSrc[2], pSrc[3]); 55 //check gz file,rfc1952 P6 56 if((*pSrc !=0x1f)||(*(pSrc+1) != 0x8b)) { 57 printf("\nuncompressGzip non Gzip\n"); 58 return -1; 59 } 60 for (i = 0; i < 3; i++) { 61 pBuf--; 62 len 0) { 185 count += ret; 186 p_tmp += ret; 187 } 188 close(sockfd); /* 关闭套接字 */ 189 190 /* 打印接收到的报文。这里不按字符串打印是因为接收到的数据中可能会包含'\0' */ 191 //printf("count = %d\n", count); 192 //for (i = 0; i < count; i++) { 193 // printf("%c", buf[i]); 194 //} 195 //fflush(stdout); /* 刷新输出缓冲区 */ 196 197 /* 解析出被编码的数据长度 */ 198 p_tmp = strstr(buf, "Content-Length: "); 199 if (p_tmp == NULL) { 200 perror("fail to strstr\n"); 201 } 202 p_tmp += strlen("Content-Length: "); 203 p_tmp2 = strstr(p_tmp, "\r\n"); 204 strncpy(len_a, p_tmp, p_tmp2 - p_tmp); 205 contet_size = atoi(len_a); 206 //printf("len = %d\n", contet_size); 207 208 /* 找到数据内容起始地址 */ 209 p_tmp = strstr(buf, "\r\n\r\n"); 210 if (p_tmp == NULL) { 211 perror("fail to strstr\n"); 212 } 213 p_tmp += strlen("\r\n\r\n"); 214 215 #if 0 /* 使用系统自带的gzip命令解码 */ 216 /* 将数据内容保存到文件中 */ 217 fd = open("contet.txt.gz", O_RDWR | O_CREAT | O_TRUNC, 0666); 218 if (fd < 0) { 219 perror("fail to open\n"); 220 return -1; 221 } 222 ret = write(fd, p_tmp, contet_size); 223 if (ret != contet_size) { 224 perror("fail to write\n"); 225 return -1; 226 } 227 close(fd); 228 229 /* 用gzip解压,解压后的文件名为contet.txt */ 230 system("gzip -d contet.txt.gz"); 231 232 /* 打开解压后的文件,得到json数据 */ 233 fd = open("contet.txt", O_RDONLY); 234 if (fd < 0) { 235 perror("fail to open\n"); 236 return -1; 237 } 238 memset(buf, 0, sizeof(buf)); 239 read(fd, buf, sizeof(buf)); 240 //printf("buf = %s\n", buf); 241 close(fd); 242 243 system("rm contet.txt"); /* 删除中间文件 */ 244 #else /* 使用zlib库进行解码 */ 245 p_tmp2 = buf; 246 ret = vidpeek_uncompressGzip(p_tmp, contet_size, &p_tmp2, &len); 247 if (ret != 0) { 248 perror("fail to decode\n"); 249 return -1; 250 } 251 memset(&buf[len], 0, sizeof(buf) - len); 252 #endif 253 254 printf("buf = %s\n", buf); /* 可以将解析后的结果打印出来 */ 255 printf("len = %d\n", len); 256 /* 解析json,得到转换过后的经纬度。由于数据简单,这里就自己解析了,若需要全面解析可以用cJSON库 */ 257 p_tmp = strstr(buf, "status"); 258 259 /* 判断转换状态是否成功 */ 260 p_tmp = p_tmp + strlen("status") + 2; 261 if (*p_tmp != '0') { 262 printf("parse failed\n"); 263 return -2; 264 } 265 266 /* 得到经度 */ 267 p_tmp = strstr(buf, "\"x\""); 268 p_tmp += 4; 269 p_tmp2 = p_tmp; 270 while (*p_tmp2 != ',') { 271 p_tmp2++; 272 } 273 strncpy(p_lon_bdmap, p_tmp, p_tmp2 - p_tmp); 274 275 /* 得到纬度 */ 276 p_tmp = strstr(p_tmp2, "\"y\""); 277 p_tmp += 4; 278 p_tmp2 = p_tmp; 279 while (*p_tmp2 != '}') { 280 p_tmp2++; 281 } 282 strncpy(p_lat_bdmap, p_tmp, p_tmp2 - p_tmp); 283 284 return 0; 285 } 286 287 /** 288 * \brief example 289 */ 290 int main(int argc, const char *argv[]) 291 { 292 int ret = 0; 293 char lon[32] = { 0 }; 294 char lat[32] = { 0 }; 295 296 ret = gps_to_bdmap(ORIGINAL_LON, ORIGINAL_LAT, lon, lat); 297 if (ret < 0) { 298 printf("ret = %d\n", ret); 299 return -1; 300 } 301 302 printf("lon = %s\n", lon); 303 printf("lat = %s\n", lat); 304 305 return 0; 306 }

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3