sshuair's note

PostGIS中的Geometry相互关系

2016-12-01

在二维地理对象中,两两实体之间的关系可以用三种关系表达:

  • Interior:包含,一个物体在另一个物体中,并且边界不相交
  • Exterior:相离,两个物体谁也不触碰谁
  • Boundary:相切,两个物体既不包含也不相离

那么在PostGIS中intersection如何定义呢?即两个相交的对象,其两者之间的关系必然是interior或者boundary,并且有共同的点,这些共有的点就是相交部分。

Intersections

PostGIS中有两个函数,一个是ST_Intersects,返回Ture或者False,另一个是ST_Intersection,返回连个对象实体相交的部分,下面举个例子说明两者的用法与差别

1
2
3
4
5
6
7
8
9
WITH geom_polygon as (SELECT st_geomfromtext('
POLYGON(( 2 4.5,3 2.6,3 1.8,2 0,
-1.5 2.2,0.056 3.222,
-1.5 4.2,2 6.5,2 4.5))') as geom ),
geom_line as (SELECT ST_GeomFromText('LINESTRING(-0.62 5.84,-0.8 0.59)') as geom)
SELECT st_intersects(geom_polygon.geom, geom_line.geom),
st_intersection(geom_polygon.geom, geom_line.geom)
from geom_polygon, geom_line;

st_intersects返回True,st_intersection返回相交部分,结果如下:

1
true,010500000002000000010200000002000000A29BA79F1D08E5BF467DAF9FE70313401EBAF08E0532E6BF477C940E948B0D40010200000002000000A24F6ECEDF46E7BF676F4BAADCA805405BECFC97EA57E8BFA1C6E103DCC3FB3F

实现的效果如下图所示:
intersect

需要注意的是:

  • ST_Intersection返回的是A和B的共有部分,包括边界;
  • ST_Intersection 和 ST_Intersects的参数都是可交换位置的,ST_Intersection(A,B) = ST_Intersection(B,A)、 ST_Intersects(A,B) = ST_Intersects(B,A);
  • A和B不一定非得是相同类型的geometry
  • ST_Intersection返回的结果一定是等于会小于输入的geometry维度,并且返回的结果不一定是一个geometry,可能是geometry collections
  • 如果A和B不相交,那么ST_Intersection返回的结果是空的geometry

为了后面的使用,这里使用一个通用的表(example_set)来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE example_set(ex_name varchar(150) PRIMARY KEY, geom geometry);
INSERT INTO example_set(ex_name, geom)
VALUES(
'A polygon with hole',
ST_GeomFromText(
'POLYGON(
(110 180, 110 335,184 316,260 335,260 180,209 212.51,
110 180), (160 280,200 240, 220 280,160 280))')
),
('A point', ST_GeomFromText('POINT(110 245)')),
(
'A linestring',
ST_GeomFromText('LINESTRING(110 245,200 260, 227 309)')
),
('A multipoint', ST_GeomFromText('MULTIPOINT(110 245,200 260)'));

包含4个feature:

  • A point
  • A multipoint
  • A linestring
  • A polygon with hole

空间关系如下图:example_set

Contains and within

Contains和within是两个相互对立的关系,如果A在B中(A within B),那么B包含A(B contain A),在PostGIS中分别用ST_Contains(B, A)、ST_Within(A, B)表示,返回结果均为Ture或者False。需要注意的是只有当其中一个为True时,另一个才为False,如果两个要素是同一个元素那么两者都为Ture,在边界上的点不能被包含(如本例中的点对于线、面的情况),利用上面的example_set来做一个演示:

1
2
3
4
5
select A.ex_name as a_name, B.ex_name as b_name,
st_contains(A.geom, B.geom) as a_contain_b,
st_within(A.geom, B.geom) as a_within_b,
st_intersects(A.geom, B.geom) as a_intersect_b
from example_set A cross join example_set B;
a_name b_name a_contain_b a_within_b a_intersect_b
A polygon with hole A polygon with hole true true true
A polygon with hole A point false false true
A polygon with hole A linestring false false true
A polygon with hole A multipoint false false true
A point A polygon with hole false false true
A point A point true true true
A point A linestring false false true
A point A multipoint false true true
A linestring A polygon with hole false false true
A linestring A point false false true
A linestring A linestring true true true
A linestring A multipoint true false true
A multipoint A polygon with hole false false true
A multipoint A point true false true
A multipoint A linestring false true true
A multipoint A multipoint true true true

Covers and covered by

那么对于contain中点落在边界的情况怎么办呢,PostGIS中提供了ST_Covers和ST_CoveredBy来包括在边界上的情况,比如上例中的线与点的关系,用ST_Covers则结果为True,而用ST_Contains则为False。

1
2
3
4
5
6
7
SELECT
A.ex_name As a_name, B.ex_name As b_name,
ST_Covers(A.geom,B.geom) As a_cover_b,
st_coveredby(A.geom, B.geom) as a_coverby_b,
ST_Intersects(A.geom,B.geom) As a_intersect_b
FROM example_set As A CROSS JOIN example_set As B
WHERE NOT (ST_Covers(A.geom,B.geom) = ST_Contains(A.geom,B.geom));

结果如下:

a_name b_name a_cover_b a_coverby_b a_intersect_b
A polygon with hole A point true false true
A linestring A point true false true

Contains properly

可以看到st_contains中,自己包含自己,如何避免这种情况呢,就可以用绝对包含(ST_ContainsProperly)来判断,绝对包含要求A必须完全在B的内部

1
2
3
4
5
6
7
SELECT
A.ex_name As a_name, B.ex_name As b_name,
ST_ContainsProperly(A.geom,B.geom) As a_co_b,
ST_Intersects(A.geom,B.geom) As a_in_b
FROM example_set As A CROSS JOIN example_set As B
WHERE NOT ( ST_ContainsProperly(A.geom,B.geom) =
ST_Contains(A.geom,B.geom) );

结果如下:

a_name b_name a_ContainsProperly_b a_Intersects_b
A polygon with hole A polygon with hole false true
A linestring A linestring false true
A linestring A multipoint false true

Overlap

overlap表示两个geometry具有相同的维度,并且相交(intersect),其中一个没有完全包含在另一个中。

Touch

邻接表示两个geometry至少有一个共同点,并且两者没有任何一个点在对方的内部(interior)。ST_Touches(A,B)=ST_Touches(B,A)。

Cross

Cross表示两个geometry有相同的属于对方的内部点,当时并不是这个对象所有的点,如果两个geometry是Touch关系,那么他们不会cross。

Disjoint

相离,Disjoint与intersect对立,表示两个geometry没有共同的内部点或者边界,通常可以用ST_Disjoint与ST_Intersects来检测geometry是否合法,如果返回的结果都是false或者都是true,那么这两个geometry中肯定有非法的geometry对象。

最后来看看所有的这些函数在example_set上的结果:

a_name b_name a_contain_b a_within_b a_cover_b a_coverby_b a_containsproperly_b a_overlap_b a_touch_b a_cross_b a_disjoint_b a_intersect_b
A polygon with hole A polygon with hole true true true true false false false false false true
A polygon with hole A point false false true false false false true false false true
A polygon with hole A linestring false false false false false false false true false true
A polygon with hole A multipoint false false false false false false true false false true
A point A polygon with hole false false false true false false true false false true
A point A point true true true true true false false false false true
A point A linestring false false false true false false true false false true
A point A multipoint false true false true false false false false false true
A linestring A polygon with hole false false false false false false false true false true
A linestring A point false false true false false false true false false true
A linestring A linestring true true true true false false false false false true
A linestring A multipoint true false true false false false false false false true
A multipoint A polygon with hole false false false false false false true false false true
A multipoint A point true false true false true false false false false true
A multipoint A linestring false true false true false false false false false true
A multipoint A multipoint true true true true true false false false false true

ST_Relate

参考:PostGIS in Action(second edition)

扫描二维码,分享此文章