| name | jts |
| description | Use when performing precise 2D computational geometry in Java — spatial predicates (contains, intersects), overlay operations, buffering, triangulation. JTS (Java Topology Suite): the canonical geometry engine used as blueprint for GEOS, Shapely, and NetTopologySuite. |
| tags | ["java","geometry","topology","spatial","de9im","wkt","wkb","geojson","gis"] |
项目地址: https://github.com/locationtech/jts
Maven Central: org.locationtech.jts:jts-core
许可证: Eclipse Public License 2.0 / Eclipse Distribution License 1.0(BSD 风格)
Javadoc: https://locationtech.github.io/jts/javadoc
概述
JTS Topology Suite(简称 JTS)是 LocationTech 项目组下的开源 Java 二维矢量几何库,是 GeoTools、GeoServer 等众多开源 GIS 项目的几何计算核心。它提供:
- 几何对象模型:Point、LineString、Polygon、MultiPoint、MultiLineString、MultiPolygon、GeometryCollection
- 空间关系判断:equals、contains、within、intersects、touches、crosses、overlaps、disjoint、relate(DE-9IM)
- 集合运算:intersection、union、difference、symDifference、buffer
- 几何分析:convexHull、centroid、area、length、distance、isValid、simplify
- 线性参考:沿线距离定位、线上插值
- 空间索引:STRtree、Quadtree,加速批量空间查询
- 格式读写:WKT、WKB、GeoJSON
环境要求: JDK 8+
快速集成
Maven
<properties>
<jts.version></jts.version>
</properties>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>${jts.version}</version>
</dependency>
如需 GeoJSON 等 I/O 支持:
<dependency>
<groupId>org.locationtech.jts.io</groupId>
<artifactId>jts-io-common</artifactId>
<version>${jts.version}</version>
</dependency>
Gradle
implementation 'org.locationtech.jts:jts-core:1.20.0'
implementation 'org.locationtech.jts.io:jts-io-common:1.20.0'
项目模块一览
| 模块 | artifactId | 用途 |
|---|
| jts-core | jts-core | ★ 核心——几何模型、算法、空间操作、索引 |
| jts-io-common | jts-io-common | 通用 I/O:WKT、WKB、GeoJSON 读写 |
| jts-io-ora | jts-io-ora | Oracle Spatial SDO_GEOMETRY 读写 |
| jts-io-sde | jts-io-sde | ArcSDE 几何读写 |
核心类一览
| 类 | 包 | 用途 |
|---|
GeometryFactory | org.locationtech.jts.geom | ★ 推荐入口——创建所有几何对象的工厂 |
Geometry | org.locationtech.jts.geom | 几何抽象基类,提供空间操作与关系判断方法 |
Point | org.locationtech.jts.geom | 点(0 维) |
LineString | org.locationtech.jts.geom | 线串(1 维) |
LinearRing | org.locationtech.jts.geom | 闭合线环(用于构建 Polygon) |
Polygon | org.locationtech.jts.geom | 面(2 维,含外环与可选内环) |
MultiPoint | org.locationtech.jts.geom | 多点集合 |
MultiLineString | org.locationtech.jts.geom | 多线集合 |
MultiPolygon | org.locationtech.jts.geom | 多面集合 |
GeometryCollection | org.locationtech.jts.geom | 任意几何集合 |
Coordinate | org.locationtech.jts.geom | 坐标值(x, y, z) |
Envelope | org.locationtech.jts.geom | 外接矩形(MBR) |
PrecisionModel | org.locationtech.jts.geom | 坐标精度模型 |
WKTReader | org.locationtech.jts.io | WKT 格式解析 |
WKTWriter | org.locationtech.jts.io | WKT 格式输出 |
WKBReader | org.locationtech.jts.io | WKB 格式解析 |
WKBWriter | |
几何对象创建
import org.locationtech.jts.geom.*;
GeometryFactory gf = new GeometryFactory();
Point point = gf.createPoint(new Coordinate(116.4, 39.9));
Point point3d = gf.createPoint(new Coordinate(116.4, 39.9, 50.0));
LineString line = gf.createLineString(new Coordinate[]{
new Coordinate(0, 0),
new Coordinate(10, 10),
new Coordinate(20, 0)
});
LinearRing ring = gf.createLinearRing(new Coordinate[]{
new Coordinate(0, 0), new Coordinate(10, 0),
(, ), (, ),
(, )
});
gf.createPolygon( []{
(, ), (, ),
(, ), (, ),
(, )
});
gf.createLinearRing( []{
(, ), (, ),
(, ), (, ),
(, )
});
gf.createLinearRing( []{
(, ), (, ),
(, ), (, ),
(, )
});
gf.createPolygon(shell, []{hole});
gf.createMultiPointFromCoords( []{
(, ), (, )
});
gf.createMultiLineString( []{line});
gf.createMultiPolygon( []{polygon});
gf.createGeometryCollection( []{point, line, polygon});
空间关系判断
boolean eq = geomA.equals(geomB);
boolean dj = geomA.disjoint(geomB);
boolean ix = geomA.intersects(geomB);
boolean tc = geomA.touches(geomB);
boolean cr = geomA.crosses(geomB);
boolean wn = geomA.within(geomB);
boolean ct = geomA.contains(geomB);
boolean ol = geomA.overlaps(geomB);
boolean rel = geomA.relate(geomB, "T*F**FFF*");
String matrix = geomA.relate(geomB).toString();
boolean near = geomA.isWithinDistance(geomB, 100.0);
集合运算
Geometry intersection = geomA.intersection(geomB);
Geometry union = geomA.union(geomB);
Geometry difference = geomA.difference(geomB);
Geometry symDiff = geomA.symDifference(geomB);
Geometry buffer = geometry.buffer(10.0);
Geometry bufferFlat = geometry.buffer(10.0, 8, BufferOp.CAP_FLAT);
import org.locationtech.jts.operation.union.UnaryUnionOp;
Geometry merged = UnaryUnionOp.union(geometryCollection);
import org.locationtech.jts.operation.overlayng.OverlayNGRobust;
Geometry robustIntersection = OverlayNGRobust.overlay(geomA, geomB, OverlayNG.INTERSECTION);
Geometry robustUnion = OverlayNGRobust.overlay(geomA, geomB, OverlayNG.UNION);
几何分析
double area = polygon.getArea();
double length = line.getLength();
double dist = geomA.distance(geomB);
Point centroid = geometry.getCentroid();
Point interiorPoint = geometry.getInteriorPoint();
Geometry hull = geometry.convexHull();
Envelope env = geometry.getEnvelopeInternal();
boolean valid = geometry.isValid();
import org.locationtech.jts.operation.valid.IsValidOp;
IsValidOp validator = new IsValidOp(geometry);
if (!validator.isValid()) {
System.out.println(validator.getValidationError());
}
import org.locationtech.jts.geom.util.GeometryFixer;
Geometry fixed = GeometryFixer.fix(geometry);
几何简化
import org.locationtech.jts.simplify.TopologyPreservingSimplifier;
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
Geometry simplified = TopologyPreservingSimplifier.simplify(geometry, 0.001);
Geometry dpSimplified = DouglasPeuckerSimplifier.simplify(geometry, 0.001);
精度模型
PrecisionModel pmFloat = new PrecisionModel();
PrecisionModel pmFixed = new PrecisionModel(1000000);
GeometryFactory gf = new GeometryFactory(pmFixed);
GeometryFactory gfSrid = new GeometryFactory(pmFloat, 4326);
格式读写
WKT(Well-Known Text)
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
WKTReader reader = new WKTReader();
Geometry point = reader.read("POINT (116.4 39.9)");
Geometry line = reader.read("LINESTRING (0 0, 10 10, 20 0)");
Geometry poly = reader.read("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
WKTWriter writer = new WKTWriter();
String wkt = writer.write(point);
WKTWriter writer3d = new WKTWriter(3);
String wkt3d = writer3d.write(gf.createPoint(new Coordinate(1, 2, 3)));
WKB(Well-Known Binary)
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKBWriter;
WKBWriter wkbWriter = new WKBWriter();
byte[] wkb = wkbWriter.write(geometry);
WKBReader wkbReader = new WKBReader();
Geometry geom = wkbReader.read(wkb);
String hex = WKBWriter.toHex(wkb);
byte[] fromHex = WKBReader.hexToBytes(hex);
GeoJSON
import org.locationtech.jts.io.geojson.GeoJsonReader;
import org.locationtech.jts.io.geojson.GeoJsonWriter;
GeoJsonReader gjReader = new GeoJsonReader();
Geometry geom = gjReader.read("{\"type\":\"Point\",\"coordinates\":[116.4,39.9]}");
GeoJsonWriter gjWriter = new GeoJsonWriter();
String json = gjWriter.write(geom);
空间索引
STRtree(R-tree,推荐用于批量查询)
import org.locationtech.jts.index.strtree.STRtree;
STRtree index = new STRtree();
for (Geometry geom : geometries) {
index.insert(geom.getEnvelopeInternal(), geom);
}
Envelope searchEnv = new Envelope(10, 20, 10, 20);
List<?> candidates = index.query(searchEnv);
import org.locationtech.jts.index.strtree.ItemDistance;
Object nearest = index.nearestNeighbour(
point.getEnvelopeInternal(), point,
(ItemDistance) (itemBoundable1, itemBoundable2) -> {
Geometry g1 = (Geometry) itemBoundable1.getItem();
Geometry g2 = (Geometry) itemBoundable2.getItem();
return g1.distance(g2);
}
);
Quadtree(四叉树,支持动态增删)
import org.locationtech.jts.index.quadtree.Quadtree;
Quadtree qtree = new Quadtree();
qtree.insert(geom.getEnvelopeInternal(), geom);
List<?> results = qtree.query(searchEnv);
qtree.remove(geom.getEnvelopeInternal(), geom);
PreparedGeometry(加速重复判断)
当需要对同一个几何反复进行空间关系判断时(如判断大量点是否在某面内),使用 PreparedGeometry 可显著提升性能:
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
PreparedGeometry prepared = PreparedGeometryFactory.prepare(polygon);
for (Point pt : points) {
if (prepared.contains(pt)) {
}
}
线性参考
import org.locationtech.jts.linearref.LengthIndexedLine;
import org.locationtech.jts.linearref.LocationIndexedLine;
LengthIndexedLine lil = new LengthIndexedLine(line);
Coordinate midPoint = lil.extractPoint(line.getLength() / 2);
Geometry subLine = lil.extractLine(10.0, 50.0);
double index = lil.indexOf(new Coordinate(5, 5));
LocationIndexedLine locLine = new LocationIndexedLine(line);
仿射变换
import org.locationtech.jts.geom.util.AffineTransformation;
AffineTransformation translate = AffineTransformation.translationInstance(10, 20);
Geometry moved = translate.transform(geometry);
AffineTransformation rotate = AffineTransformation.rotationInstance(Math.PI / 4);
Geometry rotated = rotate.transform(geometry);
AffineTransformation scale = AffineTransformation.scaleInstance(2.0, 2.0);
Geometry scaled = scale.transform(geometry);
AffineTransformation combo = new AffineTransformation();
combo.translate(10, 0);
combo.rotate(Math.PI / 6);
Geometry result = combo.transform(geometry);
典型应用场景
| 场景 | 关键类 / 方法 |
|---|
| 创建几何对象 | GeometryFactory.createPoint() / createLineString() / createPolygon() |
| 地理围栏 / 点在面内判断 | Geometry.contains() / within(),高频场景用 PreparedGeometry |
| 计算两个几何的距离 | Geometry.distance() |
| 缓冲区分析 | Geometry.buffer() 或 BufferOp |
| 面叠加分析(交并差) | Geometry.intersection() / union() / difference() |
| 鲁棒叠加运算 | OverlayNGRobust.overlay() |
| 几何格式互转 | WKTReader / WKTWriter / WKBReader / WKBWriter / GeoJsonReader / GeoJsonWriter |
| 批量空间查询加速 | STRtree / Quadtree |
| 最近邻搜索 | STRtree.nearestNeighbour() |
| 几何简化(抽稀) | TopologyPreservingSimplifier / DouglasPeuckerSimplifier |
| 几何有效性校验与修复 | IsValidOp / GeometryFixer |
| 线性参考 / 沿线定位 | LengthIndexedLine / LocationIndexedLine |
| 凸包计算 | Geometry.convexHull() |
| 几何仿射变换 | AffineTransformation |
| 数据库 WKB 交互 | WKBReader.hexToBytes() / WKBWriter.toHex() |
常见注意事项
- 使用 GeometryFactory 创建几何:不要直接
new Point(),始终通过 GeometryFactory 的工厂方法创建几何对象。
- 面必须闭合:Polygon 的外环和内环坐标数组的首尾坐标必须相同。
- 坐标顺序:JTS 使用
(x, y) 即 (经度, 纬度) 的顺序,注意与某些 GIS 系统的 (纬度, 经度) 区分。
- 几何有效性:从外部导入的几何数据应使用
geometry.isValid() 检查有效性,无效几何可用 GeometryFixer.fix() 修复。
- SRID 不参与计算:JTS 的 SRID 仅作为元数据标记,不影响空间运算;JTS 所有计算都在笛卡尔平面上进行,不处理投影。
- 性能优化:批量空间查询使用
STRtree;重复空间关系判断使用 PreparedGeometry;大量几何合并使用 UnaryUnionOp。
- 线程安全:
GeometryFactory 是线程安全的;Geometry 对象本身不可变,可安全共享;但 Reader/Writer 实例非线程安全,需为每个线程创建独立实例。
- 包名迁移:JTS 1.15+ 包名由
com.vividsolutions.jts 迁移为 org.locationtech.jts,注意旧代码升级。
- OverlayNG:对于叠加运算(intersection / union / difference),推荐使用
OverlayNGRobust,它比传统叠加引擎更加鲁棒,能处理更多边界情况。
AI 使用建议
推荐工作流
- 创建几何对象:始终通过
GeometryFactory 工厂方法创建,不要直接 new
- 格式解析:从 WKT/WKB/GeoJSON 读入外部数据,使用对应的 Reader
- 空间运算:用
PreparedGeometry 加速批量 contains/intersects 判断
- 批量查询:用
STRtree 建空间索引后查询
- 结果导出:用 Writer 输出为 WKT/WKB/GeoJSON
关键注意事项
- 坐标顺序:JTS 使用
(x, y) 即 (经度, 纬度),注意与部分 GIS 系统的 (y, x) 区分
- SRID 不参与计算:JTS 所有计算在笛卡尔平面进行,不处理地球曲率
- OverlayNG 优先:叠加运算优先使用
OverlayNGRobust.overlay(),比传统方法更鲁棒
- 线程安全:
GeometryFactory 和 Geometry 线程安全;Reader/Writer 非线程安全
- 有效性检查:外部导入的几何用
isValid() 检查后用 GeometryFixer.fix() 修复
相关技能
参考链接