前言

GIS开发中,坐标系统是重中之重,在接到任务时首先要确定的就是坐标系。大多数地图库或者互联网地图默认支持WGS84地理坐标系和Web墨卡托投影坐标系。而在我国要求使用自然资源数据使用2000国家大地坐标(CGCS2000)

1. 背景

经国务院批准,我国自2008年7月1日起,启用2000国家大地坐标系

2000国家大地坐标系与原有国家大地坐标系转换、衔接的过渡期为8至10年。根据原国家测绘地理信息局下发的《关于加快2000国家大地坐标系推广使用的通知》(国测国发﹝2013﹞11号)和原国土资源部、原国家测绘地理信息局联合发文《关于加快使用2000国家大地坐标系的通知》(国土资发﹝2017﹞30号)的文件要求,明确自2018年7月1日起全面使用2000国家大地坐标系

2018年6月底前完成全系统各类国土资源空间数据向2000国家大地坐标系转换,7月1日后涉及空间坐标的报部审查和备案项目,全部采用2000国家大地坐标系,不再接受非2000系上报的项目报件。

2018年12月,自然资源部发布《关于停止提供1954年北京坐标系和1980西安坐标系基础测绘成果的公告》(2018年第55号),决定自2019年1月1日起,全面停止向社会提供1954年北京坐标系和1980西安坐标系基础测绘成果。

基于上述内容要求,在生产实践中,从数据生产到数据成果入库,都需要使用2000国家大地坐标系。

2. 开发环境

本文使用如下开发环境,以供参考。
:::block-1
时间:2025年

GeoTools:v34-SNAPSHOT

IDE:IDEA2025.1.2

JDK:v17

OpenLayers:v9.2.4

Layui:v2.9.14
:::

3. 自定义坐标系统

在项目目录utils下新建一个工具类CrsLab,使用final定义几个常用坐标系常量。坐标系的定义有多种标准,包括ogc标准、esri标准、Proj4js以及PostGIS等。在GeoTools中,采用符合行业规范的OGC标准格式。其中OGC格式又分为OGC WKT和OGC WKT2,代码中分别表示为epsg4522wktepsg4522wkt2,经过测试验证,目前尚未支持OGC WKT2格式。

最后定义一个坐标系解析方法getCRSByCodeNumber以返回目标坐标系统,该方法接收一个数字字符串参数,即EPSG编码,如WGS84编码为4326、web墨卡托编码为3857(又900913或者3785)、CGCS2000编码为4490。将坐标系常量传递给parseWKT解析为目标坐标系。

