一、知识点:

坐标系分类:国际经纬度坐标WGS84、火星系坐标GCJ-02、百度坐标系BG-09

类型 说明 使用者
WGS84 美国GPS 国际通用,如谷歌国外地图、osm地图
火星系GCJ-02 国测局制定的标准,国内地图必须至少使用此对位置进行首次加密 高德地图、腾讯搜搜地图、阿里云地图、灵图51ditu地图、谷歌中国地图
BG-09 百度在GCJ-02标准基础上进行二次加密 百度地图

二、PHP实现方法

共有两个类,Coordinate和CoordinateTool。 Coordinate,坐标点对象:

class Coordinate
{
    public $x = 0;
    public $y = 0;
    /**
     * Coordinate constructor.
     * @param $lon float 经度
     * @param $lat float 纬度
     */
    public function __construct ($lon,$lat)
    {
        $this->x = $lon;
        $this->y = $lat;
    }
}

CoordinateTool ,坐标转换工具类:


define('X_PI',3.14159265358979324 * 3000.0 / 180.0);
class CoordinateTool
{
    private static $pi = 3.14159265358979324;  // 圆周率
    private static $a = 6378245.0; // WGS 长轴半径
    private static $ee = 0.00669342162296594323; // WGS 偏心率的平方
    /**
     * 将火星坐标系GCJ-02 坐标 转换成百度坐标系 BD-09 坐标
     * @param $gc_loc 火星坐标点(Class Coordinate)
     * @return $bg_loc Coordinate对象,百度地图经纬度坐标
     */
    public static function gcj_bd($gc_loc)
    {
        $x_pi = X_PI;
        $x = $gc_loc->x;
        $y = $gc_loc->y;
        $z = sqrt($x * $x + $y * $y) + 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) + 0.000003 * cos($x * $x_pi);
        $bd_x = $z * cos($theta) + 0.0065;
        $bd_y = $z * sin($theta) + 0.006;
        $bg_loc = new Coordinate($bd_x,$bd_y);
        return $bg_loc;
    }
    /**
     * 将百度坐标系 BD-09 坐标 转换成 火星坐标系GCJ-02 坐标
     * @param $bd_loc 火星坐标点(Class Coordinate)
     *  @return $bg_loc Coordinate对象,火星坐标系经纬度坐标
     */
    public static function bd_gcj($bd_loc)
    {
        $x_pi = X_PI;
        $x = $bd_loc->x - 0.0065;
        $y = $bd_loc->y - 0.006;
        $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
        $gc_x = $z * cos($theta);
        $gc_y = $z * sin($theta);
        $gc_loc = new Coordinate($gc_x,$gc_y);
        return $gc_loc;
    }
    /**
     * 将国际通用坐标系WGS84坐标 转换成 火星坐标系GCJ-02 坐标
     * @param $wgs_loc WGS84坐标点(Class Coordinate)
     *  @return $bg_loc Coordinate对象,火星坐标系经纬度坐标
     */
    public static function wgs_gcj($wgs_loc)
    {
        $wgs_lon = $wgs_loc->x;
        $wgs_lat = $wgs_loc->y;

        if (self::outOfChina($wgs_lon,$wgs_lat)){
            return (new Coordinate($wgs_lon,$wgs_lat));
        }
        $x_pi = X_PI;
        $dLat = self::transformLat($wgs_lon - 105.0, $wgs_lat - 35.0);
        $dLon = self::transformLon($wgs_lon - 105.0, $wgs_lat - 35.0);
        $radLat = $wgs_lat / 180.0 * $x_pi;
        $magic = sin($radLat);
        $magic = 1 - self::$ee * $magic * $magic;
        $sqrtMagic = sqrt($magic);
        $dLat = ($dLat * 180.0) / ((self::$a * (1 - self::$ee)) / ($magic * $sqrtMagic) * self::$pi);
        $dLon = ($dLon * 180.0) / (self::$a / $sqrtMagic * cos($radLat) * self::$pi);

        $mgLat = $wgs_lat + $dLat;
        $mgLon = $wgs_lon + $dLon;

        $gcj_loc = new Coordinate($mgLon,$mgLat);
        return $gcj_loc;
    }
    /**
     * 将火星坐标系GCJ-02坐标 转换成 国际通用坐标系WGS84坐标
     * @param $gcj_loc GCJ-02坐标点(Class Coordinate)
     *  @return $wgs_loc Coordinate对象,国际通用坐标系WGS84坐标
     */
    public static function gcj_wgs($gcj_loc)
    {
        $to = self::wgs_gcj($gcj_loc);
        $lon = $gcj_loc->x;
        $lat = $gcj_loc->y;
        $gcl_lon = $to->x;
        $gcl_lat = $to->y;
        $d_lat = $gcl_lat - $lat;
        $d_lon = $gcl_lon - $lon;
        return new Coordinate($lon - $d_lon,$lat - $d_lat);
    }


    private static function outOfChina($lon,$lat)
    {
        if ($lon < 72.004 || $lon > 137.8347)
            return true;
        if ($lat < 0.8293 || $lat > 55.8271)
            return true;

        return false;
    }
    private static function transformLat($x,$y)
    {
        $ret = -100.0 + 2.0 * $x + 3.0 * $y + 0.2 * $y * $y + 0.1 * $x * $y + 0.2 * sqrt(abs($x));
        $ret += (20.0 * sin(6.0 * $x * self::$pi) + 20.0 * sin(2.0 * $x * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($y * self::$pi) + 40.0 * sin($y / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (160.0 * sin($y / 12.0 * self::$pi) + 320 * sin($y * self::$pi / 30.0)) * 2.0 / 3.0;
        return $ret;
    }

    private static function transformLon($x, $y)
    {
        $ret = 300.0 + $x + 2.0 * $y + 0.1 * $x * $x + 0.1 * $x * $y + 0.1 * sqrt(abs($x));
        $ret += (20.0 * sin(6.0 * $x * self::$pi) + 20.0 * sin(2.0 * $x * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($x * self::$pi) + 40.0 * sin($x / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (150.0 * sin($x / 12.0 * self::$pi) + 300.0 * sin($x / 30.0 * self::$pi)) * 2.0 / 3.0;

        return $ret;
    }
}

三、坐标转换

在controller层调用

//将火星坐标系GCJ-02 坐标 转换成百度坐标系 BD-09 坐标
$coordinate = new Coordinate(113.3998,34.5443); //吃货天堂的坐标
$coord = CoordinateTool::gcj_bd($coordinate);

//将百度坐标系 BD-09 坐标 转换成 火星坐标系GCJ-02 坐标
$coordinate = new Coordinate(113.3996,34.543);
$coord = CoordinateTool::bd_gcj($coordinate);

//将国际通用坐标系WGS84坐标 转换成 火星坐标系GCJ-02 坐标
$coordinate = new Coordinate(113.3924,34.54036);
$coord = CoordinateTool::wgs_gcj($coordinate);

//将火星坐标系GCJ-02坐标 转换成 国际通用坐标系WGS84坐标
$coordinate = new Coordinate(113.3998,34.5443);
$coord = CoordinateTool::gcj_wgs($coordinate);