43

I'm using a PL/R function and PostGIS to generate voronoi polygons around a set of points. The function that I am using is defined here. When I use this function on a particular dataset I get the following error message:

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

From examining this part of the error message:

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

This is what the problem listed above looks like:

enter image description here

I initially thought that this message might be caused by the existence of identical points, and tried to solve this using the st_translate() function, used in the following way:

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

This does fix the problem, but my concern is that I am now translating all points up to ~20m in the x/y direction. I also can't tell what an appropriate translation amount is need.

For example, in this dataset through trial and error a 20m * random number is ok, but how can I tell if this needs to be bigger?

Based on the image above I think the problem is that the point is intersecting with the line while the algorithm is trying to intersect the point with a polygon. I'm not sure what I should be doing to ensure that the point is within a polygon, rather than intersecting with a line. The error is occurring on this line:

"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'');"

I have read through What is a "non-noded intersection"? to try to better understand this problem.

2
  • If your inputs are not valid to start with, run ST_MakeValid() on them. If they are valid, adding entropy, as you're doing, is the next trick available, and perhaps the last trick for now. Commented Feb 8, 2013 at 18:36
  • Yes, I am using WHERE ST_IsValid(p.geom) to filter the points initially.
    – djq
    Commented Feb 8, 2013 at 18:47

5 Answers 5

40

In my experience this problem is nearly always caused by:

  1. High precision in your coordinates (43.231499999999996), combined with
  2. Lines that are almost coincident but not identical

The "nudge" approach of the ST_Buffer solutions lets you get away with #2, but anything you can do to resolve these underlying causes, like snapping your geometry to a 1e-6 grid, will make your life easier. The buffered geometries are usually fine for intermediate calculations like overlap area, but you'll want to be careful about retaining them because they can make your close-but-not-quite problems worse in the long haul.

PostgreSQL's exception-handling capability lets you write wrapper functions to handle these special cases, buffering only when needed. Here's an example for ST_Intersection; I use a similar function for ST_Difference. You'll need to decide if the buffering and potential return of an empty polygon are acceptable in your situation.

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;

Another benefit with this approach is you can pinpoint the geometries that are actually causing your problems; just add some RAISE NOTICE statements in the EXCEPTION block to output WKT or something else that will help you track down the problem.

2
  • That's a clever idea. I have often found that intersection issues come from linestrings appearing during combinations of unions, differences, buffers, etc, which can be fixed by buffering everything, or dumping everything and only selecting Polygons/Mutlipolygons. This is an interesting approach. Commented Dec 2, 2014 at 15:20
  • You mention snapping the geometry to the 1e-6 grid, but I'm sitting here wondering if snapping to a power of 2 would be better. PostGIS (and GEOS) using floating point numbers, so snapping to a power of 10 might not actually truncate the coordinates very much since the number may not have a finite length binary representation. But if you snap to say 2^-16, I believe that would be guaranteed to truncate any fractional part to just 2 bytes. Or am I thinking wrong?
    – jpmc26
    Commented Jan 17, 2019 at 21:26
17

Through a lot of trial and error I eventually realized that the non-noded intersection resulted from a self-intersection problem. I found a thread that suggested using ST_buffer(geom, 0) can be used to fix the problem (though it does make it a lot slower overall). I then tried to use ST_MakeValid() and when applied directly to the geometry before any other function. This seems to fix the problem robustly.

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

I've marked this as the answer as it seems to be the only approach that fixes my problem.

1
16

I ran into this same problem (Postgres 9.1.4, PostGIS 2.1.1), and the only thing that worked for me was to wrap the geometry with a very small buffer.

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

ST_MakeValid didn't work for me, nor did the combination of ST_Node and ST_Dump. The buffer didn't seem to result in any degradation in performance, but if I made it any smaller I still received a non-noded intersection error.

Ugly, but it works.

Update:

The ST_Buffer strategy seems to work well, but I ran into an issue where it produced errors when casting the geometry to geography. For example, if a point is originally at -90.0, and it is buffered by 0.0000001, it is now at -90.0000001, which is an invalid geography.

This meant that even though ST_IsValid(geom) was t, ST_Area(geom::geography) returned NaN for many features.

To avoid the non-noded intersection problem, while maintaining valid geography, I ended up using ST_SnapToGrid like so

SELECT ST_Union(ST_MakeValid(ST_SnapToGrid(geom, 0.0001))) AS geom, common_id
    FROM table
    GROUP BY common_id;
0
6

In postgis ST_Node should break a series of lines at intersections, which should solve the non-noded intersection problem. Wrapping this in ST_Dump generates the composite array of the broken lines.

Slightly related, there's an awesome presentation PostGIS: Tips for Power Users which clearly outlines these sort of problems and solutions.

5
  • That is a great presentation (thanks @PaulRamsey). How should I use ST_Node and ST_Dump? I imagine I would need to use them near this part of the function, but am not certain: st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'') in
    – djq
    Commented Feb 8, 2013 at 19:07
  • Hmmm I didn't notice that the two lines had an identical coordinate, which should be fine. If you plot those coordinates the point of intersection is about 18cm away from the intersection. Not really a solution, just an observation.
    – WolfOdrade
    Commented Feb 8, 2013 at 20:00
  • Not entirely clear on how I use st_node here - can I use it before st_intersection?
    – djq
    Commented Feb 8, 2013 at 20:17
  • 1
    The presentation is no longer available. I'm stuck with the same problem, when trying to ST_Clip(rast, polygon)
    – Jackie
    Commented Mar 12, 2014 at 9:36
  • 1
    @Jackie: I fixed the link to the presentation in the answer: PostGIS: Tips for Power Users.
    – Pete
    Commented Jul 17, 2017 at 19:38
2

In my experience, I solved my non-noded intersection error by using the St_SnapToGrid function which solved the problem of having a high precision in the coordinates of the vertex of the polygons.

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.