坐标转换-使用geotools读取和转换地理空间表的坐标系(sqlserver、postgresql)

前言:

业务上通过GIS软件将空间数据导入到数据库时,因为不同的数据来源和软件设置,可能导入到数据库的空间表坐标系是各种各样的。
如果要把数据库空间表发布到geoserver并且统一坐标系,只是在geoserver单纯的设置坐标系只是改了定义并没有实际执行坐标转换,所以需要在数据库层面统一好坐标系,再发布到geoserver。
在这里插入图片描述

1,开发前准备

1.1,数据准备

要准备测试数据,可以参考 地理空间表的导入。
我这里使用arcgis pro导入sqlserver,如果导入postgresql需要企业数据库才行,也就是需要离线证书,比较麻烦。
我先导入一个4524的投影坐标,测试转换为4490
在这里插入图片描述

1.2,环境准备

坐标转换需要先读取数据库的空间表原坐标系,在根据原坐标系转换为目标坐标系。
使用的转换工具是geotool。
pom引入必要的依赖,geotools版本是24.3

<dependency><groupId>org.geotools</groupId><artifactId>gt-main</artifactId><version>${geotools.version}</version>
</dependency>
<dependency><groupId>org.geotools</groupId><artifactId>gt-jdbc</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools.jdbc</groupId><artifactId>gt-jdbc-sqlserver</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools.jdbc</groupId><artifactId>gt-jdbc-postgis</artifactId><version>${geotools.version}</version></dependency>

2,读取空间表原坐标系

要使用geotool读取空间表的坐标系,需要先使用geotool提供的方法创建DataStore,官网有一个示例代码
https://docs.geotools.org/latest/userguide/library/jdbc/sqlserver.html

java.util.Map params = new java.util.HashMap();
params.put( "dbtype", "sqlserver");   //(巨坑)
params.put( "host", "localhost");
params.put( "port", 4866);
params.put( "user", "geotools");
params.put( "passwd", "geotools");
DataStore dataStore=DataStoreFinder.getDataStore(params);

这是一个坑,官方说明是版本14之后支持Microsoft JDBC driver,dbtype应该就不需要使用jtds前缀了,实际上不加必报错

先写一个测试方法,传入数据库连接信息,表名,数据库类型,返回原表坐标系

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.jdbc.JDBCDataStoreFactory;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;public static int getEpsg(DatabaseConfig databaseConfig, String tableName) {DataStore dataStore = null;try {Map<String, Object> params = new HashMap<>();
//        params.put(JDBCDataStoreFactory.SCHEMA.key, "dbo");if (DatabaseType.SQLSERVER.equals(databaseConfig.getDatabaseType())) {params.put(JDBCDataStoreFactory.DBTYPE.key, "jtds-sqlserver");} else {params.put(JDBCDataStoreFactory.DBTYPE.key, "jtds-postgis");}params.put(JDBCDataStoreFactory.HOST.key, databaseConfig.getHost());params.put(JDBCDataStoreFactory.PORT.key, databaseConfig.getPort());params.put(JDBCDataStoreFactory.DATABASE.key, databaseConfig.getDatabaseName());params.put(JDBCDataStoreFactory.USER.key, databaseConfig.getUsername());params.put(JDBCDataStoreFactory.PASSWD.key, databaseConfig.getPassword());dataStore = DataStoreFinder.getDataStore(params);if (dataStore == null) {System.out.println("Failed to connect to the database.");return -1;}// Get the feature source for the "aa" tableSimpleFeatureSource featureSource = dataStore.getFeatureSource(tableName);// Get the feature type and its CRSSimpleFeatureType featureType = featureSource.getSchema();CoordinateReferenceSystem crs = featureType.getCoordinateReferenceSystem();// Print the CRS detailsif (crs != null) {System.out.println("Spatial Reference System: " + crs.getName());System.out.println("EPSG Code: " + crs.getName().getCode());System.out.println("crs : " + crs.toString());//抽取原表坐标系int result = extractEPSG(crs.toString());System.out.println("Result: " + result);return result;}// Close the data storedataStore.dispose();return 0;} catch (IOException e) {log.error("查询空间表坐标系异常:{}", e.toString());return -1;} finally {if (dataStore != null) {dataStore.dispose();}}}

然后看一下解析出来坐标信息

Spatial Reference System: EPSG:CGCS2000 / 3-degree Gauss-Kruger zone 36
EPSG Code: CGCS2000 / 3-degree Gauss-Kruger zone 36
crs : PROJCS["CGCS2000 / 3-degree Gauss-Kruger zone 36", GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]], PROJECTION["Transverse_Mercator", AUTHORITY["EPSG","9807"]], PARAMETER["central_meridian", 108.0], PARAMETER["latitude_of_origin", 0.0], PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 36500000.0], PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["Northing", NORTH], AXIS["Easting", EAST], AUTHORITY["EPSG","4524"]]

