Repository: geopandas/geopandas
Pull request #2686 (2022-12-24 10:34:02)

COMPAT: Fix pandas tz defaults
This fixes one of the CI failures at the moment, the default representation of timezones has changed on the pandas main branch.


Patch:
diff --git a/ci/envs/39-pd12-conda-forge.yaml b/ci/envs/39-pd12-conda-forge.yaml
index fc675bf9..73b62ec4 100644
--- a/ci/envs/39-pd12-conda-forge.yaml
+++ b/ci/envs/39-pd12-conda-forge.yaml
@@ -5,6 +5,7 @@ dependencies:
   - python=3.9
   # required
   - pandas=1.2
+  - numpy=1.20 # released 1 month after pandas 1.2
   - shapely
   - fiona
   - pyproj
diff --git a/geopandas/_compat.py b/geopandas/_compat.py
index 01f6fb6c..134866b9 100644
--- a/geopandas/_compat.py
+++ b/geopandas/_compat.py
@@ -20,7 +20,7 @@ PANDAS_GE_115 = Version(pd.__version__) >= Version("1.1.5")
 PANDAS_GE_12 = Version(pd.__version__) >= Version("1.2.0")
 PANDAS_GE_13 = Version(pd.__version__) >= Version("1.3.0")
 PANDAS_GE_14 = Version(pd.__version__) >= Version("1.4.0rc0")
-
+PANDAS_GE_20 = Version(pd.__version__) >= Version("2.0.0.dev0")
 
 # -----------------------------------------------------------------------------
 # Shapely / PyGEOS compat
diff --git a/geopandas/_vectorized.py b/geopandas/_vectorized.py
index b1700b49..20aa7332 100644
--- a/geopandas/_vectorized.py
+++ b/geopandas/_vectorized.py
@@ -83,6 +83,9 @@ def _pygeos_to_shapely(geom):
     if pygeos.is_empty(geom) and pygeos.get_type_id(geom) == 0:
         # empty point does not roundtrip through WKB
         return shapely.wkt.loads("POINT EMPTY")
+    elif pygeos.get_type_id(geom) == 2:
+        # linearring does not roundtrip through WKB
+        return shapely.LinearRing(shapely.wkb.loads(pygeos.to_wkb(geom)))
     else:
         return shapely.wkb.loads(pygeos.to_wkb(geom))
 
diff --git a/geopandas/base.py b/geopandas/base.py
index 70c40bda..ada4c79d 100644
--- a/geopandas/base.py
+++ b/geopandas/base.py
@@ -308,7 +308,7 @@ GeometryCollection
         >>> gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
         >>> gdf
                            geometry
-        0  GEOMETRYCOLLECTION EMPTY
+        0               POINT EMPTY
         1   POINT (2.00000 1.00000)
         2                      None
         >>> gdf.is_empty
