Here are polygons with the the same set of points but different starting points/rounding error but still the same direction.
poly1 = Polygon([(0,0),(0,1),(1,1),(1,0)])
poly2 = Polygon([(0,1),(1,1),(1,0),(0,0)])
poly3 = Polygon([(0,1),(1,1.00000001),(1,0),(0,0)])
poly4 = Polygon([(0,0),(0,1),(1,1.00000001),(1,0)])
Issue 1: poly1.almost_equals(poly2) returns False but poly1.equals(poly2) returns True. So equals can handle different starting point but almost_equals can not.
Issue 2: poly1.almost_equals(poly3) returns False but poly1.almost_equals(poly4) returns True. So almost_equals can handle rounding errors but still not different starting point.
Is this how the almost_equals function supposed to behave? I think Polygons with different starting points are still the same Polygon and should be treated so. Are there convenient way to solve this problem? I have a complicated custom solution but am wondering if such operation has been implemented in Shapely.
Yes, this is an expected behavior. It is not clearly stated in the documentation but you can find it in the docstring of the function:
def almost_equals(self, other, decimal=6): """Returns True if geometries are equal at all coordinates to a specified decimal place Refers to approximate coordinate equality, which requires coordinates be approximately equal and in the same order for all components of a geometry. """ return self.equals_exact(other, 0.5 * 10**(-decimal))
Currently, there is no any other function that you could use to solve your problem. There is an open issue on GitHub where the future of almost_equals is discussed. So, probably, soon a new convenient functionality will be introduced. And, meanwhile, you could use several workarounds:
symmetric_difference of two polygons and compare the resulting area with some minimum threshold.def almost_equals(polygon, other, threshold):
# or (polygon ^ other).area < threshold
return polygon.symmetric_difference(other).area < threshold
almost_equals(poly1, poly3, 1e-6) # True
almost_equals. To normalize them you could, for example, orient the vertices of the borders counter-clockwise and then order the vertices so that the first vertex of each polygon would be the lowest and the leftmost comparing to other vertices.from shapely.geometry.polygon import orient
def normalize(polygon):
def normalize_ring(ring):
coords = ring.coords[:-1]
start_index = min(range(len(coords)), key=coords.__getitem__)
return coords[start_index:] + coords[:start_index]
polygon = orient(polygon)
normalized_exterior = normalize_ring(polygon.exterior)
normalized_interiors = list(map(normalize_ring, polygon.interiors))
return Polygon(normalized_exterior, normalized_interiors)
normalize(poly1).almost_equals(normalize(poly3)) # True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With