最近的项目中涉及到空间数据的转换,主要把CGCS200坐标转换为WGS84坐标和BD09坐标。我在网上找了一天多,只找到了WGS84坐标转BD09坐标的工具类,但没有找到CGCS200转换到WGS84坐标的工具类。我实在没有办法,就只有自己写。但我不太熟悉测量学知识,就只有从ArcGIS提供的 ArcGIS Runtime SDK for Java中寻找方法。目前已经完成此工具类,测试效果也还不错。现在写出这篇文章,共享我的代码,希望能帮到有同样问题的同行。
首先阐释下相关的名词概念,以免读者混淆。
从数据库的角度来看,是指在表中以geometry类型存在的数据。
从java语言的角度来看,是指com.esri.arcgisruntime.geometry包下各种类型数据,包括Point、Multipoint、Polyline、Polygon等等.
本文中,xx坐标表示xx坐标系下的空间数据对象,xx坐标系才是表示xx坐标系本身。请注意区别。
CGCS2000就是2000国家大地坐标系,是我国当前最新的国家大地坐标系,英文名称为China Geodetic Coordinate System 2000,缩写为CGCS2000。
该坐标系在ArcGIS中有众多子坐标。如下图所示。
本文选用的是CGCS2000_3_Degree_GK_CM_105E。
WGS84:World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统。通过遍布世界的卫星观测站观测到的坐标建立,其初次WGS84的精度为1-2m,在1994年1月2号,通过10个观测站在GPS测量方法上改正,得到了WGS84(G730),G表示由GPS测量得到,730表示为GPS时间第730个周。1996年,National Imagery and Mapping Agency (NIMA) 为美国国防部 (U.S.Departemt of Defense, DoD)做了一个新的坐标系统。这样实现了新的WGS版本:WGS(G873)。其因为加入了USNO站和北京站的改正,其东部方向加入了31-39cm 的改正。所有的其他坐标都有在1分米之内的修正。第三次精化:WGS84(G1150),于2002年1月20日启用。(此段解释来自百度百科)
BD09坐标系就是百度地图坐标系,它是在标准Web墨卡托的基础上进行GCJ-02加偏之后,再加上百度自身的加偏算法,也就是在Web墨卡托的基础之上进行了两次加偏。坐标系的坐标值为Web墨卡托格式,单位为米。(此段解释来自百度百科)
本文通过Spring data jpa连接数据库,笔者参照 在java程序中如何读写带有Geometry对象的表 这一文章,在实体类中加入 Geometry,或Polygon 类的字段后,项目启动就会报错。 如果有读者参照那篇文章,或者用其他方式,能成功地把数据库中的geometry字段映射到实体类中,并查询出来,请留言给我,我也希望能向您学习一下。
我采用的方式是在数据库用SQL语句查询出geometry字段的String表达形式,再做后续处理。代码片段如下:
@Query( value = "select SHAPE.STAsText() FROM LAND WHERE ID=?", nativeQuery = true)
String getShapeString (String id);
下面这个字符串是本文通过此方法在数据库中查询出来的,此字符串表示一个地块的坐标,逗号分隔出每个坐标点,多个坐标点连成一个面。(此数据是测试数据,由笔者随意编造,没有描述任何真实意义)
POLYGON ((370907.330099999904697 3418398.496099999174576, 370899.395100000314615 3418410.75789999861853, 370909.574500000104318 3418415.690899999812245))
上面的这个字符串表示的是CGCS2000坐标系下的数据。我们写一个工具类将其转换为WGS84和BD09坐标。下面是具体代码。
package com.wja.test.report.utils;
import com.wja.test.report.model.dto.CoordinatesDTO;
import com.wja.test.util.JsonUtil;
import com.esri.arcgisruntime.geometry.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 用于坐标转换的工具类
*
* @author wang.jingan
* @since 2019-07-20 10:39:16
*/
public class CoordTransform {
/**
* 通过String字符串得到三个坐标系的空间数据对象和json字符串
*
* @param shape 转化为String的Geometry数据
* @return 数据集,内含三个坐标系的空间数据对象和json字符串
*/
public static Map<String, Object> shapeStringToJsons(String shape){
Map<String, Object> result = new HashMap<>();
//获取两个坐标系
SpatialReference cgcs2000 = SpatialReference.create(4544);
SpatialReference wgs84 = SpatialReference.create(4326);
//创建一个CGCS2000的点收集器,为创建Polygon对象做准备
PointCollection land = new PointCollection(cgcs2000);
//准备返回的百度坐标点集合
List<CoordinatesDTO> pointsBD09 =new ArrayList<>();
//获取需要的字符串
String shapeSubstring = shape.substring(10,shape.length()-2);
//得到每个点的原始数据
String[] points = shapeSubstring.split(",");
//遍历每个点
for (String pointString:points) {
//去前后的空格
pointString=pointString.trim();
//把经纬度分开成两个数据
String[] xy = pointString.split(" ");
Double x = Double.valueOf(xy[0]);
Double y = Double.valueOf(xy[1]);
//把点添加到点收集器
land.add(x, y);
//得到CGCS2000点数据
Point pointCgcs2000 = new Point(x, y, cgcs2000);
//把CGCS2000点数据转化为WGS84点数据
Point pointwgs84 = (Point)GeometryEngine.project(pointCgcs2000,wgs84);
//获取点数据中的经纬度,X为经度,Y为纬度
double pointwgs84X = pointwgs84.getX();
double pointwgs84Y = pointwgs84.getY();
//把wgs84点坐标转化为bdo9点坐标
double[] bd09 = GPSUtil.gps84_To_bd09(pointwgs84Y, pointwgs84X);
CoordinatesDTO coordinatesDTO = new CoordinatesDTO();
coordinatesDTO.setLat(bd09[0]);
coordinatesDTO.setLon(bd09[1]);
pointsBD09.add(coordinatesDTO);
}
//从点收集器中创建Polygon,得到一个cgcs2000的Polygon
Polygon polygonCgcs2000 = new Polygon(land);
//转换为wgs84的Polygon
Polygon polygonWgs84 = (Polygon)GeometryEngine.project(polygonCgcs2000,wgs84);
//装入返回数据
result.put("cgcs2000",polygonCgcs2000);
result.put("wgs84",polygonWgs84);
result.put("bd09",pointsBD09);
String polygonCgcs2000Json = polygonCgcs2000.toJson();
String polygonWgs84Json = polygonWgs84.toJson();
String pointsBD09Json = JsonUtil.toJson(pointsBD09);
result.put("cgcs2000Json",polygonCgcs2000Json);
result.put("wgs84Json",polygonWgs84Json);
result.put("bd09Json",pointsBD09Json);
return result;
}
}
在ArcGIS中wkid用于表示相关坐标系的代码, 并用SpatialReference.create(int wkid) 进行创建对应的坐标系。但BD09坐标系 在ArcGIS中没有相关的wkid,所以BD09坐标是通过点的集合表示,而WGS84的坐标是通过com.esri.arcgisruntime.geometry 包下的Polygon类的对象表示。
BD09坐标系的点是笔者自己定义的一个类。代码如下:
package com.wja.test.report.model.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class CoordinatesDTO {
private Double lat;
private Double lon;
}
此工具类网上有很多,同时涉及到很多测量学和数学知识,我也不太擅长那些方面,所以就没有自己写,直接用了 各系地图坐标互相转换【JS版和Java版】 这篇文章的代码。
本文的参考文献已经在正文中注明了出处。如果转载本文,也请注明出处和作者。这篇文章是我在CSDN上写的第一篇博客,时间仓促,技术有限,也不太了解CSDN上写博客的技巧,难免有些纰漏,欢迎读者指出其中的不足,也希望大家在博客下的留言区积极讨论。