还在为坐标转换头疼?这个经过验证的GCJ02转WGS-84方法太实用了

手机地图所呈现出的位置,偶尔会跟实际位置存在几百米的差距,这般偏差主要是源自于不同坐标体系相互之间的转换 。

WGS-84坐标系

WGS – 84是一种全球通用的地理坐标系统,它是由美国国防部制定的,还为全球定位系统(GPS)所采用,此坐标系的目标是提供一个标准的地球模型,从而能够在世界任何地方都可以进行精确的定位和导航。

于实际应用里,我们手持的GPS接收装置,像智能手机或者车载导航仪,所接收到的初始卫星信号一般就是基于WGS – 84坐标 ,此坐标系具有全球一致性与高精度的优点,是诸多国际应用以及科学研究的基准 。

GCJ-02坐标系

GCJ – 02坐标系,常被称作“火星坐标系”,它是由中国国家测绘地理信息局所制定的加密坐标系,它乃是在WGS – 84坐标系的基础之上,通过加入特定算法来进行非线性偏移之后形成的,而这种偏移致使了坐标出现人为偏差。

此坐标系的设立,旨在维护国家安全,它能保证公开发布的地图信息,不与真实地理坐标完全契合,进而提升敏感地区位置信息的保密程度,中国大陆地区几乎所有运营方的官方地图服务,都要采用此坐标系。

坐标偏差的产生

要是在中国大陆地区运用未经修正的GPS设备来定位,你所获取的WGS – 84坐标直接呈现在基于GCJ – 02坐标系的地图(类似于高德地图)之上,便会出现显著的定位错误。这种偏差并非是固定不变的,其会依据地理位置的不同进而发生变化。

偏差范围一般处于几百米以内,然而在一些边缘地带也许会更大。这一呈现非线性、看似随机的偏移规律,致使两种坐标间的转换并非单纯的线性计算,而是需要特定的转换参数,以及算法。

坐标转换的必要性

WGS-84坐标系 GCJ02坐标系 坐标转换方法_WGS84

之于开发者来讲,坐标转换属于无法避开的环节。要是你的应用关联定位功能,同时要在中国大陆的地图服务里精准呈现用户位置,那就得把设备获取的WGS – 84坐标转变为GCJ – 02坐标。不然的话,用户会发觉自身位置“飘”到几百米外去了。

反过来讲,要是你从地图API那里,像高德、腾讯地图之类,得到了一个地点的坐标,而且打算用这个坐标去指引原始的GPS设备,比如说某些专业测绘设备或者无人机,那么就需要把GCJ – 02坐标转化回WGS – 84坐标。

转换方法与原理

公开的、有着较高精度的转换方法平常依赖关乎被叫做“火星坐标系”的逆变换算法,这些算法借由数学模型去近似模拟GCJ-02的加密进程,并且对其开展逆向求解,网络上存在众多语言所实现的版本,其核心是同样的数学公式。

这些算法会考量待转换点的经纬度,经过一系列繁杂的计算,涵盖椭圆曲线参数以及迭代逼近,进而求解出加密前的原始坐标。虽说无法保证百分百的官方精度,然而在绝大多数民用场景当中已然完全能够满足使用需求。

实际应用建议

搞安卓应用开发之际,所提建议乃是把坐标转换功能予以封装,使之成为独立的工具类呐。例如,去创建个名为CoordinateConverter的类,于此类当中有着像gcj02ToWgs84()以及wgs84ToGcj02()这般两个静态方法。如此一来呢便能够在代码里随时进行调用,从而确保逻辑是清晰的哟。

于开展转换之际,一定要实施足够的测试,最好于各异城市、不同地貌环境当中采集多个测试点,去验证转换之后的坐标跟实际位置的吻合状况,保证转换精度契合你具体业务需求,防止给用户带来差劲的体验。


import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
public class LocationUtils {
    public static boolean isLocationEnabled(Context context) {
        int locationMode = 0;
        String locationProviders;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            try {
                locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
                return false;
            }
            return locationMode != Settings.Secure.LOCATION_MODE_OFF;
        } else {
            locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
            return !TextUtils.isEmpty(locationProviders);
        }
    }
    /**
     * GCJ02(火星坐标系)转GPS84
     * 输入GCJ经纬度 转WGS纬度
     */
    public static double GCJ2WGSLat(double lat, double lon) {
        double PI = 3.14159265358979324;//圆周率
        double a = 6378245.0;//克拉索夫斯基椭球参数长半轴a
        double ee = 0.00669342162296594323;//克拉索夫斯基椭球参数第一偏心率平方
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double radLat = lat / 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);
        return (lat - dLat);
    }
    /**
     * GCJ02(火星坐标系)转GPS84
     * 输入GCJ经纬度 转WGS经度
     */
    public static double GCJ2WGSLon(double lat, double lon) {
        double PI = 3.14159265358979324;//圆周率
        double a = 6378245.0;//克拉索夫斯基椭球参数长半轴a
        double ee = 0.00669342162296594323;//克拉索夫斯基椭球参数第一偏心率平方
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * PI;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
        return (lon - dLon);
    }
    //转换经度所需
    public static double transformLon(double x, double y) {
        double PI = 3.14159265358979324;//圆周率
        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;
    }
    //转换纬度所需
    public static double transformLat(double x, double y) {
        double PI = 3.14159265358979324;//圆周率
        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;
    }
}

当你处于开发地图应用或者使用地图应用的状况下,有没有遭遇过这般的坐标偏差问题而感到困扰呢?欢迎于评论区去分享你所拥有的经历以及解决方案,要是认为本文具备帮助作用,请进行点赞予以支持!

WGS-84坐标系 GCJ02坐标系 坐标转换方法_WGS84

发表评论