我想要的是之前我们在arcgis pro中看到的投影坐标,位于crs信息的最后一个EPSG内,针对crs信息写一个方法解析出epsg

    public static int extractEPSG(String input) {Pattern pattern = Pattern.compile("AUTHORITY\\[\"EPSG\",\"(\\d+)\"\\]");Matcher matcher = pattern.matcher(input);int lastEPSG = 0;while (matcher.find()) {lastEPSG = Integer.parseInt(matcher.group(1));}return lastEPSG;}

3,执行坐标转换

我这里目标坐标系写死,因为系统需要插入到sqlserver中的都要统一坐标系,所以直接在原表更新了。
如果要保留原表信息可以复制表在副本表更新坐标。
sqlserver与postgresql中空间函数有些差异,需要区分处理。

/*** 地理空间表坐标转换** @param sourceEpsg     原表坐标系* @param config         数据库连接信息* @param tableName      表名  dbo.ROAD* @param geometryColumn 空间字段*/public static void epsgTo4490(int sourceEpsg, DatabaseConfig config, String tableName, String geometryColumn) {String sourceEPSG = "EPSG:" + sourceEpsg;String targetEPSG = "EPSG:4490";ResultSet resultSet = null;try (Connection connection = DatabaseConnection.getConnection(config)) {//拼接sqlString sql;if (config.getDatabaseType().SQLSERVER.equals(config.getDatabaseType())) {sql = "SELECT " + geometryColumn + ".STAsText() as Shape,OBJECTID FROM " + tableName;} else {//ST_AsText(columns)sql = "SELECT ST_AsText(" + geometryColumn + ") as Shape,OBJECTID FROM " + tableName;}// 使用连接执行 SQL 查询操作PreparedStatement statement = connection.prepareStatement(sql);resultSet = statement.executeQuery();// Create MathTransformCRSFactory crsFactory = new CRSFactory();org.osgeo.proj4j.CoordinateReferenceSystem sourceCRS = crsFactory.createFromName(sourceEPSG);org.osgeo.proj4j.CoordinateReferenceSystem targetCRS = crsFactory.createFromName(targetEPSG);CoordinateTransformFactory transformFactory = new CoordinateTransformFactory();CoordinateTransform transform = transformFactory.createTransform(sourceCRS, targetCRS);// Process each row of the result setwhile (resultSet.next()) {String shape = resultSet.getString("Shape");int objectId = resultSet.getInt("OBJECTID");// Convert the string representation of the geometry to a JTS Geometry objectWKTReader reader = new WKTReader();Geometry geometry = reader.read(shape);// Perform the coordinate transformation for each coordinate in the geometryfor (int i = 0; i < geometry.getCoordinates().length; i++) {Coordinate srcCoord = geometry.getCoordinates()[i];ProjCoordinate targetCoord = new ProjCoordinate(srcCoord.getX(), srcCoord.getY());transform.transform(targetCoord, targetCoord); // 将源坐标转换为目标坐标,并保存在 targetCoord 中srcCoord.setX(targetCoord.x);srcCoord.setY(targetCoord.y);}// Convert the transformed geometry back to a stringWKTWriter writer = new WKTWriter();String transformedShape = writer.write(geometry);// Update the original table with the transformed geometry using the primary keyString updateSQL;if (DatabaseType.SQLSERVER.equals(config.getDatabaseType())) {updateSQL = "UPDATE " + tableName + " SET " + geometryColumn + " = ? WHERE OBJECTID = ?";} else {//UPDATE "public"."ROAD" SET Shape = ST_SetSRID(ST_GeomFromText("Shape"), 4490);updateSQL = "UPDATE " + tableName + " SET " + geometryColumn + " = ST_SetSRID(?,4490) WHERE OBJECTID = ?";}statement = connection.prepareStatement(updateSQL);statement.setString(1, transformedShape);statement.setInt(2, objectId);statement.executeUpdate();statement.clearParameters();}if (DatabaseType.SQLSERVER.equals(config.getDatabaseType())) {//修复多边形错误   UPDATE dbo.ROAD SET Shape = Shape.MakeValid()String updateSQL = "UPDATE " + tableName + " SET " + geometryColumn + " = " + geometryColumn + ".MakeValid()";statement = connection.prepareStatement(updateSQL);statement.executeUpdate();//指定坐标系 UPDATE dbo.ROAD SET Shape.STSrid=4490updateSQL = "UPDATE " + tableName + " SET " + geometryColumn + ".STSrid=4490";statement = connection.prepareStatement(updateSQL);statement.executeUpdate();}// Close the resourcesstatement.close();resultSet.close();} catch (SQLException e) {log.error("坐标转换中sql执行异常:{}", e.getMessage());} catch (ParseException e) {log.error("坐标转换中异常:{}", e.getMessage());}}

上述代码只是sqlservcer亲测多种坐标系转换正常,且转换后的表发布到geoserver和arcgis都能正常预览且聚焦位置正确,postgresql还有待测试

4,单元测试

    public static void main(String[] args) throws SQLException {String tableName = "ROAD";//测试sqlserverDatabaseConfig databaseConfig = new DatabaseConfig(DatabaseType.SQLSERVER, "127.0.0.1", 1433, "测试中文数据库", "sa", "xxxx");//测试postgresql//DatabaseConfig databaseConfig = new DatabaseConfig(DatabaseType.POSTGRESQL, "127.0.0.1", 5432, "postgis20", "postgres", "xxxxxxx");int sourceEpsg = TableEpsgUtil.getEpsg(databaseConfig, tableName);System.out.println("原表坐标:" + sourceEpsg);//如果获取到原表坐标并且不是4490,则执行转换if (sourceEpsg > 0 && sourceEpsg != 4490) {epsgTo4490(sourceEpsg, databaseConfig, tableName, "Shape");System.out.println("坐标转换完成");}}

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

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

相关文章

JMeter命令行执行+生成HTML报告

1、为什么用命令行模式 使用GUI方式启动jmeter&#xff0c;运行线程较多的测试时&#xff0c;会造成内存和CPU的大量消耗&#xff0c;导致客户机卡死&#xff1b; 所以一般采用的方式是在GUI模式下调整测试脚本&#xff0c;再用命令行模式执行&#xff1b; 命令行方式支持在…

MyBatis-动态SQL-foreach

目录 标签有以下常用属性&#xff1a; 小结 <froeach> <foreach>标签有以下常用属性&#xff1a; collection&#xff1a;指定要迭代的集合或数组的参数名&#xff08;遍历的对象&#xff09;。item&#xff1a;指定在迭代过程中的每个元素的别名&#xff08;遍历…

OnlyOffice社区版破解最大连接限制部署

onlyoffice社区版部署并且破解最大连接数 docker镜像 docker pull onlyoffice/documentserver:5.3.1.265.4或更高的版本已经解决了此方法的Bug 运行镜像 docker run -d --name onlyoffice --restartalways -p 暴露端口号:80 onlyoffice/documentserver:5.3.1.26进入容器内部…

策略模式——算法的封装与切换

1、简介 1.1、概述 在软件开发中&#xff0c;常常会遇到这种情况&#xff0c;实现某一个功能有多条途径。每一条途径对应一种算法&#xff0c;此时可以使用一种设计模式来实现灵活地选择解决途径&#xff0c;也能够方便地增加新的解决途径。为了适应算法灵活性而产生的设计模…

[机器学习]线性回归模型

线性回归 线性回归&#xff1a;根据数据&#xff0c;确定两种或两种以上变量间相互依赖的定量关系 函数表达式&#xff1a; y f ( x 1 , x 2 . . . x n ) y f(x_1,x_2...x_n) yf(x1​,x2​...xn​) ​ 回归根据变量数分为一元回归[ y f ( x ) yf(x) yf(x)]和多元回归[ y …

【深度学习Week4】MobileNet_ShuffleNet

报错&#xff1a;unsafe legacy renegotiation disabled 解决方案&#xff1a; 尝试了更换cryptography36.0.2版本&#xff0c;以及更换下载链接的方法&#xff0c;都不行&#xff0c;最后采用了手动下载mat文件并上传到colab的方法 高光谱图像分类数据集简介Indian Pines&…

【果树农药喷洒机器人】Part2:机器人变量喷药系统硬件选型

本专栏介绍&#xff1a;免费专栏&#xff0c;持续更新机器人实战项目&#xff0c;欢迎各位订阅关注。 关注我&#xff0c;带你了解更多关于机器人、嵌入式、人工智能等方面的优质文章&#xff01; 文章目录 一、引言二、变量喷药系统总体要求2.1系统功能要求2.2系统技术要求 三…

4.1 Windows终端安全

数据参考&#xff1a;CISP官方 目录 安全安装保护账户安全本地安全策略安全中心系统服务安全其他安全设置软件安全获取 一、安全安装&#xff08;以安装windows系统为例&#xff09; 选择合适的版本 商业版本&#xff1a;家庭版、专业版、专业工作站版、企业版特殊版本&…

pytest-xdist分布式测试原理浅析

目录 pytest-xdist执行流程&#xff1a; pytest-xdist 模块结构&#xff1a; pytest-xdist分布式测试原理&#xff1a; pytest-xdist源码浅读&#xff1a; pytest-xdist执行流程&#xff1a; 解析命令行参数&#xff1a;pytest-xdist 会解析命令行参数&#xff0c;获取用户…

海外媒体发稿:软文写作方法方式?一篇好的软文理应合理规划?

不同种类的软文会有不同的方式&#xff0c;下面小编就来来给大家分析一下&#xff1a; 方法一、要选定文章的突破点&#xff1a; 所说突破点就是这篇文章文章软文理应以什么样的视角、什么样的见解、什么样的语言设计理念、如何文章文章的标题来写。不同种类的传播效果&#…

【JMeter】 使用Synchronizing Timer设置请求集合点,实现绝对并发

目录 布局设置说明 Number of Simulated Users to Group Timeout in milliseconds 使用时需要注意的点 集合点作用域 实际运行 资料获取方法 布局设置说明 参数说明&#xff1a; Number of Simulated Users to Group 每次释放的线程数量。如果设置为0&#xff0c;等同…

以Java的方式将文件上传到阿里云OSS

文章目录 1. 开通对象存储服务2. 创建 AccessKey 密钥3. 通用代码实现 1. 开通对象存储服务 控制台 → 对象存储 OSS → 立即开通 Bucket列表 → 点击创建 Bucket 填写名称、地域&#xff0c;名称创建后不可修改&#xff0c;地域选择最近的&#xff0c;存储类型选择标准存储&…

2023-08-08 LeetCode每日一题(任意子数组和的绝对值的最大值)

2023-08-08每日一题 一、题目编号 1749. 任意子数组和的绝对值的最大值二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 … numsr-1 numsr) 。 请你找出 …

STM32基础入门学习笔记:核心板 电路原理与驱动编程

文章目录&#xff1a; 一&#xff1a;LED灯操作 1.LED灯的点亮和熄灭 延迟闪烁 main.c led.c led.h BitAction枚举 2.LED呼吸灯&#xff08;灯的强弱交替变化&#xff09; main.c delay.c 3.按键控制LED灯 key.h key.c main.c 二&#xff1a;FLASH读写程序(有…

LeNet卷积神经网络-笔记

LeNet卷积神经网络-笔记 手写分析LeNet网三卷积运算和两池化加两全连接层计算分析 修正上图中H,W的计算公式为下面格式 基于paddle飞桨框架构建测试代码 #输出结果为&#xff1a; #[validation] accuracy/loss: 0.9530/0.1516 #这里准确率为95.3% #通过运行结果可以看出&am…

Stable Diffusion - Style Editor 和 Easy Prompt Selector 提示词插件配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132122450 Stable Diffusion 的 Prompt 的功能&#xff0c;可以用文字来描述想要生成的图像&#xff0c;根据输入来创造出逼真的图像。Prompt 支持…

Python 面试必知必会(一):数据结构

《Python Cookbook》的作者David Beazley的课程PPT开源了&#xff0c;目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员&#xff0c;课程主题侧重于流行库和框架中使用的编程技术&#xff0c;主要目的是更好地理解 Python 语言本身&#xff0c;以便阅读他…

【深度学习】采用自动编码器生成新图像

一、说明 你知道什么会很酷吗&#xff1f;如果我们不需要所有这些标记的数据来训练 我们的模型。我的意思是标记和分类数据需要太多的工作。 不幸的是&#xff0c;大多数现有模型从支持向量机到卷积神经网&#xff0c;没有它们&#xff0c;卷积神经网络就无法训练。无监督学习不…

中断子系统--硬件层(GICv3)

目录 综述 硬件层--GICV3 中断类型 中断状态 Distributor组件 中断使能配置 中断触发方式配置 中断优先级配置  中断分组标记 GIC处理中断流程 综述 由上面的block图&#xff0c;我们可知linux kernel的中断子系统分成4个部分&#xff1a; 硬件层&#xff1a;最下层…

AP2400 LED汽车摩灯照明电源驱动 过EMC DC-DC降压恒流IC

产品特点 宽输入电压范围&#xff1a;5V&#xff5e;100V 可设定电流范围&#xff1a;10mA&#xff5e;6000mA 固定工作频率&#xff1a;150KHZ 内置抖频电路&#xff0c;降低对其他设备的 EMI干扰 平均电流模式采样&#xff0c;恒流精度更高 0-100%占空比控制&#xff0…