SDO_INSIDE 返回零记录

问题描述 投票:0回答:2

所以我有一个餐桌村:

CREATE TABLE village (
  building_id integer PRIMARY KEY,
  name VARCHAR2(30),
  visitors integer,
  building SDO_GEOMETRY
);

还有餐桌访客:

create table visitors(
  id integer,
  position SDO_GEOMETRY 
);

以下是插入内容:

INSERT INTO village VALUES(2,'KircheV2', 4,
  SDO_GEOMETRY(
      2003,
      NULL,
      NULL,
      SDO_ELEM_INFO_ARRAY(1,1003,1),
      SDO_ORDINATE_ARRAY(100,100, 100,120, 120,100, 120,120)
  )
);


INSERT INTO visitors VALUES (1,
  SDO_GEOMETRY(
      2001,
      NULL,
      SDO_POINT_TYPE(110, 110, NULL),
      NULL,
      NULL
  )
);

出于某种原因,当我尝试获取“KircheV2”内的所有访问者时,SQL 语句始终返回零记录:

SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and (SDO_INSIDE(village.building,visitors.POSITION) = 'TRUE');

这背后的原因可能是什么?坐标110;110实际上应该在建筑物的中间,所以它应该在建筑物内部。

oracle oracle-spatial
2个回答
0
投票

您的数据不正确。你可以这样验证:

SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;

SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13348 [Element <1>] [Ring <1>]

1 row selected.

该错误意味着:

ORA-13348: polygon boundary is not closed

在 Oracle(实际上是所有存储系统)中,根据 OGC 规则,多边形必须闭合,即第一个顶点必须与最后一个顶点重复。所以:

INSERT INTO village VALUES(2,'KircheV2', 4,
  SDO_GEOMETRY(
      2003,
      NULL,
      NULL,
      SDO_ELEM_INFO_ARRAY(1,1003,1),
      SDO_ORDINATE_ARRAY(100,100, 100,120, 120,100, 120,120, 100,100)
  )
);

但是select仍然没有返回任何结果。这是为什么?

SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;

SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13349 [Element <1>] [Ring <1>][Edge <2>][Edge <4>]

1 row selected.

这个错误意味着:

ORA-13349: polygon boundary crosses itself

这是有道理的:顶点显然形成了蝴蝶形状:

100,100、100,120、120,100、120,120、100,100

假设你想形成一个简单的矩形,那么正确的形状是:

100,100、100,120、120,120、120,100、100,100

INSERT INTO village VALUES(2,'KircheV2', 4,
  SDO_GEOMETRY(
      2003,
      NULL,
      NULL,
      SDO_ELEM_INFO_ARRAY(1,1003,1),
      SDO_ORDINATE_ARRAY(100,100, 100,120, 120,120, 120,100, 100,100)
  )
);

还是没有结果。为什么?

SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;

SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13367 [Element <1>] [Ring <1>]

1 row selected.

这意味着:

ORA-13367: wrong orientation for interior/exterior rings

多边形中的环必须正确定向。外环必须是逆时针方向,内环(孔)必须是顺时针方向。所以你需要这样写:

100,100、120,100、120,120、100,120、100,100

INSERT INTO village VALUES(2,'KircheV2', 4,
  SDO_GEOMETRY(
      2003,
      NULL,
      NULL,
      SDO_ELEM_INFO_ARRAY(1,1003,1),
      SDO_ORDINATE_ARRAY(100,100, 120,100, 120,120, 100,120, 100,100)
  )
);

还是没有结果!但那是因为您的查询的表述不正确。

SDO_INSIDE(a,b)
查找完全位于 B 内部的 A 的所有出现。在您的情况下,这就像询问访问者内部的建筑物一样。显然你想要相反,所以要么说:
SDO_INSIDE(visitor, building)
SDO_CONTAINS (building,visitor)
,就像这样:

SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and SDO_INSIDE(visitors.POSITION,village.building) = 'TRUE';
SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and SDO_CONTAINS(village.building,visitors.POSITION) = 'TRUE';

一些补充意见:

  1. 我想你的例子纯粹是人为的?在现实生活中,您的多边形将来自某些 GIS 系统 - 例如加载到数据库中的 ESRI 形状文件,或从某些 GIS 工具捕获的形状。无论哪种方式,两者都会产生正确的形状,因为所有工具都适用 OGC 规则来进行形状闭合和方向。如果不是,验证函数会告诉您错误,

    sdo_util.rectify_geometry
    函数将纠正基本错误。

  2. 您还需要了解空间运算符(INSIDE 与 CONTAINS 等)及其效果。该文档解释了它们的含义。请注意,SDO_xxx 运算符集与 OGC 定义的 ST_xxx 函数略有不同。

  3. 您未指定任何坐标系(SDO_SRID 为 NULL)。虽然这在您的人工示例中有效,但在现实生活中您应该始终使用正确的坐标系。特别是,如果您的形状是大地测量的(长/纬度),则必须使用正确的 SRID:4326 来表示。这保证了所有计算都是在地球椭圆体形状的背景下完成的。如果您想要执行基于距离的查询或测量长度、距离或面积,这一点尤其重要。对投影数据使用正确的 SRID 同样重要。它允许您执行查询,而不管使用的坐标系如何:例如查找 GPS 点位于哪个地块(在本地投影中)。

  4. 性能和索引。确保空间索引到位。虽然 Oracle 12.2 允许您在不定义索引的情况下执行查询,但之前的版本始终需要索引(如果不存在则会失败)。如果该表相当大,则在没有空间索引的表上执行查询可能会非常慢。例如,以您的村庄和游客为例。假设您想从包含 1000 万访客的表中找出某一特定建筑物中的所有访客。如果访客表上没有任何索引,数据库将需要将每个访客与所选建筑物进行比较。这对 CPU 来说会很昂贵(I/O 不是真正的问题)。

  5. 最后,正确编写查询很重要。在像

    F(a,b)
    这样的运算符中,
    b
    用于搜索
    a
    ,因此
    b
    应该是较小的集合。例如,查找该建筑物中的所有访客必须写为
    SDO_INSIDE(visitors, buildings)
    。相反(该客户在哪栋大楼?)写为
    SDO_CONTAINS(buildings, visitors)


0
投票

如果错误是

ORA-13348: polygon boundary is not closed
,可以使用

修复
SQL> Update village set SDO_UTIL.RECTIFY_GEOMETRY(building,0.005) where building_id=2;

此功能有助于修复 Oracle 上损坏的几何图形。

© www.soinside.com 2019 - 2024. All rights reserved.