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

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

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

转载于: GPS各坐标系间的坐标值转换 - 吾本村野一耕夫,为求生计走巴蜀 - OSCHINA - 中文开源技术交流社区

猜你喜欢

转载自blog.csdn.net/weixin_42602900/article/details/123062209
今日推荐