您的位置:首页 > 移动开发 > Android开发

【android】 百度地图定位显示存在偏移

2018-01-12 07:59 99 查看
1.原因解释
我们在编写android程序时,通过android系统服务之LOCATION_SERVICE得到的经纬度坐标,不论你的provider是不是GPS_PROVIDER,这个坐标都是真实的地理坐标,称之为wgs坐标。因为国家安全的原因,国家规定这个wgs坐标不允许直接出现在网络上,所以各大地图app使用的都是wgs坐标经过加密后产生的假坐标,Google地图以及大多数别的地图使用的是国测局设计的加密算法产生的gcj坐标,Baidu地图使用的是另外一种加密算法产生的bd坐标,最特殊的是,搜狗地图使用的是GPS米制坐标,因为国家禁止的wgs坐标是GPS角度制坐标,这样打擦边球。。。这样一来,我们虽然可以在各大地图app上点击关键建筑查看其地理坐标,但是得到的坐标却是加密后的虚假坐标,而不是真实的地理坐标。
所以,你把wgs坐标显示在百度地图上标示的当然不是你的当前位置了,会出现偏移。而且,网上有人说这个偏移量在一定范围内是固定的,可是实际测试中我发现,只不过移动了几百米,定位显示就从往西北偏几百米变成了往西南偏几百米,所以这个区别度还是足够大的,肯定不能用于直接显示,需要经过坐标转换。
顺便提一下,android设备使用GPS定位不需要联网,不耗流量,而是直接和卫星通信,但是时延需要2-3分钟,也就是说开启定位后需要等一大会儿才能显示位置。而且在室内肯定使用不了,定位很准。使用NETWORK定位时也不一定就需要连WiFi,但是一定需要开启数据连接,他要和基站通信,利用三角定位估算位置,(这个算法网上有,我没试过行不行),如果开启了WiFi,根据WiFi强度可以将定位变得更准确。这种方式可以在室内使用,网上说没那么准,但我觉得效果还不错了。
2.坐标转换
要实现坐标转换的话,百度地图官方就提供了一个接口,需要访问一个网址,提供自己的开发者密钥,就可以将真实坐标转换成百度坐标,但是这样肯定就存在性能损耗,而且为什么要提供密钥呢,因为百度对开发者分了级别,不同的级别每天可以进行转换的次数上限不同,频率也有限制。
网上有一个算法,提供了wgs转gcj、gcj转bd的加密算法,我试了一下,很准。注意:百度坐标是先纬度(Lat)再经度(Lng/Lon),其他坐标是先经度再纬度,
警告:将假坐标转换成真实坐标是犯法的。public class Point {

private static final double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
private static final double pi = 3.14159265358979324;
private static final double a = 6378245.0;
private static final double ee = 0.00669342162296594323;

private double lat;// 纬度
private double lng;// 经度

public Point() {
}

public Point(double lng, double lat) {
this.lng = lng;
this.lat = lat;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof Point) {
Point bmapPoint = (Point) obj;
return bmapPoint.getLng() == lng && bmapPoint.getLat() == lat;
} else {
return false;
}
}

public double getLat() {
return lat;
}

public void setLat(double lat) {
this.lat = lat;
}

public double getLng() {
return lng;
}

public void setLng(double lng) {
this.lng = lng;
}

@Override
public String toString() {
return "Point [lat=" + lat + ", lng=" + lng + "]";
}

public Point google_bd_encrypt() {
Point point = new Point();
double x = lng, y = lat;
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
double bd_lon = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
point.setLat(bd_lat);
point.setLng(bd_lon);
return point;
}

public Point wgs_gcj_encrypts() {
Point point=new Point();
double wgLat = lat,wgLon = lng;
if (outOfChina(wgLat, wgLon)) {
point.setLat(wgLat);
point.setLng(wgLon);
return point;
}
double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
double radLat = wgLat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double lat = wgLat + dLat;
double lon = wgLon + dLon;
point.setLat(lat);
point.setLng(lon);
return point;
}

private boolean outOfChina(double lat, double lon) {
return lon < 72.004 || lon > 137.8347||lat < 0.8293 || lat > 55.8271;
}

private double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}

private double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}

}声明:算法不是出自本人。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