百度,腾讯,高德地图坐标转换类(PHP版)

该类可以将百度,腾讯,高德地图坐标进行转换,即bd09,gcj02坐标转换。由于百度,腾讯等公司的坐标偏移算法是内部保密的,故该坐标转换类只能大致转换,具有一定偏差,请考虑实际情况使用。

核心代码

<?php

// +----------------------------------------------------------------------
// | Author: 贝莉卡 <beilika.com>
// +----------------------------------------------------------------------

/**
* 坐标转换类
*/
class CoordinateTransformation
{
    /**
     * bd09转gcj02
     * @param float $lat 百度坐标系的纬度
     * @param float $lng 百度坐标系的经度
     * @return array 转换后的gcj02坐标(高德坐标系)。格式array('lat'=>lat, 'lng'=>lng)
     */
    public static function bd09_to_gcj02($lat = 0, $lng = 0){
        $x = $lng - 0.0065;
        $y = $lat - 0.006;
        $z = sqrt($x*$x+$y*$y) - 0.00002 * sin($y * (M_PI * 3000.0 / 180.0));
        $theta = atan2($y, $x) -  0.000003 * cos($x * (M_PI * 3000.0 / 180.0));
        return array(
            'lat' => $z * sin($theta),
            'lng' => $z * cos($theta),
        );
    }

    /**
     * gcj02转bd09
     * @param float $lat gcj02坐标的纬度
     * @param float $lng gcj02坐标的经度
     * @return array 转换后的百度坐标系。格式array('lat'=>lat, 'lng'=>lng)
     */
    public static function gcj02_to_bd09($lat = 0, $lng = 0){
        $z = sqrt($lng * $lng + $lat * $lat) + 0.00002 * sin($lat * (M_PI * 3000.0 / 180.0));
        $theta = atan2($lat, $lng) + 0.000003 * cos($lng * (M_PI * 3000.0 / 180.0));

        return array(
            'lat' => $z * sin($theta) + 0.006,
            'lng' => $z * cos($theta) + 0.0065,
        );
    }

    /**
     * wgs84转gcj02
     * @param float $lat wgs84坐标的纬度
     * @param float $lng wgs84坐标的经度
     * @return array 转换后的gcj02坐标系。格式array('lat'=>lat, 'lng'=>lng)
     */
    public static function wgs84_to_gcj02($lat = 0, $lng = 0)
    {
        if (self::abroad($lat, $lng)) {
            return array(
                'lat' => $lat,
                'lng' => $lng,
            );
        }
        $res = self::delta($lat, $lng);
        return array(
            'lat' => $lat + $res['lat'],
            'lng' => $lng + $res['lng'],
        );
    }

    /**
     * gcj02转wgs84
     * @param float $lat gcj02坐标的纬度
     * @param float $lng gcj02坐标的经度
     * @return array 转换后的wgs84坐标系。格式array('lat'=>lat, 'lng'=>lng)
     */
    public static function gcj02_to_wgs84($lat = 0, $lng = 0)
    {
        if (self::abroad($lat, $lng)) {
            return array(
                'lat' => $lat,
                'lng' => $lng,
            );
        }
        $res = self::delta($lat, $lng);
        return array(
            'lat' => $lat - $res['lat'],
            'lng' => $lng - $res['lng'],
        );
    }

    /**
     * wgs84转墨卡托投影
     * @param float $lat wgs84坐标的纬度
     * @param float $lng wgs84坐标的经度
     */
    public static function wgs84_to_mercator($lat = 0, $lng = 0)
    {
        $x = $lng * 20037508.34 / 180.;
        $y = log(tan((90.0 + $lat) * M_PI / 360.0)) / (M_PI / 180.0);
        $y = $y * 20037508.34 / 180.0;
        return array(
            'lat' => $y,
            'lng' => $x,
        );
        /*
        if ((abs($lng) > 180 || abs($lat) > 90)){
            return null;
        }
        $x = 6378137.0 * $lng * 0.017453292519943295;
        $a = $lat * 0.017453292519943295;
        $y = 3189068.5 * log((1.0 + sin($a)) / (1.0 - sin($a)));
        return array(
            'lat' => $y,
            'lng' => $x,
        );
        */
    }

