Repository: geopandas/geopandas
Pull request #2833 (2023-03-12 09:16:09)

DEPR: deprecate the usage of GeometryArray.data to access PyGEOS geometries
Next step for the PyGEOS->Shapely migration of https://github.com/geopandas/geopandas/issues/2691

Next step after adding `_data` in https://github.com/geopandas/geopandas/pull/2785 to actually raise a warning when accessing `data` (I now am doing this always, not only when there are pygeos geometries)


Patch:
diff --git a/benchmarks/sindex.py b/benchmarks/sindex.py
index 0510f035..0767bb1f 100644
--- a/benchmarks/sindex.py
+++ b/benchmarks/sindex.py
@@ -27,7 +27,7 @@ def generate_test_df():
     }
     # ensure index is pre-generated
     for data_type in data.keys():
-        data[data_type].sindex.query(data[data_type].geometry.values.data[0])
+        data[data_type].sindex.query(data[data_type].geometry.values[0])
     return data
 
 
@@ -75,7 +75,7 @@ class BenchIndexCreation:
         tree = self.data[tree_geom_type].sindex
         # also do a single query to ensure the index is actually
         # generated and used
-        tree.query(self.data[tree_geom_type].geometry.values.data[0])
+        tree.query(self.data[tree_geom_type].geometry.values[0])
 
 
 class BenchQuery:
@@ -91,11 +91,11 @@ class BenchQuery:
 
     def time_query_bulk(self, predicate, input_geom_type, tree_geom_type):
         self.data[tree_geom_type].sindex.query_bulk(
-            self.data[input_geom_type].geometry.values.data,
+            self.data[input_geom_type].geometry.values,
             predicate=predicate,
         )
 
     def time_query(self, predicate, input_geom_type, tree_geom_type):
         tree = self.data[tree_geom_type].sindex
-        for geom in self.data[input_geom_type].geometry.values.data:
+        for geom in self.data[input_geom_type].geometry.values:
             tree.query(geom, predicate=predicate)
diff --git a/geopandas/_compat.py b/geopandas/_compat.py
index 8bbc5a91..eb9a2354 100644
--- a/geopandas/_compat.py
+++ b/geopandas/_compat.py
@@ -137,16 +137,21 @@ def set_use_pygeos(val=None):
     if USE_PYGEOS and env_use_pygeos is None and SHAPELY_GE_20:
         warnings.warn(
             "Shapely 2.0 is installed, but because PyGEOS is also installed, "
-            "GeoPandas will still use PyGEOS by default for now. To force to use and "
-            "test Shapely 2.0, you have to set the environment variable USE_PYGEOS=0. "
+            "GeoPandas still uses PyGEOS by default. However, starting with "
+            "version 0.14, the default will switch to Shapely. To force to "
+            "use Shapely 2.0 now, you can either uninstall PyGEOS or set the "
+            "environment variable USE_PYGEOS=0. "
             "You can do this before starting the Python process, or in your code "
             "before importing geopandas:"
             "\n\nimport os\nos.environ['USE_PYGEOS'] = '0'\nimport geopandas\n\n"
-            "In a future release, GeoPandas will switch to using Shapely by default. "
-            "If you are using PyGEOS directly (calling PyGEOS functions on geometries "
+            "In the next release, GeoPandas will switch to using Shapely by default, "
+            "even if PyGEOS is installed. If you only have PyGEOS installed to "
+            "get speed-ups, this switch should be smooth. However, if you are "
+            "using PyGEOS directly (calling PyGEOS functions on geometries "
             "from GeoPandas), this will then stop working and you are encouraged to "
             "migrate from PyGEOS to Shapely 2.0 "
             "(https://shapely.readthedocs.io/en/latest/migration_pygeos.html).",
+            DeprecationWarning,
             stacklevel=6,
         )
 
