1

I'm designing a system where there is a set of polygons representing different postal codes (not overlapping with each other). An user can either draw his own polygon OR fully select/deselect particular postal code. That means that each postal code can be included in the final area fully or partially.

I will use example (brown - user drawn area, light blue - postal code) example break down

I found that when user draws some area, selects a postal code and then deselects it, the output geometry isn't the same as if he would deselect the postal code right away (g1 and g2 columns in below example). Mathematically it doesn't make sense. The g1 figure have weird spikes on the edges of the deselected postal code. How to fix it?

g1 geometry with weird spikes on edges

I found out that to fix it I can pass grid size (eg. 1e-9) to the st_union / st_difference, but then st_touches doesn't work correctly, because I can't pass grid size to this function. I can pass it to st_intersection but it is less efficient.

You can see it there:

WITH source AS (
    SELECT st_geomfromgeojson('{"type":"MultiPolygon","coordinates":[[[[-85.92333,40.63875],[-85.92333,40.63392],[-85.91539,40.63492],[-85.91467,40.63875],[-85.92333,40.63875]]]]}') area,
           st_geomfromgeojson('{"type":"MultiPolygon","coordinates":[[[[-85.82047, 40.65114],[-85.82046, 40.6297],[-85.92046, 40.6297],[-85.92068, 40.63807],[-85.92047, 40.65114],[-85.82047, 40.65114]]]]}') postalcode
),
difference AS (
SELECT st_difference(st_union(postalcode, area), postalcode)             g1,
       st_difference(area, postalcode)                                   g2,
       st_difference(st_union(postalcode, area, 1e-9), postalcode, 1e-9) g3,
       area,
       postalcode
FROM source
)
SELECT *, st_touches(g1, postalcode), st_touches(g2, postalcode), st_touches(g3, postalcode)
FROM difference

I understand that this can be because of how float is represented in binary and when the PostGIS calculates points of intersection it can be some irrational number which can't be saved in binary form exactly as it should. Also I found that both of those points from g1 and g2 are the same (POINT (-85.92066907421577 40.63875)).

What should I do?

EDIT: Also forgot to mention, would switching to different srid (some without floating operations) help me?

5
  • st_intersect might be a more efficient way and using mbr [&&] gis.stackexchange.com/questions/393316/… and uses spatial indexes if available.
    – Mapperz
    Commented Dec 5 at 17:52
  • Thanks for your reply. I meant instead of using st_touches, i can use st_intersection(g1, postalcode, 1e-9) and then check if the outcome is multilinestring. What is mbr? Please also check out EDIT in main post. Commented Dec 5 at 18:10
  • postgis mbr (minimum bounding rectangle) or bbox (bounding box) gis.stackexchange.com/questions/4067/… see ST_OrientedEnvelope and postgis.net/docs/ST_Envelope.html (these are efficient as your sending/scanning less coordinates) for calculation.
    – Mapperz
    Commented Dec 5 at 18:15
  • I would like to underline that my issue is how to edit geometries correctly and that the output geometry is with those weird spikes and how to avoid them so geometry looks like it should and st_touches will work Commented Dec 5 at 19:31
  • Using a different SRID won't solve this problem, since the algorithms for overlay and relationships are the same for all coordinate systems.
    – dr_jts
    Commented Dec 10 at 1:35

2 Answers 2

0

Using a grid size argument in the overlay functions is a good way to avoid the spikes that you are seeing in the full-precision results.

ST_Touches is a very specific spatial situation. It would be nice if it supported a distance tolerance - perhaps that will be available in some future PostGIS version.

If you want to find postal code polygons which might touch or intersect a polygon, you can use ST_DWithin with a small distance tolerance.

EDIT: To emulate a "touches with tolerance" relationship, there are a few options:

  • test whether ST_Intersection(area, postalcode, tol) is linear (as the OP suggested)
  • test whether ST_Intersection(area, postalcode) has a small area (based on a fraction of the areas of the input geometries)
  • the exact equivalent of "touches with tolerance" is to test whether the boundary of area which lies within postalcode lies within the tolerance distance of the boundary of postalcode. This can be computed as:
ST_Covers( ST_Buffer(ST_Boundary(postalcode), tol),
           ST_Intersection(ST_Boundary(area), postalcode) )

(Note: it would be computationally more efficient to express this as:

ST_OrientedHausdorffDistance( 
     ST_Intersection(ST_Boundary(area), postalcode), 
     ST_Boundary(postalcode) ) 
  <= tol

but unfortunately PostGIS does not (yet) have a function to compute the oriented Hausdorff distance.)

4
  • Thanks for your answer. ST_DWithin is good approach, but it doesn't fit into my business case. When a user draws a figure I want to show him what postalcodes are included fully and partially. For this purpose I search for all postalcodes that are ST_Intersects AND NOT ST_Touches (all included) and then I check for ST_Covers. For my example this extra postalcode is seen as included, but it shouldn't Commented Dec 9 at 12:41
  • In my example when I do ST_Intersects(g3, postalcode) AND NOT ST_Touches(g3, postalcode) the answer is true, but should be false (as I differenced the whole postalcode polygon earlier) Commented Dec 9 at 12:48
  • Because of finite precision it's not possible to use ST_Touches in this way. Probably the best you can do is to test if the area of intersection is less than a small amount. Unfortunately that's expensive to compute. This is a good use case showing the need for tolerance-based relationships.
    – dr_jts
    Commented Dec 9 at 23:34
  • 1
    I think that the arguments in st_covers should be swapped. Like this: ST_Covers( ST_Buffer(ST_Boundary(g3), 1e-9) , ST_Intersection(ST_Boundary(g3), postalcode)). thanks for your ideas, i think i will stick to st_intersection(a,b,1e-9) Commented yesterday
0

I found some algorithms to remove the spikes that can be seen in the question, and for provided example it worked, but for more complex examples still st_touches is not working. I will leave the links, maybe someone could leverage on that.

https://gasparesganga.com/labs/postgis-normalize-geometry/

https://trac.osgeo.org/postgis/wiki/UsersWikiExamplesSpikeRemover

New contributor
Bartłomiej Wierciński is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

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.