天朝地图坐标和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, 对于手边的这个项目来说,已经可堪使用了.
如果设置更多更密集的参考点,误差值将进一步减小.
优点: 方便.
缺点: 计算值与实际值偏差大, 不适合高精度的定位.