04-空间关系分析

空间关系分析

概述

空间关系分析是GIS的核心功能之一。通过判断几何对象之间的空间关系,可以实现空间查询、叠加分析、选址分析等常见GIS功能。

基本空间关系

相交(Intersects)

两个几何对象有任意公共部分(点、线或面)时,称为相交:

/**
 * 判断几何是否相交
 */
public static boolean intersects(Geometry a, Geometry b) {
    return a.intersects(b);
}

应用场景

  • 空间查询:找出与指定区域相交的所有要素
  • 碰撞检测:判断两个对象是否发生空间重叠
  • 路线规划:判断路线是否经过某个区域

相离(Disjoint)

两个几何对象没有任何公共点时,称为相离:

/**
 * 判断几何是否相离
 * 一个公共点都没有
 */
public static boolean disjoint(Geometry a, Geometry b) {
    return a.disjoint(b);
}

说明disjointintersects互为反义关系,即:

disjoint(a, b) == !intersects(a, b)

接触(Touches)

两个几何对象有公共点但没有公共内部区域时,称为接触:

/**
 * 判断几何是否接触
 * 有公共点但没有公共区域
 */
public static boolean touches(Geometry a, Geometry b) {
    return a.touches(b);
}

典型情况

  • 两个相邻的地块共享边界
  • 道路与地块的边界相接
  • 点落在线或面的边界上

交叉(Crosses)

两个几何对象的内部有公共部分,但既不相等也不包含,称为交叉:

/**
 * 判断几何是否交叉
 * 有公共区域但不是包含关系
 */
public static boolean crosses(Geometry a, Geometry b) {
    return a.crosses(b);
}

典型情况

  • 道路穿过区域(线与面交叉)
  • 两条道路相交(线与线交叉)

包含(Contains)与被包含(Within)

包含关系描述一个几何完全位于另一个几何内部:

/**
 * 判断几何A是否包含几何B
 */
public static boolean contains(Geometry a, Geometry b) {
    return a.contains(b);
}

/**
 * 判断几何A是否在几何B内部
 */
public static boolean within(Geometry a, Geometry b) {
    return a.within(b);
}

关系说明

contains(a, b) == within(b, a)

应用场景

  • 判断点是否在某个区域内
  • 判断某个地块是否完全位于行政区划内
  • 空间过滤:只保留完全落入指定范围的要素

重叠(Overlaps)

两个同维度的几何对象有公共区域,但又不完全相同,称为重叠:

/**
 * 判断几何是否重叠
 * 有公共区域且包含关系不成立
 */
public static boolean overlaps(Geometry a, Geometry b) {
    return a.overlaps(b);
}

说明:重叠只发生在同维度几何之间(面与面、线与线)。

距离计算

最短距离

计算两个几何对象之间的最短距离:

/**
 * 计算几何之间的距离
 * 不论点线面都是几何间最短距离
 */
public static double distance(Geometry a, Geometry b) {
    return a.distance(b);
}

注意:返回值的单位与坐标系单位一致。地理坐标系返回的是度,投影坐标系返回的是米。

距离判断

快速判断两个几何是否在指定距离内:

/**
 * 判断几何间最短距离是否小于给定距离
 */
public static boolean isWithinDistance(Geometry a, Geometry b, double distance) {
    return a.isWithinDistance(b, distance);
}

应用场景

  • 近邻搜索:查找指定距离内的设施
  • 缓冲区分析:判断要素是否在某要素的影响范围内
  • 预警系统:判断两个移动目标是否过于接近

DE-9IM模型

DE-9IM(Dimensionally Extended 9-Intersection Model)是空间关系的标准描述模型,提供了更精确的空间关系判断。

关系矩阵

         Interior  Boundary  Exterior
Interior    II        IB        IE
Boundary    BI        BB        BE
Exterior    EI        EB        EE

每个位置的值表示两个几何对应部分的交集维度:

  • T:有交集(任意维度)
  • F:无交集
  • 0:交集为点
  • 1:交集为线
  • 2:交集为面
  • *:任意(不关心)

获取关系矩阵

/**
 * 获取几何关系矩阵
 */
public static String relate(Geometry a, Geometry b) {
    return a.relate(b).toString();
}

模式匹配

使用模式字符串判断特定空间关系:

/**
 * 判断几何是否符合给定关系
 * @param pattern 关系模式,如"T*T***FF*"
 */
