¿Cuál es la mejor manera de solucionar un problema de intersección no noded en PostGIS?

35

Estoy usando una función PL/R y PostGIS para generar polígonos voronoi alrededor de un conjunto de puntos. La función que estoy usando está definida aquí . Cuando uso esta función en un conjunto de datos en particular, recibo el siguiente mensaje de error:

Error : ERROR:  R interpreter expression evaluation error
DETAIL:  Error in pg.spi.exec(sprintf("SELECT %3$s AS id,   
st_intersection('SRID='||st_srid(%2$s)||';%4$s'::text,'%5$s') 
AS polygon FROM %1$s WHERE st_intersects(%2$s::text,'SRID='||st_srid(%2$s)||';%4$s');",  
:error in SQL statement : Error performing intersection: TopologyException: found non-noded 
intersection between LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 
264611, 594406 286813) at 568465.05533706467 264610.82749605528
CONTEXT:  In R support function pg.spi.exec In PL/R function r_voronoi

De examinar esta parte del mensaje de error:

Error performing intersection: TopologyException: found non-noded intersection between
LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 264611, 594406 286813) 
at 568465.05533706467 264610.82749605528

Esto es lo que parece el problema mencionado anteriormente:

Inicialmentepenséqueestemensajepodríadebersealaexistenciadepuntosidénticosytratéderesolverloconlafunciónst_translate(),queseusadelasiguientemanera:

ST_Translate(geom,random()*20,random()*20)asgeom

Estosolucionaelproblema,peromipreocupaciónesqueahoraestoytraduciendotodoslospuntoshasta~20menladirecciónx/y.Tampocopuedodecircuáleslacantidaddetraducciónadecuadaquesenecesita.Porejemplo,enesteconjuntodedatosatravésdepruebayerror,un20m*randomnumberestábien,pero¿cómopuedosabersiestonecesitasermásgrande?

Segúnlaimagendearriba,creoqueelproblemaesqueelpuntoseintersecaconlalínea,mientrasqueelalgoritmointentaintersecarelpuntoconunpolígono.Noestoysegurodeloquedeberíahacerparagarantizarqueelpuntoestédentrodeunpolígono,enlugardecruzarseconunalínea.Elerrorestáocurriendoenestalínea:

"SELECT 
  %3$s AS id, 
  st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'') AS polygon 
FROM 
  %1$s 
WHERE 
  st_intersects(%2$s::text,''SRID=''||st_srid(%2$s)||'';%4$s'');"

He leído esta pregunta anterior, ¿Qué es un "no-noded intersección "? para intentar comprender mejor este problema y agradecería cualquier consejo sobre la mejor manera de resolverlo.

    
pregunta djq 08.02.2013 - 17:04

5 respuestas

10

A través de muchas pruebas y errores, finalmente me di cuenta de que el non-noded intersection resultó de un problema de auto-intersección. Encontré que un hilo que sugería usar ST_buffer(geom, 0) se puede usar para solucionar el problema (aunque lo hace mucho más lento en general). Luego intenté usar ST_MakeValid() y cuando lo apliqué directamente a la geometría antes de cualquier otra función. Esto parece solucionar el problema de manera robusta.

ipoint <- pg.spi.exec(
        sprintf(
            "SELECT 
                    %3$s AS id, 
                    st_intersection(ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''::text), ST_MakeValid(''%5$s'', 0)) AS polygon 
            FROM %1$s 
            WHERE 
                ST_Intersects(ST_MakeValid(%2$s::text),ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''));",
            arg1,
            arg2,
            arg3,
            curpoly,
            buffer_set$ewkb[1]
        )
    )

He marcado esto como la respuesta, ya que parece ser el único enfoque que soluciona mi problema.

    
respondido por el djq 16.02.2013 - 20:14
26

En mi experiencia, este problema casi siempre es causado por:

  1. Alta precisión en sus coordenadas (43.231499999999996), combinada con
  2. Líneas casi coincidentes pero no idénticas