@@ -2247,7 +2247,7 @@ GeometryCollection
         0                                                 None
         1    POLYGON ((0.00000 2.00000, 2.00000 2.00000, 1....
         2    MULTILINESTRING ((0.00000 0.00000, 1.00000 1.0...
-        3                                     LINESTRING EMPTY
+        3                                   LINESTRING Z EMPTY
         4                              POINT (0.00000 1.00000)
         5                                                 None
         dtype: geometry
@@ -2362,7 +2362,7 @@ GeometryCollection
         0                                                 None
         1    POLYGON ((0.00000 2.00000, 2.00000 2.00000, 1....
         2    MULTILINESTRING ((0.00000 0.00000, 1.00000 1.0...
-        3                                     LINESTRING EMPTY
+        3                                   LINESTRING Z EMPTY
         4        MULTIPOINT (0.00000 1.00000, 1.00000 1.00000)
         5                                                 None
         dtype: geometry
diff --git a/geopandas/geoseries.py b/geopandas/geoseries.py
index a7a84ee7..ef850187 100644
--- a/geopandas/geoseries.py
+++ b/geopandas/geoseries.py
@@ -673,7 +673,7 @@ class GeoSeries(GeoPandasBase, Series):
         >>> s
         0    POLYGON ((0.00000 0.00000, 1.00000 1.00000, 0....
         1                                                 None
-        2                             GEOMETRYCOLLECTION EMPTY
+        2                                        POLYGON EMPTY
         dtype: geometry
         >>> s.isna()
         0    False
@@ -717,7 +717,7 @@ class GeoSeries(GeoPandasBase, Series):
         >>> s
         0    POLYGON ((0.00000 0.00000, 1.00000 1.00000, 0....
         1                                                 None
-        2                             GEOMETRYCOLLECTION EMPTY
+        2                                        POLYGON EMPTY
         dtype: geometry
         >>> s.notna()
         0     True


Test patch:
diff --git a/ci/envs/39-latest-conda-forge.yaml b/ci/envs/39-latest-conda-forge.yaml
index 6058f0b9..0dfc4b9c 100644
--- a/ci/envs/39-latest-conda-forge.yaml
+++ b/ci/envs/39-latest-conda-forge.yaml
@@ -1,12 +1,13 @@
 name: test
 channels:
   - conda-forge
-  # - conda-forge/label/shapely_dev
 dependencies:
   - python=3.9
   # required
   - pandas=1.3
-  # - shapely=2
+  - shapely=2
+  - geos
+  - cython
   - fiona
   - pyproj
   # use this build to have one with only shapely 2.0 installed
@@ -34,7 +35,3 @@ dependencies:
   - pyarrow
   # doctest testing
   - pytest-doctestplus
-  - pip
-  - pip:
-      - --pre
-      - shapely==2.0b1
diff --git a/geopandas/io/tests/test_file.py b/geopandas/io/tests/test_file.py
index df616a5e..416935d9 100644
--- a/geopandas/io/tests/test_file.py
+++ b/geopandas/io/tests/test_file.py
@@ -16,6 +16,7 @@ from shapely.geometry import Point, Polygon, box
 
 import geopandas
 from geopandas import GeoDataFrame, read_file
+from geopandas._compat import PANDAS_GE_20
 from geopandas.io.file import _detect_driver, _EXTENSION_TO_DRIVER
 
 from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
@@ -309,7 +310,11 @@ def test_read_file_datetime_mixed_offsets(tmpdir):
     if FIONA_GE_1814:
         # Convert mixed timezones to UTC equivalent
         assert is_datetime64_any_dtype(res["date"])
-        assert res["date"].dt.tz == pytz.utc
+        if not PANDAS_GE_20:
+            utc = pytz.utc
+        else:
+            utc = datetime.timezone.utc
+        assert res["date"].dt.tz == utc
     else:
         # old fiona and pyogrio ignore timezones and read as datetimes successfully
         assert is_datetime64_any_dtype(res["date"])
diff --git a/geopandas/tests/test_pandas_methods.py b/geopandas/tests/test_pandas_methods.py
index b29ce854..27d45f1d 100644
--- a/geopandas/tests/test_pandas_methods.py
+++ b/geopandas/tests/test_pandas_methods.py
@@ -5,7 +5,7 @@ from numpy.testing import assert_array_equal
 import pandas as pd
 
 import shapely
-from shapely.geometry import Point, GeometryCollection, LineString
+from shapely.geometry import Point, GeometryCollection, LineString, LinearRing
 
 import geopandas
 from geopandas import GeoDataFrame, GeoSeries
@@ -86,6 +86,24 @@ def test_repr_empty():
     assert "geometry" in df._repr_html_()
 
 
+def test_repr_linearring():
+    # https://github.com/geopandas/geopandas/pull/2689
+    # specifically, checking internal shapely/pygeos/wkt/wkb conversions
+    # preserve LinearRing
+    s = GeoSeries([LinearRing([(0, 0), (1, 1), (1, -1)])])
+    assert "LINEARRING" in str(s.iloc[0])  # shapely scalar repr
+    assert "LINEARRING" in str(s)  # GeoSeries repr
+
+    # check something coercible to linearring is not converted
+    s2 = GeoSeries(
+        [
+            LineString([(0, 0), (1, 1), (1, -1)]),
+            LineString([(0, 0), (1, 1), (1, -1), (0, 0)]),
+        ]
+    )
+    assert "LINEARRING" not in str(s2)
+
+
 def test_indexing(s, df):
 
     # accessing scalar from the geometry (column)