
1. 为什么需要坐标转换第一次接触地图开发时我也被各种坐标系搞得晕头转向。明明同一个地点在不同地图上显示的经纬度却不一样。后来才发现原来高德、腾讯地图用的是GCJ-02坐标系俗称火星坐标系而百度地图用的是BD-09坐标系。这就好比两个人用不同的方言描述同一个地方虽然说的是同一件事但表达方式完全不同。在实际开发中这种差异会导致很多问题。比如你的APP同时接入了高德和百度地图用户在高德地图上标记了一个位置你想在百度地图上显示这个标记如果不做坐标转换标记点就会偏移几百米。我遇到过最夸张的一次用户反馈说他的店铺在地图上显示在了马路对面就是因为没处理好坐标转换。2. 三大坐标系详解2.1 WGS84坐标系这是最基础的坐标系GPS设备直接获取的就是这个坐标。你可以把它想象成地球的原生坐标没有经过任何修饰。全球定位系统包括中国的北斗都使用这个坐标系。特点是精度高但在国内地图上直接使用会有偏移。2.2 GCJ-02坐标系这个坐标系俗称火星坐标是在WGS84基础上加入随机偏移形成的。高德地图、腾讯地图都使用这个坐标系。偏移算法是国家机密所以如果你拿到GPS设备的WGS84坐标想直接在高德地图上显示会发现位置不准。2.3 BD-09坐标系这是百度在GCJ-02基础上又加了一层加密形成的坐标系。相当于对坐标做了双重加密。所以百度地图上的坐标既不是WGS84也不是GCJ-02而是特有的BD-09。这就是为什么百度地图和其他地图的坐标不能直接混用。3. 坐标转换实战代码3.1 GCJ-02转BD-09高德/腾讯转百度下面这段Java代码是我在实际项目中验证过的可以直接使用/** * GCJ-02坐标转百度BD-09坐标 * param gcjLon 经度 * param gcjLat 纬度 * return 包含百度坐标的Map */ public static MapString, Double gcjToBd(double gcjLon, double gcjLat) { MapString, Double result new HashMap(); double x gcjLon; double y gcjLat; double z Math.sqrt(x * x y * y) 0.00002 * Math.sin(y * x_pi); double theta Math.atan2(y, x) 0.000003 * Math.cos(x * x_pi); double bdLon z * Math.cos(theta) 0.0065; double bdLat z * Math.sin(theta) 0.006; result.put(lon, bdLon); result.put(lat, bdLat); return result; }使用示例// 高德坐标北京市天安门 MapString, Double bdCoord gcjToBd(116.397428, 39.90923); System.out.println(百度坐标 bdCoord);3.2 BD-09转GCJ-02百度转高德/腾讯反向转换的代码如下/** * 百度BD-09坐标转GCJ-02坐标 * param bdLon 经度 * param bdLat 纬度 * return 包含火星坐标的Map */ public static MapString, Double bdToGcj(double bdLon, double bdLat) { MapString, Double result new HashMap(); double x bdLon - 0.0065; double y bdLat - 0.006; double z Math.sqrt(x * x y * y) - 0.00002 * Math.sin(y * x_pi); double theta Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi); double gcjLon z * Math.cos(theta); double gcjLat z * Math.sin(theta); result.put(lon, gcjLon); result.put(lat, gcjLat); return result; }4. 常见问题与解决方案4.1 转换后仍有小范围偏移这是正常现象。坐标转换算法本身就有一定误差通常在5-20米范围内。如果业务对精度要求极高建议使用同一家地图服务商的产品在目标地图上做人工校准使用更专业的GIS系统4.2 批量转换性能优化如果需要转换大量坐标点我有几个优化建议使用静态方法避免重复创建对象考虑使用多线程处理对转换结果做缓存相同坐标直接返回缓存结果4.3 其他语言实现很多开发者问Python版本怎么实现这里给出关键代码import math x_pi 3.14159265358979324 * 3000.0 / 180.0 def gcj_to_bd(lon, lat): z math.sqrt(lon * lon lat * lat) 0.00002 * math.sin(lat * x_pi) theta math.atan2(lat, lon) 0.000003 * math.cos(lon * x_pi) bd_lon z * math.cos(theta) 0.0065 bd_lat z * math.sin(theta) 0.006 return bd_lon, bd_lat5. 实际应用场景5.1 多平台位置共享我们做过一个社交APP用户可以在高德地图上分享位置其他使用百度地图的用户也能准确看到这个位置。核心就是做好坐标转换流程如下用户在高德地图选择位置获取GCJ-02坐标服务端转换为BD-09坐标百度地图客户端接收并显示转换后的坐标5.2 历史数据迁移有个客户原来用百度地图积累了10万POI数据后来要切换到高德地图。我们写了个脚本批量转换这些坐标关键是要注意确认原始数据的坐标系有些老数据可能是WGS84转换后要做抽样检查记录转换日志便于排查问题5.3 混合地图展示在后台管理系统中我们经常需要同时展示高德和百度地图。我的做法是统一使用GCJ-02坐标系作为内部标准展示百度地图时实时转换为BD-09这样能保证两个地图上的标记点位置一致