当前位置: 首页 > news >正文

坐标转换-使用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("坐标转换完成");}}

相关文章:

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

前言&#xff1a; 业务上通过GIS软件将空间数据导入到数据库时&#xff0c;因为不同的数据来源和软件设置&#xff0c;可能导入到数据库的空间表坐标系是各种各样的。 如果要把数据库空间表发布到geoserver并且统一坐标系&#xff0c;只是在geoserver单纯的设置坐标系只是改了…...

JavaScript的主要应用场景有哪些?请描述一下JavaScript的基本数据类型和引用数据类型分别是哪些?

1、JavaScript的主要应用场景有哪些&#xff1f; JavaScript是一种广泛使用的编程语言&#xff0c;它主要用于Web开发、移动应用开发、游戏开发、物联网设备开发等场景。以下是JavaScript的主要应用场景&#xff1a; Web开发&#xff1a;JavaScript是Web开发中最常用的编程语…...

webpack性能优化

文章目录 1. 性能优化-分包2. 动态导入3. 自定义分包4. Prefetch和Preload5. CDN加载配置6. CSS的提取7. terser压缩7.1 Terser在webpack中配置7.2 css压缩 8. Tree Shaking 消除未使用的代码8.1 usedExports 配置8.2 sideEffects配置8.3 CSS实现Tree Shaking 9. Scope Hoistin…...

保存和读取带有透明通道的视频

保存带有透明通道的视频&#xff1a; import osimport imageio from rembg import remove as removBg,new_session from PIL import Image import numpy as np import cv2 from tqdm import tqdmclass cls_rembg():def __init__(self,model_pth):self.session new_session(mo…...

bilibili的评论ip属地显示未知

现象 出于某些原因&#xff0c;我们在日常使用中的大部分平台都开启了IP地址显示&#xff0c;一般会显示当事人所在的地址&#xff0c;这其中就有一些奇怪的地址&#xff0c;&#xff08;在此不谈魔法&#xff09;就比如我最近在刷B站的时候&#xff0c;就在评论区发现了一些显…...

[BabysqliV3.0]phar反序列化

文章目录 [BabysqliV3.0]phar反序列化 [BabysqliV3.0]phar反序列化 开始以为是sql注入 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ST1jvadM-1691302941344)(https://raw.githubusercontent.com/leekosss/photoBed/master/202308032140269.png)…...

数据库架构演变过程

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…...

webpack 静态模块打包工具

webpack 为什么? 把静态模块内容&#xff0c;压缩&#xff0c;整合&#xff0c;转译等(前端工程化) 把less/sass转成css代码把ES6 降级成ES5支持多种模块文件类型&#xff0c;多种模块标准语法 vite 为什么不直接学习vite 而学习webpack 因为很多项目还是基于webpack来进…...

万界星空科技/免费开源MES系统/免费仓库管理

仓库管理&#xff08;仓储管理&#xff09;&#xff0c;指对仓库及仓库内部的物资进行收发、结存等有效控制和管理&#xff0c;确保仓储货物的完好无损&#xff0c;保证生产经营活动的正常进行&#xff0c;在此基础上对货物进行分类记录&#xff0c;通过报表分析展示仓库状态、…...

【暑期每日一练】 Epilogue

目录 选择题&#xff08;1&#xff09;解析&#xff1a; &#xff08;2&#xff09;解析&#xff1a; &#xff08;3&#xff09;解析&#xff1a; &#xff08;4&#xff09;解析&#xff1a; &#xff08;5&#xff09;解析&#xff1a; 编程题题一描述输入描述&#xff1a;输…...

Go微服务实践 - Rpc核心概念理解

概述 从0研究一下Golang已经Golang的微服务生态体系&#xff0c;Golang的微服务首先要从Rpc开始&#xff0c;在升级到Grpc&#xff0c;详细介绍这些技术点都在解决什么技术问题。 Rpc Rpc (Remote Procedure Call) 远程过程调用&#xff0c;简单的理解是一个节点请求另一个节…...

Effective Java笔记(27)消除非受检的警告

用泛型编程时会遇到讲多编译器警告 &#xff1a; 非受检转换警告&#xff08; unchecked cast warning &#xff09;、非受检方法调用警告、非受检参数化可变参数类型警告&#xff08; unchecked parameterized vararg type warning&#xff09;&#xff0c;以及非受检转换警告…...

Dapper

介绍 Dapper是一个轻量级的ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它可以方便地将数据库查询结果映射到.NET对象上&#xff0c;同时也支持执行原生SQL查询。下面我将详细介绍Dapper的使用方法。 安装Dapper 首先&#xff0c;你需要通过NuGet包管理器将Dap…...

Python基础知识(一)

数据类型 基本类型 数字&#xff0c;字符串&#xff0c;布尔 数字类型 int整型 整数 float浮点型 带小数的数 complex复数 abj 字符串类型 str字符串 视作文本 组成&#xff1a;由数字&#xff0c;字母&#xff0c;空格&#xff0c;其他字符等组合而成 表达&#xff…...

QTthreadPool 程序

//*******************主窗口****************************// ------------------------.H--------------------------------- ----------------------------------------------------------- #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #inclu…...

python注册机制Registry

Register用法 1. 为什么使用Register2. 先验知识-----装饰器2.1 代码视角的装饰器用法2.2 装饰器的使用场景2 3. Registry注册器参数parent介绍未完待续 1. 为什么使用Register 2. 先验知识-----装饰器 2.1 代码视角的装饰器用法 实例1 decorate def func():pass #! 等价于…...

【Megatron-DeepSpeed】张量并行工具代码mpu详解(三):张量并行层的实现及测试

相关博客 【Megatron-DeepSpeed】张量并行工具代码mpu详解(三)&#xff1a;张量并行层的实现及测试 【Megatron-DeepSpeed】张量并行工具代码mpu详解(一)&#xff1a;并行环境初始化 【Megatron-DeepSpeed】张量并行工具代码mpu详解(二)&#xff1a;Collective通信操作的封装ma…...

【SpringBoot学习笔记】02. yaml配置注入

yaml配置注入 yaml基础语法 说明&#xff1a;语法要求严格&#xff01; 1、空格不能省略 2、以缩进来控制层级关系&#xff0c;只要是左边对齐的一列数据都是同一个层级的。 3、属性和值的大小写都是十分敏感的。 yaml注入配置文件 1、在springboot项目中的resources目录…...

【初阶C语言】指针的妙用

前言&#xff1a;在C语言中&#xff0c;有一个非常重要的知识点&#xff0c;叫做指针&#xff0c;指针也是数据类型中的一种。在本节内容中&#xff0c;我们就一起来学习指针。 学习一个新知识的时候&#xff0c;我们需要从这几个方面&#xff1a;指针是什么&#xff0c;指针是…...

链表——LinkedList类的概述和实现

LinkedList类 1.1LinkedList类概述 LinkedList类底层是基于双向链表结构实现的&#xff0c;不同于ArrayList类和Vector类是基于数组实现的&#xff1b;LinkedList类是非线程安全的&#xff1b;LinkedList类元素允许为null&#xff0c;允许重复元素&#xff1b;LinkedList类插…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

深入解析 ReentrantLock:原理、公平锁与非公平锁的较量

ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...

MLP实战二:MLP 实现图像数字多分类

任务 实战&#xff08;二&#xff09;&#xff1a;MLP 实现图像多分类 基于 mnist 数据集&#xff0c;建立 mlp 模型&#xff0c;实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入&#xff0c;可视化图形数字&#xff1b; 2、完成数据预处理&#xff1a;图像数据维度转换与…...