Opened 2 years ago

Closed 18 months ago

#5293 closed defect (worksforme)

toTopoGeom: corrupted topology: face xxx could not be constructed only from edges knowing about it (like edge yyy).

Reported by: strk Owned by: strk
Priority: medium Milestone: PostGIS 3.4.0
Component: topology Version: master
Keywords: Cc:

Description

Single-process incremental population of a topology resulted in receiving corrupted-topology messages:

toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id < 1000;
UPDATE 999
Time: 2787.209 ms (00:02.787)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 1001 and 3000;
UPDATE 2000
Time: 6922.480 ms (00:06.922)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 3001 and 5000;
UPDATE 2000
Time: 26212.861 ms (00:26.213)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 5001 and 6000;
UPDATE 1000
Time: 3472.713 ms (00:03.473)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 6001 and 10000;
UPDATE 4000
Time: 68680.044 ms (01:08.680)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 10001 and 15000;
UPDATE 5000
Time: 160572.868 ms (02:40.573)
toto=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 15001 and 20000;
NOTICE:  Corrupted topology: face 6269 could not be constructed only from edges knowing about it (like edge 43264).
NOTICE:  Corrupted topology: face 4989 could not be constructed only from edges knowing about it (like edge 43601).
NOTICE:  Corrupted topology: face 14444 could not be constructed only from edges knowing about it (like edge 43499).
NOTICE:  Corrupted topology: face 4989 could not be constructed only from edges knowing about it (like edge 43601).
UPDATE 642
Time: 18218.007 ms (00:18.218)

It looks like topology was corrupted in one of the first steps. I'm opening the ticket without much detail to not forget about it. The plan is to add more detail later, when found.

PostGIS full version is:

POSTGIS="3.4.0dev 3.3.0rc2-330-gb4e198bf3" [EXTENSION] PGSQL="130" GEOS="3.11.0dev-CAPI-1.16.0" PROJ="8.2.1" LIBXML="2.9.13" LIBJSON="0.15" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)" TOPOLOGY

The topology is in meters so we're talking about a "snap tolerance" of 1mm.

Attachments (5)

test.sql.gz (114.6 KB ) - added by Lars Aksel Opsahl 21 months ago.
test.sql file
postgis_ticket_5293_topo.pgdump (10.1 KB ) - added by strk 18 months ago.
tinyFaces.png (34.7 KB ) - added by strk 18 months ago.
nearlyCoincidentEdges.png (12.8 KB ) - added by strk 18 months ago.
incomingLine.png (11.7 KB ) - added by strk 18 months ago.

Download all attachments as: .zip

Change History (20)

comment:1 by strk, 2 years ago

Interesting fact: removing the edges reported as being the start of a ring with mixed-face labeling using the qgis-topo-edit plugin fixed the invalidity.

ValidateTopology output before fixing things:

            error            |  id1   | id2
-----------------------------+--------+-----
 mixed face labeling in ring | -43601 |    
 mixed face labeling in ring | -43501 |    
 mixed face labeling in ring | -43264 |    
(3 rows)

With QGIS I've selected the 3 edges reported in column id1 and hit the 'delete edges' icon. The operation successfully removed all 3 edges (no complains about them being needed for any TopoGeometry) and at the end of that operation ValidateTopology runs clean:

toto=# select * from validatetopology('topo_uso_suolo');
NOTICE:  Checking for coincident nodes
NOTICE:  Checking for edges crossing nodes
NOTICE:  Checking for invalid or not-simple edges
NOTICE:  Checking for crossing edges
NOTICE:  Checking for edges start_node mismatch
NOTICE:  Checking for edges end_node mismatch
NOTICE:  Checking for faces without edges
NOTICE:  Checking edge linking
NOTICE:  Building edge rings
NOTICE:  Found 19141 rings, 15749 valid shells, 3392 valid holes
NOTICE:  Constructing geometry of all faces
NOTICE:  Checking faces
NOTICE:  Checked 15749 faces
NOTICE:  Checking for holes coverage
NOTICE:  Finished checking for coverage of 3392 holes
NOTICE:  Checking for node containing_face correctness
 error | id1 | id2 
-------+-----+-----
(0 rows)

Time: 14549.719 ms (00:14.550)

comment:3 by strk, 22 months ago

I could reproduce and found corruption already between 6001 and 10000:

=# update uso_suolo set tgeom = totopogeom(cgeom, 'topo_uso_suolo', 1, 1e-3) where id between 6001 and 10000;
NOTICE:  Corrupted topology: face 7486 could not be constructed only from edges knowing about it (like edge 21197).
UPDATE 4000
=# select * from validatetopology('topo_uso_suolo');
NOTICE:  Checking for coincident nodes
NOTICE:  Checking for edges crossing nodes
NOTICE:  Checking for invalid or not-simple edges
NOTICE:  Checking for crossing edges
NOTICE:  Checking for edges start_node mismatch
NOTICE:  Checking for edges end_node mismatch
NOTICE:  Checking for faces without edges
NOTICE:  Checking edge linking
NOTICE:  Building edge rings
NOTICE:  Found 14290 rings, 10818 valid shells, 3471 valid holes
            error            |  id1   | id2 
