如何快速判断经纬度坐标系?高德、百度、Google地图大揭秘

获得经纬度信息后,我们或许会感到迷茫,不知这究竟属于哪种坐标系。这种情况是很多人都会遇到的难题。而且,不同地图之间坐标系的差异,确实给使用者带来了不少困扰。这就像是一场需要破解的密码游戏,只有弄清楚坐标系,我们才能顺畅地进行后续的地图操作。

通过地图Demo测试坐标系

我们可以通过试验来确认。比如,当你手头有坐标,怀疑它们可能是火星坐标时,可以先将这些坐标更新为最新位置。然后,将它们输入到高德地图的演示版中检验,若显示的位置与你实际所在位置相符,那很可能就是火星坐标。这个演示版有特定的网址。当然,若你猜测这些坐标可能是GPS或百度坐标,也可以尝试在百度地图的演示版中进行测试。在百度地图里,有一个有趣的现象:输入GPS坐标后,地图会同时展示GPS坐标和转换后的坐标。

还有一种情形需要提一下,若将谷歌的坐标导入百度地图,因为百度地图能够实现谷歌坐标向百度坐标的转换,所以地图上会同时展示两种坐标。然而,未经转换的谷歌坐标在百度地图上的位置与高德地图有所差异,这是因为高德采用的是火星坐标系,而此处使用的是百度地图的坐标系。

不同坐标在地图上的显示示例

我们来举几个生活中的例子。比如,我有个朋友,他从一个设备上获取了一组数据。他原本以为那是百度坐标,于是拿到百度地图上查看,却发现位置偏差很大。经过一番仔细检查,他才发现是因为没有搞清楚坐标系,这组数据根本就不是百度坐标。在城市里使用地图导航等功能时,如果坐标系弄错了,很容易就会把你带到错误的地方。比如在北京,本来想去天安门,但因为坐标系搞混,可能会被导航到偏远的地区。

在这里插入图片描述

观察一下户外探险的情景。探险者们会记录下位置的具体坐标,若坐标系判断有误,将对路线规划和风险预防带来重大影响。以山区为例,可能会让搜救人员误入歧途,导致救援延误。

坐标系转换代码工具类

很幸运,我们拥有了坐标转换的辅助工具。文中提到有两种工具类可供选择。这些工具对于经常需要在不同坐标系间进行数据转换的工作人员来说,显得格外实用。以一家专注于智能交通系统开发的公司为例,他们需要将来自不同渠道的坐标数据进行整合。若缺乏坐标转换的工具,那么他们所整合的交通数据中的坐标信息将会出现混乱。

有了这些工具,可以将错误坐标系统的数据转换成准确的坐标系统数据。以某小型地图数据采集团队为例,他们采集的坐标数据包括GPS、百度和火星坐标。借助这些工具,他们能迅速将各种坐标统一转换,从而提高地图的准确性。

在旅游中的坐标系影响

在这里插入图片描述

旅行途中,坐标系统同样对我们的感受有所影响。众多游客在游览景点时,常需借助各类地图APP进行导航。若坐标系出现混乱,便可能遭遇迷失方向的困境。以东京为例,若想寻觅银座这一知名景点,若手机地图上误用了不匹配的坐标系坐标,便可能在周边徘徊,却始终找不到正确的入口。

在欧洲旅行时,这种情况尤为明显。不同国家的地图坐标系可能存在较大差异。比如,在法国地图上用某个坐标系定位的景点,到了德国可能因为坐标系不同,位置判断就会完全错误。这可能导致游客错过许多原本计划的活动,使得旅行充满了遗憾。

坐标系判断对物流行业的意义

物流行业对精确的地理坐标依赖极大。物流企业需确定运输路径和仓库位置。若经纬度坐标判断失误,满载货物的车辆可能误入歧途。在我国,众多仓库遍布广阔国土。若各仓库坐标在地图上因坐标判断问题而混乱,调度货物将变得极为困难。

作为一家大电商的物流团队,他们必须保证每个分拨点的位置信息无误,无论是查看本地地图还是国家层面的物流协调图,否则货物可能会延误到达目的地。

对科研领域的影响

科研领域,尤其是地理学研究等,例如地理学家探究地震频繁区域的坐标信息。若坐标系判断出现错误,所收集的数据便无法得到准确分析。某些科学家在极地开展研究时,由于当地环境独特,若坐标数据缺乏正确的坐标系判断依据,那么得出的研究结论可能会出现误差。

public class PositionTransUtil {
    private static double PI = Math.PI;
    private static double AXIS = 6378245.0;
    private static double OFFSET = 0.00669342162296594323;
    private static double X_PI = PI * 3000.0 / 180.0;
    