public static boolean relatePattern(Geometry a, Geometry b, String pattern) {
    return a.relate(b, pattern);
}

常用模式

关系 模式
内部相交 T********
边界相交 *T*******
A完全包含B T*****FF*
相邻(共享边界) FF*FT****

几何相等判断

精确相等

判断两个几何是否结构完全相同:

/**
 * 判断几何是否对象结构相等
 * 必须有相同的节点和相同的节点顺序
 */
public static boolean equalsExact(Geometry a, Geometry b) {
    return a.equalsExact(b);
}

/**
 * 带容差的结构相等判断
 */
public static boolean equalsExactTolerance(Geometry a, Geometry b, double tolerance) {
    return a.equalsExact(b, tolerance);
}

规范化相等

判断两个几何在规范化后是否相同:

/**
 * 判断几何是否对象结构相等(不判断节点顺序)
 */
public static boolean equalsNorm(Geometry a, Geometry b) {
    return a.equalsNorm(b);
}

拓扑相等

判断两个几何是否表示相同的点集:

/**
 * 判断几何是否拓扑相等
 */
public static boolean equalsTopo(Geometry a, Geometry b) {
    return a.equalsTopo(b);
}

区别说明

  • equalsExact:节点顺序、数量都必须完全相同
  • equalsNorm:规范化后比较,忽略节点顺序
  • equalsTopo:只比较几何表示的空间是否相同

ESRI几何引擎

除了JTS,还可以使用ESRI几何引擎进行空间关系分析,其API基于WKT字符串:

空间关系判断

/**
 * 几何是否相交
 */
public static boolean intersects(String awkt, String bwkt, Integer wkid) {
    Geometry a = EsriUtil.createGeometryByWkt(awkt);
    Geometry b = EsriUtil.createGeometryByWkt(bwkt);
    SpatialReference sr = SpatialReference.create(wkid);
    return OperatorIntersects.local().execute(a, b, sr, null);
}

/**
 * 判断几何是否相离
 */
public static boolean disjoint(String awkt, String bwkt, Integer wkid) {
    Geometry a = EsriUtil.createGeometryByWkt(awkt);
    Geometry b = EsriUtil.createGeometryByWkt(bwkt);
    SpatialReference sr = SpatialReference.create(wkid);
    return OperatorDisjoint.local().execute(a, b, sr, null);
}

/**
 * 判断几何是否接触
 */
public static boolean touches(String awkt, String bwkt, Integer wkid) {
    Geometry a = EsriUtil.createGeometryByWkt(awkt);
    Geometry b = EsriUtil.createGeometryByWkt(bwkt);
    SpatialReference sr = SpatialReference.create(wkid);
    return OperatorTouches.local().execute(a, b, sr, null);
}

/**
 * 判断几何是否包含
 */
public static boolean contains(String awkt, String bwkt, Integer wkid) {
    Geometry a = EsriUtil.createGeometryByWkt(awkt);
    Geometry b = EsriUtil.createGeometryByWkt(bwkt);
    SpatialReference sr = SpatialReference.create(wkid);
    return OperatorContains.local().execute(a, b, sr, null);
}

选择建议

  • JTS:内存中的几何对象操作,性能更好
  • ESRI:基于WKT字符串操作,接口更简洁

实践案例

案例1:空间查询

查找与指定区域相交的所有要素:

/**
 * 空间查询:找出与指定区域相交的要素
 */
public List<WktFeature> spatialQuery(WktLayer layer, String queryWkt) {
    List<WktFeature> result = new ArrayList<>();
    Geometry queryGeom = GeometryConverter.wkt2Geometry(queryWkt);
    
    for (WktFeature feature : layer.getFeatures()) {
        Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
        if (featureGeom.intersects(queryGeom)) {
            result.add(feature);
        }
    }
    return result;
}

案例2:点在面内判断

判断一个点是否位于某个多边形内部:

/**
 * 判断点是否在面内
 */
public boolean isPointInPolygon(double x, double y, String polygonWkt) {
    GeometryFactory factory = new GeometryFactory();
    Point point = factory.createPoint(new Coordinate(x, y));
    Geometry polygon = GeometryConverter.wkt2Geometry(polygonWkt);
    
    // 使用contains判断(严格在内部)
    // 或使用intersects判断(含边界上的点)
    return polygon.contains(point);
}

案例3:相邻要素查找

查找与指定要素相邻(共享边界)的所有要素:

/**
 * 查找相邻要素
 */
