关于LBS坐标系与精度的问题
關(guān)于LBS坐標系與精度的問題
@(JAVA)[java]
大部分內(nèi)容來源于:
http://www.jianshu.com/p/f8224779ca63
(一)坐標系問題
App定位遇到的第一個坑是坐標系問題。目前常見的坐標系有三種:地球坐標(WGS84,國際公認坐標),火星坐標(GCJ02,國家標準,適用于高德百度地圖大陸+港澳部分、Google地圖大陸部分),百度坐標(BD09,適用于百度地圖大陸+港澳臺部分)。坐標系需要和地圖關(guān)連才有意義,只有正確匹配地圖坐標系的坐標才能在該地圖上完美標識位置,否則就會存在偏移。另外對于旅行類App而言,經(jīng)常需要根據(jù)用戶當前位置查詢周圍酒店或者其他POI信息,并且按距離排序,如果坐標系不匹配,就會由于坐標系偏移產(chǎn)生排序問題。
iOS系統(tǒng)上通過定位服務(wù)CLLocation相關(guān)接口獲取定位信息時,獲取的經(jīng)緯度坐標系是WGS84地球坐標,如果直接將該坐標系在iOS系統(tǒng)地圖中打點,會發(fā)現(xiàn)存在偏移,因為iOS系統(tǒng)地圖查看國內(nèi)時使用的是高德地圖數(shù)據(jù)(這里有另一個坑,詳見下文),因此只接受GCJ02火星坐標。如果使用高德或者百度iOS定位SDK中的接口,是可以直接獲得火星偏移后的坐標的,由于App Size問題,攜程App沒有集成第三方SDK,而是通過近似偏移算法直接做偏移(自行Google『transform From WGS To GCJ』)。然而如果在iOS系統(tǒng)地圖中獲取當前位置,同時在國內(nèi),那么獲取到的坐標系直接是GCJ02火星坐標系,這點需要小心。
Android系統(tǒng)上通常使用高德或者百度定位SDK獲取定位信息。高德SDK沒有坐標系參數(shù)設(shè)定,在大陸和港澳地區(qū)獲取的坐標系即為GCJ02坐標系,在臺灣和海外地區(qū)都是WGS84坐標系;百度SDK可以自行設(shè)定坐標系參數(shù),即返回WGS84坐標系,還是GCJ02坐標系或者BD09坐標系(注意BD09坐標系只適用于百度地圖),如果設(shè)定的是GCJ02坐標系,它在大陸+港澳臺地區(qū)獲取的坐標系都是GCJ02坐標系。
海外地圖(非大陸和非港澳臺地區(qū))是沒有火星坐標或者百度坐標之說,都是標準的WGS84地球坐標系。
基本分析結(jié)論:【以下結(jié)果是最吻合事實的情況,但未完全確認】
1、通過百度抓取到的經(jīng)緯度都是百度坐標系。
2、ADX發(fā)送過來的經(jīng)緯度是火星坐標系。
因此做法是:將百度抓取過來的數(shù)據(jù)轉(zhuǎn)為火星坐標系,然后用二者匹配(即數(shù)據(jù)預(yù)處理時多加一步即可)
(二)精度問題
第二個常見的坑是定位精度問題,經(jīng)常有用戶或者Boss反饋,為什么兩臺一樣的手機,獲取的當前位置不一樣?我明明在這個位置,為什么定位卻顯示在附件另一個位置,相差那么遠?
這類問題的根源是手機不同定位方式導(dǎo)致的,通常手機定位方式有三種:
GPS:根據(jù)系統(tǒng)GPS模塊獲取經(jīng)緯度,精度10-100米左右,限制是容易受環(huán)境影響,在室內(nèi)幾乎不起作用。
基站:根據(jù)運營商基站位置計算經(jīng)緯度,精度1000-3000米左右,限制是定位較慢,精度差。
WIFI:根據(jù)周圍WIFI路由器位置計算經(jīng)緯度,精度100-200米左右,限制是受周圍WIFI數(shù)量和分布影響,需要打開手機WIFI開關(guān)。
(三)坐標系變換代碼
完整代碼請見: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: 百度坐標系、火星坐標系、地球坐標系之間互相轉(zhuǎn)換。*/ 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) 轉(zhuǎn)換成 中國國測局地理坐標(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) 轉(zhuǎn)換成 世界標準地理坐標(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) 轉(zhuǎn)換成 百度地理坐標(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)<火星坐標> 轉(zhuǎn)換成 百度地理坐標(BD-09)*/public static LatLng gcj02ToBd09(LatLng location) {return bd09Encrypt(location.latitude, location.longitude);}/*** @param location 百度地理坐標(BD-09)* @return 中國國測局地理坐標(GCJ-02)<火星坐標>* @brief 百度地理坐標(BD-09) 轉(zhuǎn)換成 中國國測局地理坐標(GCJ-02)<火星坐標>*/public static LatLng bd09ToGcj02(LatLng location) {return bd09Decrypt(location.latitude, location.longitude);}/*** @param location 百度地理坐標(BD-09)* @return 世界標準地理坐標(WGS-84)* @brief 百度地理坐標(BD-09) 轉(zhuǎn)換成 世界標準地理坐標(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;//假設(shè)是世界坐標系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());//假設(shè)是火星坐標系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());//假設(shè)是百度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());}}總結(jié)
以上是生活随笔為你收集整理的关于LBS坐标系与精度的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于reduce中遍历2次数据的问题
- 下一篇: LBS相关工具函数