    public static void main(String[] args) {
    	double[] amapCoor = wgs842gcj02(31.14651541,121.38916722);
																	
		System.out.println(String.format("%f,%f", amapCoor[0],
				amapCoor[1]));
		
		double[] amapCoor2 = gcj2WGS(amapCoor[0], amapCoor[1]);
		System.out.print(String.format("%f,%f", amapCoor2[0],
				amapCoor2[1]));
	}
    /**
     * 标准大地坐标转换为百度坐标(失效)
     *
     * @param lat
     * @param lon
     * @return
     */
    public static Point2D.Double wgs842bd09(double lat, double lon) {
        double[] glatlon = wgs842gcj02(lat, lon);
        double[] blatlon = wgs842gcj02(glatlon[0], glatlon[1]);
        Point2D.Double latLng = new Point2D.Double(blatlon[0], blatlon[1]);
        return latLng;
    }
    /**
     * BD09--WGS84 百度坐标--大地坐标系
     *
     * @param latitude
     * @param longitude
     * @return
     */
    public static Point2D.Double bd09ToWgs84(double latitude, double longitude) {
    	Point2D.Double gpsLatLng;
        double[] latLng = bd092WGS(latitude, longitude);
        gpsLatLng = new Point2D.Double(latLng[0], latLng[1]);
        return gpsLatLng;
    }
    /**
     * BD09--WGS84 百度坐标系--大地坐标系
     *
     * @param glat
     * @param glon
     * @return
     */
    public static double[] bd092WGS(double glat, double glon) {
        double[] latlon = bd092GCJ(glat, glon);
        return gcj2WGS(latlon[0], latlon[1]);
    }
    /**
     * BD09--GCJ-02 百度坐标系--火星坐标系
     *
     * @param glat
     * @param glon
     * @return
     */
    private static double[] bd092GCJ(double glat, double glon) {
        double x = glon - 0.0065;
        double y = glat - 0.006;
        double[] latlon = new double[2];
        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);
        latlon[0] = z * Math.sin(theta);
        latlon[1] = z * Math.cos(theta);
        return latlon;
    }
 
    /**
     * gcj02--bd09火星坐标系--百度坐标系
     *
     * @param glat
     * @param glon
     * @return
     */
    private static double[] gcj2bd09(double glat, double glon) {
        double z = Math.sqrt(glon * glon + glat * glat) + 0.00002 * Math.sin(glat * X_PI);
        double theta = Math.atan2(glat, glon) + 0.000003 * Math.cos(glon * X_PI);
        double bd_lng = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        double[] latlon = new double[2];
        latlon[0] = bd_lat;
        latlon[1] = bd_lng;
        return latlon;
    }
    /**
     * GCJ02=>WGS84 火星坐标系=>地球坐标系(粗略)
     *
     * @param glat
     * @param glon
     * @return
     */
    public static double[] gcj2WGS(double glat, double glon) {
        double[] latlon = new double[2];
        if (inChina(glat, glon)) {
            double[] deltaD = delta(glat, glon);
            latlon[0] = glat - deltaD[0];
            latlon[1] = glon - deltaD[1];
            return latlon;
        } else {
            latlon[0] = glat;
            latlon[1] = glon;
            return latlon;
        }
    }
    /**
     * wgs84--gcj02   大地坐标系转火星坐标系
     *
     * @param wlat
     * @param wlon
     * @return
     */
    public static double[] wgs842gcj02(double wlat, double wlon) {
        double[] latlon = new double[2];
        if (inChina(wlat, wlon)) {
            double[] deltaD = delta(wlat, wlon);
            latlon[0] = wlat + deltaD[0];
            latlon[1] = wlon + deltaD[1];
            return latlon;
        } else {
            latlon[0] = wlat;
            latlon[1] = wlon;
            return latlon;
        }
    }
    /**
     * 判断目标位置是否在中国境内
     *
     * @param lat
     * @param lon
     * @return
     */
    private static boolean inChina(double lat, double lon) {
        if (lon > 73.66 &&
                lon < 135.05 && lat > 3.86 && lat < 53.55) {
            return true;
        } else {
            return false;
        }
    }
    private static double[] delta(double wgLat, double wgLon) {
        double[] latlng = new double[2];
        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 - OFFSET * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtMagic) * PI);
        dLon = (dLon * 180.0) / (AXIS / sqrtMagic * Math.cos(radLat) * PI);
        latlng[0] = dLat;
        latlng[1] = dLon;
        return latlng;
    }
    private static 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 static 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;
    }
    /**
     * 计算位置间距
     *
     * @param strLat
     * @param strLon
     * @param endLat
     * @param endLon
     * @return
     */
    public static double getDistance(double strLat, double strLon, double endLat, double endLon) {
        double lat1 = (Math.PI / 180) * strLat;
        double lat2 = (Math.PI / 180) * endLat;
        double lon1 = (Math.PI / 180) * strLon;
        double lon2 = (Math.PI / 180) * endLon;
        //地球半径
        double R = 6371;
        //两点间距离 km
        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * R;
        return d;
    }
}

