第05章 - 空间关系判断
第05章 - 空间关系判断
5.1 空间关系概述
5.1.1 DE-9IM 模型
空间关系基于 DE-9IM(Dimensionally Extended 9-Intersection Model) 模型。该模型通过比较两个几何的内部(Interior)、边界(Boundary)和外部(Exterior)来定义空间关系:
几何 B
I B E
┌─────────────┐
I │ II IB IE │
几何A B │ BI BB BE │
E │ EI EB EE │
└─────────────┘
I = Interior(内部)
B = Boundary(边界)
E = Exterior(外部)
每个交集的结果可以是:
- -1 或 F:空(无交集)
- 0:0 维(点)
- 1:1 维(线)
- 2:2 维(面)
- T:任意非空维度
5.1.2 支持的空间关系
geometry-api-java 支持以下空间关系:
| 关系 | 描述 | DE-9IM 模式 |
|---|---|---|
| Equals | 完全相等 | TF**FFF |
| Disjoint | 分离(不相交) | FFFF*** |
| Intersects | 相交 | T******** 或 T****** 等 |
| Touches | 相接(边界接触) | FT******* 等 |
| Crosses | 穿越 | TT***** (L/A) |
| Within | 在内部 | TFF |
| Contains | 包含 | T*****FF* |
| Overlaps | 重叠 | TTT |
| Relate | 自定义关系 | 任意模式 |
5.1.3 几何维度对关系的影响
不同维度组合适用不同的关系:
Point(0D) Line(1D) Polygon(2D)
Point E,D,T,W,C E,D,T,W E,D,T,W
Line E,D,T,C E,D,T,O,C,Cr E,D,T,W,Cr
Polygon E,D,T,C E,D,T,C,Cr E,D,T,O,W,C
E=Equals, D=Disjoint, T=Touches, W=Within, C=Contains
O=Overlaps, Cr=Crosses
5.2 相等判断 (Equals)
5.2.1 几何相等
Polygon p1 = createPolygon();
Polygon p2 = createPolygon();
SpatialReference sr = SpatialReference.create(4326);
// 使用 GeometryEngine
boolean equals = GeometryEngine.equals(p1, p2, sr);
// 使用 Operator
OperatorEquals op = OperatorEquals.local();
boolean equals2 = op.execute(p1, p2, sr, null);
5.2.2 相等的含义
空间相等不要求:
- 顶点数相同
- 顶点顺序相同
- 路径起点相同
只要求:
- 占据相同的空间位置
- 在容差范围内
// 两个表示相同矩形的多边形
Polygon rect1 = new Polygon();
rect1.startPath(0, 0);
rect1.lineTo(10, 0);
rect1.lineTo(10, 10);
rect1.lineTo(0, 10);
rect1.closePathWithLine();
Polygon rect2 = new Polygon();
rect2.startPath(10, 10); // 不同起点
rect2.lineTo(0, 10);
rect2.lineTo(0, 0);
rect2.lineTo(10, 0);
rect2.closePathWithLine();
boolean equals = GeometryEngine.equals(rect1, rect2, sr);
System.out.println("相等: " + equals); // true
5.2.3 与 Java equals() 的区别
// Geometry.equals() - 精确比较
boolean javaEquals = p1.equals(p2);
// GeometryEngine.equals() - 空间拓扑比较
boolean spatialEquals = GeometryEngine.equals(p1, p2, sr);
// 区别:
// javaEquals 比较坐标、顶点数、顺序等
// spatialEquals 只比较空间位置(考虑容差)
5.3 分离判断 (Disjoint)
5.3.1 基本用法
Polygon p1 = createPolygon1();
Polygon p2 = createPolygon2();
SpatialReference sr = SpatialReference.create(4326);
// 判断两个几何是否分离(不相交)
boolean disjoint = GeometryEngine.disjoint(p1, p2, sr);
// Disjoint 是 Intersects 的反义
boolean intersects = !disjoint;
5.3.2 分离示例
// 两个不相交的矩形
Polygon rect1 = createRect(0, 0, 10, 10);
Polygon rect2 = createRect(20, 20, 30, 30);
boolean disjoint = GeometryEngine.disjoint(rect1, rect2, sr);
System.out.println("分离: " + disjoint); // true
// 两个相邻的矩形(边界接触)
Polygon rect3 = createRect(10, 0, 20, 10);
boolean disjoint2 = GeometryEngine.disjoint(rect1, rect3, sr);
System.out.println("分离: " + disjoint2); // false(边界接触不是分离)
5.4 相交判断 (Intersects)
5.4.1 基本用法
// Intersects 是最常用的空间关系
Geometry g1 = createGeometry1();
Geometry g2 = createGeometry2();
SpatialReference sr = SpatialReference.create(4326);
// 方式一:使用 disjoint 的反义
boolean intersects = !GeometryEngine.disjoint(g1, g2, sr);
// 方式二:使用 Operator
OperatorIntersects op = OperatorIntersects.local();
boolean intersects2 = op.execute(g1, g2, sr, null);
5.4.2 批量相交判断
// 判断一个几何与多个几何的相交关系
Polygon queryGeom = createQueryPolygon();
List<Geometry> candidates = getCandidateGeometries();
SpatialReference sr = SpatialReference.create(4326);
OperatorIntersects op = OperatorIntersects.local();
// 预先加速查询几何
if (op.canAccelerateGeometry(queryGeom)) {
op.accelerateGeometry(queryGeom, sr,
Geometry.GeometryAccelerationDegree.enumMedium);
}
List<Geometry> intersecting = new ArrayList<>();
for (Geometry candidate : candidates) {
if (op.execute(queryGeom, candidate, sr, null)) {
intersecting.add(candidate);
}
}
// 清理加速
Operator.deaccelerateGeometry(queryGeom);
5.5 包含关系 (Contains/Within)
5.5.1 Contains 判断
Polygon outer = createOuterPolygon();
Point inner = new Point(50, 50);
SpatialReference sr = SpatialReference.create(4326);
// 判断 outer 是否包含 inner
boolean contains = GeometryEngine.contains(outer, inner, sr);
// 使用 Operator
OperatorContains op = OperatorContains.local();
boolean contains2 = op.execute(outer, inner, sr, null);
5.5.2 Within 判断
// Within 是 Contains 的反向关系
// A.within(B) 等价于 B.contains(A)
boolean within = GeometryEngine.within(inner, outer, sr);
// within == contains 应该相等
5.5.3 边界情况
// 点在多边形边界上
Polygon polygon = createRect(0, 0, 10, 10);
Point onBoundary = new Point(10, 5); // 在右边界上
boolean contains = GeometryEngine.contains(polygon, onBoundary, sr);
System.out.println("边界点被包含: " + contains); // false
// Contains 要求完全在内部,边界上的点不算
// 如果需要包含边界,使用 Covers/CoveredBy(需要 Relate 实现)
5.5.4 实现 Covers 关系
/**
* Covers 关系:A covers B 当且仅当 B 的每个点都在 A 的内部或边界上
* DE-9IM: T*****FF* 或 *T****FF* 或 ***T**FF* 或 ****T*FF*
*/
public boolean covers(Geometry a, Geometry b, SpatialReference sr) {
String pattern = "T*****FF*";
if (GeometryEngine.relate(a, b, sr, pattern)) return true;
pattern = "*T****FF*";
if (GeometryEngine.relate(a, b, sr, pattern)) return true;
pattern = "***T**FF*";
if (GeometryEngine.relate(a, b, sr, pattern)) return true;
pattern = "****T*FF*";
if (GeometryEngine.relate(a, b, sr, pattern)) return true;
return false;
}
5.6 接触判断 (Touches)
5.6.1 基本用法
Polygon p1 = createRect(0, 0, 10, 10);
Polygon p2 = createRect(10, 0, 20, 10); // 共享边界
SpatialReference sr = SpatialReference.create(4326);
boolean touches = GeometryEngine.touches(p1, p2, sr);
System.out.println("接触: " + touches); // true
// 使用 Operator
OperatorTouches op = OperatorTouches.local();
boolean touches2 = op.execute(p1, p2, sr, null);
5.6.2 Touches 的条件
Touches 关系成立的条件:
- 两个几何有公共点
- 公共点只在边界上,不在内部
// 两个相邻矩形 - Touches
Polygon rect1 = createRect(0, 0, 10, 10);
Polygon rect2 = createRect(10, 0, 20, 10);
boolean touches = GeometryEngine.touches(rect1, rect2, sr); // true
// 两个重叠矩形 - 不 Touches
Polygon rect3 = createRect(5, 0, 15, 10);
boolean touches2 = GeometryEngine.touches(rect1, rect3, sr); // false
// 点在多边形边界上 - Touches
Point p = new Point(10, 5);
boolean touches3 = GeometryEngine.touches(rect1, p, sr); // true
5.7 穿越判断 (Crosses)
5.7.1 基本用法
// 线穿过多边形
Polyline line = new Polyline();
line.startPath(-10, 5);
line.lineTo(20, 5);
Polygon polygon = createRect(0, 0, 10, 10);
SpatialReference sr = SpatialReference.create(4326);
boolean crosses = GeometryEngine.crosses(line, polygon, sr);
System.out.println("穿越: " + crosses); // true
5.7.2 Crosses 适用场景
Crosses 适用于以下维度组合:
- 线/线
- 线/面
- 多点/线
- 多点/面
// 两条线相交(穿越)
Polyline line1 = new Polyline();
line1.startPath(0, 0);
line1.lineTo(10, 10);
Polyline line2 = new Polyline();
line2.startPath(0, 10);
line2.lineTo(10, 0);
boolean crosses = GeometryEngine.crosses(line1, line2, sr);
System.out.println("两线穿越: " + crosses); // true
5.7.3 Crosses vs Intersects
// 线完全在多边形内 - Intersects 但不 Crosses
Polyline insideLine = new Polyline();
insideLine.startPath(2, 2);
insideLine.lineTo(8, 8);
Polygon polygon = createRect(0, 0, 10, 10);
boolean intersects = !GeometryEngine.disjoint(insideLine, polygon, sr);
boolean crosses = GeometryEngine.crosses(insideLine, polygon, sr);
System.out.println("相交: " + intersects); // true
System.out.println("穿越: " + crosses); // false(线完全在内部)
5.8 重叠判断 (Overlaps)
5.8.1 基本用法
// 两个部分重叠的多边形
Polygon p1 = createRect(0, 0, 10, 10);
Polygon p2 = createRect(5, 5, 15, 15);
SpatialReference sr = SpatialReference.create(4326);
boolean overlaps = GeometryEngine.overlaps(p1, p2, sr);
System.out.println("重叠: " + overlaps); // true
5.8.2 Overlaps 的条件
Overlaps 要求:
- 两个几何维度相同
- 有公共内部点
- 各自都有不属于对方的部分
// 完全包含 - 不是 Overlaps
Polygon outer = createRect(0, 0, 20, 20);
Polygon inner = createRect(5, 5, 15, 15);
boolean overlaps = GeometryEngine.overlaps(outer, inner, sr);
System.out.println("包含情况重叠: " + overlaps); // false
// 相同几何 - 不是 Overlaps
boolean overlaps2 = GeometryEngine.overlaps(outer, outer, sr);
System.out.println("相同几何重叠: " + overlaps2); // false
5.9 自定义关系 (Relate)
5.9.1 使用 DE-9IM 模式
Geometry g1 = createGeometry1();
Geometry g2 = createGeometry2();
SpatialReference sr = SpatialReference.create(4326);
// DE-9IM 模式字符串
// 字符位置: II, IB, IE, BI, BB, BE, EI, EB, EE
// 字符含义: T=true, F=false, *=any, 0/1/2=dimension
// 示例:判断是否相交
String intersectsPattern = "T********"; // 内部与内部有交集
boolean intersects = GeometryEngine.relate(g1, g2, sr, intersectsPattern);
// 示例:判断是否相等
String equalsPattern = "T*F**FFF*";
boolean equals = GeometryEngine.relate(g1, g2, sr, equalsPattern);
5.9.2 常用 DE-9IM 模式
// 预定义的常用模式
public class DE9IMPatterns {
// 相等
public static final String EQUALS = "T*F**FFF*";
// 分离
public static final String DISJOINT = "FF*FF****";
// 接触
public static final String TOUCHES_PP = "FT*******"; // 点/点不适用
public static final String TOUCHES_LP = "FT*******";
public static final String TOUCHES_AL = "F**T*****";
public static final String TOUCHES_AP = "F***T****";
// 包含
public static final String CONTAINS = "T*****FF*";
// 在内部
public static final String WITHIN = "T*F**F***";
// 覆盖
public static final String COVERS = "T*****FF*"; // 简化版
// 被覆盖
public static final String COVERED_BY = "T*F**F***"; // 简化版
}
5.9.3 Relate 实现自定义关系
/**
* 判断两个多边形是否共享边界但内部不相交
* 类似于相邻的地块
*/
public boolean areAdjacent(Polygon p1, Polygon p2, SpatialReference sr) {
// 内部不相交,边界有交集
String pattern = "F***T****";
return GeometryEngine.relate(p1, p2, sr, pattern);
}
/**
* 判断线是否完全在多边形边界上
*/
public boolean lineOnBoundary(Polyline line, Polygon polygon, SpatialReference sr) {
// 线的内部在多边形边界上,线的边界在多边形边界上
String pattern = "*1*F**FF*";
return GeometryEngine.relate(line, polygon, sr, pattern);
}
5.10 距离计算 (Distance)
5.10.1 平面距离
Geometry g1 = createGeometry1();
Geometry g2 = createGeometry2();
SpatialReference sr = SpatialReference.create(4326);
// 计算两个几何之间的最短距离
double distance = GeometryEngine.distance(g1, g2, sr);
System.out.println("距离: " + distance + " 单位");
5.10.2 测地距离
// 计算地球表面两点间的测地距离(米)
Point p1 = new Point(116.397428, 39.90923); // 北京
Point p2 = new Point(121.469170, 31.224361); // 上海
double meters = GeometryEngine.geodesicDistanceOnWGS84(p1, p2);
System.out.println("北京到上海距离: " + (meters / 1000) + " 公里");
5.10.3 距离判断
/**
* 判断两个几何是否在指定距离内
*/
public boolean isWithinDistance(Geometry g1, Geometry g2,
SpatialReference sr, double maxDistance) {
double distance = GeometryEngine.distance(g1, g2, sr);
return distance <= maxDistance;
}
5.11 邻近搜索 (Proximity2D)
5.11.1 最近坐标
Polyline line = createPolyline();
Point queryPoint = new Point(5, 8);
// 找到线上距离查询点最近的坐标
Proximity2DResult result = GeometryEngine.getNearestCoordinate(
line, queryPoint, false);
Point2D nearestPoint = result.getCoordinate();
double distance = result.getDistance();
int vertexIndex = result.getVertexIndex();
System.out.println("最近点: " + nearestPoint);
System.out.println("距离: " + distance);
System.out.println("在顶点 " + vertexIndex + " 附近");
5.11.2 最近顶点
// 找到几何上距离查询点最近的顶点
Proximity2DResult result = GeometryEngine.getNearestVertex(
polygon, queryPoint);
Point2D nearestVertex = result.getCoordinate();
int vertexIndex = result.getVertexIndex();
5.11.3 范围内顶点
// 找到指定范围内的所有顶点
Proximity2DResult[] results = GeometryEngine.getNearestVertices(
polygon,
queryPoint,
10.0, // 搜索半径
5 // 最多返回 5 个
);
for (Proximity2DResult result : results) {
System.out.println("顶点: " + result.getCoordinate() +
", 距离: " + result.getDistance());
}
5.12 质心计算 (Centroid2D)
5.12.1 计算质心
OperatorCentroid2D op = OperatorCentroid2D.local();
Polygon polygon = createPolygon();
Point2D centroid = op.execute(polygon, null);
System.out.println("质心: (" + centroid.x + ", " + centroid.y + ")");
// 转换为 Point 对象
Point centroidPoint = new Point(centroid.x, centroid.y);
5.12.2 多部分几何的质心
// 对于多部分几何,质心是加权平均
Polygon multiPartPolygon = createMultiPartPolygon();
Point2D centroid = OperatorCentroid2D.local().execute(multiPartPolygon, null);
// 注意:质心可能在几何外部
// 例如:月牙形多边形
5.13 性能优化建议
5.13.1 使用几何加速
// 对于大量重复查询,预先加速查询几何
Polygon queryArea = createLargePolygon();
SpatialReference sr = SpatialReference.create(4326);
OperatorContains op = OperatorContains.local();
op.accelerateGeometry(queryArea, sr,
Geometry.GeometryAccelerationDegree.enumMedium);
// 批量查询
for (Point point : millionsOfPoints) {
boolean contains = op.execute(queryArea, point, sr, null);
// ...
}
// 清理
Operator.deaccelerateGeometry(queryArea);
5.13.2 使用边界框预过滤
/**
* 使用边界框预过滤优化空间查询
*/
public List<Geometry> findIntersecting(Geometry query,
List<Geometry> candidates, SpatialReference sr) {
// 获取查询几何的边界框
Envelope2D queryEnv = new Envelope2D();
query.queryEnvelope2D(queryEnv);
List<Geometry> results = new ArrayList<>();
for (Geometry candidate : candidates) {
// 边界框预过滤
Envelope2D candidateEnv = new Envelope2D();
candidate.queryEnvelope2D(candidateEnv);
if (!queryEnv.isIntersecting(candidateEnv)) {
continue; // 边界框不相交,跳过精确检查
}
// 精确检查
if (!GeometryEngine.disjoint(query, candidate, sr)) {
results.add(candidate);
}
}
return results;
}
5.13.3 批量操作
// 使用 Cursor 进行批量操作
SimpleGeometryCursor queryCursor = new SimpleGeometryCursor(queryGeometry);
SimpleGeometryCursor candidateCursor = new SimpleGeometryCursor(candidates);
// 一次性设置操作参数
OperatorIntersects op = OperatorIntersects.local();
// ...
5.14 本章小结
本章详细介绍了 geometry-api-java 的空间关系判断:
- DE-9IM 模型:空间关系的理论基础
- 基本关系:Equals、Disjoint、Intersects
- 包含关系:Contains、Within
- 接触关系:Touches
- 穿越重叠:Crosses、Overlaps
- 自定义关系:Relate 和 DE-9IM 模式
- 距离计算:平面距离和测地距离
- 邻近搜索:最近点/顶点查询
- 质心计算:几何中心
关键要点:
- 理解不同空间关系的含义和适用场景
- 掌握 DE-9IM 模式的使用
- 了解几何维度对关系的影响
- 使用加速和预过滤优化性能

浙公网安备 33010602011771号