1 Java Geometry空间几何数据的处理应用

  • Java Geometry空间几何数据的处理应用

WKT,是一种文本标记语言,用于表示矢量几何对象、空间参照系统及空间参照系统之间的转换。它的二进制表示方式,亦即WKB(well-known binary)则胜于在传输和在数据库中存储相同的信息。该格式由开放地理空间联盟(OGC)制定。

WKT可以表示的几何对象包括:点,线,多边形,TIN(不规则三角网)及多面体。可以通过几何集合的方式来表示不同维度的几何对象。
几何物体的坐标可以是2D(x,y),3D(x,y,z),4D(x,y,z,m),加上一个属于线性参照系统的m值。
以下为几何WKT字串样例:

  • POINT(6 10)

  • LINESTRING(3 4,10 50,20 25)

  • POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))

  • MULTIPOINT(3.5 5.6, 4.8 10.5)

  • MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))

  • MULTIPOLYGON(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 3)))

  • GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))

  • POINT ZM (1 1 5 60)

  • POINT M (1 1 80)

  • POINT EMPTY

  • MULTIPOLYGON EMPTY

2 向空间数据库插入数据

--GEOM是类型为Geometry的字段--
--我们向该字段新增了一条3D的多边形数据--
--geometry :: STGeomFromText () 是由SQLSERVER提供的函数,它能将WKT文本转换为数据库geometry类型的数据--
INSERT INTO [dbo].[TEST_GEO_TABLE] ( [GEOM] )
VALUES( geometry :: STGeomFromText ( 'POLYGON ((113.507259000000005 22.24814946 8, 113.507188600000006 22.248088559999999 9, 113.507117399999998 22.24802743 10, 113.507046099999997 22.24796624 11, 113.507017300000001 22.247888209999999 12))',4326 ));

也就是说,将坐标转化为WKT文本,我们就可以插入空间数据。接下来我们要考虑的是如何产生WKT文本。

3 使用Java创建Geometry对象

3.1 常见Geometry的JavaAPI

wkt文本仅仅是一个字符串而已,直接将坐标点拼接成符合WKT格式的字符串不就可以了吗?
道理是这个道理,要做好可就难了。

  • 拼接工作量巨大
  • 拼接过程容易出错
  • 拼接的结果不一定合法可用
    我们需要一套JAVA API对数据进行处理,能够方便的创建Geometry对象,进行地理信息的绘制、创建、验证等等功能

市面上常见的GeometryApi有

  • Esri/geometry-api-java

  • locationtech/jts (推荐)

Esri是Arcgis官方提供的javaSDK,可惜功能不多,甚至不能提供基本的空间计算功能。
jts功能较为齐全,资料也相对丰富一点

3.2 JTS的部分API使用方式

    @Testpublic void geoTest() throws ParseException {/*** GeometryFactory工厂,参数一:数据精度 参数二空间参考系SRID*/GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326);/*** 熟知文本WKT阅读器,可以将WKT文本转换为Geometry对象*/WKTReader wktReader = new WKTReader(geometryFactory);/*** Geometry对象,包含Point、LineString、Polygon等子类*/Geometry geometry = wktReader.read("POINT (113.53896635 22.36429837)");/*** 将二进制流的形式读取Geometry对象*/WKBReader wkbReader = new WKBReader(geometryFactory);/*** 单纯的一个坐标点,单点可以创建Point,多点可以创建LineString、Polygon等*/Coordinate coordinate = new Coordinate(1.00, 2.00);Point point = geometryFactory.createPoint(coordinate);Polygon polygon = geometryFactory.createPolygon(new Coordinate[]{new Coordinate(1, 2),new Coordinate(1, 2),new Coordinate(1, 2),new Coordinate(1, 2),new Coordinate(1, 2),});Geometry geometry1 = point;Geometry geometry2 = polygon;/*** WKT输出器,将Geometry对象写出为WKT文本*/WKTWriter wktWriter = new WKTWriter();String write = wktWriter.write(point);}

3.3 JTS中Geometry数据类型的子类

4 使用JAVA向空间数据库新增数据

根据上面测试类中Api的使用,让我们总结几个要点

  • 工厂类对象只需初始化一次,应放在配置类注入到Spring容器中
  • 由前端或Excel导入相关坐标数据,生成Geometry对象
  • 持久化Geometry对象到SqlServer

