IOS开发者学习笔记

app开发地图经纬度三大坐标系介绍和处理

2018-05-10  本文已影响67人  ios开发者

我们常说的坐标系有哪些?

1)GPS以及iOS系统定位获得的坐标是地理坐标系WGS1984(大地坐标);

WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。

2)Web地图一般用的坐标细是投影坐标系WGS 1984 Web Mercator;

3)国内出于相关法律法规要求,对国内所有GPS设备及地图数据都进行了加密偏移处理,代号GCJ-02(国测局坐标),这样GPS定位获得的坐标与地图上的位置刚好对应上;

GCJ02:又称火星坐标系,是由中国国家测绘局制定的地理坐标系统,是由WGS84加密后得到的坐标系。

4)特殊的是百度地图在这基础上又进行一次偏移,通称Bd-09(大地坐标);

BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。

苹果地图,高德地图,谷歌地图都是将WGS1984坐标转换成偏移后的GCJ-02才可以在地图上正确显示位置。

百度地图使用什么坐标体系?

使用百度地图的服务,需使用BD09坐标。

若使用非BD09坐标、未经过坐标转换(非BD09转成BD09)直接叠加在地图上,地图展示位置会偏移,因此通过其他坐标(WGS84、GCJ02)调用服务时,需先将其他坐标转换为BD09。

三种坐标相互转化的代码

OC代码

.h代码

#import

#import

@interface JZLocationConverterOC : NSObject

/**

世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标

@param location 世界标准地理坐标(WGS-84)

@return 中国国测局地理坐标(GCJ-02)<火星坐标>

*/

+ (CLLocationCoordinate2D)wgs84ToGcj02:(CLLocationCoordinate2D)location;

/**

世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09)

@param location 世界标准地理坐标(WGS-84)

@return 百度地理坐标(BD-09)

*/

+ (CLLocationCoordinate2D)wgs84ToBd09:(CLLocationCoordinate2D)location;

/**

中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84)

此接口有1-2米左右的误差,需要精确定位情景慎用

@param location 中国国测局地理坐标(GCJ-02)

@return 世界标准地理坐标(WGS-84)

*/

+ (CLLocationCoordinate2D)gcj02ToWgs84:(CLLocationCoordinate2D)location;

/**

  中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09)

@param location 中国国测局地理坐标(GCJ-02)<火星坐标>

@return 百度地理坐标(BD-09)

*/

+ (CLLocationCoordinate2D)gcj02ToBd09:(CLLocationCoordinate2D)location;

/**

百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

@param location 百度地理坐标(BD-09)

@return 中国国测局地理坐标(GCJ-02)<火星坐标>

*/

+ (CLLocationCoordinate2D)bd09ToGcj02:(CLLocationCoordinate2D)location;

/**

百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84)

此接口有1-2米左右的误差,需要精确定位情景慎用

@param location 百度地理坐标(BD-09)

@return  世界标准地理坐标(WGS-84)

*/

+ (CLLocationCoordinate2D)bd09ToWgs84:(CLLocationCoordinate2D)location;

@end

.m 文件

#define LAT_OFFSET_0(x,y) -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x))

#define LAT_OFFSET_1 (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0

#define LAT_OFFSET_2 (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0

#define LAT_OFFSET_3 (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_PI / 30.0)) * 2.0 / 3.0

#define LON_OFFSET_0(x,y) 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x))

#define LON_OFFSET_1 (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0

#define LON_OFFSET_2 (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0

#define LON_OFFSET_3 (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0

#define RANGE_LON_MAX 137.8347

#define RANGE_LON_MIN 72.004

#define RANGE_LAT_MAX 55.8271

#define RANGE_LAT_MIN 0.8293

#define jzA 6378245.0

#define jzEE 0.00669342162296594323

@implementation JZLocationConverterOC

+ (double)transformLat:(double)x bdLon:(double)y

{

    double ret = LAT_OFFSET_0(x, y);

    ret += LAT_OFFSET_1;

    ret += LAT_OFFSET_2;

    ret += LAT_OFFSET_3;

    return ret;

}

+ (double)transformLon:(double)x bdLon:(double)y

