关于LBS坐标系与精度的问题

@(JAVA)[java]
大部分内容来源于:
http://www.jianshu.com/p/f8224779ca63

(一)坐标系问题

App定位遇到的第一个坑是坐标系问题。目前常见的坐标系有三种:地球坐标(WGS84,国际公认坐标),火星坐标(GCJ02,国家标准,适用于高德百度地图大陆+港澳部分、Google地图大陆部分),百度坐标(BD09,适用于百度地图大陆+港澳台部分)。坐标系需要和地图关连才有意义,只有正确匹配地图坐标系的坐标才能在该地图上完美标识位置,否则就会存在偏移。另外对于旅行类App而言,经常需要根据用户当前位置查询周围酒店或者其他POI信息,并且按距离排序,如果坐标系不匹配,就会由于坐标系偏移产生排序问题。

iOS系统上通过定位服务CLLocation相关接口获取定位信息时,获取的经纬度坐标系是WGS84地球坐标,如果直接将该坐标系在iOS系统地图中打点,会发现存在偏移,因为iOS系统地图查看国内时使用的是高德地图数据(这里有另一个坑,详见下文),因此只接受GCJ02火星坐标。如果使用高德或者百度iOS定位SDK中的接口,是可以直接获得火星偏移后的坐标的,由于App Size问题,携程App没有集成第三方SDK,而是通过近似偏移算法直接做偏移(自行Google『transform From WGS To GCJ』)。然而如果在iOS系统地图中获取当前位置,同时在国内,那么获取到的坐标系直接是GCJ02火星坐标系,这点需要小心。

Android系统上通常使用高德或者百度定位SDK获取定位信息。高德SDK没有坐标系参数设定,在大陆和港澳地区获取的坐标系即为GCJ02坐标系,在台湾和海外地区都是WGS84坐标系;百度SDK可以自行设定坐标系参数,即返回WGS84坐标系,还是GCJ02坐标系或者BD09坐标系(注意BD09坐标系只适用于百度地图),如果设定的是GCJ02坐标系,它在大陆+港澳台地区获取的坐标系都是GCJ02坐标系。

海外地图(非大陆和非港澳台地区)是没有火星坐标或者百度坐标之说,都是标准的WGS84地球坐标系。

基本分析结论:【以下结果是最吻合事实的情况,但未完全确认】

1、通过百度抓取到的经纬度都是百度坐标系。
2、ADX发送过来的经纬度是火星坐标系。
因此做法是:将百度抓取过来的数据转为火星坐标系,然后用二者匹配(即数据预处理时多加一步即可)

(二)精度问题

第二个常见的坑是定位精度问题,经常有用户或者Boss反馈,为什么两台一样的手机,获取的当前位置不一样?我明明在这个位置,为什么定位却显示在附件另一个位置,相差那么远?

这类问题的根源是手机不同定位方式导致的,通常手机定位方式有三种:

  1. GPS:根据系统GPS模块获取经纬度,精度10-100米左右,限制是容易受环境影响,在室内几乎不起作用。

  2. 基站:根据运营商基站位置计算经纬度,精度1000-3000米左右,限制是定位较慢,精度差。

  3. WIFI:根据周围WIFI路由器位置计算经纬度,精度100-200米左右,限制是受周围WIFI数量和分布影响,需要打开手机WIFI开关。

(三)坐标系变换代码

完整代码请见:https://github.com/lujinhong/lujinhong-commons/blob/master/lujinhong-commons-java/src/main/java/com/lujinhong/commons/java/lbs/LBSTransformer2.java