    /**
     * 墨卡托投影转wgs84
     * @param float $lat 墨卡托投影的纬度
     * @param float $lng 墨卡托投影的经度
     */
    public static function mercator_to_wgs84($lat = 0, $lng = 0)
    {
        $x = $lng / 20037508.34 * 180.0;
        $y = $lat / 20037508.34 * 180.0;
        $y = 180 / M_PI * (2 * atan(exp($y * M_PI / 180.0)) - M_PI / 2);
        return array(
            'lat' => $y,
            'lng' => $x,
        );
        /*
        if (abs($lng) < 180 && abs($lat) < 90){
            return null;
        }
        if ((abs($lng) > 20037508.3427892) || (abs($lat) > 20037508.3427892)){
            return null;
        }
        $a = $lng / 6378137.0 * 57.295779513082323;
        $x = $a - (floor((($a + 180.0) / 360.0)) * 360.0);
        $y = (1.5707963267948966 - (2.0 * atan(exp((-1.0 * $lat) / 6378137.0)))) * 57.295779513082323;
        return array(
            'lat' => $y,
            'lng' => $x,
        );
        */
    }

    /**
     * 获取两个坐标的距离
     * @param  float $lat_a 坐标a的lat
     * @param  float $lng_a 坐标a的lng
     * @param  float $lat_b 坐标b的lat
     * @param  float $lng_b 坐标a的lng
     * @return float
     */
    public static function distance($lat_a = 0, $lng_a = 0, $lat_b = 0, $lng_b = 0)
    {
        $earthR = 6371000.0;
        $x = cos($lat_a * M_PI / 180.0) * cos($lat_b * M_PI / 180.0) * cos(($lng_a - $lng_b) * M_PI / 180);
        $y = sin($lat_a * M_PI / 180.0) * sin($lat_b * M_PI / 180.0);
        $s = $x + $y;
        if ($s > 1) {
            $s = 1;
        }
        if ($s < -1) {
            $s = -1;
        }
        $alpha = acos($s);
        $distance = $alpha * $earthR;
        return $distance;
    }

    private static function delta($lat = 0, $lng = 0)
    {
        //a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
        $a = 6378245.0;
        //ee: 椭球的偏心率。
        $ee = 0.00669342162296594323;
        $dLat = self::transformLat($lng - 105.0, $lat - 35.0);
        $dlng = self::transformlng($lng - 105.0, $lat - 35.0);
        $radLat = $lat / 180.0 * M_PI;
        $magic = sin($radLat);
        $magic = 1 - $ee * $magic * $magic;
        $sqrtMagic = sqrt($magic);
        $dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * M_PI);
        $dlng = ($dlng * 180.0) / ($a / $sqrtMagic * cos($radLat) * M_PI);
        return array(
            'lat' => $dLat,
            'lng' => $dlng,
        );
    }

    /**
     * 判断坐标是否在国外
     * @param  float $lat
     * @param  float $lng
     * @return boolen
     */
    private static function abroad($lat = 0, $lng = 0)
    {
        if ($lng < 72.004 || $lng > 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 * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
        $ret += (20.0 * sin($y * M_PI) + 40.0 * sin($y / 3.0 * M_PI)) * 2.0 / 3.0;
        $ret += (160.0 * sin($y / 12.0 * M_PI) + 320 * sin($y * M_PI / 30.0)) * 2.0 / 3.0;
        return $ret;
    }

    private static function transformlng($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 * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
        $ret += (20.0 * sin($x * M_PI) + 40.0 * sin($x / 3.0 * M_PI)) * 2.0 / 3.0;
        $ret += (150.0 * sin($x / 12.0 * M_PI) + 300.0 * sin($x / 30.0 * M_PI)) * 2.0 / 3.0;
        return $ret;
    }
}

使用方法

$coordinate = new CoordinateTransformation();

//将百度坐标转为腾讯坐标或高德坐标
$coordinate->bd09_to_gcj02($lat, $lng);

//将腾讯坐标或高德坐标转为百度坐标
$coordinate->gcj02_to_bd09($lat, $lng);

//将国际坐标转为腾讯坐标或高德坐标
$coordinate->wgs84_to_gcj02($lat, $lng);

//将腾讯坐标或高德坐标转为国际坐标
$coordinate->gcj02_to_wgs84($lat, $lng);

//计算两个坐标之间的距离
$coordinate->distance($lat_a = 0, $lng_a = 0, $lat_b = 0, $lng_b = 0);

GItHub地址:https://github.com/beilika/coordinatetransformation