您的位置:首页 > 其它

GPS各坐标系间的坐标值转换

2016-11-21 20:49 489 查看
1、地球坐标系:是国际上通用的地理坐标系wgs84,也就是我们常说的经度纬度多少多少。比如08年汶川大地震报的经纬度就是基于这个坐标系。而我们常见的一些设备比如gps也是以这个坐标系为参考的。

2、火星坐标系:主要是因为国家安全的原因为了保护一些比较敏感的坐标位置,在大天朝所有获取地图数据的第一个关卡就是国家测绘局,在他们出产的所有地图在wgs84的基础上进行一次加密,也就是所谓的按照一定的坐标偏移算法到另外一个坐标系gcj-02,这个坐标系是特有的且是变化的,大家习惯性的把他称为火星坐标系。正是因为火星坐标系的存在我们直接拿gps采集的数据在地图上显示始终都有偏差,原因就是因为两个坐标系都不一样造成的。

3、百度坐标系:百度坐标在火星坐标系gcj-02基础上,进行了BD-09二次加密措施,更加保护了个人隐私。百度对外接口的坐标系bd-09并不是GPS采集的真实经纬度,需要通过坐标转换接口进行转换。

package com.cdthgk.utils;

import java.util.List;

import com.cdthgk.utils.vo.ZuoBiao;

/**
* <p>
* MyGPSUtil類主要用於-GPS相关数据处理.
* <ul>
* 经纬度坐标系
* <li>WGS84坐标系:即地球坐标系,是为GPS全球定位系统使用而建立的坐标系统,国际上通用的坐标系。如Google Earth(不含中国)</li>
* <li>GCJ02坐标系:又称火星坐标系,中国国家测绘局制订的地理信息系统的坐标系统,WGS84坐标系经加密(加入随机的偏差)后的坐标系。如Gogole中国、搜搜、阿里云、高德</li>
* <li>BD09坐标系:即百度坐标系,在GCJ02坐标系再次加密后的坐标系。如百度</li>
* <li>CGCS2000坐标系:国家大地坐标系,是我国当前最新的国家大地坐标系,之前有北京54、西安80,同WGS84的原点、尺度、定向及定向演变的定义都是相同的,参考椭球也非常相近,仅扁率f有微小差异。如天地图</li>
* <li>国内其他坐标系:应国家要求均是在GCJ02坐标系上加密后的坐标系。如搜狗、图吧</li>
* </ul>
* </p>
* <p>
* 創建時間 2016-11-21 - 下午5:59:47
* </p>
* <p>
* copyright cdthgk 2010-2018, all rights reserved.
* </p>
* @author 城邑耕夫
* @since 1.0
* @version 1.0
*/
public class MyGPSUtil {

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MyGPSUtil.class);

/**地球半径6378.137,单位为千米 */
private static double EARTH_RADIUS = 6378.137;
/**圆周率PI */
private static double PI = Math.PI;
/**卫星椭球坐标投影到平面地图坐标系的投影因子*/
private static double AXIS = 6378245.0;
/**椭球的偏心率(a^2 - b^2) / a^2 */
private static double OFFSET = 0.00669342162296594323;
/**圆周率转换量*/
private static double X_PI = PI * 3000.0 / 180.0;

/**
* <p>
* getMiles方法主要用于-从一系列坐标轨迹中计算行驶距离.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:00:16
* </p>
* @param zbList 坐标集合(至少两个坐标,且为有效坐标点)
* @return double 单位 KM
*/
public static double getMiles(List<ZuoBiao> zbList) {
double miles = 0;
if (zbList != null && zbList.size() > 1) {
log.debug("计算开始,坐标量:" + zbList.size());
ZuoBiao zb1 = zbList.get(0);
for (int i = 1, len = zbList.size(); i < len; i++) {
ZuoBiao zb2 = zbList.get(i);
miles += getDistance1(zb1.getLat(), zb1.getLng(), zb2.getLat(), zb2.getLng());
zb1 = zb2;
}
log.debug("计算完毕");
}
return miles;
}

/**
* 计算两坐标点间的距离
* @param lat1 坐标1维度
* @param lng1 坐标1经度
* @param lat2 坐标2维度
* @param lng2 坐标2经度
* @return double 单位 KM
*/
public static double getDistance1(double lat1, double lng1, double lat2, double lng2) {
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * EARTH_RADIUS;
// s = Math.round(s * 10000) / 10000;
// s = Math.round(s * 10000d) / 10000d;

return s;
}

/**
* <p>
* distance方法主要用于-计算两坐标点间的距离.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:35:16
* </p>
* @param latA A点维度
* @param logA A点经度
* @param latB B点维度
* @param logB B点经度
* @return double 单位??
*/
public static double getDistance2(double latA, double logA, double latB, double logB) {
int earthR = 6371000;
double x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((logA - logB) * Math.PI / 180);
double y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
double s = x + y;
if (s > 1) {
s = 1;
}
if (s < -1) {
s = -1;
}
double alpha = Math.acos(s);
double distance = alpha * earthR;
return distance;
}

private static double rad(double d) {
return d * PI / 180.0;
}

/**
* <p>
* gcj02_bd09方法主要用于-GCJ02坐标转换为百度09坐标.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:06:44
* </p>
* @param lat gcj02维度
* @param lng gcj02经度
* @return latlng[] 经纬数组(bd09)
*/
public static double[] gcj02_bd09(double lat, double lng) {
double x = lng;
double y = lat;
double[] latlng = new double[2];
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);
latlng[0] = z * Math.sin(theta) + 0.006;
latlng[1] = z * Math.cos(theta) + 0.0065;
return latlng;
}

