电子地图坐标系统研究整理

「博客搬家」 新地址: 简书

本文由文末的参考链接整理、整合、修改而成


1. 电子地图坐标系统简介

  • WGS-84 坐标系:即地球坐标系,国际经纬度坐标标准
  • GCJ-02 坐标系:即火星坐标系,WGS84 坐标系经加密后的坐标系,国测局制定。
  • BD-09 坐标系:即百度坐标系,GCJ02 坐标系经加密后的坐标系。

GCJ-02 坐标系统,就是在标准的 WGS-84 坐标系统上进行了人为的偏移,比如 Google 地图、腾讯 SOSO 地图等就是直接使用了国家 GCJ-02 坐标系统,我们有一个不成文的说法,前者叫地球坐标,后者叫火星坐标,并且,火星坐标是无法转换成地球坐标的「网上虽然有一定的方法,但基本上都是基于偏移数据库,精度较高的数据库需要购买,当然这都是一种破解手段」。

所有的电子地图、导航设备,都需要加入国家保密插件。

  • 第一步,地图公司测绘地图,测绘完成后,送到国家测绘局,将真实坐标的电子地图,加密成“火星坐标”,这样的地图才是可以出版和发布的,然后才可以让 GPS 公司处理。

  • 第二步,所有的 GPS 公司,只要需要汽车导航的,需要用到导航电子地图的,都需要在软件中加入国家保密算法,将 COM 口读出来的真实的坐标信号,加密转换成国家要求的保密的坐标。这样,GPS 导航仪和导航电子地图就可以完全匹配,GPS 也就可以正常工作了。

API坐标系
百度地图 API百度坐标
腾讯搜搜地图 API火星坐标
搜狐搜狗地图 API搜狗坐标
阿里云地图 API火星坐标
图吧 MapBar 地图 API图吧坐标
高德 MapABC 地图 API火星坐标
灵图 51ditu 地图 API火星坐标
  • 注1:百度地图使用百度坐标,支持从地球坐标和火星坐标导入成百度坐标,但无法导出。并且批量坐标转换一次只能转换20个(待验证)。
  • 注2:搜狗地图支持直接显示地球坐标,支持地球坐标、火星坐标、百度坐标导入成搜狗坐标,同样,搜狗坐标也无法导出。

说完坐标系统,我们自然能够知道这里的问题,通过以下问题,来说说我对此的研究。


2、各个坐标系的相互转换


2.1 火星坐标系「GCJ-02」与百度坐标系「BD-09」的转换算法

其中 bd_encrypt 将 GCJ-02 坐标转换成 BD-09 坐标, bd_decrypt 反之。

void bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon)  
{  
    double x = gg_lon, y = gg_lat;  
    double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);  
    double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);  
    bd_lon = z * cos(theta) + 0.0065;  
    bd_lat = z * sin(theta) + 0.006;  
}  
void bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon)  
{  
    double x = bd_lon - 0.0065, y = bd_lat - 0.006;  
    double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);  
    double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);  
    gg_lon = z * cos(theta);  
    gg_lat = z * sin(theta);  
}  

2.2 地球坐标系「WGS-84」到火星坐标系「GCJ-02」的转换算法

WGS-84 到 GCJ-02 的转换「即 GPS 加偏」算法

using System;   
namespace Navi  
{  
    class EvilTransform  
    {  
        const double pi = 3.14159265358979324;  
        //   
        // Krasovsky 1940   
        //   
        // a = 6378245.0, 1/f = 298.3   
        // b = a * (1 - f)   
        // ee = (a^2 - b^2) / a^2;   
        const double a = 6378245.0;  
        const double ee = 0.00669342162296594323;  
        //   
        // World Geodetic System ==> Mars Geodetic System   
        public static void transform(double wgLat, double wgLon, out double mgLat, out double mgLon)  
        {  
            if (outOfChina(wgLat, wgLon))  
            {  
                mgLat = wgLat;  
                mgLon = wgLon;  
                return;  
            }  
            double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);  
            double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);  
            double radLat = wgLat / 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 = wgLat + dLat;  
            mgLon = wgLon + dLon;  
        }  
        static bool 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;  
        }  
        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;  
        }  
        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;  
        }  
    }  
}  

