目录
- 一、前言
- [二、核心 API 一览](#二、核心 API 一览)
- 三、环境准备
- [四、线 × 线叠加(Line × Line)](#四、线 × 线叠加(Line × Line))
- [4.1 求交点](#4.1 求交点)
- [4.2 线段并集](#4.2 线段并集)
- [五、线 × 面叠加(Line × Polygon)](#五、线 × 面叠加(Line × Polygon))
- [5.1 道路穿越区域](#5.1 道路穿越区域)
- [5.2 线段差集(取外部段)](#5.2 线段差集(取外部段))
- [六、面 × 面叠加(Polygon × Polygon)](#六、面 × 面叠加(Polygon × Polygon))
- [6.1 交集(Intersection)](#6.1 交集(Intersection))
- [6.2 并集(Union)](#6.2 并集(Union))
- [6.3 差集(Difference)](#6.3 差集(Difference))
- [6.4 对称差(Symmetric Difference)](#6.4 对称差(Symmetric Difference))
- [6.5 四种面面运算对比](#6.5 四种面面运算对比)
- [七、缓冲 + 叠加(典型业务场景)](#七、缓冲 + 叠加(典型业务场景))
- 八、完整可运行代码
- 九、常见坑位与注意事项
- 十、实践建议
- 十一、小结
一、前言
在 GIS 开发中,空间叠加运算(Overlay Operations)是最核心的能力之一。无论是区域合并、剪裁、挖洞,还是交叉口分析、服务范围计算,都离不开以下四种基本运算:
- intersection(交集)
- union(并集)
- difference(差集)
- symDifference(对称差)
JTS(Java Topology Suite)提供了完整的叠加运算 API,本文将通过大量可运行的代码示例,讲解线×线、线×面、面×面的叠加分析,并结合实际业务场景说明其应用方式。
二、核心 API 一览
以下是本文涉及的主要 JTS API 对照表:
| API | 返回类型 | 业务场景 |
|---|---|---|
intersection(a, b) |
Geometry | 区域重叠分析、路口判断 |
union(a, b) |
Geometry | 多个区域合并、服务范围聚合 |
difference(a, b) |
Geometry | 剪裁、挖洞、排除区域 |
symDifference(a, b) |
Geometry | 差异区域分析 |
buffer(d) |
Geometry | 缓冲分析、服务半径生成 |
三、环境准备
Maven 依赖:
xml
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
四、线 × 线叠加(Line × Line)
线与线的叠加最常见的场景是交叉口分析------判断两条道路是否相交,交点在哪里。
4.1 求交点
java
LineString lineA = (LineString) reader.read("LINESTRING (0 0, 10 10)");
LineString lineB = (LineString) reader.read("LINESTRING (0 10, 10 0)");
Geometry intersection = lineA.intersection(lineB);
System.out.println("交点: " + intersection.toText());
// 输出: POINT (5 5)
当两条线相交时,intersection 返回的是一个 Point 对象。如果两条线重合(共线),则返回的是 LineString。
4.2 线段并集
java
Geometry union = lineA.union(lineB);
System.out.println("并集: " + union.toText());
// 输出: MULTILINESTRING ((0 0, 5 5), (5 5, 10 10), (0 10, 5 5), (5 5, 10 0))
并集操作会将两条线合并为一个 MultiLineString,交点处会被拆分为多个线段。
五、线 × 面叠加(Line × Polygon)
线面叠加在实际业务中非常常见,例如:判断道路是否穿越某个行政区、计算道路在某区域内的长度等。
5.1 道路穿越区域
java
Polygon polygon = (Polygon) reader.read(
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
LineString crossingLine = (LineString) reader.read(
"LINESTRING (-2 5, 12 5)");
Geometry linePolygonIntersection = crossingLine.intersection(polygon);
System.out.println("线面交集: " + linePolygonIntersection.toText());
// 输出: LINESTRING (0 5, 10 5)
当线与面相交时,intersection 返回的是被剪裁后保留在面内的线段。
5.2 线段差集(取外部段)
java
Geometry linePolygonDifference = crossingLine.difference(polygon);
System.out.println("线减面: " + linePolygonDifference.toText());
// 输出: MULTILINESTRING ((-2 5, 0 5), (10 5, 12 5))
差集操作可以得到位于多边形外部的线段部分,非常适合用于"切割"场景。
六、面 × 面叠加(Polygon × Polygon)
面面叠加是 GIS 中最强大的分析能力,支持交集、并集、差集、对称差四种运算。
6.1 交集(Intersection)
java
Polygon polyA = (Polygon) reader.read(
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
Polygon polyB = (Polygon) reader.read(
"POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))");
Geometry intersectAB = polyA.intersection(polyB);
System.out.println("交集: " + intersectAB.toText());
// 输出: POLYGON ((5 10, 10 10, 10 5, 5 5, 5 10))
交集返回两个多边形重叠的部分,结果仍然是一个 Polygon。
6.2 并集(Union)
java
Geometry unionAB = polyA.union(polyB);
System.out.println("并集: " + unionAB.toText());
// 输出: POLYGON ((0 0, 0 10, 5 10, 5 15, 15 15, 15 5, 10 5, 10 0, 0 0))
并集将两个多边形合并为一个完整的多边形,重叠部分只保留一次。
6.3 差集(Difference)
java
Geometry diffAB = polyA.difference(polyB);
System.out.println("差集(A-B): " + diffAB.toText());
// 输出: POLYGON ((0 0, 0 10, 5 10, 5 5, 10 5, 10 0, 0 0))
差集 A - B 表示"属于 A 但不属于 B"的部分,常用于"挖洞"场景。
6.4 对称差(Symmetric Difference)
java
Geometry symDiffAB = polyA.symDifference(polyB);
System.out.println("对称差: " + symDiffAB.toText());
// 输出: MULTIPOLYGON (...)
对称差是 (A - B) 并 (B - A),即两个几何对象互不重叠的部分。
6.5 四种面面运算对比
| 运算 | 公式 | 含义 |
|---|---|---|
| 交集 | A ∩ B | 两者重叠的部分 |
| 并集 | A ∪ B | 两者合并的全部区域 |
| 差集 | A - B | A 中不属于 B 的部分 |
| 对称差 | (A-B) ∪ (B-A) | 两者互不重叠的部分 |
七、缓冲 + 叠加(典型业务场景)
以下是一个非常常见的实际场景:根据两个服务中心点生成服务半径缓冲区,再将两个缓冲区合并为一个完整的服务范围。
java
Point serviceCenterA = factory.createPoint(new Coordinate(5, 5));
Point serviceCenterB = factory.createPoint(new Coordinate(8, 8));
Geometry bufferA = serviceCenterA.buffer(4); // 圆形服务区
Geometry bufferB = serviceCenterB.buffer(4);
Geometry mergedServiceArea = bufferA.union(bufferB);
System.out.println("合并服务区面积: " + mergedServiceArea.getArea());
这种"缓冲 + 并集"的模式在实际项目中应用广泛,例如:
- 城市服务网点覆盖范围计算
- 配送半径内的订单分配
- 基站信号覆盖分析
- 商圈影响力范围评估
八、完整可运行代码
以下是本文所有示例的完整汇总,可直接复制到IDE中运行:
java
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
public class JtsOverlayOperationDemo {
public static void main(String[] args) throws Exception {
GeometryFactory factory = new GeometryFactory();
WKTReader reader = new WKTReader(factory);
// ===== 1. 线 × 线 =====
LineString lineA = (LineString) reader.read("LINESTRING (0 0, 10 10)");
LineString lineB = (LineString) reader.read("LINESTRING (0 10, 10 0)");
Geometry lineIntersection = lineA.intersection(lineB);
System.out.println("==== 线×线 ====");
System.out.println("交点: " + lineIntersection.toText());
// ===== 2. 线 × 面 =====
Polygon polygon = (Polygon) reader.read(
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
LineString crossingLine = (LineString) reader.read("LINESTRING (-2 5, 12 5)");
System.out.println("\n==== 线×面 ====");
System.out.println("线面交集: " + crossingLine.intersection(polygon).toText());
System.out.println("线减面: " + crossingLine.difference(polygon).toText());
// ===== 3. 面 × 面 =====
Polygon polyA = (Polygon) reader.read(
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
Polygon polyB = (Polygon) reader.read(
"POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))");
System.out.println("\n==== 面×面 ====");
System.out.println("交集: " + polyA.intersection(polyB).toText());
System.out.println("并集: " + polyA.union(polyB).toText());
System.out.println("差集: " + polyA.difference(polyB).toText());
System.out.println("对称差: " + polyA.symDifference(polyB).toText());
// ===== 4. 缓冲 + 叠加 =====
Geometry bufferA = factory.createPoint(new Coordinate(5, 5)).buffer(4);
Geometry bufferB = factory.createPoint(new Coordinate(8, 8)).buffer(4);
Geometry merged = bufferA.union(bufferB);
System.out.println("\n==== 缓冲+并集 ====");
System.out.println("合并服务区面积: " + merged.getArea());
}
}
运行结果

九、常见坑位与注意事项
- 结果可能为空 :当两个几何对象不相交时,
intersection返回的是一个空的GeometryCollection,需要用isEmpty()判断。
java
if (!lineA.intersection(lineB).isEmpty()) {
// 有交集
}
-
经纬度下面积无实际意义 :如果传入的是 WGS84 经纬度数据,
buffer()生成的缓冲区半径单位是"度",不是米,必须先投影。 -
运算结果可能产生新的几何类型 :例如
union可能返回Polygon、MultiPolygon或者仅是一个简单的Point,不要强制类型转换。 -
方向性问题 :
difference(A, B)和difference(B, A)的结果是不同的,顺序很重要。 -
性能问题 :大量多边形并集时,建议使用
CascadedPolygonUnion而非循环调用union()。
十、实践建议
-
输入校验 :永远先用
isValid()检查几何对象的合法性,非法几何会导致叠加运算结果不可预期。 -
结果类型判断 :用
instanceof判断返回类型,而不是强制转型,避免ClassCastException。 -
经纬度数据务必投影 :使用 GeoTools 的
JTS.transform转换到 EPSG:3857 或 UTM 后再进行缓冲和面积计算。 -
批量运算用级联并集 :多边形合并优先使用
CascadedPolygonUnion,性能远优于循环union。 -
空结果处理 :所有叠加运算结果都应检查
isEmpty(),避免后续空指针异常。 -
单位清晰:在代码中明确标注度量单位(度 / 米 / 平方米),避免后续维护误用。
十一、小结
本文系统讲解了 JTS 中的空间叠加运算能力:
- 线 × 线:求交点、线段并集
- 线 × 面:线段被多边形剪裁、取外部段
- 面 × 面:交集 / 并集 / 差集 / 对称差
- 缓冲 + 叠加:服务范围合并的典型业务场景
这些 API 是 GIS 空间分析的基础。