El enfoque de "empuje" de las soluciones ST_Buffer le permite salirse con el # 2, pero cualquier cosa que pueda hacer para resolver estas causas subyacentes, como ajustar su geometría a una cuadrícula 1e-6, le hará la vida más fácil. Las geometrías con búfer generalmente están bien para los cálculos intermedios, como el área de superposición, pero querrás tener cuidado de retenerlas porque pueden empeorar tus problemas de cerca pero no del todo a la larga.

La capacidad de manejo de excepciones de PostgreSQL le permite escribir funciones de envoltura para manejar estos casos especiales, almacenando en búfer solo cuando sea necesario. Aquí hay un ejemplo para ST_Intersection ; Yo uso una función similar para ST_Difference . Deberá decidir si el búfer y el retorno potencial de un polígono vacío son aceptables en su situación.

CREATE OR REPLACE FUNCTION safe_isect(geom_a geometry, geom_b geometry)
RETURNS geometry AS
$$
BEGIN
    RETURN ST_Intersection(geom_a, geom_b);
    EXCEPTION
        WHEN OTHERS THEN
            BEGIN
                RETURN ST_Intersection(ST_Buffer(geom_a, 0.0000001), ST_Buffer(geom_b, 0.0000001));
                EXCEPTION
                    WHEN OTHERS THEN
                        RETURN ST_GeomFromText('POLYGON EMPTY');
    END;
END
$$
LANGUAGE 'plpgsql' STABLE STRICT;

Otro beneficio de este enfoque es que puede identificar las geometrías que realmente están causando sus problemas; simplemente agregue algunas declaraciones RAISE NOTICE en el bloque EXCEPTION para generar WKT o algo más que le ayude a localizar el problema.

    
respondido por el dbaston 20.03.2014 - 00:04
10

Me encontré con este mismo problema (Postgres 9.1.4, PostGIS 2.1.1), y lo único que me funcionó fue envolver la geometría con un búfer muy pequeño.

SELECT ST_Intersection(
    (SELECT geom FROM table1), ST_Union(ST_Buffer(geom, 0.0000001))
) FROM table2

ST_MakeValid no funcionó para mí, ni tampoco la combinación de ST_Node y ST_Dump . El búfer no pareció producir ninguna degradación en el rendimiento, pero si lo hice más pequeño aún recibí un error de intersección sin asentimiento.

Feo, pero funciona.

Actualización:

La estrategia ST_Buffer parece funcionar bien, pero me encontré con un problema en el que producía errores al convertir la geometría en geografía. Por ejemplo, si un punto está originalmente en -90.0 y está almacenado en búfer por 0.0000001, ahora está en -90.0000001, que es una geografía no válida.

Esto significa que aunque ST_IsValid(geom) fue t , ST_Area(geom::geography) devolvió NaN para muchas características.

Para evitar el problema de la intersección no identificada, mientras mantenía una geografía válida, terminé usando ST_SnapToGrid como tal

SELECT ST_Union(ST_MakeValid(ST_SnapToGrid(geom, 0.0001))) AS geom, common_id
    FROM table
    GROUP BY common_id;
    
respondido por el jczaplew 19.03.2014 - 21:36
4

En postgis ST_Node debe romper una serie de líneas en las intersecciones, lo que debería resolver el problema de la intersección no identificada. . Envolviendo esto en ST_Dump genera la matriz compuesta de las líneas discontinuas.

Ligeramente relacionado, hay una presentación impresionante PostGIS: Consejos para usuarios avanzados que describe claramente este tipo de problemas y soluciones.

    
respondido por el WolfOdrade 08.02.2013 - 19:40
1

En mi experiencia, resolví mi error non-noded intersection utilizando la función St_SnapToGrid que resolvió el problema de tener un Alta precisión en las coordenadas del vértice de los polígonos.

SELECT dissolve.machine, dissolve.geom FROM (
        SELECT machine, (ST_Dump(ST_Union(ST_MakeValid(ST_SnapToGrid(geom,0.000001))))).geom 
        FROM cutover_automatique
        GROUP BY machine) as dissolve
WHERE ST_isvalid(dissolve.geom)='t' AND ST_GeometryType(dissolve.geom) = 'ST_Polygon';
    
respondido por el CptGasse 14.07.2017 - 16:55

Lea otras preguntas en las etiquetas