{

    double ret = LON_OFFSET_0(x, y);

    ret += LON_OFFSET_1;

    ret += LON_OFFSET_2;

    ret += LON_OFFSET_3;

    return ret;

}

+ (BOOL)outOfChina:(double)lat bdLon:(double)lon

{

    if (lon < RANGE_LON_MIN || lon > RANGE_LON_MAX)

        return true;

    if (lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX)

        return true;

    return false;

}

+ (CLLocationCoordinate2D)gcj02Encrypt:(double)ggLat bdLon:(double)ggLon

{

    CLLocationCoordinate2D resPoint;

    double mgLat;

    double mgLon;

    if ([self outOfChina:ggLat bdLon:ggLon]) {

        resPoint.latitude = ggLat;

        resPoint.longitude = ggLon;

        return resPoint;

    }

    double dLat = [self transformLat:(ggLon - 105.0)bdLon:(ggLat - 35.0)];

    double dLon = [self transformLon:(ggLon - 105.0) bdLon:(ggLat - 35.0)];

    double radLat = ggLat / 180.0 * M_PI;

    double magic = sin(radLat);

    magic = 1 - jzEE * magic * magic;

    double sqrtMagic = sqrt(magic);

    dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * M_PI);

    dLon = (dLon * 180.0) / (jzA / sqrtMagic * cos(radLat) * M_PI);

    mgLat = ggLat + dLat;

    mgLon = ggLon + dLon;

    resPoint.latitude = mgLat;

    resPoint.longitude = mgLon;

    return resPoint;

}

+ (CLLocationCoordinate2D)gcj02Decrypt:(double)gjLat gjLon:(double)gjLon {

    CLLocationCoordinate2D  gPt = [self gcj02Encrypt:gjLat bdLon:gjLon];

    double dLon = gPt.longitude - gjLon;

    double dLat = gPt.latitude - gjLat;

    CLLocationCoordinate2D pt;

    pt.latitude = gjLat - dLat;

    pt.longitude = gjLon - dLon;

    return pt;

}

+ (CLLocationCoordinate2D)bd09Decrypt:(double)bdLat bdLon:(double)bdLon

{

    CLLocationCoordinate2D gcjPt;

    double x = bdLon - 0.0065, y = bdLat - 0.006;

    double z = sqrt(x * x + y * y) - 0.00002 * sin(y * M_PI);

    double theta = atan2(y, x) - 0.000003 * cos(x * M_PI);

    gcjPt.longitude = z * cos(theta);

    gcjPt.latitude = z * sin(theta);

    return gcjPt;

}

+(CLLocationCoordinate2D)bd09Encrypt:(double)ggLat bdLon:(double)ggLon

{

    CLLocationCoordinate2D bdPt;

    double x = ggLon, y = ggLat;

    double z = sqrt(x * x + y * y) + 0.00002 * sin(y * M_PI);

    double theta = atan2(y, x) + 0.000003 * cos(x * M_PI);

    bdPt.longitude = z * cos(theta) + 0.0065;

    bdPt.latitude = z * sin(theta) + 0.006;

    return bdPt;

}

/**

世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标

@param location 世界标准地理坐标(WGS-84)

@return 中国国测局地理坐标(GCJ-02)<火星坐标>

*/

+ (CLLocationCoordinate2D)wgs84ToGcj02:(CLLocationCoordinate2D)location{

    return [self gcj02Encrypt:location.latitude bdLon:location.longitude];

}

/**

世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09)

@param location 世界标准地理坐标(WGS-84)

@return 百度地理坐标(BD-09)

*/

+ (CLLocationCoordinate2D)wgs84ToBd09:(CLLocationCoordinate2D)location{

    CLLocationCoordinate2D gcj02Pt = [self gcj02Encrypt:location.latitude bdLon:location.longitude];

    return [self bd09Encrypt:gcj02Pt.latitude bdLon:gcj02Pt.longitude] ;

}

/**

中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84)

此接口有1-2米左右的误差,需要精确定位情景慎用

@param location 中国国测局地理坐标(GCJ-02)

@return 世界标准地理坐标(WGS-84)

*/

+ (CLLocationCoordinate2D)gcj02ToWgs84:(CLLocationCoordinate2D)location{

    return [self gcj02Decrypt:location.latitude gjLon:location.longitude];

}

