Java实现经纬度坐标转换

2024-06-07 00:36

本文主要是介绍Java实现经纬度坐标转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、坐标系统简介

坐标系统,是描述物质存在的空间位置(坐标)的参照系,通过定义特定基准及其参数形式来实现。

坐标是描述位置的一组数值,按坐标的维度一般分为一维坐标(公路里程碑)和二维坐标(笛卡尔平面直角坐标、高斯平面直角坐标)、三维坐标(大地坐标、空间直角坐标)。

为了描述或确定位置,必须建立坐标系统,坐标只有存在于某个坐标系统才有实际的意义与具体的位置。

地球是一个球体,球面上的位置,是以经纬度来表示,它称为“球面坐标系统”或“地理坐标系统”。
在球面上计算角度距离十分麻烦,而且地图是印刷在平面纸张上,要将球面上的物体画到纸上,就必须展平,这种将球面转化为平面的过程,称为“投影”。

1、经纬度坐标系

经纬度坐标系是一种地理坐标系统,用于描述地球表面上任意位置的坐标。它是基于地球的自转和赤道的划分而建立的。

  • 经度(Longitude):表示地球表面上一个点相对于本初子午线的东西方向的位置。经度的度量单位是度(°),范围从0°到180°,以东经为正值,西经为负值。本初子午线位于英国伦敦的皇家格林尼治天文台,它被定义为经度0°。
  • 纬度(Latitude):表示地球表面上一个点相对于赤道的北南方向的位置。纬度的度量单位也是度(°),范围从0°到90°,以北纬为正值,南纬为负值。赤道位于纬度0°。

经纬度坐标系统是全球通用的地理坐标系统。

经纬度坐标系统使用经度和纬度的组合来确定地球表面上的特定位置。一个点的经纬度坐标表示为两个数值的组合,例如:40°N,120°E 表示北纬40度,东经120度的位置。

2、坐标系统

(1)WGS84(World Geodetic System 1984,GPS标准)

  • 定义:WWGS84,全称“世界大地坐标系统1984”,是一个国际广泛接受的地心地固坐标系统,也是全球定位系统(GPS)的标准坐标系。WGS84是基于地球椭球体模型,提供全球统一的地理坐标框架,是开放和透明的,适用于全球范围内的导航、定位和地图制作。
  • 历史:经历了多次精化,包括WGS84(G730)、WGS84(G873)和WGS84(G1150)。
  • 参数:长半轴为6378137.0米,扁率为1/298.257223563。
  • 应用场景:全球范围内的GPS定位、地图绘制等。

(2)GCJ-02(国测局坐标系,也被称为火星坐标系)

  • 定义:GCJ-02,全称“中国国测局坐标系统”,也称为“火星坐标”或“火星加密算法”。它是中国国家测绘局制定的一种地理坐标系,用于对中国大陆的地理位置进行偏移加密处理。
  • 特点:它是中国政府为了国家安全而对公开的WGS84坐标数据进行了加密处理,使得在未授权的情况下难以直接使用全球定位系统(GPS)获得精确的位置信息。相对于WGS84坐标系进行了加密处理,用于保护国家安全。
  • 应用场景:在国内的地图服务、导航系统、地理信息系统等应用中得到广泛使用,例如高德地图、腾讯地图等。手机上的地图导航软件利用GCJ-02坐标系实现了高精度的定位和导航功能。

(3)BD-09(Baidu Coordinate System)

  • 定义:BD-09是百度地图使用的一种坐标系。
  • BD-09是百度地图使用的坐标系统,它是在GCJ-02的基础上进行的二次加密。
  • 特点:由于百度地图在中国提供服务,它需要遵守GCJ-02的加密规则,但为了增强定位精度和防止第三方直接解密GCJ-02坐标,百度在其服务中采用了更复杂的加密算法。即基于GCJ-02坐标系进行了加密偏移,提供了更好的数据保护性能。因此,从WGS84到BD-09,需要经过两次转换,先由WGS84转为GCJ-02,然后再转为BD-09。
  • 应用场景:主要用于中国境内各种位置服务应用,如百度地图的定位和导航服务。

