一图理解geos的九交矩阵模型——面面关系

九交模型用来表示二维点线面几何对象的关系,网上有很多介绍。本文将结合geos的实现和工作中遇到的问题来探讨九交模型。

(一)九交模型的定义

【英文名】Dimensionally Extended nine-Intersection Model (DE-9IM)

九交矩阵用来表示图形之间的关系。其横竖分别是两对象AB的内部interior、边界boundary、外部exterior的交集的维数,见下表。维数的取值为0-交于1点,1-交于线,2-交于面,F-代表-1,即不相交。见下表。

A  \  Binteriorboundaryexterior
interiorDim(I(A),I(B))Dim(I(A),B(B))Dim(I(A),E(B))
boundaryDim(B(A),I(B))Dim(B(A),B(B))Dim(B(A),E(B))
exteriorDim(E(A),I(B))Dim(E(A),B(B))Dim(E(A),E(B))

对于面域,其相交关系为完全重合、内含(这里指边点均无接触的包含)/外离、内/外共点、内/外共边、面交错,见下面黄线图。

(二)geos的九交模型之面

调试构面程序时,使用geos实现的九交模型来去除重复面总是各种问题,怀疑geos用的有问题,随即画图枚举各种情况进行测试。因为之前对九交模型理解的并不透彻,想当然地认为如下图第1个多边形与其自身及其他7个多边形间的8种关系应该是不同的九交矩阵。 表格中是geos得出的结果,

显然,虚线框里的2和7相对于多边形1的关系是同样的九交矩阵,即在B(当前比较图形,plgId的右Id)在A(第1个图形,plgId的左Id)“内部”时,内含和边接触无法区分,两者与点接触可以区分。(常见文字中的"内部"、"外部"字眼,感觉很模糊,不足以区分交于点、交于线、交错面.)

而B在A外部时,从plgId中13、14、15的mx可以看到,第5个元素(中心元素)分别是F、0、1,很好的区分出不同。

再来一个例子专门考究边接触。图中4、5号面与1号面相交的边的端点是1号面也有的端点,3、6号面与1号面相交的边的端点不是1号面的端点,是绘制时捕捉与1号面的边交点产生的。理论上4、5的九交矩阵一致,3、6的也一致。实际上,表格中4、5一致,2、6一致,3、6不同。说明对3、6图的测试产生的矩阵具有随机性。再看测试后求大面与交集求差的结果,intersectedNum字段为1号面减去交集区后的ring个数(-后为外环顶点)。3、4、5号面各自与大面做差后是一个环,2、6号面做差后是两个环。如果6号应该是两个,那么3号面为何不是两个?是因为精度吗?

    geos::geom::PrecisionModel *pm = new geos::geom::PrecisionModel(1000, 0, 0);
    geos::geom::GeometryFactory geomFact(pm);

下表是按照pm到0.001的精度的结果。实测按照0.01的精度结果也是一样。回到构面程序中去重复过程,因之前有打断这一步,不存在3和6的图形,当参与比较的两面为包含关系时,仅从大面与交集做差后的环数即可判断是否内含而不内接。

总结:当面域不含视觉上的公共边时,求交可靠;对于视觉上的共边对象,当重合边的小面顶点也是大面的顶点时,可靠;而当重合边的小面顶点不是大面的顶点时,九交矩阵有随机性、不可靠。

(三)geos的面域比较——difference()

对于下图,表格增加了intersectN列,表明将plgId的两个多边形求A->difference(B)后的多边形内外环总个数。多边形7 与多边形10,视觉上相同,但intersectN的值不同。原因是,多边形7与1的公共部分边的两节点M、N,在多边形1上并无那两个节点,是捕捉最近点绘制的,而多边形10与1的公共部分边的两个节点M、N也是多边形1的节点。个人觉得,geos这种问题的处理理想状态更应该是对于7多边形,与1比较difference后都是1个环,正如与6和9比较一样。对于8多边形,若用一个环则是自相交多边形,很别扭,反而是两个环符合预期。

最后,至于contains\covers\overlaps\touches\crosses\disjoint的区别,本人英语so so,还得从图、表结合才能理解准确。以下矩阵形式来源于geos的头文件注释,预究其坑还待作图考证。

isEquals(DimA,DimB)——T*F**FFF*.

isContains——即完全包含,可以点、边相接也可内含或相等,九交矩阵为T*****FF*;

isWithin——被包含,与contains相反,T*F**F***,是contains矩阵的转置;