本例中推荐两种方式进行Geometry对象的持久化:

  1. 获取Geometry对象的WKT文本,再使用SqlServer提供的geometry :: STGeomFromText ()函数将WKT文本存储为数据库Geometry类型
  2. 将jts包中Geometry对象转换成SqlServer JDBC包中的Geometry对象,将Geometry对象以二进制的形式持久化到数据库

环境:
本例代码基于JTS、SpringBoot、Mybatis-Plus、mssql-jdbc环境

5 使用TypeHandler映射自定义对象字段插入Geometry数据

5.1 自定义TypeHandler

当我们使用Mybatis框架时,Mybatis提供了自定义类型转换器TypeHandler实现特殊对象与Sql字段的映射关系

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** @author wangqichang* @since 2019/8/28*/
@Slf4j
@MappedTypes(value = {Geometry.class})
public class GeometryTypeHandler extends BaseTypeHandler<Geometry> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, Geometry geometry, JdbcType jdbcType) throws SQLException {/*** 获取jts包对象的wkt文本,再转换成sqlserver的Geometry对象* 调用ps的setBytes()方法,以二进制持久化该geometry对象*/com.microsoft.sqlserver.jdbc.Geometry geo = com.microsoft.sqlserver.jdbc.Geometry.STGeomFromText(geometry.toText(), geometry.getSRID());preparedStatement.setBytes(i, geo.STAsBinary());}@Overridepublic Geometry getNullableResult(ResultSet resultSet, String s) {try {/*** 从ResultSet中读取二进制转换为SqlServer的Geometry对象* 使用jts的WKTReader将wkt文本转成jts的Geometryd对象*/com.microsoft.sqlserver.jdbc.Geometry geometry1 = com.microsoft.sqlserver.jdbc.Geometry.STGeomFromWKB(resultSet.getBytes(s));String s1 = geometry1.toString();WKTReader wktReader = SpringContextUtil.getBean(WKTReader.class);Geometry read = wktReader.read(s1);return read;} catch (Exception e) {log.error(e.getMessage());throw new ServiceException(e.getMessage());}}@Overridepublic Geometry getNullableResult(ResultSet resultSet, int i) throws SQLException {return null;}@Overridepublic Geometry getNullableResult(CallableStatement callableStatement, int i) throws SQLException {return null;}
}

5.2 实体对象

实体对象如下:

  • objectid为Integer类型,非自增(此字段为Arcgis维护,不能修改)@TableId是mybatis-plus插件的注解,告知插件该字段为主键字段,字段名为OBJECT,主键策略为用户输入
  • shape为jts的Geometry对象(该对象JSON序列化结果非常吓人,所以使用@JsonIgnore修饰)
  • @KeySequence也是mybatis-plus的插件,作用是标识该对象需要使用的主键序列名。此处我实现了一个IKeyGenerator,作用类似于插入数据前查询Oracle的序列名以填充主键。
@Data
@TableName("LINE_WELL")
@KeySequence(value = "LINE_WELL",clazz = Integer.class)
public class Well extends MyGeometry implements Serializable {@TableId(value = "OBJECTID", type = IdType.INPUT)private Integer objectid;@JsonIgnoreprotected Geometry shape;
}

5.3 自定义主键生成策略

在arcgis中,空间表中的主键字段为int,并且非自增,不能进行修改。当修改为自增时arcgis会出现一些错误。因此,java后台插入空间数据需要自己完成主键的查询生成。
IKeyGenerator是Mybatis-Plus提供的接口。此实现的作用是,当指定这个主键生成策略时,mp框架将会在新增数据前调用此实现,将结果赋值给对象的ID(类似于Oracle的序列)
注意,该类需要注入到Spring容器中

import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;/*** @author wangqichang* @since 2019/8/30*/
public class SqlServerKeyGenerator implements IKeyGenerator {@Overridepublic String executeSql(String incrementerName) {return "select max(OBJECTID)+1 from " + incrementerName;}
}

5.4 Geometry对象持久化

当我们调用mybatis-plus提供的方法持久化对象

 String str = "POLYGON ((113.52048666400003 22.248443089000034, 113.5206744190001 22.24822462700007, 113.52082998700007 22.248343788000057, 113.52060468200011 22.248547355000028, 113.52048666400003 22.248443089000034))";Geometry read = null;try {/*** 这里使用wkt文本生成了一个jts包下的Geometry对象*/read = SpringContextUtil.getBean(WKTReader.class).read(str);} catch (ParseException e) {e.printStackTrace();}Well well = new Well();well.setShape(read);//这里是Mybatis-Plus提供的save接口,调用其内部实现直接储存对象wellService.save(well);System.out.println("持久化成功");