-----------------------------+--------+-----
 mixed face labeling in ring | -21197 |    
(1 row)

I'm not sure if it's good for the Corrupted topology message to be just a NOTICE rather than an Exception, at that point …

comment:5 by Lars Aksel Opsahl, 22 months ago

Here is test creating topology error with one tread and 7 input polygon https://gitlab.com/nibioopensource/resolve-overlap-and-gap/-/jobs/3736311302

The error we get is this

validation postgis_ticket_5293_1|face has multiple shells|45126

And the issue is here https://gitlab.com/nibioopensource/resolve-overlap-and-gap/-/issues/46

If i run the same test on db01tempdata.nibio.no postgres@rmp_build_tiltak_layers=#

POSTGIS="3.4.0dev 3.3.0rc2-573-g9db2a5fb0" [EXTENSION] PGSQL="120" GEOS="3.9.1-CAPI-1.14.2" SFCGAL="1.3.7" PROJ="7.2.1" LIBXML="2.9.10" LIBJSON="0.13.1" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)" TOPOLOGY

I get this errors

validation postgis_ticket_5293_1 | invalid next_right_edge | 128 | -127 validation postgis_ticket_5293_1 | invalid next_left_edge | 127 | 131

by Lars Aksel Opsahl, 21 months ago

Attachment: test.sql.gz added

test.sql file

comment:6 by Lars Aksel Opsahl, 21 months ago

I am testing on

POSTGIS="3.2.3 2f97b6c" [EXTENSION] PGSQL="140" GEOS="3.11.0-CAPI-1.17.0" PROJ="9.0.1" LIBXML="2.9.14" LIBJSON="0.16" LIBPROTOBUF="1.4.1" WAGYU="0.5.0 (Internal)" TOPOLOGY

This are also the same error messages related to this error https://gitlab.com/nibioopensource/resolve-overlap-and-gap/-/issues/46

When I run the command below with test.sql a couple of times this, I see the log below.

rm /tmp/error.out; psql nibio_reg -f /tmp/test.sql 2>/tmp/error.out; cat /tmp/error.out 

I find this in error.out file.

Larss-MacBook-Pro:resolve-overlap-and-gap lop$ cat /tmp/error.out 
psql:/tmp/test.sql:1: NOTICE:  00000: Dropping all layers from topology 'postgis_ticket_5293_topo' (3)
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:1: NOTICE:  00000: drop cascades to 6 other objects
DETAIL:  drop cascades to table postgis_ticket_5293_topo.face
drop cascades to table postgis_ticket_5293_topo.node
drop cascades to table postgis_ticket_5293_topo.edge_data
drop cascades to view postgis_ticket_5293_topo.edge
drop cascades to sequence postgis_ticket_5293_topo.layer_id_seq
drop cascades to table postgis_ticket_5293_topo.relation
LOCATION:  reportDependentObjects, dependency.c:1216
psql:/tmp/test.sql:74: ERROR:  XX000: Corrupted topology: adjacent edges -64 and -64 bind different face (45 and 0)
LOCATION:  pg_error, lwgeom_pg.c:342
psql:/tmp/test.sql:79: ERROR:  XX000: Corrupted topology: adjacent edges -64 and -64 bind different face (45 and 0)
LOCATION:  pg_error, lwgeom_pg.c:342
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for coincident nodes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for edges crossing nodes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for invalid or not-simple edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for crossing edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for edges start_node mismatch
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for edges end_node mismatch
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for faces without edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking edge linking
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Building edge rings
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Found 95 rings, 63 valid shells, 32 valid holes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Constructing geometry of all faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checked 63 faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for holes coverage
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Finished checking for coverage of 32 holes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:111: NOTICE:  00000: Checking for node containing_face correctness
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:112: ERROR:  XX000: Corrupted topology: adjacent edges -64 and -64 bind different face (45 and 52)
LOCATION:  pg_error, lwgeom_pg.c:342
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for coincident nodes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for edges crossing nodes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for invalid or not-simple edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for crossing edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for edges start_node mismatch
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for edges end_node mismatch
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for faces without edges
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking edge linking
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Building edge rings
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Found 95 rings, 63 valid shells, 32 valid holes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Constructing geometry of all faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checked 63 faces
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for holes coverage
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Finished checking for coverage of 32 holes
LOCATION:  exec_stmt_raise, pl_exec.c:3873
psql:/tmp/test.sql:113: NOTICE:  00000: Checking for node containing_face correctness
LOCATION:  exec_stmt_raise, pl_exec.c:3873

Last edited 21 months ago by Lars Aksel Opsahl (previous) (diff)

comment:7 by strk, 18 months ago

I've just tested the test you attached (thanks!) and it gives me a slightly different (and funny) error:

ERROR: Corrupted topology: adjacent edges 65 and 65 bind different face (52 and 45)