package org.geotools.utils;import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.referencing.CRS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class CrsLab {private static final Logger log = LoggerFactory.getLogger(CrsLab.class);// ogc wkt formatfinal static String epsg4490 = "GEOGCS["China Geodetic Coordinate System 2000",n" +"    DATUM["China_2000",n" +"        SPHEROID["CGCS2000",6378137,298.257222101,n" +"            AUTHORITY["EPSG","1024"]],n" +"        AUTHORITY["EPSG","1043"]],n" +"    PRIMEM["Greenwich",0,n" +"        AUTHORITY["EPSG","8901"]],n" +"    UNIT["degree",0.0174532925199433,n" +"        AUTHORITY["EPSG","9122"]],n" +"    AUTHORITY["EPSG","4490"]]";// ogc wkt formatfinal static String epsg4522wkt = "PROJCS["CGCS2000 / 3-degree Gauss-Kruger zone 34",n" +"    GEOGCS["China Geodetic Coordinate System 2000",n" +"        DATUM["China_2000",n" +"            SPHEROID["CGCS2000",6378137,298.257222101,n" +"                AUTHORITY["EPSG","1024"]],n" +"            AUTHORITY["EPSG","1043"]],n" +"        PRIMEM["Greenwich",0,n" +"            AUTHORITY["EPSG","8901"]],n" +"        UNIT["degree",0.0174532925199433,n" +"            AUTHORITY["EPSG","9122"]],n" +"        AUTHORITY["EPSG","4490"]],n" +"    PROJECTION["Transverse_Mercator"],n" +"    PARAMETER["latitude_of_origin",0],n" +"    PARAMETER["central_meridian",102],n" +"    PARAMETER["scale_factor",1],n" +"    PARAMETER["false_easting",34500000],n" +"    PARAMETER["false_northing",0],n" +"    UNIT["metre",1,n" +"        AUTHORITY["EPSG","9001"]],n" +"    AUTHORITY["EPSG","4522"]]";final static String epsg4522wkt2 = "PROJCRS["CGCS2000 / 3-degree Gauss-Kruger zone 34",BASEGEOGCRS["China Geodetic Coordinate System 2000",DATUM["China 2000",ELLIPSOID["CGCS2000",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4490]],CONVERSION["3-degree Gauss-Kruger zone 34",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",102,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",34500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (X)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (Y)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping (large scale)."],AREA["China - between 100°30'E and 103°30'E."],BBOX[21.13,100.5,42.69,103.5]],ID["EPSG",4522]]";// esri wkt formatfinal static String epsg4522esriwkt =  "PROJCS["CGCS2000_3_Degree_GK_Zone_34",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",34500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",102.0],PARAMETER["Scale_Factor",1.0],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]";final static String epsg3857 = "PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]";public static CoordinateReferenceSystem getCRSByCodeNumber(String epsgCode) throws Exception {CoordinateReferenceSystem crs = null;if(epsgCode.equals("4490")){crs = CRS.parseWKT(epsg4490);}else if(epsgCode.equals("4522")){crs = CRS.parseWKT(epsg4522esriwkt);}else if(epsgCode.equals("3857")){crs = CRS.parseWKT(epsg3857);}System.out.println("坐标系统:"+CRS.toSRS(crs));// 坐标系统:CGCS2000_3_Degree_GK_Zone_34return crs;}
}

编写完成以上代码已经可以根据系统要求自定义坐标系了。

以下介绍一些拓展信息。GeoTools中有一个接口CRSAuthorityFactory提供了getSupportedCodes(String authority)方法,用于获取指定权威机构(authority)支持的所有坐标参考系统(CRS)的代码。这里的authority参数指的是定义CRS的权威机构的名称,如“EPSG”。

常见的权威机构包括:

可以通过以下方法获取各权威机构坐标定义数量及编号使用情况。

// 获取 CRS 权威工厂
CRSAuthorityFactory authorityFactory = CRS.getAuthorityFactory(true);// 指定权威机构并获取支持的代码
String authority = "EPSG"; // 这里可以替换为其他权威机构
Set<String> authorityCodes = authorityFactory.getAuthorityCodes(CoordinateReferenceSystem.class);
System.out.println("authorityCodes:"+authorityCodes);Set<String> supportedCodes = CRS.getSupportedCodes(authority);
// 输出默认支持的坐标系
System.out.println("默认支持的EPSG坐标系数量:"+supportedCodes.size());
System.out.println("默认支持的EPSG坐标列表:"+supportedCodes);

以EPSG权威机构为例,输出信息如下,首次调用输出情况可能会稍慢,数据量还是有点多。
:::block-1
authorityCodes:[AUTO:42001, AUTO:42002, AUTO:42003, AUTO:42004, AUTO:97001,······]

默认支持的EPSG坐标系数量:7921

默认支持的EPSG坐标:[WGS84(DD), 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,······]
:::
检查目标坐标系是否存在,如CGCS2000_3_Degree_GK_Zone_34(4522)

// 检查指定坐标是否被支持
String epsgcode = "4522"; // CGCS2000_3_Degree_GK_Zone_34
if (supportedCodes.contains(epsgcode)) {System.out.println(authority + "权威机构支持 EPSG 编码为 " + epsgcode + " 的坐标系");
}else{System.out.println(authority + " 权威机构暂不支持 EPSG 编码为 " + epsgcode + " 的坐标系");
}

在EPSG权威机构中进行查找,以上代码将会输出EPSG权威机构支持 EPSG 编码为 4522 的坐标系

将上述变量epsgcode的值改为4490,通过输出结果EPSG权威机构支持 EPSG 编码为 4490 的坐标系可以看到该坐标系也是受到支持的。

以下代码可以用于获取常用权威机构支持的坐标信息。

