天朝地图坐标和gps坐标不在一个次元中.
具体原因可以参见这篇文章: gps纠偏及大陆地图偏移原因
地图纠偏的主要手段有以下几种:
(一)利用已有的api接口
(1)百度api:
地址: http://api.map.baidu.com/ag/coord/convert?x=121.583140&y=31.341174&from=0&to=2&mode=1
结果:[{"error":0,"x":"MTIxLjU4NzM2NDA5NTA1","y":"MzEuMzM5MDI3NTA2NTE="}]
说明:
请求参数中x为gps经度, y为gps纬度. from to是标识类型转换的方式.具体可以去查百度的api.
返回结果中error为0表示没有错误,返回的x和y是base64算法后的结果(可以自行Google加解密base64),解密后就是:121.58736409505和31.33902750651,这个就是百度坐标。
(2)其他人已经实现好了的api
如: http://map.yanue.net/gpsApi.php?lat=22.502412986242&lng=113.93832783228 等.大家看一下就明白,不具体描述了.
优点: 方便, 可靠, 免费
缺点: 受限于网络速度与api查询次数
(二)利用数据库纠偏
原理和实现方式,参见: gps纠偏数据库及gps纠偏算法PHP
有点: 可靠.适合于大型项目
缺点: 需要数据库支持.免费的纠偏数据库精度低,基本不更新.高精度的库需要额外付费,成本大.
(三)利用代码实现小范围地区的纠偏
原理和c的实现,参见: 一种根据纠偏数据对火星坐标进行完美拟合的方法
简单说, 在一个极有限的范围内, 我们可以将gps和百度坐标之间的变化视作是一种与参考点相关的线性的变化.
参见上面的博客, 我的java代码实现如下:
package test; public class Tester { // 定义4个坐标参考点 gps (百度坐标值) // 福州东北参考点(福州市晋安区东山村)gps 26.105833333333333, 119.36166666666666 // (26.1087320550,119.3731015851) // System.out.println(degress(26, 06, 21) + ", " + degress(119, 21, 42)); // 福州西北参考点(绿洲寨公园)gps 26.105833333333333, 119.21166666666667 // (26.1083582702,119.2230388300) // System.out.println(degress(26, 06, 21) + ", " + degress(119, 12, 42)); // 福州西南参考点(福州市闽侯县南屿镇)gps 25.974999999999998, 119.21166666666667 // (25.9776145157,119.2230386554) // System.out.println(degress(25, 58, 30) + ", " + degress(119, 12, 42)); // 福州东南参考点(城门镇)gps 25.974999999999998, 119.36166666666666 // (25.9779880657,119.3731014562) // System.out.println(degress(25, 58, 30) + ", " + degress(119, 21, 42)); static MapSubject x0y0 = new MapSubject(25.974999999999998, 119.21166666666667, 25.9776145157, 119.2230386554); static MapSubject x1y0 = new MapSubject(26.105833333333333, 119.21166666666667, 26.1083582702, 119.2230388300); static MapSubject x1y1 = new MapSubject(26.105833333333333, 119.36166666666666, 26.1087320550, 119.3731015851); static MapSubject x0y1 = new MapSubject(25.974999999999998, 119.36166666666666, 25.9779880657, 119.3731014562); /** * gps经纬度转小数 * @param v1 度 * @param v2 分 * @param v3 秒 * @return */ public static double degree(double v1, double v2, double v3) { return v1 / 1.0 + v2 / 60.0 + v3 / 3600.0; } /** * 小数转经纬度 * @param degree * @return */ public static String reverDegree(double degree){ StringBuilder sb = new StringBuilder(); int du = (int)(degree / 1); sb.append(String.valueOf(du) + "度"); degree = degree % 1 * 60; int fen = (int)( degree / 1); sb.append(String.valueOf(fen) + "分"); degree = degree % 1 * 60; double miao = degree; sb.append(String.valueOf(miao) + "秒"); return sb.toString(); } public static MapSubject gpsToBaidu(double tGpsLat, double tGpsLng){ return gpsToBaidu(tGpsLat, tGpsLng, ""); } /** * * @param tGpsLat 目标gps纬度 * @param tGpsLng 目标gps经度 * @param realBaidu 从百度api获取的坐标 * @return */ public static MapSubject gpsToBaidu(double tGpsLat, double tGpsLng, String realBaidu) { /*// 参考点的纠偏数据 System.out.println(x0y0.getOffsetLat() + ", " + x0y0.getOffsetLng()); System.out.println(x1y0.getOffsetLat() + ", " + x1y0.getOffsetLng()); System.out.println(x1y1.getOffsetLat() + ", " + x1y1.getOffsetLng()); System.out.println(x0y1.getOffsetLat() + ", " + x0y1.getOffsetLng());*/ System.out.println("targetGps: " + tGpsLat + "," + tGpsLng); System.out.println("Real targetBaidu: " + realBaidu); // 计算纠偏影响系数 double param0 = (tGpsLat - x0y0.getBdLat()) * (tGpsLng - x0y0.getBdLng()); double param1 = (x0y1.getBdLat() - tGpsLat) * (tGpsLng - x0y1.getBdLng()); double param2 = (x1y1.getBdLat() - tGpsLat) * (x1y1.getBdLng() - tGpsLng); double param3 = (tGpsLat - x1y0.getBdLat()) * (x1y0.getBdLng() - tGpsLng); double tOffsetLat = x0y0.getOffsetLat() * param0 + x0y1.getOffsetLat() * param1 + x1y1.getOffsetLat() * param2 + x1y0.getOffsetLat() * param3; double tOffsetLng = x0y0.getOffsetLng() * param0 + x0y1.getOffsetLng() * param1 + x1y1.getOffsetLng() * param2 + x1y0.getOffsetLng() * param3; double tBdLat = tGpsLat + tOffsetLat + 0.003; //0.003 修正值.为多次测试比较后得到的一个近似的常量值 double tBdLng = tGpsLng + tOffsetLng + 0.011; //同上 System.out.println("Calc targetBaidu: " + tBdLat + "," + tBdLng); System.out.println("---------------------------"); return new MapSubject(tGpsLat, tGpsLng, tBdLat, tBdLng); } public static void main(String[] args) { gpsToBaidu(26.1100000000, 119.2700000000, "26.1131933362,119.2811939364"); gpsToBaidu(26.0697361026, 119.3022537231, "26.0725808678,119.3136200409"); gpsToBaidu(26.0481851155, 119.2573320865, "26.0513207295,119.2685508427"); gpsToBaidu(26.0135952280, 119.2724275589, "26.0168640068,119.2836105099"); gpsToBaidu(26.0163287437, 119.3032836914, "26.0192058631,119.3146067513"); gpsToBaidu(26.0888307969, 119.3262434006, "26.0914428213,119.3376982627"); gpsToBaidu(25.974999999999998, 119.21166666666667, "25.9776145157,119.2230386554"); } public static class MapSubject { double gpsLng; //gps经度 double gpsLat; //gps纬度 double bdLng; //百度经度 double bdLat; //百度纬度 double offsetLng; //经度纠偏值 = 百度经度 - gps经度 double offsetLat; //纬度纠偏值 = 百度纬度 - gps纬度 public MapSubject() { } public MapSubject(double gpsLat, double gpsLng, double bdLat, double bdLng) { this.gpsLat = gpsLat; this.gpsLng = gpsLng; this.bdLat = bdLat; this.bdLng = bdLng; this.offsetLat = bdLat - gpsLat; this.offsetLng = bdLng - gpsLng; } public double getGpsLng() { return gpsLng; } public void setGpsLng(double gpsLng) { this.gpsLng = gpsLng; } public double getGpsLat() { return gpsLat; } public void setGpsLat(double gpsLat) { this.gpsLat = gpsLat; } public double getBdLng() { return bdLng; } public void setBdLng(double bdLng) { this.bdLng = bdLng; } public double getBdLat() { return bdLat; } public void setBdLat(double bdLat) { this.bdLat = bdLat; } public double getOffsetLng() { return offsetLng; } public void setOffsetLng(double offsetLng) { this.offsetLng = offsetLng; } public double getOffsetLat() { return offsetLat; } public void setOffsetLat(double offsetLat) { this.offsetLat = offsetLat; } } }
使用http://www.gpsspg.com/maps.htm验证,使用代码计算在预设范围内的点,gps转百度坐标的结果与实际值的误差在0.0001-0.001, 对于手边的这个项目来说,已经可堪使用了.
如果设置更多更密集的参考点,误差值将进一步减小.
优点: 方便.
缺点: 计算值与实际值偏差大, 不适合高精度的定位.