BD09 坐标 WGS84 坐标 GCJ02 坐标的相互转换

谷歌地图采用的是 WGS84 地理坐标系(中国范围除外),谷歌中国地图、搜搜中国地图、高德地图采用的是 GCJ02 地理坐标系,百度采用的是 BD09 坐标系,而设备一般包含 GPS 芯片或者北斗芯片获取的经纬度为 WGS84 地理坐标系,所以我们要根据得到的经纬度的坐标类型和地图厂商类型在地图上标点,否则会出现获取的位置误差。

为什么不统一用 WGS84 地理坐标系这就是国家地理测绘总局对于出版地图的要求,出版地图必须符合 GCJ02 坐标系标准了,也就是国家规定不能直接使用 WGS84 地理坐标系。所以定位大家感觉不准确很多又叫出版地图为火星地图其实只是坐标系不一样而已。

以百度地图为例,百度地图采用的是自己的 BD09 坐标。如果我们通过 GPS 获取的坐标,也就是 WGS84 坐标,那么我们首先要将 WGS 坐标转换成 BD09 坐标。代码如下:

  1. package com.jiuqi.mobile.patrol.util;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.OutputStream;
  6. import java.net.Socket;
  7. import java.text.ParseException;
  8. /**
  9. * 将真实的GPS经纬度信息转换成百度地图的经纬度
  10. */
  11. public class GpsToBaidu {
  12. public static void main(String args[]) throws ParseException {
  13. String xy = changgeXY("113.684405", "34.785423");
  14. System.out.println("——" + xy);
  15. }
  16. /**
  17. * 转换经纬度
  18. */
  19. public static String changgeXY(String xx, String yy) {
  20. try {
  21. Socket s = new Socket("api.map.baidu.com", 80);
  22. BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
  23. OutputStream out = s.getOutputStream();
  24. StringBuffer sb = new StringBuffer("GET /ag/coord/convert?from=0&to=4");
  25. sb.append("&x=" + xx + "&y=" + yy);
  26. sb.append("&callback=BMap.Convertor.cbk_3976 HTTP/1.1\r\n");
  27. sb.append("User-Agent: Java/1.6.0_20\r\n");
  28. sb.append("Host: api.map.baidu.com:80\r\n");
  29. sb.append("Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n");
  30. sb.append("Connection: Close\r\n");
  31. sb.append("\r\n");
  32. out.write(sb.toString().getBytes());
  33. String json = "";
  34. String tmp = "";
  35. while ((tmp = br.readLine()) != null) {
  36. // System.out.println(tmp);
  37. json += tmp;
  38. }
  39. System.out.println(json);
  40. int start = json.indexOf("cbk_3976");
  41. int end = json.lastIndexOf("}");
  42. if (start != -1 && end != -1 && json.contains("\"x\":\"")) {
  43. json = json.substring(start, end);
  44. String[] point = json.split(",");
  45. String x = point[1].split(":")[1].replace("\"", "");
  46. String y = point[2].split(":")[1].replace("\"", "");
  47. return (new String(decode(x)) + "," + new String(decode(y)));
  48. } else {
  49. System.out.println("gps坐标无效!!");
  50. }
  51. out.close();
  52. br.close();
  53. } catch (Exception e) {
  54. e.printStackTrace();
  55. }
  56. return null;
  57. }
  58. /**
  59. * 解码
  60. *
  61. * @param str
  62. * @return string
  63. */
  64. public static byte[] decode(String str) {
  65. byte[] bt = null;
  66. try {
  67. BASE64Decoder decoder = new BASE64Decoder();
  68. bt = decoder.decodeBuffer(str);
  69. // System.out.println(new String (bt));
  70. } catch (IOException e) {
  71. e.printStackTrace();
  72. }
  73. return bt;
  74. }
  75. }