// 获取常用权威机构支持的代码
Set<String> epsgCodes = getSupportedCodes("EPSG");  // 标准地理坐标系
Set<String> esriCodes = getSupportedCodes("ESRI");  // Esri特定坐标系
Set<String> iauCodes = getSupportedCodes("IAU");    // 天体坐标系
Set<String> autoCodes = getSupportedCodes("AUTO");  // 自动投影

我们知道在GeoTools中可以直接使用CRS.decode("EPSG:4326")此种方式定义目标坐标系,这种方式使用起来简单明了,那么为什么不直接使用此种方式,而是要自定义坐标系统呢?

下面来看一下我使用两种方式将Shp数据经过坐标转换后导入PostGIS空间数据库的输出结果。坐标系为WGS84地理坐标系,元数据定义如下:
:::block-1
GEOGCS["Geographic Coordinate System",DATUM["D_WGS84",SPHEROID["WGS84",6378137,298.257223560493]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
:::
(1)CrsLab.getCRSByCodeNumber("4522")

(2)CRS.decode("EPSG:4522")

从以上输出结果来看,使用两种方式转换的数据,坐标值存在一定的差异。第二种方式x和y值的顺序相反,与工作实际使用不太相符,要怎么解决这种情况呢?需要在坐标定义时多加一个布尔值参数,CoordinateReferenceSystem targetCrs = CRS.decode("EPSG:4522",Boolean.TRUE),并将参数值设置为TRUE即可。

在GIS软件ArcMap中打开经过投影转换后的Shp数据,其坐标显示结果与使用GeoTools转换入库后的结果保持一致。

4. 坐标转换

在进行坐标转换前,需要获取源数据坐标系信息,然后使用以下任意一种方式定义目标坐标系,使用第二种方式的时候需要注意x和y坐标的顺序,将第二个参数设置为TRUE可使得数据与日常使用习惯保持一致。

坐标转换流程大致如下

// 源数据坐标系
CoordinateReferenceSystem originalCrs = dataSchema.getCoordinateReferenceSystem();
// 方式一:自定义坐标系
// CoordinateReferenceSystem targetCrs =  CrsLab.getCRSByCodeNumber("4522");// 方式二:使用epsg code 定义坐标系,强制使用经度在前、纬度在后的顺序
CoordinateReferenceSystem targetCrs =  CRS.decode("EPSG:4522",Boolean.TRUE);

使用findMathTransform进行坐标转换。第一个参数为原始坐标系,第二个参数为目标坐标系,第三个参数表示即使没有可用于基准偏移的信息,也应该创建数学变换。默认值为false。

// 定义坐标转换
MathTransform transform = CRS.findMathTransform(originCrs,targetCrs,true);

获取到源几何对象之后,调用JTS.transform方法对其进行坐标转换,最后将转换完成的几何对象写入几何字段。

// 源几何对象
Geometry sourceGeometry = (Geometry) feature.getDefaultGeometry();
// 转换后的几何对象
Geometry crsTransformGeometry = JTS.transform(sourceGeometry, transform);
// 设置参考系数字
crsTransformGeometry.setSRID(4522);
// 写入转换后的几何对象
targetFeature.setAttribute("geom", crsTransformGeometry);

5. 查看数据库坐标系

将成果数据导入PostGIS空间数据库之后,怎么知道使用了正确的坐标系了呢?在PostGIS中,有以下几种方式可以查看数据坐标系。

(1)使用ST_SRID函数

SELECT ST_SRID(geom) AS srid FROM province LIMIT 3;

该方法会返回表中第一个几何列的空间参考ID(SRID)。

(2)查询 geometry_columns视图
:::block-1
SELECT f_table_name, f_geometry_column, srid FROM geometry_columns WHERE f_table_name = ‘你的表名’; // 如 province
:::
该方式也会返回几何列的空间参考ID(SRID)。

6. 坐标拓展

https://epsg.io网站查询出的坐标系信息有多种格式,包括OGC WKT、OGC WKT2以及ESRI WKT等。正常情况下,可以采用OGC WKT、ESRI WKT两种方式定义坐标系。

(1)OGC WKT格式

(2)OGC WKT2格式

(3)ESRI WKT格式

使用时可以直接选中复制坐标信息,也可以点击【Copy Text】按钮或者【Copy Formatted】按钮进行复制。

7. 参考资料

  • EPSG 坐标系网站:https://epsg.io

  • GeoTools API:https://docs.geotools.org/latest/javadocs/org/geotools/api/referencing/crs/CoordinateReferenceSystem.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92123.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92123.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92123.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[特殊字符] Java反射从入门到飞升:手撕类结构,动态解析一切![特殊字符]

【&#x1f50d;震撼揭秘】 你是否曾想窥探Java类的内部结构&#xff1f;&#x1f914; 是否好奇Spring框架如何实现"万物皆可注入"&#xff1f;✨ 本文将带你从反射小白晋升为反射高手&#xff0c;用一行代码透视任意类的构造方法、成员变量和私有方法&#xff01;&…

CMake与catkin_make的find_package()命令使用说明

在 CMake 中&#xff0c;find_package() 是一个核心函数&#xff0c;用于查找并加载外部依赖库的配置。它的主要作用是定位头文件、库文件&#xff0c;并设置相关变量&#xff0c;以便后续编译和链接。以下是详细解析&#xff1a; 1. 基本语法 find_package(<PackageName&g…

Spring--BeanFactoryPostProcessor的用法

原文网址&#xff1a;Spring--BeanFactoryPostProcessor的用法_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Spring的BeanFactoryPostProcessor的用法。 BeanPostProcessor和BeanFactoryPostProcessor的区别 项BeanPostProcessorBeanFactoryPostProcessor处理的对象处理…

了解类加载器吗?类加载器的类型有哪些?

一、什么是类加载器&#xff08;ClassLoader&#xff09; 类加载器是 Java 虚拟机中的一部分&#xff0c;负责将 .class 文件加载到 JVM 内存中&#xff0c;生成对应的 Class 对象。 Java 程序中所有的类在使用前都必须通过类加载器加载进 JVM&#xff0c;才能被执行。二、类加…

PHP面向对象高级特性:魔术方法、对象迭代器与设计模式应用

引言 在前一篇文章中,我们探讨了PHP的Traits、匿名类和对象比较机制。本文将深入PHP面向对象编程的更多高级特性,包括魔术方法、对象迭代器以及常用设计模式的实际应用,这些特性能够帮助开发者构建更加灵活和强大的面向对象系统。 魔术方法深度解析 魔术方法是PHP中一组以…

【Java基础】一个月教你轻松掌握Java——第三篇Git

一、Java概述&#xff08;之前的文章&#xff09;二、版本控制工具Git其实这个与Java基础关系不大&#xff0c;但是这个工具还是很重要的&#xff0c;不管是团队之间打比赛还是就业都应该学会它&#xff0c;秉持着学的早一些&#xff0c;用的时间长一点&#xff0c;会更熟练。&…

【C# in .NET】16. 探秘类成员-索引器:通过索引访问对象

探秘类成员-索引器:通过索引访问对象 在 C# 中,索引器(Indexer)是一种独特的类成员,它允许类或结构的实例像数组一样被索引访问,为数据访问提供了极大的灵活性。本文将从基础概念出发,深入.NET 框架底层,剖析索引器的实现机制,并通过实战案例展示其强大的应用价值。 …

idea出现:java: Target level ‘1.7‘ is incompatible with source level ‘1.8‘.解决办法

在文件->设置->java编译器&#xff0c;把这里版本对应上。这里用的是8版本

ssms(SQL 查询编辑器) 添加快捷键 Ctrl+D(功能等于Ctrl+C + Ctrl+V),一步到位

1,打开ssms 工具&#xff0c;打开对应添加快捷键得地方2&#xff0c;分配 快捷键3&#xff0c;看效果

数学建模--层次分析法

层次分析法&#xff08;AHP&#xff09;笔记 一、核心概念 &#xff08;一&#xff09;问题本质 面对多方案、多准则决策&#xff0c;将复杂问题分层拆解&#xff0c;通过定性与定量结合&#xff0c;确定各因素权重&#xff0c;选出最优方案&#xff0c;比如选“微博之星”时综…

人工智能教研室暑期培训flask全栈开发培训

人工智能教研室暑期培训flask全栈开发培训第一天&#xff1a;Flask 基础入门与环境搭建实践项目&#xff1a;搭建个人博客首页&#xff0c;包含文章列表与详情页上午&#xff1a;环境搭建与 Flask 基础1. 安装 Python 与虚拟环境配置2. Flask 框架简介与第一个 "Hello Wor…

MySQL(141)如何处理重复数据问题?

处理重复数据问题是数据管理中的一个常见挑战。重复数据会影响数据库的性能、占用资源&#xff0c;并且可能导致数据分析结果的偏差。以下是处理重复数据问题的详细步骤以及结合代码的示例。 一、识别重复数据 首先&#xff0c;需要识别数据库中的重复数据。可以使用 SQL 查询来…

MySQL 核心知识点梳理(3)

目录 SQL优化 23什么是慢SQL 如何优化呢? 如何利于覆盖索引 如何使用联合索引 如何进行分页优化 Join代替子查询 为什么要小表驱动大表? 为什么避免join太多的表? 如何进行排序优化 什么是filesort 全字段排序和rowid排序 条件下推 索引 索引为什么能提高MyS…

关于注册登录功能制作的步骤(文件IO存储+LVGL弹窗提示)

按你的需求&#xff08;文件IO存储LVGL弹窗提示&#xff09;&#xff0c;工程需创建以下文件&#xff0c;代码按功能模块化存放&#xff0c;清晰明了&#xff1a;一、需要创建的文件清单 文件名 作用 存放内容 main.c 程序入口 主函数、硬件初始化、LVGL初始化、启动界面 ui.…

自媒体端后台设计指南:从注册认证到内容管理的全流程搭建

自媒体端后台设计指南&#xff1a;从注册认证到内容管理的全流程搭建自媒体端后台是专业创作者管理内容、粉丝和数据的核心阵地&#xff0c;其设计直接影响创作效率和平台运营质量。一个功能清晰、操作便捷的后台系统&#xff0c;能让创作者专注于内容生产&#xff0c;而非被复…

uniapp扫描二维码反色处理

在开发扫描二维码过程中&#xff0c;发现白底黑码可以直接用uni.scanCode扫描出来&#xff0c;但是黑底白码就扫不出来&#xff0c;于是就试试反色后的二维码能不能扫描出来&#xff0c;没想到真的可以&#xff0c;下面附上完整代码&#xff1a; <u-icon name"scan&quo…

C语言定义fixed_t什么意思

在 C 语言中&#xff0c;fixed_t 通常是一个自定义的类型别名&#xff08;typedef&#xff09;&#xff0c;用于表示固定点数&#xff08;Fixed-Point Number&#xff09;&#xff0c;而非 C 语言标准库中的原生类型。它主要用于需要高效实数运算但无法使用浮点数的场景&#x…

音频3A处理简介之ANS(自动噪声抑制)

我们常用的手机、消费类摄像头等产品的麦克风所采集的原始声音信号中往往包含了比较多的背景噪音&#xff0c;不仅影响用户录音和回放的使用体验&#xff0c;而且这些噪声数据还会降低音频编码的压缩效率&#xff0c;因此有必要对音频底噪进行抑制处理&#xff0c;这就是ANS&am…

Python 使用期物处理并发(使用concurrent.futures模块启动 进程)

使用concurrent.futures模块启动进程 concurrent.futures 模块的文档 &#xff08;https://docs.python.org/3/library/concurrent.futures.html&#xff09;副标题 是“Launching parallel tasks”&#xff08;执行并行任务&#xff09;。这个模块实现的是真正 的并行计算&…

【系统全面】Linux内核原理——基础知识介绍

理解内核&#xff1a;内核原理 计算机系统的软件分层 不同于单片机中使用代码直接与硬件交互&#xff0c;对于这种方式的缺点深有&#xff1a; &#xff08;1&#xff09;复杂度高&#xff0c;调用难度高&#xff0c;需要深入理解硬件的工作原理和细节。 &#xff08;2&#xf…