下午好!我是Victor,Gems开发人员。每天,我们的团队都会处理复杂性和质量各异的空间数据。在Postgresql中使用Postgis执行空间相交操作时,我们遇到以下错误:
XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001
导致错误的请求如下所示:
select q1.key,st_asGeoJson(geoloc)
from usahalinsk.V_GEO_OOPT q1
where ST_Intersects(geoloc,
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":
[[[11165.15,2087.5],[11112,2066.6],[11127.6,2022.5],
[11122.6,2020.7],
[11122.25,2021.2],[11107.07,2015.7],
[11121,1947],[11123.48,1922.99],[11128.42,1874.4],
[11131.5,1875],[11140.96,1876.81],[11160.73,1880.59],
[11201.04,1888.3],[11194.2,1908],[11221.93,1916.57],
[11223.3,1917],[11165.15,2087.5]]]}'))
该问题的解决方案阻止了用户的工作,因为它不允许在数据上构建报告,并且减慢了提供服务的工作。我们正在开发的系统中的许多动作,例如:为土地图准备布局,为土地图准备城市规划计划,以及其他,都使用这样的空间操作。
我们假设问题是几何图形不正确。如果查询中涉及的对象具有自相交或重复点,则通常由相交操作生成此错误。这些几何形状错误的示例如下所示。 (多边形边界相交,并且直线上有两个相同的坐标)
我们已经进行了自己的调查,以找出错误的原因,并希望告诉您。
我们目前正在使用Postgis 2.4和Postgresql 9.6。让我们直接练习。让我们检查常量几何的有效性,并发现一切正常。
我们可以假设问题在表usahalinsk.V_GEO_OOPT中(我们在其中查找交集)。为了确认假设,我们也将检查这些数据。
但是我们在这里也没有发现错误。另外,数据根本不包括在样本中。如果它们是正确的,则将通过Postgis st_makeValid函数更正找到的条目来解决该任务。
但是视图中没有错误,并且不执行请求。我们建议看一下他的计划。
注意:在实际模型中,我们使用三列几何图形(用于多边形,直线和点),但是为了简洁起见,我们将其称为geoloc字段-它存储几何图形并在视图中显示。
我们的视图usahalinsk.V_GEO_OOPT是从具有空间数据usahalinsk.d_geometry的表中进行选择而构建的,并且在具有几何形状的字段上创建了空间索引。
这意味着在执行查询时,正在读取索引,并且在表中的某个位置(没有进入我们的选择),索引中包含无效的空间数据。它跨整个表构建。
让我们尝试删除索引:
DROP INDEX usahalinsk.d_geometry_cs1_all_sx;
让我们尝试解决有问题的请求。
它运行没有错误。我们确认问题出在索引中。您可以返回索引,但要有正确的几何条件:
CREATE INDEX d_geometry_cs1_all_sx
ON usahalinsk.d_geometry
USING gist(geoloc)
where st_isvalid(geoloc)=true;
让我们检查实施并查看计划。
该请求运行无误,并且还使用了计划中的索引。这种解决方案的缺点可能是插入/更新速度变慢,因为 另外,重建索引时将检查条件。
让我们将此更改返回,仍然尝试查找索引中的哪些对象导致我们的查询失败。
DROP INDEX usahalinsk.d_geometry_cs1_all_sx;
CREATE INDEX d_geometry_cs1_all_sx
ON usahalinsk.d_geometry
USING gist
(geoloc);
让我提醒您,我们具有错误位置的坐标:
XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001
但是,如果我们在数据中搜索或作为IsValidReason函数的结果(给出错误原因)进行搜索,我们将找不到任何类似的结果。
select key,ST_IsValidReason(geoloc)
from usahalinsk.d_geometry
where st_isvalid(geoloc)!=true
and ST_AsText(geoloc) like '%3844.9200000000001%';
select key,ST_IsValidReason(geoloc)
from usahalinsk.d_geometry
where st_isvalid(geoloc)!=true
and ST_IsValidReason(geoloc) like '%3844.9200000000001%';
您可以使用以下脚本来查找影响查询的对象。我们将检查表中的每个对象,并将其与所需的常数相交。在执行期间,我们捕获异常并检查其内容。如果错误包含我们需要的坐标,则这就是我们的问题几何。
do
$$
declare
tKey bigint;
rec record;
error_text text;
--
error_info text:='GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001';
begin
--
for rec in(select key from usahalinsk.d_geometry)
loop
begin
select key into tKey
from (select * from usahalinsk.d_geometry q1
--
where q1.key=rec.key
and ST_Intersects(geoloc,
--
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[11165.15,2087.5],
[11112,2066.6],[11127.6,2022.5],[11122.6,2020.7],
[11122.25,2021.2],[11107.07,2015.7],[11121,1947], [11123.48,1922.99],[11128.42,1874.4],
[11131.5,1875],[11140.96,1876.81], [11160.73,1880.59],[11201.04,1888.3],
[11194.2,1908],[11221.93,1916.57],[11223.3,1917],
[11165.15,2087.5]]]}'))) geoQ;
exception when others then
--
GET STACKED DIAGNOSTICS error_text = MESSAGE_TEXT;
-- ,
if error_text=error_info then
raise info '%',rec.key;
end if;
end;
end loop;
end$$;
结果,我们得到了三个易于修复的几何图形键:
update usahalinsk.d_geometry
set cs1_geometry_polygone=st_collectionextract(st_makevalid(geoloc),3)
where key in(
1000010001988961,
1000010001989399,
1000010004293508);
我将回答出现的问题:“为什么不可能校正表中的所有错误几何图形,以免选择性地寻找原因?” ...
事实是空间数据是从各种来源(包括Rosreestr)进入我们的系统的,我们无法对所有数据进行校正(通常会伴随失真)。收到必要的密钥后,我们将分析它们代表的数据以及是否有可能对其进行更正。
找出错误原因的琐碎工作可以变成整个调查,最后是更正脚本。
问题的一个更复杂的版本:如果交集不是用常量执行,而是用另一个表执行,该怎么办?或者,将第一个表中的每个参与对象与第二个表中的每个对象相交。并捕获异常。
您多久遇到一次几何问题,如何确保空间数据的质量?