(4)CGCS2000(中国2000国家大地坐标系)

  • 定义:CGCS2000,全称“2000国家大地坐标系统”,是中国最新的地心地固坐标系统,替代了之前的北京54和西安80坐标系。以ITRF 97为参考框架,以2000.0作为参考历元。
  • 特点:原点设定在地球的质量中心,Z轴指向IERS参考极,X轴和Y轴通过右手规则确定。
  • CGCS2000基于地球椭球体模型,与国际标准兼容,尤其与北斗卫星导航系统配合使用时,提供高精度的定位服务。它是中国自主的全球定位系统,与WGS84类似,但更适合中国的地理特性。
  • 与WGS84的关系:在定义上与WGS84非常相似,包括原点、尺度和定向。但在扁率上的差异会导致椭球面上的纬度和高度产生微小的变化。
  • 应用场景:作为国家基础坐标系,用于各种测绘和地理信息系统工作。

3、坐标转换简介

在地图应用中,不同的地图服务商通常使用不同的坐标系,坐标转换就是将一个地图服务商的坐标系转换为另一个地图服务商的坐标系,以便在不同的地图上显示相同的位置信息。

GPS(谷歌地图)|高德|百度地图对坐标系统的使用:

  • WGS84:地理坐标系统,GPS仪器记录的经纬度信息。Google Earth采用,Google Map中国范围外使用,高德地图中国范围外使用。
  • GCJ-02:投影坐标系统,火星坐标系,中国国家测绘局制定的坐标系统,由 WGS-84加密后的坐标。适用于高德地图。
  • BD-09:投影坐标系统,百度坐标系,GCJ-02加密后的坐标系,只适用于百度地图。
  • CS2000:中国2000国家大地坐标系统,与WG-S84类似,只适用于北斗卫星。

注意:

  • WGS84、GCJ-02和BD-09之间通过转换算法或者API可以实现互转。
  • GCS2000与GCJ-02和BD-09之间没有直接的转换关系,通常需要将 GCS2000转换为 WGS84,然后通过这个中间坐标系(WGS84)来进行间接转换。
  • 在国内是不允许直接用 WGS84坐标系标注经纬度的,必须经过加密后才能用。所以必须至少使用 GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的 BD-09坐标系。

不同地图服务商有提供其丰富的 API文档功能,包括经纬度坐标转换功能。有的地图服务商API需要收费。

二、地图经纬度转换工具类

对于 CGCS2000 需要引入 proj4j依赖:

<dependency><groupId>org.locationtech.proj4j</groupId><artifactId>proj4j</artifactId><version>1.3.0</version>
</dependency>
<dependency><groupId>org.locationtech.proj4j</groupId><artifactId>proj4j-epsg</artifactId><version>1.3.0</version>
</dependency>
/*** 坐标转换工具类* <p>* 参考文章-实现的Java版本:https://github.com/wandergis/coordtransform*/
@Slf4j
public class CoordinateTransformUtil {static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;// πstatic double pi = 3.1415926535897932384626;// 长半轴static double a = 6378245.0;// 扁率static double ee = 0.00669342162296594323;/*** WGS84 转 GCJ-02** @param lng WGS84经度* @param lat WGS84纬度* @return*/public static Coordinate wgs84ToGcj02(double lng, double lat) {if (outOfChina(lng, lat)) {return new Coordinate(lng, lat);}double dlat = transformlat(lng - 105.0, lat - 35.0);double dlng = transformlng(lng - 105.0, lat - 35.0);double radlat = lat / 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);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);double mgLat = lat + dlat;double mgLng = lng + dlng;return new Coordinate(mgLng, mgLat);}/*** GCJ-02 转 WGS84** @param lng GCJ-02经度* @param lat GCJ-02纬度* @return*/public static Coordinate gcj02ToWgs84(double lng, double lat) {if (outOfChina(lng, lat)) {return new Coordinate(lng, lat);}double dlat = transformlat(lng - 105.0, lat - 35.0);double dlng = transformlng(lng - 105.0, lat - 35.0);double radlat = lat / 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);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);double mgLat = lat + dlat;double mgLng = lng + dlng;return new Coordinate(lng * 2 - mgLng, lat * 2 - mgLat);}/*** GCJ-02 转 BD-09** @param lng GCJ-02经度* @param lat GCJ-02纬度* @return*/public static Coordinate gcj02ToBd09(double lng, double lat) {double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);double bd_lng = z * Math.cos(theta) + 0.0065;double bd_lat = z * Math.sin(theta) + 0.006;return new Coordinate(bd_lng, bd_lat);}/*** BD-09 转 GCJ-02** @param lng BD-09经度* @param lat BD-09纬度*/public static Coordinate bd09ToGcj02(double lng, double lat) {double x = lng - 0.0065;double y = lat - 0.006;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);double gg_lng = z * Math.cos(theta);double gg_lat = z * Math.sin(theta);return new Coordinate(gg_lng, gg_lat);}/*** BD-09 转 WGS84** @param lng BD-09经度* @param lat BD-09纬度* @return*/public static Coordinate bd09ToWgs84(double lng, double lat) {Coordinate gcj02 = bd09ToGcj02(lng, lat);Coordinate wgs84 = gcj02ToWgs84(gcj02.longitude, gcj02.latitude);return wgs84;}/*** WGS84 转 BD-09** @param lng WGS84经度* @param lat WGS84纬度* @return*/public static Coordinate wgs84ToBd09(double lng, double lat) {Coordinate gcj02 = wgs84ToGcj02(lng, lat);Coordinate bd09 = gcj02ToBd09(gcj02.longitude, gcj02.latitude);return bd09;}/*** 纬度转换** @param lng* @param lat* @return*/public static double transformlat(double lng, double lat) {double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;return ret;}/*** 经度转换** @param lng* @param lat* @return*/public static double transformlng(double lng, double lat) {double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;return ret;}/*** 判断是否在国内,不在国内不做偏移** @param lng* @param lat* @return*/public static boolean outOfChina(double lng, double lat) {if (lng < 72.004 || lng > 137.8347) {return true;} else if (lat < 0.8293 || lat > 55.8271) {return true;}return false;}/*** 坐标类*/@Datapublic static class Coordinate {/*** 经度*/private double longitude;/*** 维度*/private double latitude;public Coordinate(double longitude, double latitude) {// 保留6位小数,四舍五入模式//BigDecimal latBigDecimal = new BigDecimal(latitude).setScale(6, RoundingMode.HALF_UP);//BigDecimal lonBigDecimal = new BigDecimal(longitude).setScale(6, RoundingMode.HALF_UP);//this.longitude = lonBigDecimal.doubleValue();//this.latitude = latBigDecimal.doubleValue();this.longitude = longitude;this.latitude = latitude;}}// 定义CGCS2000的坐标系private final static String CGCS2000 = "EPSG:4490";// 定义WGS84的坐标系final static String WGS84 = "EPSG:4326";/*** CGCS2000 转 WGS84** @param lng CGCS2000经度* @param lat CGCS2000纬度* @return*/public static Coordinate cgcs2000ToWgs84(double lng, double lat) {CRSFactory crsFactory = new CRSFactory();// 创建CGCS2000的坐标参考系统CoordinateReferenceSystem sourceCRS = crsFactory.createFromName(CGCS2000);// 创建WGS84的坐标参考系统CoordinateReferenceSystem targetCRS = crsFactory.createFromName(WGS84);// 定义坐标转换器CoordinateTransformFactory ctFactory = new CoordinateTransformFactory();// 创建转换器CoordinateTransform transform = ctFactory.createTransform(sourceCRS, targetCRS);// 执行坐标转换ProjCoordinate srcCoord = new ProjCoordinate(lng, lat);ProjCoordinate targetCoord = new ProjCoordinate();transform.transform(srcCoord, targetCoord);// 4. 输出转换后的正常经纬度坐标return new Coordinate(targetCoord.x, targetCoord.y);}/*** WGS84 转 CGCS2000** @param lng WGS84经度* @param lat WGS84纬度* @return*/public static Coordinate wgs84ToCgcs2000(double lng, double lat) {CRSFactory crsFactory = new CRSFactory();// 定义源和目标投影CoordinateReferenceSystem sourceCRS = crsFactory.createFromName(WGS84);CoordinateReferenceSystem targetCRS = crsFactory.createFromName(CGCS2000);// 定义坐标转换器CoordinateTransformFactory ctFactory = new CoordinateTransformFactory();// 创建转换器CoordinateTransform transform = ctFactory.createTransform(sourceCRS, targetCRS);// 执行坐标转换ProjCoordinate srcCoord = new ProjCoordinate(lng, lat);ProjCoordinate targetCoord = new ProjCoordinate();transform.transform(srcCoord, targetCoord);// 输出转换后的正常经纬度坐标return new Coordinate(targetCoord.x, targetCoord.y);}public static void main(String[] args) {double GPSLon = 108.876152;double GPSLat = 34.226685;CoordinateTransformUtil.Coordinate wgs84ToGcj02 = CoordinateTransformUtil.wgs84ToGcj02(GPSLon, GPSLat);CoordinateTransformUtil.Coordinate wgs84ToBd09 = CoordinateTransformUtil.wgs84ToBd09(GPSLon, GPSLat);log.info("GPS wgs84ToGcj02 : longitude={}, latitude={}", wgs84ToGcj02.longitude, wgs84ToGcj02.latitude);log.info("GPS wgs84ToBd09  : longitude={}, latitude={}", wgs84ToBd09.longitude, wgs84ToBd09.latitude);double aMapLon2 = 108.880753;double aMapLat2 = 34.225075;CoordinateTransformUtil.Coordinate gcj02ToWgs84 = CoordinateTransformUtil.gcj02ToWgs84(aMapLon2, aMapLat2);CoordinateTransformUtil.Coordinate gcj02ToBd09 = CoordinateTransformUtil.gcj02ToBd09(aMapLon2, aMapLat2);log.info("高德 gcj02ToWgs84 : longitude={}, latitude={}", gcj02ToWgs84.longitude, gcj02ToWgs84.latitude);log.info("高德 gcj02ToBd09  : longitude={}, latitude={}", gcj02ToBd09.longitude, gcj02ToBd09.latitude);double baiduLon3 = 108.887314;double baiduLat3 = 34.230897;CoordinateTransformUtil.Coordinate bd09ToWgs84 = CoordinateTransformUtil.bd09ToWgs84(baiduLon3, baiduLat3);CoordinateTransformUtil.Coordinate bd09ToGcj02 = CoordinateTransformUtil.bd09ToGcj02(baiduLon3, baiduLat3);log.info("百度 gcj02ToWgs84 : longitude={}, latitude={}", bd09ToWgs84.longitude, bd09ToWgs84.latitude);log.info("百度 gcj02ToBd09  : longitude={}, latitude={}", bd09ToGcj02.longitude, bd09ToGcj02.latitude);/*** CGCS2000*/double CGCS2000Lon4 = 108.887314;double CGCS2000Lat4 = 34.230897;CoordinateTransformUtil.Coordinate CGCS2000ToWgs84 = CoordinateTransformUtil.cgcs2000ToWgs84(CGCS2000Lon4, CGCS2000Lat4);CoordinateTransformUtil.Coordinate CGCS2000ToBd09 = CoordinateTransformUtil.wgs84ToCgcs2000(CGCS2000Lon4, CGCS2000Lat4);log.info("中国2000 CGCS2000ToWgs84 : longitude={}, latitude={}", CGCS2000ToWgs84.longitude, CGCS2000ToWgs84.latitude);log.info("中国2000 CGCS2000ToBd09  : longitude={}, latitude={}", CGCS2000ToBd09.longitude, CGCS2000ToBd09.latitude);}}

在这里插入图片描述

参考文章:

  • 【Java】WGS84转2000国家大地坐标系(CGCS2000):https://blog.csdn.net/inaUI/article/details/135651870

– 求知若饥,虚心若愚。

这篇关于Java实现经纬度坐标转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1037687

相关文章

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法