以上参考自:http://www.xue5.com/Mobile/iOS/679842.html


2.3 百度在线转换API

http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=longitude&y=latitude
from: 来源坐标系「0 表示 WGS-84 坐标;2 表示GCJ-02 坐标」
to: 转换后的坐标「4 表示 BD-09 坐标,即百度地图使用的行」
x: 经度
y: 纬度

请求之后会返回一串 Json

{  
    "error":0,  
    "x":"MTIxLjUwMDIyODIxNDk2",  
    "y":"MzEuMjM1ODUwMjYwMTE3"  
}  
error:是结果是否出错标志位,0 表示成功
x: 坐标系 2 时为经度,4 时为纬度(Base64编码)
y: 坐标系 4 时为经度,2 时为纬度(Base64编码)

什么情况,经纬度居然还加密?那接下来也只好见招拆招了

import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
import java.io.OutputStreamWriter;  
import java.net.URL;  
import java.net.URLConnection;  
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;  
public class BaiduAPIConverter extends Thread {  
  public static void testPost(String x, String y) throws IOException {  
    try {  
      URL url = new URL("http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x="+ x + "&y=" + y);  
      URLConnection connection = url.openConnection();  
      connection.setDoOutput(true);  
      OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "utf-8");  
      // remember to clean up   
      out.flush();  
      out.close();  
      // 一旦发送成功,用以下方法就可以得到服务器的回应:   
      String sCurrentLine, sTotalString;  
      sCurrentLine = sTotalString = "";  
      InputStream l_urlStream;  
      l_urlStream = connection.getInputStream();  
      BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream));  
      while ((sCurrentLine = l_reader.readLine()) != null) {  
        if (!sCurrentLine.equals(""))  
          sTotalString += sCurrentLine;  
      }  
      sTotalString = sTotalString.substring(1, sTotalString.length() - 1);  
      String[] results = sTotalString.split("\\,");  
      if (results.length == 3) {  
        if (results[0].split("\\:")[1].equals("0")) {  
          String mapX = results[1].split("\\:")[1];  
          String mapY = results[2].split("\\:")[1];  
          mapX = mapX.substring(1, mapX.length() - 1);  
          mapY = mapY.substring(1, mapY.length() - 1);  
          mapX = new String(Base64.decode(mapX));  
          mapY = new String(Base64.decode(mapY));  
          System.out.println("\t" + mapX + "\t" + mapY);  
        }  
      }  
     sleep(10000);  
    } catch (InterruptedException e) {  
      // TODO Auto-generated catch block   
      e.printStackTrace();  
    }  
  }  
  /** 
   * @param args 
   * @throws IOException 
   */  
  public static void main(String[] args) throws IOException {  
    testPost("120.151379", "30.184678");  
    System.out.println("ok");  
  }  
}  

到这里也差不多好了,主要的代码都写出来了,其他的您就自己写吧。
以上参考自:http://scalpel.me/archives/136


2.4 百度内置转换方法,可以不局限于百度定位 SDK

在百度地图中取得 WGS-84 坐标,调用如下方法:

BMapManager.getLocationManager().setLocationCoordinateType(MKLocationManager.MK_COORDINATE_WGS84);

这样从百度 API 中取得的坐标就是 WGS-84 了,可是这种坐标如果显示到百度地图上就会偏移,也就是说取出一个坐标,原封不动的显示上去就偏移了,所以为了显示也是正常就需要在绘制到百度地图上之前转换成 BD-09 。
转换成 BD-09,调用方法:

GeoPoint wgs84;
GeoPoint bd09= CoordinateConvert.bundleDecode(CoordinateConvert.fromWgs84ToBaidu(wgs84));

这里实在不明白为何要设计成 CoordinateConvert.fromWgs84ToBaidu(wgs84) 返回了一个 Bundle,所以还需要 CoordinateConvert.bundleDecode() 再转成 GeoPoint 。


3. 参考文章

  1. 国内各地图API坐标系统比较与转换
  2. 关于百度地图坐标转换接口的研究
  3. 构建自己的百度地图坐标纠偏数据库
  4. GPS纠偏算法,适用于google,高德体系的地图
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值