第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(外部)

每个交集的结果可以是:

  • -1F:空(无交集)
  • 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 关系成立的条件:

  1. 两个几何有公共点
  2. 公共点只在边界上,不在内部
// 两个相邻矩形 - 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 要求:

  1. 两个几何维度相同
  2. 有公共内部点
  3. 各自都有不属于对方的部分
// 完全包含 - 不是 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 的空间关系判断:

  1. DE-9IM 模型:空间关系的理论基础
  2. 基本关系:Equals、Disjoint、Intersects
  3. 包含关系:Contains、Within
  4. 接触关系:Touches
  5. 穿越重叠:Crosses、Overlaps
  6. 自定义关系:Relate 和 DE-9IM 模式
  7. 距离计算:平面距离和测地距离
  8. 邻近搜索:最近点/顶点查询
  9. 质心计算:几何中心

关键要点

  • 理解不同空间关系的含义和适用场景
  • 掌握 DE-9IM 模式的使用
  • 了解几何维度对关系的影响
  • 使用加速和预过滤优化性能

← 上一章:空间操作详解 | 下一章:数据格式转换 →

posted @ 2025-12-03 15:15  我才是银古  阅读(5)  评论(0)    收藏  举报