Repository: geopandas/geopandas
Pull request #3436 (2024-10-05 11:35:26)

COMPAT: ensure that bbox in to_geo_dict and __geo_interface__ uses float, not np.float64
I've ranomly stumbled upon an issue of seeing `np.float64(0)` in the output of `__geo_interface__` representing bbox. This should fix it.

martinfleis: When a string `np.float64(0)` gets sent to JavaScript which has no clue what it means. I've hit this in the wild, which is what triggered the PR.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66d9621b..582e6554 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,14 @@ Notes on dependencies:
 - GeoPandas 1.1 now requires Python 3.10 or greater and pandas 2.0, numpy 1.24, pyproj 3.5,
   are now the minimum required version for these dependencies.
   Furthermore, the minimum tested version for optional dependencies has been updated to
-  fiona 1.8.21, scipy 1.9, matplotlib 3.7, mapclassify 2.5, folium 0.12 and 
+  fiona 1.8.21, scipy 1.9, matplotlib 3.7, mapclassify 2.5, folium 0.12 and
   SQLAlchemy 2.0. Older versions of these libraries may continue to work, but are no longer
   considered supported (#3371).
 
+Bug fixes:
+
+- Fix an issue that showed numpy dtypes in bbox in `to_geo_dict` and `__geo_interface__`. (#3436)
+
 ## Version 1.0.2 (???)
 
 Bug fixes:
diff --git a/ci/envs/310-minimal.yaml b/ci/envs/310-minimal.yaml
index b04427e1..7768abeb 100644
--- a/ci/envs/310-minimal.yaml
+++ b/ci/envs/310-minimal.yaml
@@ -5,7 +5,7 @@ dependencies:
   - python=3.10
   # required
   - numpy=1.24
-  - pandas=1.5
+  - pandas=2.0
   - shapely=2.0.0
   # - pyogrio=0.7.2
   - packaging
diff --git a/ci/envs/312-dev.yaml b/ci/envs/312-dev.yaml
index 278a3076..840c14f8 100644
--- a/ci/envs/312-dev.yaml
+++ b/ci/envs/312-dev.yaml
@@ -14,7 +14,7 @@ dependencies:
   - pytest-cov
   - pytest-xdist
   - fsspec
-  - pytz
+
   # optional
   #- geopy
   - SQLalchemy<2.1 # test oldest support sqlalchemy version
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
index d9125b1d..4c5d76c4 100644
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -1160,7 +1160,7 @@ properties': {'col1': 'name1'}, 'geometry': {'type': 'Point', 'coordinates': (1.
         }
 
         if show_bbox:
-            geo["bbox"] = tuple(self.total_bounds)
+            geo["bbox"] = tuple(self.total_bounds.tolist())  # tolist to avoid np dtypes
 
         return geo
 


Test patch:
diff --git a/geopandas/io/tests/test_file.py b/geopandas/io/tests/test_file.py
index d87935a6..74857fef 100644
--- a/geopandas/io/tests/test_file.py
+++ b/geopandas/io/tests/test_file.py
@@ -6,6 +6,7 @@ import pathlib
 import shutil
 import tempfile
 import warnings
+import zoneinfo
 from collections import OrderedDict
 from packaging.version import Version
 
@@ -46,7 +47,6 @@ except ImportError:
     fiona = False
     FIONA_GE_19 = False
 
-pytz = pytest.importorskip("pytz")
 
 PYOGRIO_MARK = pytest.mark.skipif(not pyogrio, reason="pyogrio not installed")
 FIONA_MARK = pytest.mark.skipif(not fiona, reason="fiona not installed")
@@ -196,9 +196,11 @@ def test_to_file_bool(tmpdir, driver, ext, engine):
 
 
 TEST_DATE = datetime.datetime(2021, 11, 21, 1, 7, 43, 17500)
-eastern = pytz.timezone("America/New_York")
-
-datetime_type_tests = (TEST_DATE, eastern.localize(TEST_DATE))
+# from pandas 2.0, utc equality checks less stringent, forward compat with zoneinfo
+utc = datetime.timezone.utc
+eastern = zoneinfo.ZoneInfo("America/New_York")
+test_date_eastern = TEST_DATE.replace(tzinfo=eastern)
+datetime_type_tests = (TEST_DATE, test_date_eastern)
 
 
 @pytest.mark.filterwarnings(
@@ -231,11 +233,11 @@ def test_to_file_datetime(tmpdir, driver, ext, time, engine):
         expected = df["b"].dt.as_unit("ms")
     actual = df_read["b"]
     if df["b"].dt.tz is not None:
-        # US/Eastern becomes pytz.FixedOffset(-300) when read from file
+        # US/Eastern becomes a UTC-5 fixed offset when read from file
         # as GDAL only models offsets, not timezones.
         # Compare fair result in terms of UTC instead
-        expected = expected.dt.tz_convert(pytz.utc)
-        actual = actual.dt.tz_convert(pytz.utc)
+        expected = expected.dt.tz_convert(utc)
+        actual = actual.dt.tz_convert(utc)
 
     assert_series_equal(expected, actual)
 
@@ -311,10 +313,6 @@ def test_read_file_datetime_mixed_offsets(tmpdir):
     res = read_file(tempfilename)
     # Convert mixed timezones to UTC equivalent
     assert is_datetime64_any_dtype(res["date"])
-    if not PANDAS_GE_20:
-        utc = pytz.utc
-    else:
-        utc = datetime.timezone.utc
     assert res["date"].dt.tz == utc
 
 
diff --git a/geopandas/tests/test_geodataframe.py b/geopandas/tests/test_geodataframe.py
index a123f66d..c34ef948 100644
--- a/geopandas/tests/test_geodataframe.py
+++ b/geopandas/tests/test_geodataframe.py
@@ -951,6 +951,8 @@ class TestDataFrame:
         assert "bbox" in geo.keys()
         assert len(geo["bbox"]) == 4
         assert isinstance(geo["bbox"], tuple)
+        for bound in geo["bbox"]:
+            assert not isinstance(bound, np.float64)
         for feature in geo["features"]:
             assert "bbox" in feature.keys()