package com.lujinhong.commons.java.lbs;import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;/*** AUTHOR: LUJINHONG* CREATED ON: 17/2/21 22:10* PROJECT NAME: aplus_dmp* DESCRIPTION: 百度坐标系、火星坐标系、地球坐标系之间互相转换。*/
public class LBSTransformer2 {private static final double LAT_OFFSET_0(double x, double y) {return -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));}private static final double LAT_OFFSET_1(double x, double y) {return (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;}private static final double LAT_OFFSET_2(double x, double y) {return (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;}private static final double LAT_OFFSET_3(double x, double y) {return (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;}private static final double LON_OFFSET_0(double x, double y) {return 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));}private static final double LON_OFFSET_1(double x, double y) {return (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;}private static final double LON_OFFSET_2(double x, double y) {return (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;}private static final double LON_OFFSET_3(double x, double y) {return (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;}private static double RANGE_LON_MAX = 137.8347;private static double RANGE_LON_MIN = 72.004;private static double RANGE_LAT_MAX = 55.8271;private static double RANGE_LAT_MIN = 0.8293;private static double jzA = 6378245.0;private static double jzEE = 0.00669342162296594323;public static double transformLat(double x, double y) {double ret = LAT_OFFSET_0(x, y);ret += LAT_OFFSET_1(x, y);ret += LAT_OFFSET_2(x, y);ret += LAT_OFFSET_3(x, y);return ret;}public static double transformLon(double x, double y) {double ret = LON_OFFSET_0(x, y);ret += LON_OFFSET_1(x, y);ret += LON_OFFSET_2(x, y);ret += LON_OFFSET_3(x, y);return ret;}public static boolean outOfChina(double lat, double lon) {if (lon < RANGE_LON_MIN || lon > RANGE_LON_MAX)return true;if (lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX)return true;return false;}public static LatLng gcj02Encrypt(double ggLat, double ggLon) {LatLng resPoint = new LatLng();double mgLat;double mgLon;if (outOfChina(ggLat, ggLon)) {resPoint.latitude = ggLat;resPoint.longitude = ggLon;return resPoint;}double dLat = transformLat(ggLon - 105.0, ggLat - 35.0);double dLon = transformLon(ggLon - 105.0, ggLat - 35.0);double radLat = ggLat / 180.0 * Math.PI;double magic = Math.sin(radLat);magic = 1 - jzEE * magic * magic;double sqrtMagic = Math.sqrt(magic);dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * Math.PI);dLon = (dLon * 180.0) / (jzA / sqrtMagic * Math.cos(radLat) * Math.PI);mgLat = ggLat + dLat;mgLon = ggLon + dLon;resPoint.latitude = mgLat;resPoint.longitude = mgLon;return resPoint;}public static LatLng gcj02Decrypt(double gjLat, double gjLon) {LatLng gPt = gcj02Encrypt(gjLat, gjLon);double dLon = gPt.longitude - gjLon;double dLat = gPt.latitude - gjLat;LatLng pt = new LatLng();pt.latitude = gjLat - dLat;pt.longitude = gjLon - dLon;return pt;}public static LatLng bd09Decrypt(double bdLat, double bdLon) {LatLng gcjPt = new LatLng();double x = bdLon - 0.0065, y = bdLat - 0.006;double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * Math.PI);double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * Math.PI);gcjPt.longitude = z * Math.cos(theta);gcjPt.latitude = z * Math.sin(theta);return gcjPt;}public static LatLng bd09Encrypt(double ggLat, double ggLon) {LatLng bdPt = new LatLng();double x = ggLon, y = ggLat;double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * Math.PI);double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * Math.PI);bdPt.longitude = z * Math.cos(theta) + 0.0065;bdPt.latitude = z * Math.sin(theta) + 0.006;return bdPt;}/*** @param location 世界标准地理坐标(WGS-84)* @return 中国国测局地理坐标(GCJ-02)<火星坐标>* @brief 世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>* <p>* ####只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标*/public static LatLng wgs84ToGcj02(LatLng location) {return gcj02Encrypt(location.latitude, location.longitude);}/*** @param location 中国国测局地理坐标(GCJ-02)* @return 世界标准地理坐标(WGS-84)* @brief 中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84)* <p>* ####此接口有1-2米左右的误差,需要精确定位情景慎用*/public static LatLng gcj02ToWgs84(LatLng location) {return gcj02Decrypt(location.latitude, location.longitude);}/*** @param location 世界标准地理坐标(WGS-84)* @return 百度地理坐标(BD-09)* @brief 世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09)*/public static LatLng wgs84ToBd09(LatLng location) {LatLng gcj02Pt = gcj02Encrypt(location.latitude, location.longitude);return bd09Encrypt(gcj02Pt.latitude, gcj02Pt.longitude);}/*** @param location 中国国测局地理坐标(GCJ-02)<火星坐标>* @return 百度地理坐标(BD-09)* @brief 中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09)*/public static LatLng gcj02ToBd09(LatLng location) {return bd09Encrypt(location.latitude, location.longitude);}/*** @param location 百度地理坐标(BD-09)* @return 中国国测局地理坐标(GCJ-02)<火星坐标>* @brief 百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>*/public static LatLng bd09ToGcj02(LatLng location) {return bd09Decrypt(location.latitude, location.longitude);}/*** @param location 百度地理坐标(BD-09)* @return 世界标准地理坐标(WGS-84)* @brief 百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84)* <p>* ####此接口有1-2米左右的误差,需要精确定位情景慎用*/public static LatLng bd09ToWgs84(LatLng location) {LatLng gcj02 = bd09ToGcj02(location);return gcj02Decrypt(gcj02.latitude, gcj02.longitude);}public static class LatLng {public double latitude;public double longitude;public LatLng(double latitude, double longitude) {this.latitude = latitude;this.longitude = longitude;}public LatLng() {}public double getLatitude() {return latitude;}public void setLatitude(double latitude) {this.latitude = latitude;}public double getLongitude() {return longitude;}public void setLongitude(double longitude) {this.longitude = longitude;}}private static final String TAB_SEPERATOR = "\t";public static void main(String[] args) throws IOException {double lat = 23.117500452966457;double lng = 113.4247365;LatLng ll = null;//假设是世界坐标系ll = wgs84ToBd09(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());ll = wgs84ToGcj02(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());//假设是火星坐标系ll = gcj02ToWgs84(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());ll = gcj02ToBd09(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());//假设是百度ll = bd09ToWgs84(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());ll = bd09ToGcj02(new LatLng(lat, lng));System.out.println(ll.getLatitude() + "\t" + ll.getLongitude());}}

关于LBS坐标系与精度的问题相关推荐

