先了解几个基本概念,有助于了解本文应用场景

地理信息系统GIS-百度百科

地理信息系统(Geographic Information System或 Geo-Information system,GIS)有时又称为“地学信息系统”。它是一种特定的十分重要的空间信息系统。它是在计算机硬、软件系统支持下,对整个或部分地球表层(包括大气层)空间中的有关地理分布数据进行采集、储存、管理、运算、分析、显示和描述的技术系统

ArcGIS平台

ArcGIS产品线为用户提供一个可伸缩的,全面的GIS平台。ArcObjects包含了许多的可编程组件,从细粒度的对象(例如单个的几何对象)到粗粒度的对象(例如与现有ArcMap文档交互的地图对象)涉及面极广,这些对象为开发者集成了全面的GIS功能。

Geometry数据类型

Geometry是一种空间几何数据类型,常用于描述空间几何信息,例如坐标点、线、面、三维信息等。

也就是说,GIS一般使用Geometry数据类型来存储及展示地理信息。

常见的支持Geometry的数据库有Oracle、SqlServer、Mysql、PostgreSQL

Sql-Server中支持的Geometry文档以及Sql-Server中可选择的地理数据类型如图

应用场景示例

如图,该应用主要为使用ArcGIS做地理信息系统,使用SqlServer作为空间数据库,使用Java搭建后台服务处理数据,使用JS的Vue结合ArcGIS的API做前端的渲染,成功的将地理信息高亮标识在地图上

本文关注点:使用java对Geometry数据进行处理,附带对arcgis系统一些简单使用说明 简单描述一个gis应用系统的处理流程

1. 使用ArcGIS创建地理数据库,发布GIS服务

数据库和 ArcGIS Enterprise

如何注册数据库到 ArcGIS Server 站点

发布地图服务

通过以上的文档,我们可以创建一个地理数据库,并将数据库通过arcgis进行连接,发布为一个Restful服务

访问该接口,如图

image.png

那这个接口提供了那些功能呢?

支持的操作大概如下:

Supported Operations: Query Apply Edits Add Features Update Features Delete Features Calculate Validate SQL Generate Renderer Return Updates Iteminfo Thumbnail Metadata

由ArcGIS提供的这些服务,我们可以以可视化的方式,对空间地理信息进行查询及展示

后面我们将做进一步的说明

2. 空间数据以及WKT熟知文本

好的,我们有了数据库,也有了arcgis提供的服务,现在该如何新增空间数据呢?

先通过WKT了解下空间数据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

到这里我们清楚了,所谓空间数据就是一个或一组坐标,这个坐标或坐标组有类型(POINT点LINESTRING线POLYGON面),通过这些坐标,GIS系统可以完整地定位查询绘制这些坐标信息

3. 向空间数据库插入数据

那数据库如何新增这些空间数据呢?

先了解一下SQLSERVER空间数据的插入语句长啥样

--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文本

4. 使用Java创建Geometry对象

4.1 常见Geometry的JavaAPI

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

道理是这个道理,要做好可就难了。

拼接工作量巨大

拼接过程容易出错

拼接的结果不一定合法可用

我们需要一套JAVA API对数据进行处理,能够方便的创建Geometry对象,进行地理信息的绘制、创建、验证等等功能

市面上常见的GeometryApi有

Esri是Arcgis官方提供的javaSDK,可惜功能不多,甚至不能提供基本的空间计算功能。

jts功能较为齐全,资料也相对丰富一点

点击上面链接可以访问其Github地址,本文将以jts为例进行说明

4.2 JTS的部分API使用方式

@Test

public 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);

}

4.3 JTS中Geometry数据类型的子类

image.png

根据github中相关文档介绍,我们已经可以在项目中引入相关坐标,并创建Geometry对象,构造WKT文本了

5. 使用JAVA向空间数据库新增数据

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

工厂类对象只需初始化一次,应放在配置类注入到Spring容器中

由前端或Excel导入相关坐标数据,生成Geometry对象

持久化Geometry对象到SqlServer

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

获取Geometry对象的WKT文本,再使用SqlServer提供的geometry :: STGeomFromText ()函数将WKT文本存储为数据库Geometry类型

将jts包中Geometry对象转换成SqlServer JDBC包中的Geometry对象,将Geometry对象以二进制的形式持久化到数据库

环境:

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

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

6.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 {

@Override

public 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());

}

@Override

public 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());

}

}

