如何把空间数据从CGCS2000转换到WGS84和BD09 ——JAVA语言实现

1 背景说明

最近的项目中涉及到空间数据的转换,主要是把CGCS200坐标转换为WGS84坐标和BD09坐标。我在网上找了许久,只找到了WGS84坐标转BD09坐标的工具类,但没有找到CGCS200转换到WGS84坐标的工具类。我实在没有办法,就只有自己写。但我没有GIS行业的教育背景,不了解坐标转换的专业知识,不过我断定GIS行业一定有现成的相关方法。通过查阅相关资料,最后在ArcGIS提供的 ArcGIS Runtime API for Java中找到了相关API。我借此实现了个工具类,测试效果也还不错。现在写出这篇文章,共享我的代码,希望能帮到有同样问题的开发者。
为了方便GIS行业外的朋友更流畅地阅读本文,先阐释下相关名词概念。

1.1 空间数据

从数据库的角度来看,是指在表中以geometry类型存在的数据。
从java语言的角度来看,是指com.esri.arcgisruntime.geometry包下各种类的对象,包括Point、Multipoint、Polyline、Polygon等等.

1.2 CGCS2000

CGCS2000:China Geodetic Coordinate System 2000,2000国家大地坐标系,是我国当前最新的国家大地坐标系。该坐标系在ArcGIS中有众多子坐标系。如下图所示。本文选用的是CGCS2000_3_Degree_GK_CM_105E。下方的坐标系详情中有WKID,您需要记录下这个值,后续会用到。
在这里插入图片描述

1.3 WGS84

WGS84:World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统,在全球有广泛的使用。

1.4 BD09

BD09:百度地图坐标系,它是在标准Web墨卡托的基础上进行GCJ-02加偏之后,再加上百度自身的加偏算法。

2 读取数据库中的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))

3 代码实现

3.1 CGCS2000转WGS84

上面的这个字符串表示的是CGCS2000坐标系下的数据。下面的代码实现了将多边形地块坐标转换为WGS84,再从WGS84转换为BD09。

package cn.wja.utils;

import cn.wja.model.BD09Point;
import cn.wja.model.PolygonResult;
import com.esri.arcgisruntime.geometry.*;

import java.util.ArrayList;
import java.util.List;

/**
 * 用于坐标转换的工具类
 *
 * @author wang.jingan
 * @since 2019-07-20 10:39:16
 */
public class CoordinateSystemTransform {
     /**
     * 通过String字符串得到三个坐标系的空间数据对象
     *
     * @param  shape 转化为String的Geometry数据
     * @return  PolygonResult对象,内含三个坐标系的空间数据对象
     */
    public  static PolygonResult shapeStringToPolygons(String shape) {
        //获取两个坐标系
        SpatialReference cgcs2000 = SpatialReference.create(4544);
        SpatialReference wgs84 = SpatialReference.create(4326);
        //创建一个CGCS2000的点收集器,为创建Polygon对象做准备
        PointCollection land = new PointCollection(cgcs2000);
        //准备返回的百度坐标点集合
        List<BD09Point> 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);
            BD09Point coordinatesDTO = new BD09Point();
            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);
        //装入返回数据
        PolygonResult result = new PolygonResult();
        result.setCgcs2000(polygonCgcs2000);
        result.setWgs84(polygonWgs84);
        result.setBd09(pointsBD09);
        return result;
    }
}

3.2 BD09坐标系中点的表示

在ArcGIS中wkid用于表示相关坐标系的代码, 并用SpatialReference.create(int wkid) 进行创建对应的坐标系。但BD09坐标系 在ArcGIS中没有相关的wkid,所以BD09坐标是通过点的集合表示,而WGS84的坐标是通过com.esri.arcgisruntime.geometry 包下的Polygon类的对象表示。
BD09坐标系的点是笔者自己定义的一个类。代码如下:

package cn.wja.model;

public class BD09Point {
    private Double lat;
    private Double lon;

    public Double getLat() {
        return lat;
    }
    public void setLat(Double lat) {
        this.lat = lat;
    }
    public Double getLon() {
        return lon;
    }
    public void setLon(Double lon) {
        this.lon = lon;
    }
    @Override
    public String toString() {
        return "BD09Point{" + "lat=" + lat + ", lon=" + lon + '}';
    }
}