  1. LBS基站定位和GPS卫星定位对比

    备注: 本文为网上找到的一个word文档,看了之后,觉得总结相当不错,现在把其贴出来共享!同时感谢原作者无私的奉献精神! 位置定位大体上可以分为两大类: 1. GPS(Global Positioni ...

  2. 高精地图生产中的坐标系

    漫话地图之高精地图生产中的坐标系_高德技术-程序员宝宝 - 程序员宝宝 (cxybb.com) 1.如何定义地球 1.1 地球椭球体 众所周知地球表面凸凹不平,是一个无法用数学公式表达的曲面,这样的曲 ...

  3. app定位、地图、坐标系的那些坑

    原文地址:  http://www.jianshu.com/p/f8224779ca63 开发App时会遇到各种坑,本文分享我们在iOS/Android系统中定位和地图中遇到的坑,以及携程App的解决 ...

  4. 漫话地图之高精地图生产中的坐标系

    导读 高精地图区别于普通电子地图,一般来说高精地图的精度要求在亚米级甚至厘米级,其所含有的道路交通元素也更丰富和细致.这就造成高精地图数据生产过程中会遇到很多普通地图没有的问题和困难,更需要有专业的测 ...

  5. 做移动应用使用地图API时需要注意的问题

    最近在做一个基于地点提醒的移动应用,当初考虑大家都心知肚明的原因,谨慎的选择了百度地图,现在想想其实完全没有必要,好的应用本来就不分国界的,最后可能还是得换回Google地图.毕竟Google地图在技 ...

  6. 国内各地图API坐标系统比较与转换

    转载自: http://blog.csdn.net/yorling/article/details/9175913 备注:资料均来源与网上,这里稍加整理,有错欢迎指出 一.各个坐标系的概况 众所周知地 ...

  7. 谷歌和ESRI眼中的Web Mercator

    网上已经有好多作者都不吝笔墨,写了好多有关 Web Mercator这个坐标系的前世今生.多搜罗多摄入,我们会得到很多有用的信息.今天讨论到 3758,3857,102100,900913-- 这些I ...

  8. mysql查找附近算法_附近地点搜索解决方案

    随着移动互联网的兴起,越来越多的App中加入了LBS的元素.而在各种LBS应用中,查找附近的地点是一种最基本也是最常见的形式.前段时间项目中加入了一个新的特性,需要根据用户所在的位置,查找附近的用户和 ...

  9. 详细讲解 “双评价”——建设指向的土地资源评价

    因为本系列只是起研究模拟作用,因此以广东省(省级)为例,地理坐标系使用WGS1984(许多网上得到的数据都是WGS84,所以不使用CGCS2000),投影使用WGS_1984_UTM_Zone_49N ...

最新文章

  1. 42张图详解 NAT : 换个马甲就能上网
  2. 微服务架构的四大金刚利器
  3. R语言ggplot2可视化移除多余的图例信息实战
  4. Spring-配置bean的方法(工厂方法和Factorybean)【转】
  5. OpenGL 泛光Bloom
  6. 「Python」 ElementTree模块解析xml文件,建议小白阅读全文
  7. 【转】RabbitMQ六种队列模式-5.主题模式
  8. 知易游戏开发教程cocos2d-x移植版001
  9. json mysql乱码问题_读写json中文ASCII乱码问题的解决方法
  10. 倍福软件安装及注意事项
  11. vscode格式化代码设置
  12. 数据分析师面试题攻略
  13. 人脸服务器如何与门禁系统对接,人脸识别门禁系统功能介绍
  14. 西门子证实将出售手机业务【ZZ】
  15. SqlDbx远程链接DB2数据库
  16. 关于python教学
  17. proteus 8 打开proteus 7版本仿真文件的两个方法
  18. 顶会 INFOCOM 巴黎进行时,最高荣誉花落微软老将
  19. 案例分享 | 数字化综合人才管理平台
  20. rj45 千兆接口定义_RJ45接口针脚定义(各种接口针脚定义)

热门文章

  1. 剑指 Offer 31. 栈的压入、弹出序列【无取巧,易于理解!】
  2. C语言:L1-035 情人节 (15分)(解题报告)
  3. 动态路由和动态路由中的RIP协议
  4. jq遍历子元素_leetcode第196周赛第三题统计全 1 子矩形
  5. python清洗数据去除停用词_Python从pandas数据帧中删除停用词
  6. Juniper 210 密码清不掉_三分钟学会如何找回mysql密码
  7. python legb_理解 Python 的 LEGB.
  8. 命令python所在的驱动器和文件夹_Python读取不同本地驱动器位置的文件
  9. 一套完整的java程序_编写一个完整的Java Application 程序
  10. 代码模板在哪里_C++的可变参数模板