6

I was trying to make a function that modifies polygons removing narrow spaces inside (setting a minimum distance between a vertex and a segment that is not adjacent) and at the same time remove sharp edges (angles < 90). It is important that the resulting polygon doesn't have round edges. Something like this, the original shape is grey, while what I want is in red: enter image description here The use of the function buffer seems to be the answer.

Given a polygon Shape, I need to do something like Shape.buffer(-h).buffer(h) to remove narrow spaces inside, and by using the variables cap_style, join_style and mitre_limit, it should be able to deal with the sharp edges. The problem is that I have no clue of what these variables mean, the documentation is very confusing regarding this function, the descriptions and examples are ambiguous. For instance it is not clear to me the difference between cap_style and join_style, from experience, the last seems to be overriding the former. Changing cap_style while maintaining fixed join_style doesn't change the shape at all. From experience too, mitre_ratio doesn't work while using join_style = 3. In the end, I found out that

shape2 = shape.buffer(-0.2, join_style = 2, mitre_limit=2., cap_style=2)
shape3 = shape2.buffer(0.2, join_style = 2, mitre_limit=2., cap_style=1)

Almost works, if it wasn't for a very small difference between the original polygon and the modified one. Even though the distance parameter is exactly the same, the curves deviate somehow: enter image description here

What is wrong with this solution? and what exactly are those parameters, and how are explicitly defined these methods?

1 Answer 1

6

What you are doing should work, as the following minimal working example shows:

import geopandas as gpd
import shapely
shape = shapely.geometry.Polygon([(-1,0),(-1,1),(0,.05),(1,1),(1,0),(-1,0)])
cutDist = .1

cutshape = shape.buffer(-cutDist,join_style=3).buffer(cutDist,join_style=3)
print(cutshape.difference(shape).wkt) #POLYGON EMPTY

gdshape = gpd.GeoDataFrame([shape], columns=['geometry'])
gdcut = gdshape.buffer(-cutDist,join_style=3).buffer(cutDist,join_style=3)
print(gdcut.difference(gdshape)) #POLYGON  EMPTY

The buffer function of geopandas uses shapely internally. The exact inner workings of shapely can be found in the code, or GEOS documentation. It uses Geos (C++) internally for all computations. the differences between joins are best explained by this image:

enter image description here

and cap_style defines the behaviour around line-ends. In your case, we do not have these, because all polygons are closed. mitre_limit sets the limit for when corners should be extended. Since you use beveled joins, this parameter is not used. These small differences are caused by join_style=2(mitre). For a reference to those, read the documentation

2
  • Thanks, so I don't need the cap_style parameter. Regarding the join style, I think it wouldn't be beveled joins, since I don't want all edges to be cut. Just those with angles < 90º. Mitre joins seemed right, but they produce artifacts such as the deviations between the red and grey lines. That is wrong because I erode and dilate exactly the same amount. Btw, the joins in my example look like beveled joins according to your image, but they are mitred.
    – Agustín
    Nov 29, 2020 at 4:48
  • Yes, they produce artifacts, I'm not really sure why, but I think there's no easy solution to that. The joins that look beveled are >90°, so over your mitre limit. You now have the choice: 1. Only use bevel, and all narrow parts will be affected, no artifacts. 2. Use mitre, with a proper mitre limit for 90°. However, this will have artifacts, and narrow parts where both corners are >90° will not split. I don't think there'll be an easy fix for this
    – Fee
    Nov 29, 2020 at 14:57

Your Answer

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

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