在开发 GIS 相关系统的时候,我们常常遇见坐标转换的问题。
这里先大致介绍下坐标系的原理。
我们常用到的坐标系一般分为两大类
地理坐标系就是把地球当成一个球体来看,以球心为参照点,通过经纬度来定位某个坐标点。
投影坐标系是指把地球这个球体垂直投影在一个平面。
图片引用自 数据规划那点事儿
投影坐标系
由于我们地球并非一个完美球体,而是凹凸不平的球体。面对地球表面的复杂地理环境,各地所使用的坐标系也就各不相同。
凹凸不平的地球
我国常见的地理坐标系有:
地理坐标系进行水平面投影后,即可得到投影坐标系,常用投影坐标系:
这里还得介绍下 WKID,由于各地使用了不同的坐标系、不同的投影方式、不同的投影分带,我们为众多的坐标系统打上一个唯一编号,也就是 WKID。
WKID 是一个ID,它的内容被称为WKT(Well-known Text)
,WKT 的二进制表现方式叫做WKB(well-known binary)
。WKT 包含内容如下:
举例:
["UTM Zone 19, Northern Hemisphere",(总体投影坐标带名字)
GEOGCS["Geographic Coordinate System",(地理坐标系统名)
DATUM["NAD83",(基准面)
SPHEROID["GRS 1980",6378137,298.2572220960423]椭球体,半径和反偏率
],
PRIMEM["Greenwich",0],(中央经线,子午线)
UNIT["degree",0.0174532925199433](转换因子 ""degree",0.0174532925199433" ""degree",0.0174532925199433")
],
PROJECTION["Transverse_Mercator"], (投影方式)
PARAMETER["latitude_of_origin",0], (起始经度)
PARAMETER["central_meridian",-69], (中央经线)
PARAMETER["scale_factor",0.9996], (缩放比率)
PARAMETER["false_easting",500000], (坐标整体向东偏移)
PARAMETER["false_northing",0], (坐标向北偏移)
UNIT["Meter",1] (单位米)
]
在开发 GIS 相关系统的时候,我们常常遇见坐标转换的问题。
比较常见的转换,例如WGS-84
转百度BD09坐标系
、转大地2000坐标系
等网上都有很多实现,国内的地图服务商(如百度、腾讯等)也有提供转换接口可供使用,但当你遇到一些比较特别的坐标系的时候,就需要自己来转换了。
经调研,有以下几种实现方式:
手动实现:通过三参数七参数来计算出不同坐标系的转换结果,由于计算过程复杂,参数一般也不公开,需要向政府申请获取,总体来说比较麻烦。
GDAL:GDAL 是栅格和矢量地理空间数据格式的翻译库,隶属于OSGeo(开源地理空间基金会)
下的开源产品,它提供了任意栅格/矢量文件转换与处理。(如果有需要进行栅格/矢量文件转换与处理的话,可以选择 GDAL-ORG)
GeoTools:也隶属于OSGeo(开源地理空间基金会)
下的开源产品,为地理空间数据提供工具。(如果单纯转坐标系,可以选择,GeoTools 只需引入包即可进行转换,很方便)
ArcGIS:由ESRI
出品的一个地理信息系统系列软件。
考虑到后续可能要转 GIS 地图,所以这里选了 ArcGIS 的方式来转换坐标系。
java8 最高可使用100.4.0
版本,如果是 java11 可以自己填最新的 ArcGIS 版本号。
这里先放个调用 ArcGIS 的示例。
SpatialReference 空间参考坐标系A = SpatialReference.create( wkidA );
SpatialReference 空间参考坐标系B = SpatialReference.create( wkidB );
Point A点= new Point(x, y, 空间参考坐标系A);
Point B点= (Point) GeometryEngine.project(A点,空间参考坐标系B);
如果是某地区的独立坐标,可能没有 wkid,这时就得自己编写 WKT 文本(相当于自定义一个 wkid)。
这里是去官方拷了下最接近我的需求的 CGCS2000_3_Degree_GK_CM_114E
的 WKT 文本,然后稍微修改了下变成了符合我需要的坐标系:
// 自定义一个空间参考坐标系
String xx2000WktMercator = "PROJCS[\"CGCS2000_3_Degree_GK_CM_114E\"" +
",GEOGCS[\"GCS_China_Geodetic_Coordinate_System_2000\"" +
",DATUM[\"D_China_2000\"" +
",SPHEROID[\"CGCS2000\",6378137.0,298.257222101]]" +
",PRIMEM[\"Greenwich\",0.0]" +
",UNIT[\"Degree\",0.0174532925199433]]" +
",PROJECTION[\"Gauss_Kruger\"]" +
",PARAMETER[\"False_Easting\",500000.0]" +
",PARAMETER[\"False_Northing\",0.0]" +
",PARAMETER[\"Central_Meridian\",114.0]" +
",PARAMETER[\"Scale_Factor\",1.0]" +
",PARAMETER[\"Latitude_Of_Origin\",0.0]" +
",UNIT[\"Meter\",1.0]]";
// 创建自定义独立坐标系
SpatialReference xx2000sp = SpatialReference.create(xx2000WktMercator);
// 创建 WGS84 坐标系
SpatialReference wgs84sp = SpatialReference.create(4326);
定义了需要的坐标系后就可以实现从一个独立坐标系转换为 WGS84 的功能,然后再从 WGS84 转成其他的坐标系,如百度的 BD09 等:
// 自定义独立坐标系的点数据
Point pointXx2000 = new Point(lon, lat, xx2000sp);
// 把独立坐标系的点数据转化为 WGS84 点数据
Point pointWgs84 = (Point) GeometryEngine.project(pointXx2000, wgs84sp);
// 获取点数据中的经纬度, X为经度lon,Y为纬度lat
double pointWgs84X = pointWgs84.getX();
double pointWgs84Y = pointWgs84.getY();
ArcGIS 不像 GeoTools 那么方便(直接导包即可使用),所以需要配置一下。
第一步:设置私服地址:
由于 ArcGIS 的 maven 库是私服的,需要自己设置私服地址。
maven设置:settings.xml:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!arcgis,!arcgis-plugin</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
重点在<mirrorOf>*,!arcgis,!arcgis-plugin</mirrorOf>
,这里我 maven 之前设置过私服,所以要排除掉 ArcGIS 的私服下载,这样的话 maven 会使用项目POM.xml
中的自定义配置:
<!--自定义的私服:包括阿里云私服以及arcgis地图工具的私服 -->
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>arcgis</id>
<url>https://esri.bintray.com/arcgis</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>arcgis-plugin</id>
<url>https://esri.bintray.com/arcgis</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<!--ArcGIS Runtime SDK jar dependency java8最高使用100.4.0版本,如果是java11可以自己填最新版本号-->
<dependency>
<groupId>com.esri.arcgisruntime</groupId>
<artifactId>arcgis-java</artifactId>
<version>100.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--ArcGIS Java Maven Plugin-->
<plugin>
<groupId>com.esri.arcgisruntime</groupId>
<artifactId>arcgis-java-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<!--specify the SDK version-->
<version>100.4.0</version>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.mycompany.app.App</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>io.takari</groupId>
<artifactId>maven</artifactId>
<version>0.7.4</version>
</plugin>
</plugins>
</build>
plugin 填写后可以直接执行 maven 命令下载 ArcGIS 依赖库
Mavendependency:unpack
或者在 IDEA 里双击执行:
等同 Mavendependency:unpack
以上操作会自动将本地库下载解压到本机的$USER_HOME/.arcgis
文件夹中:
$USER_HOME/.arcgis
也可以直接手动把db
文件夹下的.arcgis
解压到上面的文件夹中。
API 将自动在此目录中查找以查找本机库(也可以手动指定,如设置环境变量或放至应用根目录,请查阅官方sdk指南)。
如果未正确配置本机库,你将看到类似于以下内容的异常:
Caused by: java.lang.RuntimeException: Could not find runtime in any of:
- A directory specified by calling ArcGISRuntimeEnvironment.setInstallDirectory()
- The current directory C:\Users\johndoe\my-project-directory
- A location specified by the environment variable ARCGISRUNTIMESDKJAVA_100_4_0
- Within the ".arcgis" directory in the user's home path C:\Users\johndoe\.arcgis
手动设置本地库的方法:
设置环境变量:ARCGISRUNTIMESDKJAVA_10******
指向arcgis的根目录(环境变量名需要使用当前版本的变量名,而 arcgis 本地依赖库目录无须版本号):
配置环境变量 ARCGISRUNTIMESDKJAVA_100_4_0 指向自定义文件夹 D:testArcGIS
或者直接把本地依赖库放到应用根目录里:
应用根目录
自己实际用的工具类示例:
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReference;
/**
* 地图坐标转换工具类
*/
public class MapCoordTransUtils {
/**
* fs2000 转 wgs84
*/
private static CoordinatesDTO fs2000ToWgs84(Double lat, Double lon) {
// 自定义一个空间参考坐标系:佛山2000独立系
// 通过zf公开文件得知的值:中央子午线113度和700KM偏移
String fs2000WktMercator = "PROJCS[\"CGCS2000_3_Degree_GK_CM_113E\"" +
",GEOGCS[\"GCS_China_Geodetic_Coordinate_System_2000\"" +
",DATUM[\"D_China_2000\"" +
",SPHEROID[\"CGCS2000\",6378137.0,298.257222101]]" +
",PRIMEM[\"Greenwich\",0.0]" +
",UNIT[\"Degree\",0.0174532925199433]]" +
",PROJECTION[\"Gauss_Kruger\"]" +
",PARAMETER[\"False_Easting\",700000.0]" +
",PARAMETER[\"False_Northing\",0.0]" +
",PARAMETER[\"Central_Meridian\",113.0]" +
",PARAMETER[\"Scale_Factor\",1.0]" +
",PARAMETER[\"Latitude_Of_Origin\",0.0]" +
",UNIT[\"Meter\",1.0]]";
SpatialReference fs2000sp = SpatialReference.create(fs2000WktMercator);
SpatialReference wgs84sp = SpatialReference.create(4326);
// FS2000点数据
Point pointFs2000 = new Point(lon, lat, fs2000sp);
// 把FS2000点数据转化为WGS84点数据
Point pointWgs84 = (Point) GeometryEngine.project(pointFs2000, wgs84sp);
// 获取点数据中的经纬度, X为经度lon,Y为纬度lat
double pointWgs84X = pointWgs84.getX();
double pointWgs84Y = pointWgs84.getY();
return new CoordinatesDTO(pointWgs84Y, pointWgs84X);
}
/**
* 佛山2000转百度09
*
* @param lat =y
* @param lon =x
* @return
*/
public static CoordinatesDTO fs2000ToBD09(Double lat, Double lon) {
// 先转wgs84
CoordinatesDTO wgs84 = fs2000ToWgs84(lat, lon);
// 把wgs84点坐标转化为bdo9点坐标
// 这里是直接使用别人写好的工具类:
// https://blog.csdn.net/a13570320979/article/details/51366355
// 而且改名为:MapCoordConversionUtils
double[] gps84ToBd09 = MapCoordConversionUtils.gps84_To_bd09(wgs84.getLat(), wgs84.getLon());
return new CoordinatesDTO(gps84ToBd09[0], gps84ToBd09[1]);
}
}
自己 build 用的 dockerfile
# 这里填写中间件版本,不填版本号默认最新版
FROM tomcat:9.0.21-jdk8
# 使用root用户
USER root
# 进入目录
WORKDIR /usr/local/
# 创建项目文件夹,并将外部文件夹内容添加进去
RUN mkdir project
ADD ./project ./project
RUN cd project
# 配置环境变量
ENV ARCGISRUNTIMESDKJAVA_100_4_0 /usr/local/project
# 国内镜像
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
# 安装环境 arcgis 需要 [libgl1-mesa-glx]
RUN apt-get clean && apt-get update && apt install -y nano vim libgl1-mesa-glx
#开放端口
EXPOSE 8080
#启动命令
CMD ["java","-jar","/usr/local/project/springboot.jar","--spring.profiles.active=docker"]
libgl1-mesa-glx
依赖 ↑ArcGIS Runtime SDK 官方文档[1]
WKID 官方文档[2]
GIS坐标系转换指南-叫我三三就好[3]
geotools 官网[4]
gdal 官网[5]
gdal 坐标参考系和坐标转换教程[6]
[1]
ArcGIS Runtime SDK 官方文档: https://developers.arcgis.com/java/get-started/
[2]
Projected Coordinate Systems(WKID 官方文档): https://developers.arcgis.com/javascript/3/jshelp/pcs.html
[3]
这可能是最简单的GIS坐标系转换指南了-叫我三三就好: https://zhuanlan.zhihu.com/p/122217326
[4]
geotools 官网: https://www.geotools.org/
[5]
gdal官网: https://gdal.org/
[6]
gdal 官网的坐标参考系和坐标转换教程: https://gdal.org/tutorials/osr_api_tut.html
今天的分享就先到这,本文主要讲了如何使用 ArcGIS 来转换坐标系数据。
下一篇分享如何使用 GDAL 实现任意栅格/矢量文件转换。