almost_equals
should return True, if equals
is True.
import numpy as np
from shapely.geometry import Polygon, box
coords = np.array(
[[ 577., 1250.],
[ 593., 1325.],
[2447., 1002.],
[2447., 945.]], dtype=np.float32
)
p = Polygon(coords)
rect = Polygon.from_bounds(*p.bounds)
rect2 = box(*p.bounds)
print(rect.equals(rect2))
print(rect.almost_equals(rect2))
Ubuntu Linux 16
1.7.0
@dashesy this is the expected behaviour, although certainly a "gotcha" when using almost_equals
.
If you look at the two rectangles:
In [12]: print(rect)
POLYGON ((577 945, 577 1325, 2447 1325, 2447 945, 577 945))
In [13]: print(rect2)
POLYGON ((2447 945, 2447 1325, 577 1325, 577 945, 2447 945))
you can see they are not "exactly" equal (the order of the coordinate pairs differ). The equals
method check topological / spatial equality, and thus returns True as both polygons describe the same rectangle area.
But the almost_equals
method, on the other hand, checks if two objects are structurally equal. This method uses exact coordinate equality, which requires coordinates to be equal (within specified tolerance) and and in the same order for all components of a geometry.
There is actually a similar method equals_exact
(which IMO better reflects this constraint in its name as almost_equals
).
This is mentioned in the docstring:
But it seems that a similar note would be welcome in the almost_equals
entry in the manual, as there this constraint is not very explicitly mentioned: https://shapely.readthedocs.io/en/latest/manual.html#object.almost_equals
Interesting. Yes documentation would be helpful, but I think the name is unfortunate. Maybe the current almost_equals
should be called almost_equals_exact
and have a almost_equals
that is a version of equals
with some error tolerance.
have a almost_equals that is a version of equals with some error tolerance.
That is not possible, since for a tolerance, you need to be able to compare coordinates up to that tolerance. But spatial equality doesn't compare coordinates, only structural equality does that (and which then requires the same order).
For spatial equality that is not exact, you could look at other measures, like "intersection over union" and define a threshold how close to 1 that needs to be. But such measures are very application-specific (and can be implemented in terms of other methods defined in shapely), so I don't think that makes sense to add to shapely.
One possibility that could help, though, would be to automatically "normalize" the geometries passed to almost_equals
(or at least have an option for that).
Yeah I am now using IOU, but wanted to try other measures, thanks for the info.
@dashesy we could deprecate almost_equals (confusing name, I agree) and move its tolerance argument to equals_exact (default tolerance of 0). Would that improve the situation from your perspective?
@sgillies yes I think that makes everything simpler and clearer.
I would be +1 on deprecating almost_equals
.
(note that it's equals_exact
that has a tolerance argument, almost_equals
has a "decimals" argument which gets translated to a tolerance)
I was looking at JTS some time ago (https://docs.geotools.org/latest/userguide/library/jts/equals.html) for possible inspiration, and they have a "equalsTopo" (for a more explicit name for "equals"), and a "equalsNorm" (which does an exact equals but automatically normalizes the geometries first)
I think certainly the normalization (either as separate function as JTS, or as option in equals_exact
) would be useful (I think it will be more accessible than a separate normalization function).
Would it be better to add a normalize
kwarg to equals_exact
or to expose a separate equals_normalized
method? My first instinct is a kwarg which defaults to False
.
Most helpful comment
I would be +1 on deprecating
almost_equals
.(note that it's
equals_exact
that has a tolerance argument,almost_equals
has a "decimals" argument which gets translated to a tolerance)I was looking at JTS some time ago (https://docs.geotools.org/latest/userguide/library/jts/equals.html) for possible inspiration, and they have a "equalsTopo" (for a more explicit name for "equals"), and a "equalsNorm" (which does an exact equals but automatically normalizes the geometries first)
I think certainly the normalization (either as separate function as JTS, or as option in
equals_exact
) would be useful (I think it will be more accessible than a separate normalization function).