/**
* <p>
* bd09_gcj02方法主要用于-百度09坐标转转换为GCJ02坐标.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:19:02
* </p>
* @param lat bd09维度
* @param lng bd09经度
* @return latlng[] 维经数组(gcj02)
*/
public static double[] bd09_gcj02(double lat, double lng) {
double x = lng - 0.0065;
double y = lat - 0.006;
double[] latlng = new double[2];
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);
latlng[0] = z * Math.sin(theta);
latlng[1] = z * Math.cos(theta);
return latlng;
}

/**
* <p>
* bd09_wgs84方法主要用于-BD09坐标转为WGS84(地球坐标系).
* <br>转换过程:bd09->gcj02->wgs84.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:23:15
* </p>
* @param lat 维度 (bd09)
* @param lng 经度(bd09)
* @return latlng[] 维经数组(wgs84)
*/
public static double[] bd09_wgs84(double lat, double lng) {
double[] latlng = bd09_gcj02(lat, lng);
return gcj02_wgs84_1(latlng[0], latlng[1]);
}

/**
* <p>
* wgs84_bd09方法主要用于-wgs84地球坐标换转为百度09坐标.
* <br>转换过程:wgs84->gcj02->bd09.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:29:08
* </p>
* @param lat 维度(wgs84)
* @param lng 经度(wgs84)
* @return latlng[] 维经数组(bd09)
*/
public static double[] wgs84_bd09(double lat, double lng) {
double[] latlon = wgs84_gcj02(lat, lng);
return gcj02_bd09(latlon[0], latlon[1]);
}

/**
* <p>
* wgs84_gcj02方法主要用于-wgs84地球坐标转换为gcj02.
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:29:45
* </p>
* @param lat 维度(wgs84)
* @param lng 经度(wgs84)
* @return latlng[] 维经数组(gcj-02)
*/
public static double[] wgs84_gcj02(double lat, double lng) {
double[] latlon = new double[2];
if (outOfChina(lat, lng)) {
latlon[0] = lat;
latlon[1] = lng;
return latlon;
}
double[] deltaD = transform(lat, lng);
latlon[0] = lat + deltaD[0];
latlon[1] = lng + deltaD[1];
return latlon;
}

/**
* <p>
* gcj02_wgs84_1方法主要用于-gcj02坐标转为地球坐标wgs84(粗放).
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:30:20
* </p>
* @param lat 维度(gcj02)
* @param lng 经度(gcj02)
* @return latlng[] 维经数组(wgs84)
*/
public static double[] gcj02_wgs84_1(double lat, double lng) {
double[] latlon = new double[2];
if (outOfChina(lat, lng)) {
latlon[0] = lat;
latlon[1] = lng;
return latlon;
}
double[] deltaD = transform(lat, lng);
latlon[0] = lat - deltaD[0];
latlon[1] = lng - deltaD[1];
return latlon;
}

/**
* <p>
* gcj02_wgs84_2方法主要用于-gcj02坐标转为地球坐标wgs84(精确).
* </p>
* <p>
* 城邑耕夫 2016-11-21 - 下午6:30:51
* </p>
* @param lat 维度(gcj02)
* @param lng 经度(gcj02)
* @return latlng[] 维经数组(wgs84)
*/
public static double[] gcj02_wgs84_2(double lat, double lng) {
double initDelta = 0.01;
double threshold = 0.000000001;
double dLat = initDelta, dLon = initDelta;
double mLat = lat - dLat, mLon = lng - dLon;
double pLat = lat + dLat, pLon = lng + dLon;
double wgsLat, wgsLon, i = 0;
while (true) {
wgsLat = (mLat + pLat) / 2;
wgsLon = (mLon + pLon) / 2;
double[] tmp = wgs84_gcj02(wgsLat, wgsLon);
dLat = tmp[0] - lat;
dLon = tmp[1] - lng;
if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold)) {
break;
}
if (dLat > 0) {
pLat = wgsLat;
} else {
mLat = wgsLat;
}
if (dLon > 0) {
pLon = wgsLon;
} else {
mLon = wgsLon;
}
if (++i > 10000) {
break;
}
}
double[] latlon = new double[2];
latlon[0] = wgsLat;
latlon[1] = wgsLon;
return latlon;
}

/**
* <p>
* transform方法主要用于-wgs84与gcj02的坐标转换.
* </p>
* <p>
* 山河戀夢 2016-11-21 - 下午8:06:56
* </p>
* @param lat 维度
* @param lng 经度
* @return double[] 两坐标系间的偏移
*/
public static double[] transform(double lat, double lng) {
double[] latlng = new double[2];
double dLat = transformLat(lng - 105.0, lat - 35.0);
double dLon = transformLng(lng - 105.0, lat - 35.0);
double radLat = lat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - OFFSET * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (AXIS / sqrtMagic * Math.cos(radLat) * PI);
latlng[0] = dLat;
latlng[1] = dLon;
return latlng;
}

public 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;
}

public static double transformLng(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;
}

public static boolean 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;
}

public static void main(String[] args) {
double dis = getDistance1(31.868278, 106.757043, 31.868245, 106.757106);
System.out.println(dis);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息