地图中坐标转换的那些事儿
来源:互联网 发布:ubuntu 休眠设置 编辑:程序博客网 时间:2024/06/16 22:56
地图中坐标转换的那些事儿
地图有很多种,在同一个项目中可能会用到多种地图,而不同地图之间由于这样那样的关系(有竞争关系也有政策关系或者保密措施),于是就出现了不同地图
厂商之间的坐标系是不一样的,最后就造成了一种烦人的问题,用一个地图广商的SDK定位的位置无法在另一家地图厂商的SDK上显示出来。
为什么会出现这样的问题呢?现有的坐标系统有哪些?
其实,我们在手机上定位到的位置,其经纬度并不是真实的“Earth Location“,为啥呢?主要有以下原因:
国家安全、战略安全的需求---火星坐标系 (GCJ-02,国测局02年发布的坐标体系)
据说,保密局开发了一个系统,能将实际的坐标转换成虚拟的坐标。所有在中国销售的数字地图必须使用这个系统进行坐标转换之后方可上市。这是生产环节,这种电子地图被称为火星地图。在使用环节,GPS终端设备必须集成保密局提供的加密算法(集成工作由保密局完成),把从GPS卫星那里得到的坐标转换成虚拟坐标,然后再去火星地图上查找,这样就在火星坐标系上完成了地图的匹配。推出这个系统的名义是为了国家安全。然后呢,需要收取一定的费用。GCJ-02也是国内最广泛使用的坐标体系。
GPS设备直接有GPS卫星(非北斗)获取的原始位置---WGS-84(原始坐标系)
一般用国际标准的GPS记录仪记录下来的坐标,都是GPS的坐标。很可惜,在中国,任何一个地图产品都不允许使用GPS坐标,据说是为了保密。GPS坐标形式如图,度分秒形式的经纬度
由此,火星坐标系则是国内其他地图开发商使用的基础了。现统计出以下国内坐标系分布:
WGS84坐标系:即地球坐标系,国际上通用的坐标系。
GCJ02坐标系:即火星坐标系,WGS84坐标系经加密后的坐标系。
BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系。
搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。
各个地图API采用的坐标系
百度地图API 百度坐标
腾讯搜搜地图API 火星坐标
搜狐搜狗地图API 搜狗坐标*
阿里云地图API 火星坐标
图吧MapBar地图API 图吧坐标
高德MapABC地图API 火星坐标
灵图51ditu地图API 火星坐标
package com.bbk.chart.util;import java.lang.Math;import android.location.Location;/************************************************************ * @project_name :VChat * @author: huangxinayang@vivo.com.cn * @department:IT * @date:2015-9-21 *************************************************************/public class LocationUtil{static double pi = 3.14159265358979324;static double a = 6378245.0;static double ee = 0.00669342162296594323; /* * 说明:百度手机地图对外接口中的坐标系默认是bd09ll,高德使用的坐标系是“gcj02”也就是大家所说的“火星坐标” */public 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;}public 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 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;}public static Location gcj02Encrypt(double ggLat, double ggLon){Location resPoint = new Location("");double mgLat;double mgLon;if (outOfChina(ggLat, ggLon)){resPoint.setLatitude(ggLat);resPoint.setLongitude(ggLon);return resPoint;}double dLat = transformLat(ggLon, (ggLat - 35.0));double dLon = transformLon((ggLon - 105.0), (ggLat - 35.0));double radLat = ggLat / 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);mgLat = ggLat + dLat;mgLon = ggLon + dLon;resPoint.setLatitude(mgLat);resPoint.setLongitude(mgLon);return resPoint;}public static Location gcj02Decrypt(double gjLat, double gjLon){Location resPoint = gcj02Encrypt(gjLat, gjLon);double dLon = resPoint.getLongitude() - gjLon;double dLat = resPoint.getLatitude() - gjLat;Location pt = new Location("");pt.setLatitude(gjLat - dLat);pt.setLongitude(gjLon - dLon);return pt;}public static Location bd09Decrypt(double bdLat, double bdLon){Location gcjPt = new Location("");double x = bdLon - 0.0065, y = bdLat - 0.006;double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * pi);double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * pi);gcjPt.setLongitude(z * Math.cos(theta));gcjPt.setLatitude(z * Math.sin(theta));return gcjPt;}public static Location bd09Encrypt(double ggLat, double ggLon){Location bdPt = new Location("");double x = ggLon, y = ggLat;double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * pi);double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * pi);bdPt.setLongitude(z * Math.cos(theta) + 0.0065);bdPt.setLatitude(z * Math.sin(theta) + 0.006);return bdPt;}/* * * @brief 世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标> * * ####只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标 * * @param location 世界标准地理坐标(WGS-84) * * @return 中国国测局地理坐标(GCJ-02)<火星坐标> */public static Location wgs84ToGcj02(Location location){return gcj02Encrypt(location.getLatitude(), location.getLongitude());}/* * * @brief 中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84) * * ####此接口有1-2米左右的误差,需要精确定位情景慎用 * * @param location 中国国测局地理坐标(GCJ-02) * * @return 世界标准地理坐标(WGS-84) */public static Location gcj02ToWgs84(Location location){return gcj02Decrypt(location.getLatitude(), location.getLongitude());}/* * @brief 世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09) * * @param location 世界标准地理坐标(WGS-84) * * @return 百度地理坐标(BD-09) */public static Location wgs84ToBd09(Location location){Location gcj02Pt = gcj02Encrypt(location.getLatitude(), location.getLongitude());return bd09Encrypt(gcj02Pt.getLatitude(), gcj02Pt.getLongitude());}/* * * @brief 中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09) * * @param location 中国国测局地理坐标(GCJ-02)<火星坐标> * * @return 百度地理坐标(BD-09) */public static Location gcj02ToBd09(Location location){return bd09Encrypt(location.getLatitude(), location.getLongitude());}/* * ** * * @brief 百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标> * * @param location 百度地理坐标(BD-09) * * @return 中国国测局地理坐标(GCJ-02)<火星坐标> */public static Location bd09ToGcj02(Location location){return bd09Decrypt(location.getLatitude(), location.getLongitude());}/* * * @brief 百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84) * * ####此接口有1-2米左右的误差,需要精确定位情景慎用 * * @param location 百度地理坐标(BD-09) * * @return 世界标准地理坐标(WGS-84) */public static Location bd09ToWgs84(Location location){Location gcj02 = bd09ToGcj02(location);return gcj02Decrypt(gcj02.getLatitude(), gcj02.getLongitude());}}ios:
//// JZLocationConverter.h// JZCLLocationMangerDome//// Created by jack zhou on 13-8-22.// Copyright (c) 2013年 JZ. All rights reserved.//#import <Foundation/Foundation.h>#import <CoreLocation/CoreLocation.h>@interface JZLocationConverter : NSObject/** *@brief世界标准地理坐标(WGS-84) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标> * * ####只在中国大陆的范围的坐标有效,以外直接返回世界标准坐标 * *@param location 世界标准地理坐标(WGS-84) * *@return中国国测局地理坐标(GCJ-02)<火星坐标> */+ (CLLocationCoordinate2D)wgs84ToGcj02:(CLLocationCoordinate2D)location;/** *@brief中国国测局地理坐标(GCJ-02) 转换成 世界标准地理坐标(WGS-84) * * ####此接口有1-2米左右的误差,需要精确定位情景慎用 * *@param location 中国国测局地理坐标(GCJ-02) * *@return世界标准地理坐标(WGS-84) */+ (CLLocationCoordinate2D)gcj02ToWgs84:(CLLocationCoordinate2D)location;/** *@brief世界标准地理坐标(WGS-84) 转换成 百度地理坐标(BD-09) * *@param location 世界标准地理坐标(WGS-84) * *@return百度地理坐标(BD-09) */+ (CLLocationCoordinate2D)wgs84ToBd09:(CLLocationCoordinate2D)location;/** *@brief中国国测局地理坐标(GCJ-02)<火星坐标> 转换成 百度地理坐标(BD-09) * *@param location 中国国测局地理坐标(GCJ-02)<火星坐标> * *@return百度地理坐标(BD-09) */+ (CLLocationCoordinate2D)gcj02ToBd09:(CLLocationCoordinate2D)location;/** *@brief百度地理坐标(BD-09) 转换成 中国国测局地理坐标(GCJ-02)<火星坐标> * *@param location 百度地理坐标(BD-09) * *@return中国国测局地理坐标(GCJ-02)<火星坐标> */+ (CLLocationCoordinate2D)bd09ToGcj02:(CLLocationCoordinate2D)location;/** *@brief百度地理坐标(BD-09) 转换成 世界标准地理坐标(WGS-84) * * ####此接口有1-2米左右的误差,需要精确定位情景慎用 * *@param location 百度地理坐标(BD-09) * *@return世界标准地理坐标(WGS-84) */+ (CLLocationCoordinate2D)bd09ToWgs84:(CLLocationCoordinate2D)location;@endJZLocationConverter.m
//// JZLocationConverter.m// JZCLLocationMangerDome//// Created by jack zhou on 13-8-22.// Copyright (c) 2013年 JZ. All rights reserved.//#import "JZLocationConverter.h"#import <CoreLocation/CoreLocation.h>#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// jzA = 6378245.0, 1/f = 298.3// b = a * (1 - f)// ee = (a^2 - b^2) / a^2;#define jzA 6378245.0#define jzEE 0.00669342162296594323@implementation JZLocationConverter+ (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;}+ (CLLocationCoordinate2D)wgs84ToGcj02:(CLLocationCoordinate2D)location{ return [self gcj02Encrypt:location.latitude bdLon:location.longitude];}+ (CLLocationCoordinate2D)gcj02ToWgs84:(CLLocationCoordinate2D)location{ return [self gcj02Decrypt:location.latitude gjLon:location.longitude];}+ (CLLocationCoordinate2D)wgs84ToBd09:(CLLocationCoordinate2D)location{ CLLocationCoordinate2D gcj02Pt = [self gcj02Encrypt:location.latitude bdLon:location.longitude]; return [self bd09Encrypt:gcj02Pt.latitude bdLon:gcj02Pt.longitude] ;}+ (CLLocationCoordinate2D)gcj02ToBd09:(CLLocationCoordinate2D)location{ return [self bd09Encrypt:location.latitude bdLon:location.longitude];}+ (CLLocationCoordinate2D)bd09ToGcj02:(CLLocationCoordinate2D)location{ return [self bd09Decrypt:location.latitude bdLon:location.longitude];}+ (CLLocationCoordinate2D)bd09ToWgs84:(CLLocationCoordinate2D)location{ CLLocationCoordinate2D gcj02 = [self bd09ToGcj02:location]; return [self gcj02Decrypt:gcj02.latitude gjLon:gcj02.longitude];}@end
- 地图中坐标转换的那些事儿
- 坐标转换那些事儿
- 窗口坐标与地图坐标的转换
- 地图坐标与屏幕坐标的转换
- Python 地图坐标的转换
- 地图坐标之间的转换
- 游戏中45度地图的坐标转换
- 百度地图坐标转换中遇到的问题
- iOS-地理坐标转换,原生地图获取的原始坐标转换为地图真实坐标
- IOS百度地图的坐标转换
- 地图坐标转换接口的研究
- 关于地图坐标转换的小总结
- 地图坐标转换->火星坐标
- 搜狗地图:路上的那些事儿(三十九)
- 关于地图坐标到屏幕坐标的转换
- 一个屏幕坐标和地图坐标转换的js代码
- 瓦片地图坐标与触摸坐标的转换
- 将地图坐标转换成GPS坐标的简单算法
- Git的基本使用
- URAL 1910 Titan Ruins: Hidden Entrance
- Kafka错误2_ERROR Unexpected exception, exiting abnormally异常的一种原因
- android 学习activity生命周期和启动模式
- UILable , Button
- 地图中坐标转换的那些事儿
- VMware中安装VMware Tools
- 项目____form提交数据,action中接收相关(含ID NAME区别及用法)
- matlab自带的分类器对人脸、鼻子、嘴进行检测
- C++Primer第五版 练习11.24(解答)
- Delphi XE7 开发Android对话框方式
- @Autowired使用中的问题及@Qualifire的使用
- [转载]jQuery学习之prop和attr的区别
- linux 内核与用户空间通信之netlink使用方法