shapely: minimum_rotated_rectangle is incorrect in 2.0.0

Expected behavior and actual behavior.

In shapely 2.0.0 the following code produces an incorrect minimum_rotated_rectangle with area=20.4 when it should be 20.

In shapely <2.0.0 the correct minimum_rotated_rectangle with area=20 is found.

Steps to reproduce the problem.

"""
In shapely 2.0.0 the following code produces an incorrect
minimum_rotated_rectangle with area=20.4 and raises:

>       assert new_minimum_rotated_rectangle.area == true_minimum_rotated_rectangle.area
E       assert 20.400000000000006 == 20.0
E        +  where 20.400000000000006 = <POLYGON ((5.1 5.3, 1.5 6.5, -0.2 1.4, 3.4 0.2, 5.1 5.3))>.area
E        +  and   20.0 = <POLYGON ((1 1, 1 6, 5 6, 5 1, 1 1))>.area
bug_in_2_0_0.py:32: AssertionError
"""
import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import MultiPoint, Polygon

def test_minimum_rotated_rectangle():
    points = np.array(
        [[1, 1], [1, 5], [3, 6], [4, 2], [5, 5]]
    )
    pts = MultiPoint(points)
    new_minimum_rotated_rectangle = pts.minimum_rotated_rectangle

    true_minimum_rotated_rectangle = Polygon([[1, 1], [1, 6], [5, 6], [5, 1]])

    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))
    ax.plot(*points.T, c="red", marker=".")
    ax.plot(*np.array(new_minimum_rotated_rectangle.boundary.coords).T,
            c="green", marker="o", alpha=0.2
            )
    ax.plot(*np.array(true_minimum_rotated_rectangle.boundary.coords).T,
            c="blue", marker="X", alpha=0.2
            )
    ax.set(xlim=(0, 10), ylim=(0, 10))
    plt.show()

    print(true_minimum_rotated_rectangle.area)
    print(true_minimum_rotated_rectangle.boundary)
    print(new_minimum_rotated_rectangle.area)
    print(new_minimum_rotated_rectangle.boundary)
    assert new_minimum_rotated_rectangle.area == true_minimum_rotated_rectangle.area

Operating system

Ubuntu, MacOS, Windows

Shapely version and provenance

Shapely 2.0.0 installed from PyPI using pip or from conda-forge using mamba.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 25 (20 by maintainers)

Commits related to this issue

Most upvoted comments

give the default no name (mode=None)

I like this option; it is shorthand for “use whatever is implemented in this version of GEOS”

I like the keyword solution defaulting to None and GEOS.

In my head the idea would be that the default is still the original (1.x) shapely version? So the default of None would be “use whatever shapely currently think is best”, and in the future that might change to use the GEOS version if that gets fixed.

That preserves behaviour compared to shapely 1.8, and still gives the option to pass method="minimum_diameter" (or different naming) for the people that prefer the currently faster method.

On https://github.com/shapely/shapely/issues/1670#issuecomment-1607101266:

The question then is whether it is still worth adding a custom keyword for this, though (someone who wants the speed and not the precise result, can ensure they have GEOS 3.12 installed. Although eg for users installing with pip, that won’t yet be possible on the short term (assuming we would keep 2.0.x wheels bundled with GEOS 3.11.x, and do shapely 2.1.x with GEOS 3.12))

For the updated version of https://github.com/shapely/shapely/pull/1708 that I would like to include in the release, I removed the method keyword, given that it’s not something we would like to keep long term, and the latest GEOS already provides the fast option. Conda users will already have GEOS 3.12 when installing shapely 2.0.2, and for pip users we will hopefully have a shapely 2.1 in some time (for those users, it’s a temporary step back in performance, but also only for a new feature of 2.0, not compared to 1.8)