isCovers——包括contains,九交矩阵可以是T*****FF*,*T****FF*,***T**FF*,****T*FF*;

isCoveredBy——其矩阵是covers的转置;

isOverlaps(DimA,DimB)——面交叠,面面\点点是T*T***T**,线线是1*T***T**;

isTouches(DimA,DimB)——边或点外接触,九交矩阵可以是FT*******, F**T*****, F***T****,第一个是F表明接触只能是外部的。

isCrosses(DimA,DimB)——非面面关系,点线、点面、线面是T*T******,线线是0********;

isDisjoint——外离,FF*FF****,仅图中 1和3关系是外离,也只有外离时,A->distance(B)才返回非0.

isIntersects——非disjoint 的都返回 true;

【参考】

[GIS算法] 2.1.2 维数扩展的九交集模型 空间关系的判定_geodoer的博客-CSDN博客_九交矩阵

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GEOS(Geometry Engine - Open Source,几何引擎开源库)是一个用于处理地理空间数据的C++库,提供了许多的几何分析函数,包括缓冲区分析。 要利用GEOS求一条线的缓冲区的外框线,可以按照以下步骤进行: 1. 定义线要素 首先需要定义一条线的要素,可以使用GEOS提供的GEOSGeom_createLineString函数创建一个线要素。 ```c GEOSCoordSequence* coords = GEOSCoordSeq_create(2, 2); GEOSCoordSeq_setX(coords, 0, 0.0); GEOSCoordSeq_setY(coords, 0, 0.0); GEOSCoordSeq_setX(coords, 1, 1.0); GEOSCoordSeq_setY(coords, 1, 1.0); GEOSGeometry* line = GEOSGeom_createLineString(coords); ``` 2. 计算缓冲区 使用GEOS提供的GEOSBuffer函数计算线的缓冲区,并指定缓冲区的半径和缓冲区的段数。 ```c double bufferDistance = 0.1; int bufferSegments = 16; GEOSGeometry* buffer = GEOSBuffer(line, bufferDistance, bufferSegments); ``` 3. 获取缓冲区的外框线 缓冲区的外框线是指缓冲区的边界线,可以使用GEOS提供的GEOSGeom_getExteriorRing函数获取缓冲区的外框线。 ```c GEOSGeometry* exterior = GEOSGeom_getExteriorRing(buffer); ``` 4. 输出外框线的坐标 最后可以使用GEOS提供的GEOSGeom_getCoordSeq函数获取外框线的坐标序列,并输出坐标。 ```c const GEOSCoordSequence* exteriorCoords = GEOSGeom_getCoordSeq(exterior); unsigned int exteriorSize = 0; GEOSCoordSeq_getSize(exteriorCoords, &exteriorSize); for (unsigned int i = 0; i < exteriorSize; i++) { double x, y; GEOSCoordSeq_getX(exteriorCoords, i, &x); GEOSCoordSeq_getY(exteriorCoords, i, &y); printf("(%f, %f)\n", x, y); } ``` 完整代码示例: ```c #include <geos_c.h> #include <stdio.h> int main() { // 创建线要素 GEOSCoordSequence* coords = GEOSCoordSeq_create(2, 2); GEOSCoordSeq_setX(coords, 0, 0.0); GEOSCoordSeq_setY(coords, 0, 0.0); GEOSCoordSeq_setX(coords, 1, 1.0); GEOSCoordSeq_setY(coords, 1, 1.0); GEOSGeometry* line = GEOSGeom_createLineString(coords); // 计算缓冲区 double bufferDistance = 0.1; int bufferSegments = 16; GEOSGeometry* buffer = GEOSBuffer(line, bufferDistance, bufferSegments); // 获取缓冲区的外框线 GEOSGeometry* exterior = GEOSGeom_getExteriorRing(buffer); // 输出外框线的坐标 const GEOSCoordSequence* exteriorCoords = GEOSGeom_getCoordSeq(exterior); unsigned int exteriorSize = 0; GEOSCoordSeq_getSize(exteriorCoords, &exteriorSize); for (unsigned int i = 0; i < exteriorSize; i++) { double x, y; GEOSCoordSeq_getX(exteriorCoords, i, &x); GEOSCoordSeq_getY(exteriorCoords, i, &y); printf("(%f, %f)\n", x, y); } // 释放内存 GEOSGeom_destroy(line); GEOSGeom_destroy(buffer); GEOSGeom_destroy(exterior); GEOSCoordSeq_destroy(coords); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值