/**

中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09)

@param location 中国国测局地理坐标(GCJ-02)<火星坐标>

@return 百度地理坐标(BD-09)

*/

+ (CLLocationCoordinate2D)gcj02ToBd09:(CLLocationCoordinate2D)location{

    return  [self bd09Encrypt:location.latitude bdLon:location.longitude];

}

/**

百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

@param location 百度地理坐标(BD-09)

@return 中国国测局地理坐标(GCJ-02)<火星坐标>

*/

+ (CLLocationCoordinate2D)bd09ToGcj02:(CLLocationCoordinate2D)location{

    return [self bd09Decrypt:location.latitude bdLon:location.longitude];

}

/**

百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84)

此接口有1-2米左右的误差,需要精确定位情景慎用

@param location 百度地理坐标(BD-09)

@return  世界标准地理坐标(WGS-84)

*/

+ (CLLocationCoordinate2D)bd09ToWgs84:(CLLocationCoordinate2D)location{

    CLLocationCoordinate2D gcj02 = [self bd09ToGcj02:location];

    return [self gcj02Decrypt:gcj02.latitude gjLon:gcj02.longitude];

}

swift代码

//单例模式

    static var shared:JZLocationConverterSwfit{

        struct Static {

            static let instance:JZLocationConverterSwfit = JZLocationConverterSwfit()

        }

        return Static.instance

    }

    let RANGE_LON_MAX = 137.8347

    let RANGE_LON_MIN = 72.004

    let RANGE_LAT_MAX = 55.8271

    let RANGE_LAT_MIN = 0.8293

    let jzA = 6378245.0

    let jzEE = 0.00669342162296594323

    func transformLat(_ x:Double,bdLon y:Double) -> Double {

        var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x))

        ret += (20.0 * sin(6.0 * x * Double.pi) + 20.0 * sin(2.0 * x *  Double.pi)) * 2.0 / 3.0

        ret += (20.0 * sin(y * Double.pi) + 40.0 * sin(y / 3.0 * Double.pi)) * 2.0 / 3.0

        ret += (160.0 * sin(y / 12.0 * Double.pi) + 320 * sin(y * Double.pi / 30.0)) * 2.0 / 3.0

        return ret

    }

    func transformLon(_ x:Double,bdLon y:Double) -> Double {

        var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x))

        ret += (20.0 * sin(6.0 * x * Double.pi) + 20.0 * sin(2.0 * x * Double.pi)) * 2.0 / 3.0

        ret += (20.0 * sin(x * Double.pi) + 40.0 * sin(x / 3.0 * Double.pi)) * 2.0 / 3.0

        ret += (150.0 * sin(x / 12.0 * Double.pi) + 300.0 * sin(x / 30.0 * Double.pi)) * 2.0 / 3.0

        return ret

    }

    func outOfChina(_ lat:Double,bdLon lon:Double) -> Bool {

        if (lon < RANGE_LON_MIN || lon > RANGE_LON_MAX) {

            return true

        }

        if (lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX){

            return true

        }

        return false

    }

    func gcj02Encrypt(_ ggLat:Double, bdLon ggLon:Double) -> CLLocationCoordinate2D {

        var resPoint:CLLocationCoordinate2D = CLLocationCoordinate2D()

        var mgLat:Double = 0.0

        var mgLon:Double = 0.0

        if self.outOfChina(ggLat, bdLon: ggLon) {

            resPoint.latitude = ggLat

            resPoint.longitude = ggLon

            return resPoint

        }

        var dLat:Double = self.transformLat((ggLon - 105.0), bdLon: (ggLat - 35.0))

        var dLon:Double = self.transformLon((ggLon - 105.0), bdLon: (ggLat - 35.0))

        let radLat:Double = ggLat / 180.0 * Double.pi

        var magic:Double = sin(radLat)

        magic = 1 - jzEE * magic * magic

        let sqrtMagic:Double = sqrt(magic)

        dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * Double.pi);

        dLon = (dLon * 180.0) / (jzA / sqrtMagic * cos(radLat) * Double.pi);

        mgLat = ggLat + dLat;

        mgLon = ggLon + dLon;

        resPoint.latitude = mgLat;

        resPoint.longitude = mgLon;

        return resPoint;

    }

    func gcj02Decrypt(_ gjLat:Double, withGjLon gjLon:Double) -> CLLocationCoordinate2D {

        let gPt:CLLocationCoordinate2D = self.gcj02Encrypt(gjLat, bdLon: gjLon)

        let dLon:Double = gPt.longitude - gjLon;

        let dLat:Double = gPt.latitude - gjLat;

        var pt:CLLocationCoordinate2D = CLLocationCoordinate2D();

        pt.latitude = gjLat - dLat;

        pt.longitude = gjLon - dLon;

        return pt;

    }

    func bd09Decrypt(_ bdLat:Double, withBdLon bdLon:Double) -> CLLocationCoordinate2D {

        var gcjPt:CLLocationCoordinate2D = CLLocationCoordinate2D();

        let x:Double = bdLon - 0.0065, y = bdLat - 0.006;

        let z:Double = sqrt(x * x + y * y) - 0.00002 * sin(y * Double.pi);

        let theta:Double = atan2(y, x) - 0.000003 * cos(x * Double.pi);

        gcjPt.longitude = z * cos(theta);

        gcjPt.latitude = z * sin(theta);

        return gcjPt;

    }

    func bd09Encrypt(_ ggLat:Double, bdLon ggLon:Double) -> CLLocationCoordinate2D {

        var bdPt:CLLocationCoordinate2D = CLLocationCoordinate2D();

        let x:Double = ggLon, y = ggLat;

        let z:Double = sqrt(x * x + y * y) + 0.00002 * sin(y * Double.pi);

        let theta:Double = atan2(y, x) + 0.000003 * cos(x * Double.pi);

        bdPt.longitude = z * cos(theta) + 0.0065;

        bdPt.latitude = z * sin(theta) + 0.006;

        return bdPt;

    }

    /// 世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

    /// 只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标

    /// - Parameter location: 世界标准地理坐标(WGS-84)

    /// - Returns: 中国国测局地理坐标(GCJ-02)<火星坐标>

    func wgs84ToGcj02(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        return self.gcj02Encrypt(location.latitude, bdLon: location.longitude)

    }

    /// 世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09)

    ///

    /// - Parameter location: 世界标准地理坐标(WGS-84)

    /// - Returns: 百度地理坐标(BD-09)

    func wgs84ToBd09(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        let gcj02Pt:CLLocationCoordinate2D = self.gcj02Encrypt(location.latitude, bdLon: location.longitude)

        return self.bd09Encrypt(gcj02Pt.latitude, bdLon: gcj02Pt.longitude)

    }

    /// 中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84)

    /// 此接口有1-2米左右的误差,需要精确定位情景慎用

    /// - Parameter location: 中国国测局地理坐标(GCJ-02)

    /// - Returns: 世界标准地理坐标(WGS-84)

    func gcj02ToWgs84(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        return self.gcj02Decrypt(location.latitude, withGjLon: location.longitude)

    }

    /// 中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09)

    ///

    /// - Parameter location: 中国国测局地理坐标(GCJ-02)<火星坐标>

    /// - Returns: 百度地理坐标(BD-09)

    func gcj02ToBd09(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        return self.bd09Encrypt(location.latitude, bdLon: location.longitude)

    }

    /// 百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标>

    ///

    /// - Parameter location: 百度地理坐标(BD-09)

    /// - Returns: 中国国测局地理坐标(GCJ-02)<火星坐标>

    func bd09ToGcj02(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        return self.bd09Decrypt(location.latitude, withBdLon: location.longitude)

    }

    /// 百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84)

    /// 此接口有1-2米左右的误差,需要精确定位情景慎用

    /// - Parameter location: 百度地理坐标(BD-09)

    /// - Returns: 世界标准地理坐标(WGS-84)

    func bd09ToWgs84(_ location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {

        let gcj02:CLLocationCoordinate2D = self.bd09ToGcj02(location)

        return self.gcj02Decrypt(gcj02.latitude, withGjLon: gcj02.longitude)

    }

上一篇下一篇

猜你喜欢

热点阅读