Opened 7 years ago

Closed 7 years ago

Last modified 7 years ago

#1641 closed defect (fixed)

TopoGeo_addLineString: SQL/MM Spatial exception - geometry crosses an edge

Reported by: lrssvt Owned by: strk
Priority: medium Milestone: PostGIS 2.0.0
Component: topology Version: 2.0.x
Keywords: Cc:

Description

Running SQL script (attached file polyTopo) I get the following error for my layer (attached file layerPoly).

ERROR:  SQL/MM Spatial exception - geometry crosses an edge
CONTEXT: PL/pgSQL function "topogeo_addlinestring" line 143 at assignment
SQL statement "SELECT array_cat(edges, array_agg(x)) FROM ( select topology.TopoGeo_addLinestring(atopology, rec.geom, tolerance) as x ) as foo"
PL/pgSQL function "topogeo_addpolygon" line 23 at assignment
SQL statement "INSERT INTO poly_topo.relation(topogeo_id, layer_id, element_type, element_id) SELECT 2, 1, 3, topogeo_addPolygon('poly_topo', '0103000020BC0B0000010000000A000000640FEAD1CAC0DABFA8D653CFF82FE43F807A7AC40C63C9BF0CABC1DC9906E43FB8E2E5B7779ECCBF751D41DFF25ADE3F6084AF11173FC23FF783D649EFFDD93F803B8CFD12DBABBFAE520CA6A8F4C43F785AF3A726AEC93FA0F9F2CB618885BF00B612B697557ABFB4386EE1F850B9BF00CEDC644808873FFF54F94ABD5AC6BF18995CCF8903C0BF3A9BD621CEC4C3BF640FEAD1CAC0DABFA8D653CFF82FE43F'::geometry, 0);"
PL/pgSQL function "totopogeom" line 125 at EXECUTE statement

Attachments (6)

polyTopo.sql (442 bytes) - added by lrssvt 7 years ago.
layerPoly.sql (2.8 KB) - added by lrssvt 7 years ago.
geomcrossedge.png (1.5 KB) - added by strk 7 years ago.
toleranceimage.png (10.9 KB) - added by lrssvt 7 years ago.
CreateTopologyTolerance.png (2.9 KB) - added by lrssvt 7 years ago.
toTopoGeomTolerance.png (3.1 KB) - added by lrssvt 7 years ago.

Download all attachments as: .zip

Change History (27)

Changed 7 years ago by lrssvt

Attachment: polyTopo.sql added

Changed 7 years ago by lrssvt

Attachment: layerPoly.sql added

comment:1 Changed 7 years ago by lrssvt

Yes, passing a tolerance to toTopoGeom works:

UPDATE test.poly SET topogeom = toTopoGeom(geom, 'poly_topo', 1, 0.1) WHERE gid=2;
UPDATE test.poly SET topogeom = toTopoGeom(geom, 'poly_topo', 1, 0.1) WHERE gid=3;

How do I determine the correct tolerance? Must I necessarily pass it in the function toTopoGeom?

comment:2 Changed 7 years ago by strk

I suspect the default "0" tolerance should be taken as "float8" tolerance which is an expanding grid starting from EPSILON at the origin. This is what I've implemented in the last two cases.

Anyway there's not yet a well-defined semantic for such "tolerance". Research topic: wiki:ToleranceDiscussion

comment:3 Changed 7 years ago by strk

2 lines o two vertexes:

  'LINESTRING(-0.223586 0.474301, 0.142550 0.406124)'
  'LINESTRING(0.095989 0.113619, -0.064646 0.470149)'

Add them in sequence to an empty topology, using TopoGeo_addLineString. Reproduces the problem.

Changed 7 years ago by strk

Attachment: geomcrossedge.png added

comment:4 Changed 7 years ago by strk

comment:5 Changed 7 years ago by strk

Alright in this case noding of the two lines results in the line being added (the semi-vertical one) being split in two. Then first point is added as a node (the lowest) and when last point is added as a note the code fails to find this second node _on_the_ existing line.

The query is in the form: ST_Dwithin(intersection_node, existing_line, 0)

Well, it fails. Using a tolerance of 1e-17 still fails. 1e-16 succeeds in finding it. This part of the code isn't attempting to do any tolerance. We could add that.

But still, what GEOS does is completely out of our control as GEOS uses its own heuristic to get to a result, responding to topology exceptions by reducing precision (for example). So it could still be possible for us to use a too low tolerance to account for what GEOS did.

comment:6 in reply to:  1 Changed 7 years ago by strk

Replying to lrssvt:

How do I determine the correct tolerance?