diff --git a/geopandas/array.py b/geopandas/array.py
index 41f75f7a..a5a3b72d 100644
--- a/geopandas/array.py
+++ b/geopandas/array.py
@@ -295,6 +295,18 @@ class GeometryArray(ExtensionArray):
 
     @property
     def data(self):
+        warnings.warn(
+            "Accessing the underlying geometries through the `.data` attribute is "
+            "deprecated and will be removed in GeoPandas 1.0. You can use "
+            "`np.asarray(..)` or the `to_numpy()` method instead.\n"
+            "Note that if you are using PyGEOS and using this attribute to get an "
+            "array of PyGEOS geometries, those other methods will always return an "
+            "array of Shapely geometries. Accessing the underlying PyGEOS geometries "
+            "directly is deprecated, and you should migrate to use Shapely >= 2.0 "
+            "instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
         return self._data
 
     @property
diff --git a/geopandas/sindex.py b/geopandas/sindex.py
index f3097f90..6f055416 100644
--- a/geopandas/sindex.py
+++ b/geopandas/sindex.py
@@ -716,7 +716,7 @@ if compat.SHAPELY_GE_20 or compat.HAS_PYGEOS:
                 A numpy array of pygeos geometries.
             """
             if isinstance(geometry, np.ndarray):
-                return geometry
+                return array.from_shapely(geometry)._data
             elif isinstance(geometry, geoseries.GeoSeries):
                 return geometry.values._data
             elif isinstance(geometry, array.GeometryArray):


Test patch:
diff --git a/geopandas/tests/test_array.py b/geopandas/tests/test_array.py
index b26e3ba3..b62e3f0c 100644
--- a/geopandas/tests/test_array.py
+++ b/geopandas/tests/test_array.py
@@ -8,7 +8,7 @@ from pyproj import CRS
 import shapely
 import shapely.affinity
 import shapely.geometry
-from shapely.geometry.base import CAP_STYLE, JOIN_STYLE
+from shapely.geometry.base import CAP_STYLE, JOIN_STYLE, BaseGeometry
 import shapely.wkb
 import shapely.wkt
 
@@ -250,7 +250,18 @@ def test_to_wkt():
 
 def test_data():
     arr = from_shapely(points_no_missing)
-    assert isinstance(arr.data, np.ndarray)
+    with pytest.warns(DeprecationWarning):
+        np_arr = arr.data
+
+    assert isinstance(np_arr, np.ndarray)
+    if compat.USE_PYGEOS:
+        np_arr2 = arr.to_numpy()
+        assert isinstance(np_arr2[0], BaseGeometry)
+        np_arr3 = np.asarray(arr)
+        assert isinstance(np_arr3[0], BaseGeometry)
+    else:
+        assert arr.to_numpy() is np_arr
+        assert np.asarray(arr) is np_arr
 
 
 def test_as_array():
diff --git a/geopandas/tests/test_sindex.py b/geopandas/tests/test_sindex.py
index e9d2e8ed..0cc2e42b 100644
--- a/geopandas/tests/test_sindex.py
+++ b/geopandas/tests/test_sindex.py
@@ -444,7 +444,7 @@ class TestPygeosInterface:
         tree_df = geopandas.GeoDataFrame(geometry=tree_polys)
         test_df = geopandas.GeoDataFrame(geometry=test_polys)
 
-        test_geo = test_df.geometry.values.data[0]
+        test_geo = test_df.geometry.values[0]
         res = tree_df.sindex.query(test_geo, sort=sort)
 
         # asserting the same elements
@@ -649,11 +649,11 @@ class TestPygeosInterface:
 
         # test numpy array
         res = self.df.sindex.query_bulk(
-            test_geom.geometry.values.data, predicate=predicate
+            test_geom.geometry.values.to_numpy(), predicate=predicate
         )
         assert_array_equal(res, expected)
         res = self.df.sindex.query_bulk(
-            test_geom.geometry.values.data, predicate=predicate
+            test_geom.geometry.values.to_numpy(), predicate=predicate
         )
         assert_array_equal(res, expected)