一、坐标系统简介

坐标系统,是描述物质存在的空间位置(坐标)的参照系,通过定义特定基准及其参数形式来实现。坐标是描述位置的一组数值,按坐标的维度一般分为一维坐标(公路里程碑)和二维坐标(笛卡尔平面直角坐标、高斯平面直角坐标)、三维坐标(大地坐标、空间直角坐标)。为了描述或确定位置,必须建立坐标系统,坐标只有存在于某个坐标系统才有实际的意义与具体的位置。

地球是一个球体,球面上的位置,是以经纬度来表示,它称为“球面坐标系统”或“地理坐标系统”。在球面上计算角度距离十分麻烦,而且地图是印刷在平面纸张上,要将球面上的物体画到纸上,就必须展平,这种将球面转化为平面的过程,称为“投影”。

1、经纬度坐标系

经纬度坐标系是一种地理坐标系统,用于描述地球表面上任意位置的坐标。它是基于地球的自转和赤道的划分而建立的。

  • 经度(Longitude):表示地球表面上一个点相对于本初子午线的东西方向的位置。经度的度量单位是度(°),范围从0°到180°,以东经为正值,西经为负值。本初子午线位于英国伦敦的皇家格林尼治天文台,它被定义为经度0°。
  • 纬度(Latitude):表示地球表面上一个点相对于赤道的北南方向的位置。纬度的度量单位也是度(°),范围从0°到90°,以北纬为正值,南纬为负值。赤道位于纬度0°。

经纬度坐标系统使用经度和纬度的组合来确定地球表面上的特定位置。一个点的经纬度坐标表示为两个数值的组合,例如:40°N,120°E 表示北纬40度,东经120度的位置。

经纬度坐标系统是全球通用的地理坐标系统。

2、坐标系统

大地测量(含导航定位)中常用的坐标系统概念简介:https://zhuanlan.zhihu.com/p/473678638

坐标系统: 用于定位的系统,就跟二维笛卡尔坐标系统一样,一个点使用(x,y),就能确定该点在笛卡尔坐标系统中的唯一位置。这里讲的坐标系统,相对于笛卡尔坐标系统,要复杂许多,但作用却都是一样,主要用于定位,也就是精确地定位地表上的一点。

地理坐标系统: WGS84就是一种地理坐标系统。地理坐标坐标是对地球进行简单几何建模,比如:将地球看成一个球体或者类球体,然后再将地表上点投影到该球面上形成的坐标就是地理坐标系统。
WGS84就是定义了如何将地球抽象成球体或者类球体的规则。或者简单地来说,WGS84就是一堆参数,用于建立球体或者类球体,来近似地球。

投影坐标系统: 由于地球是一个球状,所以一般将其某个区域投影在平面上,形成的坐标系称为投影坐标系。

3、坐标转换

在地图应用中,不同的地图服务商通常使用不同的坐标系,坐标转换就是将一个地图服务商的坐标系转换为另一个地图服务商的坐标系,以便在不同的地图上显示相同的位置信息。

GPS(谷歌地图)|高德|百度地图对坐标系统的使用:

  • WG-S84: 地理坐标系统,GPS仪器记录的经纬度信息。Google Earth采用,Google Map中国范围外使用,高德地图中国范围外使用。
  • GCJ-02: 投影坐标系统,火星坐标系,中国国家测绘局制定的坐标系统,由 WGS-84加密后的坐标。适用于高德地图。
  • BD-09: 投影坐标系统,百度坐标系,GCJ-02加密后的坐标系,只适用于百度地图。

在国内是不允许直接用 WGS84坐标系标注经纬度的,必须经过加密后才能用。所以必须至少使用 GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的坐标系,如百度坐标系。

不同地图服务商有提供其丰富的 API文档功能,包括经纬度坐标转换功能。有的API需要收费,下面通过 Java实现地图坐标转换功能。

二、Java实现经纬度坐标转换

1、GPS转高德

/**
 * 经纬度 GPS转高德工具类
 */
public class GPSTransToAMapUtil {

    /**
     * 椭球参数
     */
    private static double pi = 3.14159265358979324;
    /**
     * 卫星椭球坐标投影到平面地图坐标系的投影因子
     */
    private static double a = 6378245.0;
    /**
     * 椭球的偏心率
     */
    private static double ee = 0.00669342162296594323;

    /**
     * 经纬度 GPS转高德
     *
     * @param wgLon GPS经度
     * @param wgLat GPS维度
     * @return 转化后的经纬度坐标
     */
    public static AMap transform(double wgLon, double wgLat) {
        if (outOfChina(wgLat, wgLon)) {
            return new AMap(wgLon, wgLat);
        }

        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 transLat = wgLat + dLat;
        double transLon = wgLon + dLon;
        return new AMap(transLon, transLat);
    }

    /**
     * 判断是否为国外坐标,,不在国内不做偏移
     *
     * @param lat
     * @param lon
     * @return
     */
    private static Boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }

    /**
     * 纬度转换
     *
     * @param x
     * @param y
     * @return
     */
    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;
    }

    /**
     * 经度转换
     *
     * @param x
     * @param y
     * @return
     */
    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;
    }

    /**
     * 高德经纬度类
     */
    public static class AMap {
        /**
         * 经度
         */
        private double longitude;

        /**
         * 维度
         */
        private double latitude;

        public AMap(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        public double getLongitude() {
            return longitude;
        }

        public double getLatitude() {
            return latitude;
        }
    }

    public static void main(String[] args) {
        double lon = 108.766167;
        double lat = 34.207948;
        AMap aMap = transform(lon, lat);
        // 108.766167,34.207948
        System.out.println("GPS转高德之前:" + lon + "," + lat);
        // 108.77088779593853,34.206482360676944
        System.out.println("GPS转高德之后:" + aMap.getLongitude() + "," + aMap.getLatitude());
        /**
         * 108.77090467665,34.206496310764
         * 高德API(https://lbs.amap.com/api/webservice/guide/api/convert)经纬度转换之后.
         * 两者误差不是很大
         */
    }

}

– 求知若饥,虚心若愚。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