public List<WktFeature> findAdjacentFeatures(WktLayer layer, WktFeature target) {
    List<WktFeature> result = new ArrayList<>();
    Geometry targetGeom = GeometryConverter.wkt2Geometry(target.getWkt());
    
    for (WktFeature feature : layer.getFeatures()) {
        if (feature.getWfId().equals(target.getWfId())) {
            continue;  // 跳过自己
        }
        
        Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
        
        // 相邻:接触但不重叠
        if (featureGeom.touches(targetGeom)) {
            result.add(feature);
        }
    }
    return result;
}

案例4:缓冲区范围查询

查找指定位置周边一定距离内的设施:

/**
 * 缓冲区范围查询
 * @param centerWkt 中心点WKT
 * @param distance 查询距离(米)
 * @param layer 要素图层
 */
public List<WktFeature> bufferQuery(String centerWkt, double distance, WktLayer layer) {
    List<WktFeature> result = new ArrayList<>();
    
    // 转换到投影坐标系计算缓冲区
    int projWkid = CrsUtil.getProjectedWkid(CrsUtil.getDh(centerWkt));
    String projWkt = CrsUtil.transform(centerWkt, 4490, projWkid);
    
    // 创建缓冲区
    Geometry center = GeometryConverter.wkt2Geometry(projWkt);
    Geometry buffer = center.buffer(distance);
    
    // 将缓冲区转回地理坐标系进行查询
    buffer = CrsUtil.transform(buffer, projWkid, layer.getWkid());
    
    // 查询相交要素
    for (WktFeature feature : layer.getFeatures()) {
        Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
        if (featureGeom.intersects(buffer)) {
            result.add(feature);
        }
    }
    return result;
}

案例5:空间关系矩阵分析

使用DE-9IM进行精确的空间关系分析:

/**
 * 分析两个地块的空间关系
 */
public String analyzeRelation(String wkt1, String wkt2) {
    Geometry geom1 = GeometryConverter.wkt2Geometry(wkt1);
    Geometry geom2 = GeometryConverter.wkt2Geometry(wkt2);
    
    // 获取关系矩阵
    String matrix = relate(geom1, geom2);
    
    // 解析关系类型
    if (geom1.equals(geom2)) {
        return "相等";
    } else if (geom1.contains(geom2)) {
        return "A包含B";
    } else if (geom1.within(geom2)) {
        return "A在B内";
    } else if (geom1.overlaps(geom2)) {
        return "部分重叠";
    } else if (geom1.touches(geom2)) {
        return "相邻(共享边界)";
    } else if (geom1.crosses(geom2)) {
        return "穿越";
    } else if (geom1.intersects(geom2)) {
        return "相交";
    } else {
        return "相离";
    }
}

性能优化

空间索引

对于大数据量的空间查询,使用空间索引可以显著提升性能:

// 构建R树索引
STRtree index = new STRtree();
for (WktFeature feature : layer.getFeatures()) {
    Geometry geom = GeometryConverter.wkt2Geometry(feature.getWkt());
    index.insert(geom.getEnvelopeInternal(), feature);
}
index.build();

// 使用索引查询
Geometry queryGeom = GeometryConverter.wkt2Geometry(queryWkt);
List<?> candidates = index.query(queryGeom.getEnvelopeInternal());

// 对候选要素进行精确判断
for (Object obj : candidates) {
    WktFeature feature = (WktFeature) obj;
    Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
    if (featureGeom.intersects(queryGeom)) {
        // 真正相交的要素
    }
}

外包矩形预筛选

在进行精确空间关系判断前,先用外包矩形快速过滤:

// 先判断外包矩形是否相交
if (!geomA.getEnvelopeInternal().intersects(geomB.getEnvelopeInternal())) {
    return false;  // 外包矩形不相交,几何必定不相交
}
// 再进行精确判断
return geomA.intersects(geomB);

小结

本章介绍了空间关系分析的核心内容:

  1. 基本空间关系:相交、相离、接触、交叉、包含、重叠
  2. 距离计算:最短距离计算和距离判断
  3. DE-9IM模型:精确的空间关系描述和模式匹配
  4. 几何相等:精确相等、规范化相等、拓扑相等
  5. 性能优化:空间索引和外包矩形预筛选

下一章将介绍空间计算操作,学习如何进行缓冲区分析、叠加分析等操作。

posted @ 2025-11-26 20:20  我才是银古  阅读(0)  评论(0)    收藏  举报