DE-9IM 空间关系模型 与Boost Geometry Lib
DE-9IM 空间关系模型
DE-9IM 空间关系模型
简述#
DE-9IM 是Dimensionally Extended 9-Intersection Model 的缩写,直接翻译为 维度扩展的 9 相交模型好像比较别扭,但一时也找不到比较好的翻译。
DE-9IM 模型是用于描述两个 二维几何对象(点、线、面) 之间的空间关系的一种模型,它使用一个 3 x 3
的矩阵来描述几何关系类别(相交部分的维度)。
网上很多关于 DE-9IM 的介绍都是翻译自 https://en.wikipedia.org/wiki/DE-9IM 或者 GeoTools/userguide/dim9 等文档的,我这里就不做这些翻译了。
因为要给别人讲述清楚这个东西,所以自己总结了下,在这里做个记录。
空间关系#
这里主要是说二维平面下的两个几何对象之间的空间关系。
主要有两种关系:
- 相交(Intersects)
- 相离(Disjoint)
相离 关系很简单,就是两个几何对象相互独立,之间没有任何相接触到的地方。形象的说,比如一张纸上画了两个多边形(或者点或线),你可以再画一条线把它们分开,而且这条画的线可以不碰到这两个多边形。
相交(Intersects)关系图解#
相交(Intersects)关系就比较复杂一点了,它又可以分为好几类。
下面对相交(Intersects)的各种情况进行分类介绍,包括点、线、面相互之间形成相应关系的9种情况的图像说明(图是网上找的,来自一个国外的网站)。
下面的关系又称为空间谓词
,下面的图式中都说明的是 红色
几何对象 相对于 蓝色
几何对象的空间关系(方向不能反)。
包含(Contains )#
包含表示一个几何对象完全在另一个几何对象内部。
下面图是红色
几何对象包含(Contains)
了蓝色
几何对象的在其里头的示意图。
点
不可能包含线
和面
,线
也不可能包含面
在里头,所以有三个位置是 X
。
横跨(Crosses)#
横跨关系一般又叫做交叉或者跨立,这里我还是觉得横跨比较合适。
横跨关系一定是建立在线
与线
或者线
与面
之间(可以理解为线
才能够横在别的形状上嘛)。
下面图是红色
几何对象横跨(Crosses)
在蓝色
几何对象的上面的示意图。
等于(Equals)#
等于(Equals)表示两几何对象完全一致(包括形状和位置),是一种完全重合的情况。
下面图是红色
几何对象等于(Equals)
蓝色
几何对象的示意图。
重叠(Overlaps)#
重叠(Overlaps)表示两个几何对象对象之间有一部分是重合的情况(完全重合就是 Equals 了)。
重叠只发生在 线-线
、面-面
之间,线
与面
只能是Crosses
、Within
或者Touches
,点没有长度和面积(就是不存在部分,不可以分割),不可能Overlaps
。
下面图是红色
几何对象重叠(Overlaps)
在蓝色
几何对象上的示意图。
触碰(Touches)#
触碰(Touches)这个一般又叫做接触或者触及,表示两个几何对象的边界
部分有重合部分的情况,也就是说两个几何对象有公共点或者公共边线的情况。
点
和点
之间是没有Touches
关系,点
和线
与面
的触碰关系一定是点
在线的端点或者面的边上。
线
和面
两种形状之间的Touches
关系可能是有公共点,也可能是有公共边。
被包含(Within)#
被包含(Within)一般也描述为在什么内部(所以也有用 inside 的),表示一个几何对象完全处于另一个几何对象内部。这个其实就是包含(Contains)的反向关系。
就是说 A Within B
就等于是 B Contains A
。
下面图是红色
几何对象被包含(Within)
在蓝色
几何对象里头的示意图。
DE-9IM 模型#
DE-9IM 模型把几何对象分为 内部
、边界
、外部
三个部分,两个几何对象这三个部分两两之间的关系,就可以组合为一个3X3
大小(就是 9 个值)的矩阵,这9个值的组合,就表示两个几何对象的空间关系。
如上图所示,淡紫色的多边形为条件几何对象(A)
,淡红色的多边形为 测试几何对象(B)
,图中的 黑色 部分表示两个几何对象对应的部分(内部、边界、外部)之间的相交(或者说重合)部分( A.[I/B/E] intersection B.[I/B/E]
)。
DE-9IM 用数值来表示相交(重合)部分的情况(维度),是点还是线还是面,或者是没有。
-1
没有重合部分0
重合部分为点
(零维)1
重合部分为线
(一维)2
重合部分为面
(二维)
如上面图中,它的 DE-9IM 计算值(在 GeoTools/GEOS 中使用 relate 计算)就是 2 1 2 1 0 1 2 1 2
。
使用下面的方式来表示矩阵中①~⑨的值是以下的任意一种空间关系:
T
= 相交部分维度为0
、1
、2
。F
= 相交部分维度< 0
。*
= 相交部分维度为任意值。0
= 相交部分维度为0
。1
= 相交部分维度为1
。2
= 相交部分维度为2
。
而两个几何对象具有什么空间关系(就是上面所说的Intersects
、Disjoint
、Contains
...等),就可以通过两个几何对象的 DE-9IM 值来确定。
根据上面对各个空间谓词的定义,可以得到每个空间谓词对于的 DE-9IM 关系表示。一般我们写程序的时候不直接使用 relate
计算的结果去判断两个几何对象的空间关系,而是直接使用相应的空间谓词去判断。
空间谓词 | DE-9IM值 | 说明 | 等价于 |
---|---|---|---|
Equals | T*F**FFF* |
如果两个几何对象的内部相交,并且一个几何对象的内部或边界没有任何部分与另一个几何对象的外部相交,那么这两个几何对象在拓扑上是相等的 | Within & Contains |
Disjoint | FF*FF**** |
两个几何对象它们没有公共点。 它们形成了一组不连续的几何形状。 | not Intersects |
Touches | FT******* 或F**T***** 或F***T**** |
两个几何对象它们至少有一个公共点,但它们的内部不相交。 | |
Contains | T*****FF* |
被测试几何对象B位于条件几何对象A内部(A Contains B)。 | Within(B,A) |
Within(Inside) | T*F**F*** |
上面的 Contains 操作对象调换下方向就是。 | Contains(B,A) |
Crosses | 线 Corsses 面 = T*T****** 面 Corsses 线 = T*****T** 线 Corsses 线 = 0******** |
A Contains B:它们有一些但不是全部的内部点是相同的,并且相交部分的维度数小于A或B中至少一个的维度数。 | |
Overlaps | 面 Overlaps 面 = T*T***T** 线 Overlaps 线 = 1*T***T** |
A Overlaps B:它们有一些但不是所有的点是相同的,它们有相同的维数,两个几何内部的交点和这些几何本身的维数是相同的。 | |
Intersects | T******** 或*T******* 或***T***** 或****T**** |
两个几何对象它们至少有一个公共点。 | not Disjoint |
Covers (涵盖了) |
T*****FF* 或*T****FF* 或***T**FF* 或****T*FF* |
A Covers B:A的至少一个点位于B,A的任何点都不位于B的外部 | CoveredBy(B,A) |
CoveredBy (被涵盖) |
T*F**F*** 或*TF**F*** 或**FT*F*** 或**F*TF*** |
A CoveredBy B:A上至少有一个点在 B 上,而 A 上没有一个点在b的外部 | Covers(B,A) |
最在项目中要用到计算几何的东西,计算三维空间中面片与六面体的相交判断,通过各种搜索发现boost库中的Geometry模块还不错,可以比较容易地实现。这里记录一下这个库的基本情况。
1、常见几何对象
#include <boost/geometry.hpp> #include <boost/geometry/geometries/point_xy.hpp> #include <boost/geometry/geometries/point.hpp> #include <boost/geometry/geometries/multi_point.hpp> #include <boost/geometry/geometries/segment.hpp> #include <boost/geometry/geometries/polygon.hpp> #include <boost/geometry/geometries/multi_polygon.hpp> #include <boost/geometry/geometries/linestring.hpp> #include <boost/geometry/geometries/multi_linestring.hpp> #include <boost/geometry/geometries/box.hpp> #include <boost/geometry/geometries/ring.hpp> #include <boost/geometry/geometries/variant.hpp>
Boost.Geometry的model有point_xy, point, multi_point, ,segment,linestring,multi_linestring, box,ring,polygon,multi_polygon, variant.
model::point
model::d2::point_xy
model::linestring
model::polygon
model::multi_point
model::multi_linestring
model::multi_polygon
model::box,
model::ring
model::segment
model::referring_segment
model::point
Basic point class, having coordinates defined in a neutral way.
Description
Defines a neutral point class, fulfilling the Point Concept. Library users can use this point class, or use their own point classes. This point class is used in most of the samples and tests of Boost.Geometry This point class is used occasionally within the library, where a temporary point class is necessary.
model::d2::point_xy
2D point in Cartesian coordinate system
model::linestring
A linestring (named so by OGC) is a collection (default a vector) of points.
model::polygon
The polygon contains an outer ring and zero or more inner rings.
2、常见算法
提供的算法有:面积、长度、周长、质心、凸壳、交集(剪裁)、内(多边形中的点)、距离、包络线(边界框)、简化、变换等。
area
assign
append
buffer
centroid
clear
convert
convex_hull
correct
covered_by
crosses
densify
difference
discrete_frechet_distance
discrete_hausdorff_distance
disjoint
distance
envelope
equals
expand
for_each
intersection
intersects
is_empty
is_simple
is_valid
length
make
num_geometries
num_interior_rings
num_points
num_segments
overlaps
perimeter
relate
relation
reverse
simplify
sym_difference
touches
transform
union_
unique
within
常见的有以下几种:
计算面积
Boost::Geometry::area(obj1)
计算距离
Boost::Geometry::distance(obj1, obj2)
判断是否相交
Boost::Geometry::intersects(obj1, obj2)
计算交点
Boost::Geometry::intersection(obj1, obj2, result)
判断是否在box内
Boost::Geometry::within(obj1, obj2)
3、几何图形的输入输出
DSV (Delimiter-Separated Values)
WKT (Well-Known Text)
SVG (Scalable Vector Graphics)
前两种是文本格式数据流,第三种是图形化输入和输出。
4、几何图形的算术运算
add_point
add_value
assign_point
assign_value
cross_product
cross_product
divide_point
divide_value
dot_product
multiply_point
multiply_value
subtract_point
subtract_value
6、官方示例
https://www.boost.org/doc/libs/1_69_0/libs/geometry/doc/html/geometry/spatial_indexes/rtree_examples/index_of_polygons_stored_in_vector.html
#include <boost/geometry.hpp> #include <boost/geometry/geometries/point.hpp> #include <boost/geometry/geometries/box.hpp> #include <boost/geometry/geometries/polygon.hpp> #include <boost/geometry/index/rtree.hpp> #include <cmath> #include <vector> #include <iostream> #include <boost/foreach.hpp> namespace bg = boost::geometry; namespace bgi = boost::geometry::index; int main() { typedef bg::model::point<float, 2, bg::cs::cartesian> point; //define point 2 dimension typedef bg::model::box<point> box; typedef bg::model::polygon<point, false, false> polygon; // ccw, open polygon typedef std::pair<box, unsigned> value; // polygons std::vector<polygon> polygons; // create some polygons for ( unsigned i = 0 ; i < 10 ; ++i ) { // create a polygon polygon p; for ( float a = 0 ; a < 6.28316f ; a += 1.04720f ) { float x = i + int(10*::cos(a))*0.1f; float y = i + int(10*::sin(a))*0.1f; p.outer().push_back(point(x, y)); } // add polygon polygons.push_back(p); } // display polygons std::cout << "generated polygons:" << std::endl; BOOST_FOREACH(polygon const& p, polygons) std::cout << bg::wkt<polygon>(p) << std::endl; // create the rtree using default constructor bgi::rtree< value, bgi::rstar<16, 4> > rtree; // fill the spatial index for ( unsigned i = 0 ; i < polygons.size() ; ++i ) { // calculate polygon bounding box box b = bg::return_envelope<box>(polygons[i]); // insert new value rtree.insert(std::make_pair(b, i)); } // find values intersecting some area defined by a box box query_box(point(0, 0), point(5, 5)); std::vector<value> result_s; rtree.query(bgi::intersects(query_box), std::back_inserter(result_s)); // find 5 nearest values to a point std::vector<value> result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); // note: in Boost.Geometry the WKT representation of a box is polygon // note: the values store the bounding boxes of polygons // the polygons aren't used for querying but are printed // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt<box>(query_box) << std::endl; std::cout << "spatial query result:" << std::endl; BOOST_FOREACH(value const& v, result_s) std::cout << bg::wkt<polygon>(polygons[v.second]) << std::endl; std::cout << "knn query point:" << std::endl; std::cout << bg::wkt<point>(point(0, 0)) << std::endl; std::cout << "knn query result:" << std::endl; BOOST_FOREACH(value const& v, result_n) std::cout << bg::wkt<polygon>(polygons[v.second]) << std::endl; return 0; }