执行日志如下:
数据插入前执行了SqlServerKeyGenerator中的sql获取主键
插入代码中字段shape为Geometry对象的二进制

2019-08-30 15:54:23.541  INFO 8484 --- [nio-8905-exec-1] jdbc.sqltiming                           : SELECT max(OBJECTID) + 1 FROM LINE_WELL {executed in 4 msec}
2019-08-30 15:54:23.631  INFO 8484 --- [nio-8905-exec-1] jdbc.sqltiming                           : INSERT INTO LINE_WELL (OBJECTID, shape) VALUES (3, '<byte[]>') {executed in 17 msec}

6 手写xml插入Geometry数据

使用SqlServer提供的函数geometry :: STGeomFromText( #{wktText},4326)将Geometry转换成WKT文本再进行插入

    <insert id="insertCorridorBySql" parameterType="com.zh.xxx.entity.xxx" useGeneratedKeys="true"keyProperty="objectid">INSERT INTO [LINE_CORRIDOR] (shape)values (geometry :: STGeomFromText( #{wktText},4326))</insert>

注意,wktText是一个非表字段的临时字段,我在此定义了一个父类,所有包含Geometry的空间表实体均继承此类,用于处理wkt文本

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTWriter;import java.io.Serializable;/*** 针对Geometry获取Wkt文本字段做处理的Geometry父类,getWktText替代getText,输出三维wkt文本* 针对sql_server无法识别POLYGON Z 语法,对wkt文本进行替换*/
@Data
public class MyGeometry implements Serializable {/*** 三维wkt输出,默认为2D不带Z*/@TableField(exist = false)@JsonIgnoreprivate WKTWriter wktWriter = new WKTWriter(3);/*** sql_server 与 jts wkt不兼容问题*/@TableField(exist = false)@JsonIgnoreprivate static final String THREE_D_PRIFIX = "POLYGON Z";@TableField(exist = false)@JsonIgnoreprivate static final String TWO_D_PRIFIX = "POLYGON";@JsonIgnoreprotected Geometry shape;@TableField(exist = false)@JsonIgnoreprivate String wktText;public String getWktText() {if (StrUtil.isBlank(wktText)){if (getShape() != null) {String wkt = wktWriter.write(shape);if (wkt.startsWith(THREE_D_PRIFIX)) {wktText = StrUtil.replace(wkt, THREE_D_PRIFIX, TWO_D_PRIFIX);} else {wktText = wkt;}}}return wktText;}
}

7 采坑记录

7.1 jts与sqlserver识别的wkt不兼容

[2019-07-01 16:40:20,637] [ERROR] [http-nio-8905-exec-5] jdbc.audit 111 7. PreparedStatement.execute() INSERT INTO [zhundergroundcableline].[dbo].[LINE_CORRIDOR] ( [Shape] ) values ( geometry :: STGeomFromText( 'POLYGON Z((113.5079365 22.24850034
0, 113.5078521 22.24845659 0, 113.5077674 22.24841271 0, 113.5076826 22.24836872 0, 113.5075978 22.24832498 0))',4326) ) com.microsoft.sqlserver.jdbc.SQLServerException: 在执行用户定义例程或聚合“geometry”期间出现 .NET Framework 错误:
System.FormatException: 24142: 在位置 8 处应为 "(",但输入中实际为 "Z"。
System.FormatException: 在 Microsoft.SqlServer.Types.WellKnownTextReader.RecognizeToken(Char token)在 Microsoft.SqlServer.Types.SqlGeometry.GeometryFromText(OpenGisType type, SqlChars text, Int32 srid)

Java Geometry空间几何数据的处理应用相关推荐

  1. java geometry mysql_Java Geometry空间几何数据的处理应用

    先了解几个基本概念,有助于了解本文应用场景 地理信息系统GIS-百度百科 地理信息系统(Geographic Information System或 Geo-Information system,GI ...

  2. JTS Java空间几何计算、距离、最近点、subLine等计算

    文章目录 前言 地理坐标系和投影坐标系 地理坐标系 投影坐标系 地图投影 墨卡托/Web墨卡托 常见坐标系 地理坐标系和投影坐标系互转 EPSG:3857和EPSG:4326 Java各坐标系之间的转 ...

  3. JTS Java空间几何计算、距离、最近点、subLine等 稳健的一比,持续更新中

    文章目录 前言 地理坐标系和投影坐标系 地理坐标系 投影坐标系 地图投影 墨卡托/Web墨卡托 常见坐标系 地理坐标系和投影坐标系互转 EPSG:3857和EPSG:4326 Java各坐标系之间的转 ...

  4. Geometry(几何对象定义空间位置和关联几何形状)

    Geometry 描述 几何对象定义空间位置和关联几何形状. 讨论 在许多地理处理工作流中,您可能需要使用坐标和几何信息运行特定操作,但不一定想经历创建新(临时)要素类.使用光标填充要素类.使用要素类 ...

  5. Python地信专题 | 基于geopandas的空间数据分析—数据结构篇

    作者:费弗里 博客地址: https://www.cnblogs.com/feffery/p/11898190.html 说明:本文经作者授权转载,禁止二次转载 全文8500字 本文对应代码已上传至我 ...

  6. Python处理空间地理数据

    python中处理空间地理数据时,往往需要处理shp文件PyShp package可以用于shp文件的读取.写入等操作. Shapefile属于一种矢量图形格式,它能够保存几何图形的位置及相关属性.但 ...

  7. java geometry mysql_java程序操作Geometry对象

    Geometry 空间地理对象,Oracle中存储Geometry对象的字段类型是 MDSYS.SDO_GEOMETRY,在数据库中构建Geometry对象的方法: v_pointarray MDSY ...

  8. 最新3D GAN可生成三维几何数据了!模型速度提升7倍,英伟达斯坦福出品

    明敏 发自 凹非寺 量子位 报道 | 公众号 QbitAI 2D图片变3D,还能给出3D几何数据? 英伟达和斯坦福大学联合推出的这个GAN,真是刷新了3D GAN的新高度. 而且生成画质也更高,视角随 ...

  9. 《深入理解Java虚拟机》(二)Java虚拟机运行时数据区

    Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第二章 ,为了整理思路,简单记录一下,方便后期查阅. 2.2 运行时数据区域 Java虚拟机 ...

最新文章

  1. HDOJ 1874 HDU 1874 畅通工程续 ACM 1874 IN HDU
  2. saltstack模块 --cp
  3. 19道Python练习题
  4. python matplot.pyplot.plot() 的用法 plt.plot()(绘制y相对于x的线条和/或标记。)
  5. 十一、“由专入分易,由分入专难。”(2020.12.18)
  6. NSLocalizedString不起作用
  7. selenium+ python自动化--断言assertpy
  8. 【ElasticSearch】Es 源码之 Netty4HttpServerTransport 源码解读
  9. 三大运营商将解决新老用户套餐不同权问题;罗永浩与抖音合作;Git 2.26发布 | 极客头条...
  10. Bootstrap 3之美03-独立行,文字环绕,图片自适应,隐藏元素
  11. python在财务中的应用-利用python实现周期财务统计可视化
  12. 《Windows编程循序渐进》——对话框应用程序
  13. Java 去除重复数据的五种方式
  14. hdu 4325 Flowers 离散化+线段树 多校联合赛(三) 第六题
  15. 达观数据助力浙江大华构建企业级知识搜索共享平台,盘活沉淀数据
  16. 子类可以重新定义父类的同名方法,并且允许他们有不同的返回值类型吗?
  17. css碎步测量,隧洞测量实习日记.doc
  18. 园区网典型组网架构及案例实践
  19. 论文翻译:2021_Acoustic Echo Cancellation with Cross-Domain Learning
  20. 美国大学计算机专业排名2019,2019年美国大学计算机排名

热门文章

  1. JD最新青龙面板+诺兰方舟星链计划对接傻妞芝士等机器人网页短信搭建教程+拉库教程+资产一对一推送教程
  2. “法外狂徒”张三经典语录
  3. SX1278超时设置与计算
  4. 教你2种常用的电商高并发处理解决方案
  5. python处理word替换_python替换word中的关键文字(使用通配符)
  6. Replacing Elements
  7. python浪漫微信_教你用python做一个哄女友的微信自动回复机器人
  8. X006---交叉表(Cross Tab)和转置(Transpose)
  9. java程序 jnlp,使用JNLP文件启动应用程序
  10. Unity中的警告--warning CS0108:'XXXX' hides inherited member 'AAAAA'. Use...的原因以及解决办法