JTS 简介
JTS (Java Topology Suite) 是一个开源 Java 软件库,它为欧几里得平面线性几何提供了一套对象模型以及一组基本的几何函数。JTS 主要作为矢量 GIS 软件的核心组件,也可以作为提供几何算法的通用库使用。
常用的 JTS 实现框架有 vividsolutions 以及 locationtech 两种,本文内容基于 locationtech 库。
模块总览
Java Topology Suite 核心类库由以下模块构成:
- algorithm:封装了一些几何属性的计算算法,例如计算几何体面积、线段长度等;
- awt:基于 Java AWT 的图形绘制工具,不常用;
- densify:内部填充器,用于将几何体致密化;
- dissolve:用于将几何体中的线性分量融合成一组最大长度的 Linestring;
- edgegraph:用于描述几何体边缘
- geom:用于描述简单几何体
- geomgraph:用于描述地理信息
- index:索引(数据结构相关)
- io:用于读取并解析 WKT/WKB、KML 以及 GML 语言;
- linearref:线性参考系;
- math:空间计算数学工具包;
- noding:用于计算几何体的交点;
- operation:用于操作几何图形,例如获取几何体的 buffer、计算几何体之间的距离等;
- planargraph:用于描述平面图形;
- precision:用于设定精确度;
- shape:用于快速生成一些几何模型,例如生成随机点、生成莫顿曲线等;
- simplify:几何体简化工具;
- triangulate:用于处理三角形相关内容;
- util:一些工具类。
Geometry
引pom
<!-- https://mvnrepository.com/artifact/org.locationtech.jts/jts-core -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
几何类型
JTS 在 org.locationtech.jts.geom 包中提供了以下几种几何类型:
- Point
- MultiPoint
- LineString
- MultiLineString
- LinearRing
- Polygon
- MultiPolygon
- GeometryCollection
其架构如下:
创建几何类型
JTS 通过 GeometryFactory
工厂创建几何对象:
GeometryFactory geometryFactory = new GeometryFactory();
下面我们演示创建各类几何对象的方法。
创建 Point、MultiPoint
-
通过坐标(Coordinate)创建
Coordinate coordinate = new Coordinate(12.654210, 69.332525); Point point = geometryFactory.createPoint(coordinate); Coordinate coordinate1 = new Coordinate(13.654210, 70.332525); Point point1 = geometryFactory.createPoint(coordinate1); MultiPoint multiPoint = geometryFactory.createMultiPoint(new Point[]{point,point1});
-
通过 WKT 创建
WKTReader wktReader = new WKTReader(geometryFactory); Point point = (Point) wktReader.read("POINT (12.654210, 69.332525)"); MultiPoint multiPoint = (MultiPoint) wktReader.read("MULTIPOINT (12.654210 69.332525, 13.654210 70.332525)");
创建 LineString、MultiLineString
-
通过坐标创建
Coordinate[] coords = new Coordinate[] {new Coordinate(2, 2), new Coordinate(3, 3)}; LineString lineString = geometryFactory.createLineString(coords); Coordinate[] coords1 = new Coordinate[] {new Coordinate(3, 3), new Coordinate(4, 4)}; LineString lineString1 = geometryFactory.createLineString(coords1); LineString[] lineStrings = new LineString[]{lineString, lineString1}; MultiLineString multiLineString = geometryFactory.createMultiLineString(lineStrings);
-
通过 WKT 创建
LineString lineString = (LineString) reader.read("LINESTRING(0 0, 2 0)"); MultiLineString multiLineString = (MultiLineString) reader.read("MULTILINESTRING((0 0, 2 0), (1 1,2 2))");
创建闭合线 LinearRing
LinearRing linearRing = geometryFactory.createLinearRing(
new Coordinate[]{
new Coordinate(0, 0),
new Coordinate(0, 10),
new Coordinate(10, 10),
new Coordinate(10, 0),
new Coordinate(0, 0)
}
);
创建 Polygon、MultiPolygon
多边形由一个外壳 (shell) 和内部的一些孔洞 (hole) 构成
-
通过坐标创建
LinearRing shell = new GeometryFactory().createLinearRing(..); LinearRing hole = new GeometryFactory().createLinearRing(..); LinearRing hole1 = new GeometryFactory().createLinearRing(..); Polygon polygon = geometryFactory.createPolygon(shell, new LinearRing[]{hole, hole1}); MultiPolygon mp = geometryFactory.createMultiPolygon(new Polygon[]{polygon, ...});
-
通过 WKT 创建
WKTReader reader = new WKTReader(geometryFactory); Polygon polygon = (Polygon) reader.read("POLYGON((20 10, 30 0, 40 10, 30 20, 20 10))"); MultiPolygon mpolygon = (MultiPolygon) reader.read("MULTIPOLYGON(((40 10, 30 0, 40 10, 30 20, 40 10), (30 10, 30 0, 40 10, 30 20, 30 10)))");
创建 GeometryCollection
Geometry[] garray = new Geometry[]{geometry1, geometry2, ...};
GeometryCollection gc = geometryFactory.createGeometryCollection(garray);
空间关系判断
DE-9IM 模型
DE-9IM 全称 Dimensionally Extended nine-Intersection Model,是一种拓扑模型,是描述两个几何图形空间关系的一种标准。在专业领域,通常将每个几何图形分为三部分:外部,边界和内部。
两个图形的关系判断,实际上就是对这三个部分的分别判断,因此就会有一个 3*3
交叉矩阵,这个矩阵就是 DE-9IM 模型,如下表:
以上 DE-9IM 矩阵中:
a^0
表示 a 内部;∂a
表示 a 边界;a^e
表示 a 外部。dim()
表示相交部分的维度。- 如果相交部分是面,则 dim = 2;
- 如果相交部分是线,则 dim = 1;
- 如果相交部分为一些点,则 dim = 0;
- 如果不相交,则 dim = -1;
接下来我们找两个平面举例:
这两个几何体的 DE-9IM 矩阵如下:
以从左往右,从上往下的顺序读取这个矩阵,便可以用一个字符串来表示:212101212
。
空间谓词
任何基于 DE-9IM 二进制空间关系的拓扑属性都是空间谓词,为了便于使用,我们可以定义一些常用的“命名空间谓词”:
注意: 在能够忽略相交维度的情况下,空间谓词可以用布尔值来代替相交的维度:有相交部分为 T(rue),否则为 F(alse)。
空间关系分析
缓冲区分析 (buffer)
含义:包含所有的点在一个指定距离内的多边形或多多边形。示意图如下:
API 详解:
public Geometry buffer(double distance);
distance
参数:缓冲区扩展的距离(可以是正数、负数或 0):
-
大于 0:向外扩张,示意图:
-
等于 0:buffer 即是边界,示意图:
-
小于 0:向内缩,如果内缩后 buffer 的面积小于等于 0,则返回
POLYGON EMPTY
,示意图:
public Geometry buffer(double distance, int quadrantSegments);
-
quadrantSegments
:控制缓冲区突出位置的圆角精度-
quadrantSegments >= 1
: 手动控制逼近弧度的精度,值越大弧越精确,以下是quadrantSegments = 2
的示意图:
-
quadrantSegments < 1
:JTS 自己控制逼近程度
-
public Geometry buffer(double distance, int quadrantSegments, int endCapStyle);
endCapStyle
:端点处 buffer 形状,只对 LineString 有效
-
BufferParameters.CAP_ROUND
:端点处缓冲区为圆的一部分
-
BufferParameters.CAP_FLAT
:端点处的缓冲区边界直接穿过端点切下来
-
BufferParameters.CAP_SQUARE
:端点处向外延伸出一个长方形区域
除了常规的
buffer
,JTS 还支持用于LineString
的SideBuffer
等,道理与普通buffer
一样,就不展开了。
交叉分析 (Intersection)
含义:获取多边形交叉部分的共同点构成的集合。示意图如下:
API 详解:
public Geometry intersection(Geometry other);
geometryA.intersection(geometryB)
:获取两个几何体之间的交叉部分的几何体。
凸壳分析 (ConvexHull)
含义:包含几何形体的所有点的最小凸壳多边形(外包多边形)。示意图如下:
过大的wkt在不缺失太多精度的情况下,缩小
public Geometry convexHull();
获取几何体的凸壳
联合分析 (Union)
含义:获取多面体所有点的集合。示意图如下:
API 详解:
public Geometry union(Geometry other);
geometryA.union(geometryB)
:获取两个几何体之间的联合部分。
差异分析 (Difference)
含义:获取一个多面体里有,另一个多面体里没有的点的集合。示意图如下:
API 详解:
public Geometry difference(Geometry other);
geometryA.difference(geometryB)
:获取两个几何体之间的差异部分。
对称差异分析 (SymDifference)
含义:不同时在两个多面体中的所有点的集合。示意图如下:
API 详解:
public Geometry symDifference(Geometry other);
geometryA.symDifference(geometryB)
:获取不同时相交与两个几何体的部分。
构建不规则三角网 (TIN)
除了前文介绍的点、线、面等基本几何类型,GIS 系统中还广泛使用一种特殊的几何类型,即不规则三角网 (TIN, Triangulated Irregular Network)。
TIN 是在 GIS 系统和计算机图形领域中广泛使用的一种地形建模和空间数据表示方法。其基本思想是将地形表面分割为许多不规则的三角形,每个三角形由地面上的三个离散点定义。示意图如下:
JTS 可以通过可以通过点集和 WKT 来创建 TIN 几何对象,相关实现位于 org.locationtech.jts.triangulate 包中。
通过点集构建 TIN
首先我们创建四个坐标点,分别是 (1,1)、(1,2)、(2,2)、(2,1),这四个点将会构成一个正方形:
/* ^
* 2 | .--------.
* | | | 四个点构成一个正方形
* | | |
* 1 | `--------'
* |
* 0 +----------------------------->
* 0 1 2
*/
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = new Coordinate[] {
new Coordinate(1, 1),
new Coordinate(2, 1),
new Coordinate(2, 2),
new Coordinate(1, 2),
// 当然,这里可以追加更多坐标点
};
然后通过 DelaunayTriangulationBuilder
创建 TIN:
// 获取 builder
DelaunayTriangulationBuilder dtb = new DelaunayTriangulationBuilder();
// 拿到点阵集合 coordinates,并注册到 builder 内
dtb.setSites(Arrays.asList(coordinates));
// 然后获取 tin 集合体
Geometry tinGeometry = dtb.getTriangles(geometryFactory);
最终得到的 TIN 几何体为 GEOMETRYCOLLECTION(POLYGON((1 2,1 1,2 1,1 2)),POLYGON((1 2,2 1,2 2,1 2)))
,其形状如下:
可见,JTS 将这个平行四边形从中间划分开,形成了由两个三角形组成的 TIN。
通过 WKT 构建 TIN
通过 WKT 创建 TIN 也很简单,还是以这四个点为例:
GeometryFactory geometryFactory = new GeometryFactory();
DelaunayTriangulationBuilder dtb = new DelaunayTriangulationBuilder();
WKTReader wktReader = new WKTReader(geometryFactory);
// 创建这四个点的 Geometry
Geometry geometry = wktReader.read("POLYGON((1 1, 1 2, 2 2, 2 1, 1 1))");
// 注册
dtb.setSites(geometry);
// 获取 tin
Geometry tinGeometry = dtb.getTriangles(geometryFactory);
首先通过 WKT 创建点集的 Geometry
,然后调用 setSites
的重载方法完成注册,之后的流程就与之前一样了。
几何体致密化 (Densify)
在日常工作中,经常会遇到需要进行垂线或投影操作的情况。然而,在 JTS 中进行投影操作时,本质上是计算被投影体到目标体上最近点的距离。如果目标体上的点比较稀疏,就会产生较大的误差。为了提高投影的准确性,我们可以使用 Densify 对几何体进行致密化处理,以提升计算精度。
API 详解:
Densifer.densify(Geometry geom, double distanceTolerance);
geom
:待处理的几何体;distanceTolerance
:致密化后的点距,单位为弧度。
示意图如下:
至此,JTS 开发的重点内容就介绍完了。
扩展:WKT 与 WKB 标记语言
WKT
Well-known text (WKT) 是一种用于表示矢量几何对象的文本标记语言,其表示的几何对象有:点、线、多边形、TIN 和多面体。下面是一些例子:
GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))
POINT ZM (1 1 5 60)
POINT M (1 1 80)
POINT EMPTY
MULTIPOLYGON EMPTY
TRIANGLE((0 0 0,0 1 0,1 1 0,0 0 0))
TIN (((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)))
POLYHEDRALSURFACE Z ( PATCHES
((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),
((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),
((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),
((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),
((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),
((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1))
)
- 几何坐标可以是 2D (x, y)、3D (x, y, z)、4D (x, y, z, m),其中 m 值是线性参考系的一部分。
- 或者是 2D + m 值 (x, y, m)。
- 也可以通过在类型名称后使用符号 EMPTY 来指定不包含坐标的空几何。
WKB
Well-known binary (WKB) 是 WKT 的二进制等效物,用于以更紧凑的形式传输和存储相同的信息。WKB 由 1 字节无符号整数 + 4 字节无符号整数 + 8 字节双精度数字组成:
-
第一个字节表示字节顺序:
00
:大端模式01
:小端模式
-
接下来的四个字节表示几何类型:
类型 2D Z M ZM Geometry 0000 1000 2000 3000 Point 0001 1001 2001 3001 LineString 0002 1002 2002 3002 Polygon 0003 1003 2003 3003 MultiPoint 0004 1004 2004 3004 MultiLineString 0005 1005 2005 3005 MultiPolygon 0006 1006 2006 3006 GeometryCollection 0007 1007 2007 3007 CircularString 0008 1008 2008 3008 CompoundCurve 0009 1009 2009 3009 CurvePolygon 0010 1010 2010 3010 MultiCurve 0011 1011 2011 3011 MultiSurface 0012 1012 2012 3012 Curve 0013 1013 2013 3013 Surface 0014 1014 2014 3014 PolyhedralSurface 0015 1015 2015 3015 TIN 0016 1016 2016 3016 Triangle 0017 1017 2017 3017 Circle 0018 1018 2018 3018 GeodesicString 0019 1019 2019 3019 EllipticalCurve 0020 1020 2020 3020 NurbsCurve 0021 1021 2021 3021 Clothoid 0022 1022 2022 3022 SpiralCurve 0023 1023 2023 3023 CompoundSurface 0024 1024 2024 3024 BrepSolid 1025 AffinePlacement 102 1102 - 下面以存储 POINT(2.0 4.0) 为例:00 00000001 4000000000000000 4010000000000000
- 00 :表示大端模式
- 00000001 :表示 Point 2D (x,y) 类型数据
- 4000000000000000 :64 位双精度浮点数,表示 x 坐标
- 4010000000000000 :64 位双精度浮点数,表示 y 坐标
- 下面以存储 POINT(2.0 4.0) 为例:00 00000001 4000000000000000 4010000000000000