So to answer this again (I didn't really answer before). I suggest you use the value that you do tollerate in your topology. Millimeter precision is enough and your units are meters ? Use 1e-3 !

There is support for specifying a topology-wide tolerance (it's an argument to CreateTopology?) but no funtion currently really uses it at the moment. You can surely use it as a reference.

comment:7 Changed 7 years ago by lrssvt

Using tolarance of 0.03 works (0.03, in map units, is the length of the short segment of the line sub vertical).

Changed 7 years ago by lrssvt

Attachment: toleranceimage.png added

comment:8 Changed 7 years ago by lrssvt

comment:9 Changed 7 years ago by strk

Try 1e-10 :)

comment:10 in reply to:  9 Changed 7 years ago by lrssvt

Replying to strk:

Try 1e-10 :)

1e-2 also works!

I'm confused! have a break!

comment:11 Changed 7 years ago by strk

Milestone: PostGIS 2.0.0PostGIS 2.1.0
Summary: SQL/MM Spatial exception - geometry crosses an edgeTopoGeo_addLineString: SQL/MM Spatial exception - geometry crosses an edge

Not a 2.0 task, needs a lot more thinking than you can have during closeup

comment:12 in reply to:  11 Changed 7 years ago by lrssvt

Replying to strk:

Not a 2.0 task, needs a lot more thinking than you can have during closeup

I know, I am confident in your future success! ;-)

comment:13 Changed 7 years ago by strk

With r9374 I've "exported" the "min-tolerance-for-given-extent" function into its own function. The function is tagged as "internal" and I was thinking it could be used when the functions accepting a tolerance are passed a tolerance value of 0.

BUT, if we had to do that, your toTopoGeom calls would each have a different tolerance value in that each new geometry you'd be adding to the topology would have a different extent. The problem with that is that a short edge closer to the origin may create two very close nodes in the topology and such "vicinity" would be below the tolerance computed when adding a longer edge, resulting in an ambiguity about which node ("hot-pixel") intersections should be snapped to.

So, "global" tolerance is still a much better approach. Tolerance-taking functions could then check topology.tolerance and use that. But if topology.tolerance is till zero you'll need something and you won't know what the max extent of the topology would be.

I start thinking that's why GRASS wants you to create those "locations" before use :P

comment:14 Changed 7 years ago by lrssvt

Why I not get error, using this (without tolerance):

'LINESTRING(-0.2668 0.5357,-0.2536 0.1239,-0.2536 0.1239)'

'LINESTRING(-0.4582 0.4565,0.05253 0.5806,0.0525 0.5806)'

Good question!

comment:15 in reply to:  13 Changed 7 years ago by lrssvt

Replying to strk:

I start thinking that's why GRASS wants you to create those "locations" before use :P

ESRI also, in topology builder, use a default tolerance [1] to 0.0001! I think for the same reason, perhaps!

[1] - http://jiangsuimage.com/ArcGIS/Manager/Help/index.htm#geodatabases/topology_in_arcgis.htm

Do you think you can follow the same approach?

comment:16 Changed 7 years ago by strk

Milestone: PostGIS 2.1.0PostGIS 2.0.0
Resolution: fixed
Status: newclosed

With r9395 I added a different handing of 0-tolerance. That is, if you pass 0, which is the default, the code goes looking up topology.topology.precision (the tolerance you pass to CreateTopology?) and if that's also zero ends up computing the minimum tolerance required to represent changes in the largest coordinate in input.

There's no automated testing for the topology.precision use or lookup. Well, works fine with the cases we have so far. See #785 for more discussion about this.

comment:17 Changed 7 years ago by strk

@lrssvt : if you want to relaunch the whole import I'll be happy to have a report about what happens now. Also you may want to create the topology with a precision that really reflects your data precision (cm? mm?) and see how that affects further toTopoGeom calls (they should be using the precision you specified at CreateTopology? time as a tolerance).

Changed 7 years ago by lrssvt

Attachment: CreateTopologyTolerance.png added

Changed 7 years ago by lrssvt

Attachment: toTopoGeomTolerance.png added

comment:18 in reply to:  17 Changed 7 years ago by lrssvt

Replying to strk:

@lrssvt : if you want to relaunch the whole import I'll be happy to have a report about what happens now. Also you may want to create the topology with a precision that really reflects your data precision (cm? mm?) and see how that affects further toTopoGeom calls (they should be using the precision you specified at CreateTopology? time as a tolerance).

well, I did some tests and now it looks better, but I have a doubt!

I tested the demo attached and the end result will change if I define tolerance in CreateTopology? (see image) or toTopoGeom (see image).

Is that correct?

comment:19 Changed 7 years ago by strk

Did you use the _same_ tolerance value ?

CreateTopology?:

toTopoGeom:

comment:20 in reply to:  19 Changed 7 years ago by lrssvt

Replying to strk:

Did you use the _same_ tolerance value ?

Yes, i did use 0.001!

comment:21 Changed 7 years ago by strk

In that case it sounds like a bug, or a missing upgrade on your side. Note that the _order_ in which you add geometries matters. Do you have a short self-contained example reproducing the problem ? Actually, best if attached to another ticket, thanks.

Note: See TracTickets for help on using tickets.