In any case it's capable of reproducing the problem so great work there!

by strk, 18 months ago

Attachment: tinyFaces.png added

by strk, 18 months ago

Attachment: nearlyCoincidentEdges.png added

comment:8 by strk, 18 months ago

I've split your script in a "prepare" and a "test" phase, whereas the "prepare" creates the topology up to the point in which you test it for validity and it passes it, and the "test" attempts to add the last line, failing with the message above and rolls it back.

In this was I was able to further reduce the dataset and I've attached a smaller topology dump file ( to be used with pgtopo_import ) to create the starting condition. https://trac.osgeo.org/postgis/attachment/ticket/5293/postgis_ticket_5293_topo.pgdump

Next I'm looking at the found-to-be-valid topology via QGIS and immediately realizing there are a lot of nearly cohincident edges, so close each other that QGIS itself is unable to show the gap. The Topology Viewer is capable of showing us the faces though, via the "face seed" layer, which I'm showing here:

The edges referred to by my error message are showed here, with all the limits QGIS imposes (is unable to show the node, and to show the labels):

Faces 52 and 45 are the "external" faces (not the tiny faces in between those two edges).

I guess when the incoming line is noded with the existing lines those almost coincident edges are going to confuse the algorithms computing sides and order of edges around a node.

The good thing is that on my system there's no corruption because the attempt to insert the last line fails instead. My system's versions:

 POSTGIS="3.4.0dev 3.3.0rc2-902-g9ea7418ef" [EXTENSION] PGSQL="130" GEOS="3.12.0dev-CAPI-1.18.0" PROJ="9.1.1" LIBXML="2.9.13" LIBJSON="0.15" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)" TOPOLOGY

comment:9 by strk, 18 months ago

For the record: in the situation described above, removing one of the two edges contending that node allowed to succeed in inserting the incoming line:

select topology.ST_RemEdgeModFace('postgis_ticket_5293_topo', 65);

I should note that after adding the incoming line we're still left with many tiny tiny faces:

=# select face_id, st_area(topology.st_getfacegeometry('postgis_ticket_5293_topo', face_id)) from postgis_ticket_5293_topo.face where face_id > 0;
 face_id |        st_area         
---------+------------------------
      35 |  3.225338615683592e-15
      36 | 3.0876028734852634e-16
      37 |  1.325799232980171e-14
      38 |  1.692312908156489e-14
      39 | 1.2586570072969456e-16
      40 |  5.816003342750131e-15
      41 |  9.482633197950748e-16
      42 |  5.071487202375476e-15
      43 | 1.9242697442172733e-15
      44 | 2.7261901069676923e-15
      46 |  2.687186386597784e-15
      47 | 1.2678269542001519e-14
      48 | 4.2527341168471194e-16
      45 | 1.4590039951102688e-06
(14 rows)

I'm guessing only face 45 is a face really needed, given it's size.

comment:10 by strk, 18 months ago

The smallest input still able to trigger the error after loading the topology from my dump is just two vertices, so a corrupt.sql script could contain just this code:

BEGIN; -- so it can be reproducible

SELECT topology.TopoGeo_addLinestring(
  'postgis_ticket_5293_topo',
  'SRID=4258;LINESTRING(5.803646305 59.263416658000004, 5.803874188 59.263357466500004)',
  0
);
ROLLBACK;

by strk, 18 months ago

Attachment: incomingLine.png added

comment:11 by strk, 18 months ago

This is a picture of the small incoming line found, with evidence of the first point of it added to the topology.

The problem is with adding that single point, and specifically with the code trying to figure out in which face that point would fall, and doing so by attempting to compute angles. The test thus reduces to this:

BEGIN; -- so it can be reproducible
SELECT topology.TopoGeo_addPoint(
  'postgis_ticket_5293_topo',
  'SRID=4258;POINT(5.803646305 59.263416658000004)',
  0
);
ROLLBACK;

The problem seems to be with ordering the nearly cohincident edges (64 and 65) around their node to find where the newly added node lays.

From the logs I discover the problem being in the code computing the endpoint, bogusly considering the last two points of edge 64 being the same, due to use of p2d_same function using FP_EQUALS instead of strict equality.

comment:12 by strk, 18 months ago

This pull request fixes the problem with the small test: https://git.osgeo.org/gitea/postgis/postgis/pulls/124

I've yet to test this with the original submission

comment:13 by strk, 18 months ago

I now believe the original submission in this bug is different, in that it was about a corrupted topology resulting from loading data from a single thread. The problem reported by Lars in comment:6 and then simplified by me is instead about the inability to populate a topology due to an exception with a message reporting a topology corruption that doesn't really exist. I've created ticket #5394 to track that case, so we can keep this for the original submission.

comment:14 by strk, 18 months ago

I could not reproduce the problem in the original submission, so closing as non actable.

comment:15 by strk, 18 months ago

Resolution: worksforme
Status: newclosed
Note: See TracTickets for help on using tickets.