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

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;
    }

}
声明:算法不是出自本人。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值