Coordinate value conversion between Android GPS coordinate systems

Coordinate value conversion between GPS coordinate systems

1. Earth coordinate system: It is the internationally accepted geographic coordinate system wgs84, which is the longitude and latitude we often say. For example, the latitude and longitude of the Wenchuan earthquake in 2008 is based on this coordinate system. And some of our common devices such as gps are also based on this coordinate system.

2. Mars Coordinate System: Mainly for national security reasons, in order to protect some more sensitive coordinate positions, the first checkpoint for obtaining map data in the Great Celestial Dynasty is the National Bureau of Surveying and Mapping, and all the maps they produce are based on wgs84 Encrypting once, that is, according to a certain coordinate offset algorithm to another coordinate system gcj-02, this coordinate system is unique and changing, and everyone habitually calls it the Mars coordinate system. It is precisely because of the existence of the Mars coordinate system that we directly use the data collected by gps to display on the map there are always deviations. The reason is because the two coordinate systems are different.

3. Baidu coordinate system: Baidu coordinate has carried out BD-09 secondary encryption measures on the basis of Mars coordinate system gcj-02, which further protects personal privacy. The coordinate system bd-09 of Baidu's external interface is not the real latitude and longitude collected by GPS, and needs to be converted through the coordinate conversion interface.

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

 

Reprinted in:  Coordinate value conversion between GPS coordinate systems

Guess you like

Origin blog.csdn.net/weixin_42602900/article/details/123062209