【Java代码】坐标系说明+WGS84\GCJ02\BD09坐标系转换工具+Java坐标系转换及验证源代码分享(粘贴可用)
1. 坐標(biāo)系說(shuō)明
開(kāi)發(fā)地圖應(yīng)用服務(wù)時(shí),一定會(huì)接觸到各種坐標(biāo)系,而保證坐標(biāo)系的正確與合理是一切數(shù)據(jù)分析的前提,總的來(lái)說(shuō),坐標(biāo)系可以分為兩大類(lèi):地理坐標(biāo)系GCS(Geographic Coordinate System)和投影坐標(biāo)系PCS(Projected Coordinate System),地理坐標(biāo)系進(jìn)行地圖投影后就變成了投影坐標(biāo)系。例如我們常見(jiàn)的世界地圖,使用的就是一種墨卡托投影。
其中地理坐標(biāo)系又可分為參心坐標(biāo)系和地心坐標(biāo)系,常見(jiàn)的參心坐標(biāo)系北京54、西安80,常見(jiàn)的地心坐標(biāo)系有WGS84、GCJ02、BD09、GCS2000。
1.1 WGS84(World Geodetic System 1984)
WGS84 是為 GPS 全球定位系統(tǒng)建立的坐標(biāo)系統(tǒng),是世界上第一個(gè)統(tǒng)一的地心坐標(biāo)系,因此也被稱(chēng)為大地坐標(biāo)系、原始坐標(biāo)系。一般通過(guò) GPS 記錄儀記錄下來(lái)的經(jīng)緯度,就是基于 WGS84 坐標(biāo)系的數(shù)據(jù)。Google 和高德地圖定位的的經(jīng)緯度(國(guó)外)都是基于WGS84坐標(biāo)系的;但是在國(guó)內(nèi)是不允許直接用 WGS84 坐標(biāo)系標(biāo)注的,必須經(jīng)過(guò)加密后才能使用。
1.2 GCJ02(國(guó)家測(cè)量局02號(hào)標(biāo)準(zhǔn))
GCJ02 是由中國(guó)國(guó)家測(cè)繪局制訂的地理信息系統(tǒng)的坐標(biāo)系統(tǒng),是在 WGS84 經(jīng)緯度的基礎(chǔ)上執(zhí)行加密算法而成。因?yàn)?GPS 得到的經(jīng)緯度直接在 GCJ02 坐標(biāo)系下會(huì)定位到錯(cuò)誤的地點(diǎn),有種到了火星的感覺(jué),因此在坊間也將 GCJ-02 戲稱(chēng)為火星坐標(biāo)系。
國(guó)測(cè)局規(guī)定,國(guó)內(nèi)出版的各種地圖系統(tǒng)(包括電子形式),必須至少采用 GCJ02 對(duì)地理位置進(jìn)行首次加密的坐標(biāo)系,騰訊(搜搜)地圖、阿里云地圖、Google和高德(國(guó)內(nèi))都是使用 GCJ02 坐標(biāo)系,可以說(shuō) GCJ02 是國(guó)內(nèi)最廣泛使用的坐標(biāo)系。
1.3 BD09(百度坐標(biāo)系09)
BD09 是在 GCJ02 坐標(biāo)系的基礎(chǔ)上再次加密偏移后形成的坐標(biāo)系,官方解釋為進(jìn)一步保護(hù)用戶(hù)隱私,當(dāng)前只適用于百度地圖。
GPS經(jīng)緯度是39°54'26.2"N 116°23'28.4"E,轉(zhuǎn)化為度的單位就是39.907270 116.391213(小數(shù)部分 = 分 / 60 + 秒 / 3600)
2. 坐標(biāo)系轉(zhuǎn)換
- 國(guó)測(cè)局規(guī)定:互聯(lián)網(wǎng)地圖在國(guó)內(nèi)必須至少使用 GCJ02 進(jìn)行首次加密,不允許直接使用 WGS84 坐標(biāo)下的地理數(shù)據(jù),同時(shí)任何坐標(biāo)系均不可轉(zhuǎn)換為 WGS84 坐標(biāo)。因此不存在將 GCJ02 坐標(biāo)轉(zhuǎn)換為 WGS84 坐標(biāo)的官方轉(zhuǎn)換方法。
- 目前百度 API 提供了從其它坐標(biāo)系轉(zhuǎn)換為 BD09 坐標(biāo)系的 API,但卻沒(méi)有從 BD09 坐標(biāo)系轉(zhuǎn)為其他坐標(biāo)系的API。
2.1 WGS84轉(zhuǎn)換
WGS84轉(zhuǎn)換測(cè)試工具 可將 WGS84 轉(zhuǎn)換成 GCJ02、BD09ll 坐標(biāo)系:
2.2 高德坐標(biāo)系轉(zhuǎn)換
高德坐標(biāo)系轉(zhuǎn)換工具 能夠?qū)⒂脩?hù)輸入的非高德坐標(biāo)(GPS坐標(biāo)、mapbar坐標(biāo)、baidu坐標(biāo))轉(zhuǎn)換成高德坐標(biāo):
以上的兩個(gè)工具可以用來(lái)驗(yàn)證Java坐標(biāo)系轉(zhuǎn)換結(jié)果是否正確。
3. Java坐標(biāo)系轉(zhuǎn)換工具
package yz.com.javautil.utilpackage;import yz.com.javautil.vo.TransformPoint;import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier;/*** 坐標(biāo)系轉(zhuǎn)換工具類(lèi)** @author Administrator*/ public class CoordinateTransferUtils {/*** 國(guó)內(nèi)坐標(biāo)邊界*/private static final double MIN_LON = 72.004D;private static final double MAX_LON = 137.8347D;private static final double MIN_LAT = 0.8293D;private static final double MAX_LAT = 55.8271D;/*** PI 圓周率*/private static final double PI = 3.14159265358979324D;/*** A WGS 長(zhǎng)軸半徑*/private static final double A = 6378245.0D;/*** EE WGS 偏心率的平方*/private static final double EE = 0.00669342162296594323D;/*** WGS84轉(zhuǎn)換GCJ02適用于繼承的情況** @param t 繼承TransformPoint類(lèi)的子類(lèi)對(duì)象*/public static <T extends TransformPoint> void wgs84ToGcj02(T t) {wgs84ToGcj02(t::getLng, t::getLat, t::setTransformLng, t::setTransformLat);}/*** WGS84轉(zhuǎn)換GCJ02核心方法** @param fromLon 轉(zhuǎn)換前的經(jīng)度* @param fromLat 轉(zhuǎn)換前的緯度* @return 轉(zhuǎn)換后的經(jīng)緯度map對(duì)象*/public static Map<String, Double> wgs84ToGcj02(double fromLon, double fromLat) {HashMap<String, Double> transformRes = new HashMap<>(2);// 國(guó)外坐標(biāo)不用進(jìn)行加密if (outOfChina(fromLon, fromLat)) {transformRes.put("lon", fromLon);transformRes.put("lat", fromLat);return transformRes;}// 計(jì)算轉(zhuǎn)換后的經(jīng)緯度坐標(biāo)double dLat = transformLat(fromLon - 105.0, fromLat - 35.0);double dLon = transformLon(fromLon - 105.0, fromLat - 35.0);double radLat = fromLat / 180.0 * PI;double magic = Math.sin(radLat);magic = 1 - EE * magic * magic;double sqrtMagic = Math.sqrt(magic);dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);dLon = (dLon * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);double mgLat = fromLat + dLat;double mgLon = fromLon + dLon;transformRes.put("lon", new BigDecimal(mgLon + "", new MathContext(9, RoundingMode.HALF_UP)).doubleValue());transformRes.put("lat", new BigDecimal(mgLat + "", new MathContext(9, RoundingMode.HALF_UP)).doubleValue());return transformRes;}/*** GCJ02轉(zhuǎn)換WGS84** @param lon 轉(zhuǎn)換前的經(jīng)度* @param lat 轉(zhuǎn)換后的緯度* @return 轉(zhuǎn)換后的經(jīng)緯度map對(duì)象*/public static Map<String, Double> gcj02ToWgs84(double lon, double lat) {Map<String, Double> transformRes = new HashMap<>(2);double longitude = lon * 2 - wgs84ToGcj02(lon, lat).get("lon");double latitude = lat * 2 - wgs84ToGcj02(lon, lat).get("lat");transformRes.put("lon", longitude);transformRes.put("lat", latitude);return transformRes;}/*** GCJ02轉(zhuǎn)換BD09** @param gcjLat GCL緯度坐標(biāo)* @param gcjLng GCL經(jīng)度坐標(biāo)*/public static Map<String, Double> Gcj02ToBd09(double gcjLat, double gcjLng) {Map<String, Double> transformRes = new HashMap<>(2);double z = Math.sqrt(gcjLng * gcjLng + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * PI);double theta = Math.atan2(gcjLat, gcjLng) + 0.000003 * Math.cos(gcjLng * PI);transformRes.put("lon", z * Math.cos(theta) + 0.0065);transformRes.put("lat", z * Math.sin(theta) + 0.006);return transformRes;}/*** BD09轉(zhuǎn)換GCJ02** @param bdLat 百度緯度坐標(biāo)* @param bdLng 百度經(jīng)度坐標(biāo)*/public static Map<String, Double> bd09ToGcj02(double bdLat, double bdLng) {Map<String, Double> transformRes = new HashMap<>(2);double x = bdLng - 0.0065, y = bdLat - 0.006;double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI);double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI);transformRes.put("lon", z * Math.cos(theta));transformRes.put("lat", z * Math.sin(theta));return transformRes;}/*** WGS84轉(zhuǎn)換GCJ02優(yōu)化(處理轉(zhuǎn)換前經(jīng)緯度坐標(biāo)為null問(wèn)題)** @param fromLon 轉(zhuǎn)換前的經(jīng)度* @param fromLat 轉(zhuǎn)換前的緯度* @param toLon 轉(zhuǎn)換后的經(jīng)度* @param toLat 轉(zhuǎn)換后的緯度*/private static void wgs84ToGcj02(Supplier<BigDecimal> fromLon, Supplier<BigDecimal> fromLat, Consumer<BigDecimal> toLon, Consumer<BigDecimal> toLat) {wgs84ToGcj02(Optional.ofNullable(fromLon.get()).orElse(BigDecimal.ZERO), Optional.ofNullable(fromLat.get()).orElse(BigDecimal.ZERO), toLon, toLat);}/*** WGS84轉(zhuǎn)換GCJ02優(yōu)化(處理轉(zhuǎn)換后經(jīng)緯度坐標(biāo))** @param fromLon 轉(zhuǎn)換前的經(jīng)度* @param fromLat 轉(zhuǎn)換前的緯度* @param toLon 轉(zhuǎn)換后的經(jīng)度* @param toLat 轉(zhuǎn)換后的緯度*/private static void wgs84ToGcj02(BigDecimal fromLon, BigDecimal fromLat, Consumer<BigDecimal> toLon, Consumer<BigDecimal> toLat) {final Map<String, Double> transformRes = wgs84ToGcj02(fromLon.doubleValue(), fromLat.doubleValue());toLon.accept(new BigDecimal(transformRes.get("lon") + ""));toLat.accept(new BigDecimal(transformRes.get("lat") + ""));}/*** 坐標(biāo)是否在國(guó)外** @param lon 經(jīng)度* @param lat 緯度* @return true 國(guó)外坐標(biāo) false 國(guó)內(nèi)坐標(biāo)*/private static boolean outOfChina(double lon, double lat) {if (lon < MIN_LON || lon > MAX_LON) {return true;}return lat < MIN_LAT || lat > MAX_LAT;}/*** 轉(zhuǎn)換經(jīng)度坐標(biāo)** @param x 偏移后的經(jīng)度* @param y 偏移后的緯度* @return double 轉(zhuǎn)換經(jīng)度坐標(biāo)*/private static double transformLat(double x, double y) {double transform = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));transform += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;transform += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;transform += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;return transform;}/*** 轉(zhuǎn)換緯度坐標(biāo)** @param x 偏移后的經(jīng)度* @param y 偏移后的緯度* @return double 轉(zhuǎn)換緯度坐標(biāo)*/private static double transformLon(double x, double y) {double transform = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));transform += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;transform += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;transform += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;return transform;}/*** 坐標(biāo)系轉(zhuǎn)換工具類(lèi)測(cè)試** @param args 參數(shù)*/public static void main(String[] args) {// WGS84ToGCJ02 116.404, 39.915 轉(zhuǎn)換后 [ 116.41024449916938, 39.91640428150164 ]double lon3 = 116.404D;double lat3 = 39.915D;Map<String, Double> transform3Result = wgs84ToGcj02(lon3, lat3);System.out.println("WGS84ToGCJ02 轉(zhuǎn)換前經(jīng)緯度(" + lon3 + "," + lat3 + ") 轉(zhuǎn)換后經(jīng)緯度(" + transform3Result.get("lon") + "," + transform3Result.get("lat") + ")");// GCJ02ToWGS84 116.404, 39.915 轉(zhuǎn)換后 [ 116.39775550083061, 39.91359571849836 ]double lon4 = 116.404D;double lat4 = 39.915D;Map<String, Double> transform4Result = gcj02ToWgs84(lon4, lat4);System.out.println("GCJ02ToWGS84 轉(zhuǎn)換前經(jīng)緯度(" + lon4 + "," + lat4 + ") 轉(zhuǎn)換后經(jīng)緯度(" + transform4Result.get("lon") + "," + transform4Result.get("lat") + ")");// BD09ToGCJ02 116.404, 39.915 轉(zhuǎn)換后 [ 116.39762729119315, 39.90865673957631 ]double lon5 = 116.404D;double lat5 = 39.915D;Map<String, Double> transform5Result = bd09ToGcj02(lon5, lat5);System.out.println("BD09ToGCJ02 轉(zhuǎn)換前經(jīng)緯度(" + lon5 + "," + lat5 + ") 轉(zhuǎn)換后經(jīng)緯度(" + transform5Result.get("lon") + "," + transform5Result.get("lat") + ")");// GCJ02ToBD09 116.404, 39.915 轉(zhuǎn)換后 [ 116.41036949371029, 39.92133699351021 ]double lon6 = 116.404D;double lat6 = 39.915D;Map<String, Double> transform6Result = gcj02ToBd09(lon6, lat6);System.out.println("GCJ02ToBD09 轉(zhuǎn)換前經(jīng)緯度(" + lon6 + "," + lat6 + ") 轉(zhuǎn)換后經(jīng)緯度(" + transform6Result.get("lon") + "," + transform6Result.get("lat") + ")");} }總結(jié)
以上是生活随笔為你收集整理的【Java代码】坐标系说明+WGS84\GCJ02\BD09坐标系转换工具+Java坐标系转换及验证源代码分享(粘贴可用)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Java代码】道格拉斯-普克 Doug
- 下一篇: 【Java代码】未分页数据根据参数进行分