3.3 WGS84转BD09

此工具类网上有很多,同时涉及到很多测量学和数学知识,我也不太擅长那些方面,所以就没有自己写,直接用了 高德,百度,Google地图定位偏移以及坐标系转换 这篇文章的代码,您也可以自行在网上寻找类似工具。

3.4测试结果

如下图所示,测试结果未发现异常。
在这里插入图片描述

4 依赖和环境

这篇文章首次发表是2019年7月,当时我用使用的ArcGIS Runtime SDK for Java版本为100.4.0。

<dependency>
  <groupId>com.esri.arcgisruntime</groupId>
  <artifactId>arcgis-java</artifactId>
  <version>100.4.0</version>
</dependency>

当时是只需要在pom.xml中加入上面的依赖即可,默认配置的maven仓库或者阿里云仓库都可以自动下载jar包,也不需要安装任何ArcGIS环境,就可以使用运行。

但文章发布后大约半年,就陆续有开发者留言反馈说,不能正确加载jar包,报错提示“程序包com.esri.arcgisruntime.geometry不存在”。开始我以为是他们的Maven配置出了问题,我就手动下载100.8.0的jar文件,放在了我的CSDN资源中,供需要的朋友下载。之所以下载100.8.0版本,是因为原来的100.4.0的jar包我也没有刻意保存,也找不到了。但解决完jar包加载问题后,又有不少开发者反馈出现了如下报错:

Caused by: java.lang.RuntimeException: Could not find runtime in any of:
- A directory specified by calling ArcGISRuntimeEnvironment.setInstallDirectory()
- The current directory F:\Code\Intimate\other\gis-demo
- A location specified by the environment variable ARCGISRUNTIMESDKJAVA_100_10_0
- Within the “.arcgis” directory in the user’s home path C:\Users\admin.arcgis

我在自己的电脑上在测试,也重现了这个问题。开始我以为是100.8.0作为更高的版本,有其新的要求。我就花了不少功夫到arcgis的官网中找到并下载到了100.4.0的jar包,但是运行后也会报一样的错。我看这个jar包上传时间是2020年10月30日,估计是新下载的100.4.0,相对于2019年的文件,也做了些更新吧。

其实上述问题的实质就是ArcGIS Runtime API for Java的运行环境没有成功构建。

那么解决办法就只有两个了:

  • 第一个方法是找到老版本的100.4.0.jar。我尝试了很久,都没有找到相关资源,所以就暂时放弃了,如果有读者找到,可以联系我。
  • 第二个方法是使用新版本的jar包,并在此基础上构建ArcGIS Runtime API for Java的运行环境。我进行了一些探索后,成功实现了。
    解决办法请参见我新写的另一篇博客:《如何构建ArcGIS Runtime API for Java的运行环境》(左侧博客标题有超链接,请点击跳转)。
    您也可以直接下载示例代码:ArcGIS Runtime API for Java使用示例:CGCS2000转WGS84和BD09 (左侧资源标题含有超链接,请点击跳转 )。

5 提示

虽然我是通过公开途径下载的 ArcGIS Runtime API for Java 的相关jar包和资源文件,并未曾接收到任何关于收费的提示,但我也未查询到 ArcGIS Runtime API for Java 的供应商Esri 有表明这款软件是可以免费使用的开源软件。因此如需在实际项目开发中使用ArcGIS Runtime API for Java,请先向Esri公司咨询授权的相关事宜,避免不必要的纠纷和损失。
我在撰写本文时就职的公司是购买了ArcGIS 的软件使用权,在使用 ArcGIS Runtime API for Java时,也得到了Esri的技术支持。
如果您希望使用开源软件完成类似功能,建议查询一下geotools。

6 结语

本文的参考文献已经在正文中注明了出处。如果转载本文,也请注明出处和作者。这篇文章是我在CSDN上写的第一篇博客,时间仓促,技术有限,也不太了解CSDN上写博客的技巧,难免有些纰漏,欢迎读者指出其中的不足,也希望大家在博客下的留言区积极讨论。

评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

农民工老王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值