如果拿到的坐标是国测局坐标,也就是 GCJ02,那么使用下面方法,可以转换成 BD09。

  1. /**
  2. * 国测局坐标和百度坐标的相互转换
  3. */
  4. public class GCJToBD {
  5. public static void main(String[] args) {
  6. double l = 113.69147;
  7. double a = 34.78425;
  8. double lng = bd_encrypt(l, a)[0];
  9. double lat = bd_encrypt(l, a)[1];
  10. System.out.println(lng);
  11. System.out.println(lat);
  12. }
  13. static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
  14. /**
  15. * 火星坐标系 (国测局标准)(GCJ-02) 转换为百度坐标系 (BD-09) 的转换算法
  16. * @param gg_lon
  17. * @param gg_lat
  18. * @return
  19. */
  20. public static double[] bd_encrypt(double gg_lon, double gg_lat) {
  21. double[] d = new double[2];
  22. double x = gg_lon, y = gg_lat;
  23. double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
  24. double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
  25. double bd_lon = z * Math.cos(theta) + 0.0065;
  26. double bd_lat = z * Math.sin(theta) + 0.006;
  27. d[0] = bd_lon;
  28. d[1] = bd_lat;
  29. return d;
  30. }
  31. /**
  32. * BD-09 坐标转换成GCJ-02 坐标
  33. * @param bd_lon 经度
  34. * @param bd_lat 纬度
  35. * @return
  36. */
  37. public static double[] bd_decrypt(double bd_lon, double bd_lat) {
  38. double[] d = new double[2];
  39. double x = bd_lon 0.0065, y = bd_lat 0.006;
  40. double z = Math.sqrt(x * x + y * y) 0.00002 * Math.sin(y * x_pi);
  41. double theta = Math.atan2(y, x) 0.000003 * Math.cos(x * x_pi);
  42. double gg_lon = z * Math.cos(theta);
  43. double gg_lat = z * Math.sin(theta);
  44. d[0] = gg_lon;
  45. d[1] = gg_lat;
  46. return d;
  47. }
  48. }

转换成百度坐标下的经纬度即可有在地图上标点,当然,百度获取的文字详情不如高德,有时想要获取位置详情,需要调用高德的接口,而高德采用的标准是 GCJ02,因此,如果拿到百度地图的经纬度或是 GPS 经纬度,则要转成国测局的标准,以获取位置详细信息,方法如下:

  1. //WGS84(GPS) 转换成 GCJ02(国测局)
  2. public class GPSToGCJ {
  3. static double pi = 3.14159265358979324;
  4. static double a = 6378245.0;
  5. static double ee = 0.00669342162296594323;
  6. // World Geodetic System ==> Mars Geodetic System
  7. public static double[] transform(double wgLon, double wgLat) {
  8. double[] d = new double[2];
  9. double mgLat;
  10. double mgLon;
  11. if (outOfChina(wgLat, wgLon)) {
  12. mgLat = wgLat;
  13. mgLon = wgLon;
  14. }
  15. double dLat = transformLat(wgLon 105.0, wgLat 35.0);
  16. double dLon = transformLon(wgLon 105.0, wgLat 35.0);
  17. double radLat = wgLat / 180.0 * pi;
  18. double magic = Math.sin(radLat);
  19. magic = 1 ee * magic * magic;
  20. double sqrtMagic = Math.sqrt(magic);
  21. dLat = (dLat * 180.0) / ((a * (1 ee)) / (magic * sqrtMagic) * pi);
  22. dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
  23. mgLat = wgLat + dLat;
  24. mgLon = wgLon + dLon;
  25. d[0] = mgLon;
  26. d[1] = mgLat;
  27. return d;
  28. }
  29. static boolean outOfChina(double lat, double lon) {
  30. if (lon < 72.004 || lon > 137.8347)
  31. return true;
  32. if (lat < 0.8293 || lat > 55.8271)
  33. return true;
  34. return false;
  35. }
  36. static double transformLat(double x, double y) {
  37. 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));
  38. ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
  39. ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
  40. ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
  41. return ret;
  42. }
  43. static double transformLon(double x, double y) {
  44. double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
  45. ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
  46. ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
  47. ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
  48. return ret;
  49. }
  50. public static void main(String[] args) {
  51. double l = 113.68444;
  52. double a = 34.78545;
  53. double lng = transform(l, a)[0];
  54. double lat = transform(l, a)[1];
  55. System.out.println(lng);
  56. System.out.println(lat);
  57. }
  58. }

如果配合 android 端返回经纬度,百度的 api 中类,可以设置返回的坐标。

Android API 类参考3.3 - GeoLocation

  1. public void setCoorType ( String )

我们支持返回若干种坐标系,包括国测局坐标系、百度坐标系,需要更多坐标系请联系我们,需要深度合作。目前这些参数的代码为。因此需要在请求时指定类型,如果不指定,默认返回百度坐标系。注意当仅输入IP时,不会返回坐标。目前这些参数的代码为:

  • 返回国测局经纬度坐标系 coor=gcj02

  • 返回百度墨卡托坐标系 coor=bd09

  • 返回百度经纬度坐标系 coor=bd09ll

百度手机地图对外接口中的坐标系默认是 bd09ll,如果配合百度地图产品的话,需要注意坐标系对应问题。

有关坐标系的更多问题,请看常见问题

(完)