PostGis. ¿Cómo encuentro un error en una consulta espacial?

imagen


¡Buenas tardes! Soy Victor, desarrollador de Gems Development. Todos los días, nuestro equipo trabaja con datos espaciales de diversa complejidad y calidad. Al realizar una operación de intersección espacial con Postgis en Postgresql, encontramos el siguiente error:



XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001



La solicitud que conduce al error se ve así:



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]]]}'))



La solución a este problema bloquea el trabajo de los usuarios, ya que no permite construir informes sobre los datos y ralentiza el trabajo de prestación de servicios. Muchas acciones en el sistema que estamos desarrollando, como: preparar un trazado para un terreno, preparar un plan urbanístico para un terreno, y otras, utilizar operaciones espaciales como esta.



Supongamos que el problema es una geometría incorrecta. A menudo, este error es generado por la operación de intersección si los objetos involucrados en la consulta tienen auto-intersecciones o puntos duplicados. Un ejemplo de estos errores geométricos se puede ver a continuación. (El borde del polígono se cruza y hay dos coordenadas idénticas en la línea)





Realizamos nuestra propia investigación para encontrar las causas del error y queremos informarle al respecto.

Actualmente estamos usando Postgis 2.4 y Postgresql 9.6. Vayamos directamente a practicar. Comprobemos la validez de la geometría constante y encontremos que todo funciona correctamente.





Podemos suponer que el asunto está en la tabla (vista) usahalinsk.V_GEO_OOPT en la que buscamos intersecciones. Para confirmar la hipótesis, también comprobaremos estos datos.





Pero aquí tampoco encontramos errores. Además, los datos no se incluyeron en la muestra en absoluto. Si lo fueran, entonces la tarea se resolvería corrigiendo las entradas encontradas a través de la función st_makeValid de Postgis.



Pero no hay errores en la vista y la solicitud no se ejecuta. Sugerimos mirar su plan.





Nota: en el modelo real usamos tres columnas para la geometría (para polígonos, líneas y puntos), pero por brevedad lo llamaremos campo geoloc: almacena la geometría y la muestra en la vista.



Nuestra vista usahalinsk.V_GEO_OOPT se construye como una selección de la tabla con datos espaciales usahalinsk.d_geometry y se crea un índice espacial en el campo con geometría.



Esto significa que cuando se ejecuta la consulta, se lee el índice y, en algún lugar de la tabla, sin entrar en nuestra selección, hay datos espaciales no válidos que se incluyeron en el índice. está construido en toda la mesa.



Intentemos eliminar el índice:



DROP INDEX usahalinsk.d_geometry_cs1_all_sx;


E intentemos cumplir con la solicitud problemática.





Funcionó sin errores. Confirmamos que el problema está en el índice. Puede devolver el índice, pero con la condición de la geometría correcta:



CREATE INDEX d_geometry_cs1_all_sx
  ON usahalinsk.d_geometry
  USING gist(geoloc)
  where st_isvalid(geoloc)=true;


Revisemos la implementación y veamos el plan.





La solicitud se ejecutó sin errores y también se utiliza el índice del plan. La desventaja de esta solución puede ser la ralentización de la inserción / actualización, tk. Además, se comprobará la condición al reconstruir el índice.



Devolvamos este cambio y aún tratemos de encontrar qué objetos en el índice están causando que nuestra consulta falle.



DROP INDEX usahalinsk.d_geometry_cs1_all_sx;
 
CREATE INDEX d_geometry_cs1_all_sx
  ON usahalinsk.d_geometry
  USING gist
  (geoloc);


Permítanme recordarles que tenemos las coordenadas de la ubicación del error:



XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001



Pero si buscamos en los datos o como resultado de la función IsValidReason, que devuelve el motivo del error, no encontraremos nada similar.



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%';


Puede utilizar la siguiente secuencia de comandos para buscar objetos que afecten a la consulta. Comprobaremos cada objeto en la tabla y lo cruzaremos con la constante deseada. Durante la ejecución, detectamos excepciones y comprobamos su contenido. Si el error contiene las coordenadas que necesitamos, entonces esta es la geometría de nuestro problema.



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$$;


Como resultado, obtenemos tres claves de geometría que son fáciles de arreglar:



update usahalinsk.d_geometry 
set cs1_geometry_polygone=st_collectionextract(st_makevalid(geoloc),3)
where key in(
1000010001988961,
1000010001989399,
1000010004293508);


Responderé a la pregunta que surge: "¿por qué es imposible corregir toda la geometría errónea en la tabla, para no buscar selectivamente las razones?" ...



El hecho es que los datos espaciales llegan a nuestro sistema de varias fuentes (incluso de Rosreestr) y no podemos realizar la corrección (por regla general, va acompañada de distorsión) de todos los datos. Recibidas las claves necesarias, analizamos qué datos representan y si es posible corregirlos.



La tarea trivial de encontrar la causa del error puede convertirse en una investigación completa con un guión de corrección al final.



Una versión más compleja del problema: ¿qué pasa si la intersección no se realiza con una constante, sino con otra tabla? Alternativamente, cruce cada uno de los objetos participantes en la primera tabla con cada objeto en la segunda. Y captura excepciones.



¿Con qué frecuencia se encuentra con problemas de geometría y cómo garantiza la calidad de sus datos espaciales?



All Articles