研究项目在收集坐标数据时,来源可能各不相同。若无法准确识别和转换坐标系,论文和课题研究很可能会遭受重大不利影响。

你是否有过因坐标定位失误而感到麻烦的经历?欢迎点赞、转发,并在评论区展开讨论。

public class CoordinateTransformUtil {
    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    // π
    static double pi = 3.1415926535897932384626;
    // 长半轴
    static double a = 6378245.0;
    // 扁率
    static double ee = 0.00669342162296594323;
    /**
     * 百度坐标系(BD-09)转WGS坐标
     *
     * @param lng 百度坐标纬度
     * @param lat 百度坐标经度
     * @return WGS84坐标数组
     */
    public static double[] bd09towgs84(double lng, double lat) {
        double[] gcj = bd09togcj02(lng, lat);
        double[] wgs84 = gcj02towgs84(gcj[0], gcj[1]);
        return wgs84;
    }
    /**
     * WGS坐标转百度坐标系(BD-09)
     *
     * @param lng WGS84坐标系的经度
     * @param lat WGS84坐标系的纬度
     * @return 百度坐标数组
     */
    public static double[] wgs84tobd09(double lng, double lat) {
        double[] gcj = wgs84togcj02(lng, lat);
        double[] bd09 = gcj02tobd09(gcj[0], gcj[1]);
        return bd09;
    }
    /**
     * 火星坐标系(GCJ-02)转百度坐标系(BD-09)
     *
     * @param lng 火星坐标经度
     * @param lat 火星坐标纬度
     * @return 百度坐标数组
     */
    public static double[] gcj02tobd09(double lng, double lat) {
        double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
        double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
        double bd_lng = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        return new double[] { bd_lng, bd_lat };
    }
    /**
     * 百度坐标系(BD-09)转火星坐标系(GCJ-02)
     *
     * @return 火星坐标数组
     */
    public static double[] bd09togcj02(double bd_lon, double bd_lat) {
        double x = bd_lon - 0.0065;
        double y = bd_lat - 0.006;
        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 gg_lng = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new double[] { gg_lng, gg_lat };
    }
    /**
     * WGS84转GCJ02(火星坐标系)
     *
     * @param lng WGS84坐标系的经度
     * @param lat WGS84坐标系的纬度
     * @return 火星坐标数组
     */
    public static double[] wgs84togcj02(double lng, double lat) {
        if (out_of_china(lng, lat)) {
            return new double[] { lng, lat };
        }
        double dlat = transformlat(lng - 105.0, lat - 35.0);
        double dlng = transformlng(lng - 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);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
        double mglat = lat + dlat;
        double mglng = lng + dlng;
        return new double[] { mglng, mglat };
    }
    /**
     * GCJ02(火星坐标系)转GPS84
     *
     * @param lng 火星坐标系的经度
     * @param lat 火星坐标系纬度
     * @return WGS84坐标数组
     */
    public static double[] gcj02towgs84(double lng, double lat) {
        if (out_of_china(lng, lat)) {
            return new double[] { lng, lat };
        }
        double dlat = transformlat(lng - 105.0, lat - 35.0);
        double dlng = transformlng(lng - 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);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
        double mglat = lat + dlat;
        double mglng = lng + dlng;
        return new double[] { lng * 2 - mglng, lat * 2 - mglat };
    }
    /**
     * 纬度转换
     *
     * @param lng
     * @param lat
     * @return
     */
    public static double transformlat(double lng, double lat) {
        double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }
    /**
     * 经度转换
     *
     * @param lng
     * @param lat
     * @return
     */
    public static double transformlng(double lng, double lat) {
        double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }
    /**
     * 判断是否在国内,不在国内不做偏移
     *
     * @param lng
     * @param lat
     * @return
     */
    public static boolean out_of_china(double lng, double lat) {
        if (lng < 72.004 || lng > 137.8347) {
            return true;
        } else if (lat < 0.8293 || lat > 55.8271) {
            return true;
        }
        return false;
    }
}

发表评论