@Override

public Geometry getNullableResult(ResultSet resultSet, int i) throws SQLException {

return null;

}

@Override

public Geometry getNullableResult(CallableStatement callableStatement, int i) throws SQLException {

return null;

}

}

6.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;

@JsonIgnore

protected Geometry shape;

}

6.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 {

@Override

public String executeSql(String incrementerName) {

return "select max(OBJECTID)+1 from " + incrementerName;

}

}

6.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, '')

{executed in 17 msec}

7. 手写xml插入Geometry数据

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

keyProperty="objectid">

INSERT INTO [LINE_CORRIDOR] (

shape

)

values (

geometry :: STGeomFromText( #{wktText},4326)

)

注意,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)

@JsonIgnore

private WKTWriter wktWriter = new WKTWriter(3);

/**

* sql_server 与 jts wkt不兼容问题

*/

@TableField(exist = false)

@JsonIgnore

private static final String THREE_D_PRIFIX = "POLYGON Z";

@TableField(exist = false)

@JsonIgnore

private static final String TWO_D_PRIFIX = "POLYGON";

@JsonIgnore

protected Geometry shape;

@TableField(exist = false)

@JsonIgnore

private 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;

}

}

8 采坑记录:

8.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 mysql_Java Geometry空间几何数据的处理应用相关推荐

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

    1 Java Geometry空间几何数据的处理应用 Java Geometry空间几何数据的处理应用 WKT,是一种文本标记语言,用于表示矢量几何对象.空间参照系统及空间参照系统之间的转换.它的二进 ...

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

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

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

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

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

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

  5. Planar Homography (共面点成像) Epipolar Geometry(对极几何)

    转载:http://blog.csdn.NET/yvonnezju/article/details/40982192 这一篇,要搞清楚两个概念,Planar Homography (共面点成像)&am ...

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

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

  7. Python处理空间地理数据

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

  8. 30分钟学会shapely空间几何分析

    shapely是python中开源的空间几何对象库,支持Point(点),LineString(线),        Polygon(面)等几何对象及相关空间操作. 公众号后台回复关键字:" ...

  9. Java虚拟机的内存空间有几种

    Java虚拟机的内存空间有几种?(1)问题分析: JVM(虚拟机)的内存划分 不同的数据使用的是哪一块内存空间 (2)核心答案讲解: Java虚拟机有那几块内存空间: 1)栈内存:方法运行时所进入的内 ...

最新文章

  1. 什么是多重共线性问题?如何判别多重共线性问题?怎么避免多重共线性对模型和学习的干扰?
  2. p2148 [SDOI2009]ED
  3. yii2 RESTful api的详细使用
  4. Androidstudio坑
  5. LNK2019无法解析的外部符号 public: static struct cv::Ptr class cv::xfeatures2d::SURF问题解决
  6. MATLAB读取文件夹中所有图像
  7. 如果我用你待我的方式来待你 恐怕你早已离去
  8. Python 10 行以内代码能有什么高端操作?| 原力计划
  9. js中变量名提升和函数名提升
  10. linux端口被墙了 开通端口
  11. 预测控制matlab程序,预测控制matlab程序
  12. python blp模型 估计_简述BLP模型
  13. 如何使用 win10 虚拟桌面
  14. linux watch 查看文件,linux watch肿么退出
  15. pygame-KidsCanCode系列jumpy-part18-背景滚动
  16. 生物群落数据分析最常用的统计方法:回归和混合效应模型、多元统计分析技术及结构方程等数量分析方法
  17. TIA博途中如何通过画面编号+变量更改事件实现画面跳转?
  18. 分享8个前端可以制作360度WebVr全景视图框架
  19. SQLyog通过excel导入数据
  20. 微信小程序实现短信认证功能

热门文章

  1. Android 获取CellId以及IMEI 获取基站id
  2. 分布式计算,大型网站技术架构:核心原理与案例分析
  3. 语义分析的一些方法(三)
  4. 详细解析英特尔奔腾金牌银牌处理器规格参数
  5. 解决STM32 硬件IIC死锁在BUSY状态的方法讨论
  6. 编码电位器c语言程序,360°编码电位器原理
  7. DCIC2021 入门数据比赛(1)
  8. 任天堂官宣塞尔达传说新作:为新产品护航?
  9. 利用opencv进行图片水印消除
  10. moia调度mysql到hive_必看:数据平台的搭建教程及软件工具