PostGis。如何在空间查询中发现错误?

图片


下午好!我是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)进入我们的系统的,我们无法对所有数据进行校正(通常会伴随失真)。收到必要的密钥后,我们将分析它们代表的数据以及是否有可能对其进行更正。



找出错误原因的琐碎工作可以变成整个调查,最后是更正脚本。



问题的一个更复杂的版本:如果交集不是用常量执行,而是用另一个表执行,该怎么办?或者,将第一个表中的每个参与对象与第二个表中的每个对象相交。并捕获异常。



您多久遇到一次几何问题,如何确保空间数据的质量?



All Articles