Merge remote-tracking branch 'upstream/main' into pr/wassname/416-2

This commit is contained in:
Martin Fleischmann
2022-02-21 20:02:39 +00:00
19 changed files with 90 additions and 117 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
env:
- ci/envs/37-minimal.yaml
- ci/envs/38-no-optional-deps.yaml
- ci/envs/37-pd10.yaml
- ci/envs/37-pd11.yaml
- ci/envs/37-latest-defaults.yaml
- ci/envs/37-latest-conda-forge.yaml
- ci/envs/38-latest-conda-forge.yaml
+6 -8
View File
@@ -6,9 +6,10 @@ dependencies:
- python=3.7
# required
- numpy=1.18
- pandas==0.25
- shapely=1.6
- fiona=1.8.13
- pandas==1.0.5
- shapely=1.7
- fiona=1.8.13.post1
- pyproj=2.6.1.post1
- packaging
#- pyproj
# testing
@@ -19,12 +20,9 @@ dependencies:
# optional
- rtree
- matplotlib
- matplotlib=3.1
# - mapclassify=2.4.0 - doesn't build due to conflicts
- matplotlib=3.2
- mapclassify=2.4.0
- geopy
- SQLalchemy
- libspatialite
- pyarrow
- pip
- pip:
- pyproj==2.2.2
@@ -4,10 +4,11 @@ channels:
dependencies:
- python=3.7
# required
- pandas=1.0
- pandas=1.1
- shapely
- fiona
- numpy=<1.19
- pyproj=3.1.0
#- pyproj
- geos
- packaging
@@ -24,7 +25,6 @@ dependencies:
- libspatialite
- pip
- pip:
- pyproj==3.0.1
- geopy
- mapclassify==2.4.0
- pyarrow
+5 -5
View File
@@ -138,10 +138,10 @@ Dependencies
Required dependencies:
- `numpy`_
- `pandas`_ (version 0.25 or later)
- `shapely`_ (interface to `GEOS`_)
- `fiona`_ (interface to `GDAL`_)
- `pyproj`_ (interface to `PROJ`_; version 2.2.0 or later)
- `pandas`_ (version 1.0 or later)
- `shapely`_ (interface to `GEOS`_; version 1.7 or later)
- `fiona`_ (interface to `GDAL`_; version 1.8 or later)
- `pyproj`_ (interface to `PROJ`_; version 2.6.1 or later)
- `packaging`_
Further, optional dependencies are:
@@ -155,7 +155,7 @@ Further, optional dependencies are:
For plotting, these additional packages may be used:
- `matplotlib`_ (>= 3.1.0)
- `matplotlib`_ (>= 3.2.0)
- `mapclassify`_ (>= 2.4.0)
+7 -7
View File
@@ -5,10 +5,10 @@ dependencies:
- python
# required
- fiona>=1.8
- pandas>=0.25
- pandas>=1.0.0
- pygeos
- pyproj>=2.2.0
- shapely>=1.6
- pyproj>=2.6.1.post1
- shapely>=1.7
- packaging
# testing
@@ -33,10 +33,10 @@ dependencies:
# geocoding
- geopy
# geodatabase access
- psycopg2>=2.5.1
- SQLAlchemy>=0.8.3
- psycopg2>=2.8.0
- SQLAlchemy>=1.3
# plotting
- matplotlib>=2.2
- matplotlib>=3.2
- mapclassify
# spatial access methods
- rtree>=0.8
- rtree>=0.9
+2 -2
View File
@@ -15,7 +15,6 @@ import shapely.geos
# pandas compat
# -----------------------------------------------------------------------------
PANDAS_GE_10 = Version(pd.__version__) >= Version("1.0.0")
PANDAS_GE_11 = Version(pd.__version__) >= Version("1.1.0")
PANDAS_GE_115 = Version(pd.__version__) >= Version("1.1.5")
PANDAS_GE_12 = Version(pd.__version__) >= Version("1.2.0")
@@ -28,7 +27,6 @@ PANDAS_GE_14 = Version(pd.__version__) >= Version("1.4.0rc0")
# -----------------------------------------------------------------------------
SHAPELY_GE_17 = Version(shapely.__version__) >= Version("1.7.0")
SHAPELY_GE_18 = Version(shapely.__version__) >= Version("1.8")
SHAPELY_GE_20 = Version(shapely.__version__) >= Version("2.0")
@@ -222,9 +220,11 @@ try:
except ImportError:
HAS_RTREE = False
# -----------------------------------------------------------------------------
# pyproj compat
# -----------------------------------------------------------------------------
PYPROJ_LT_3 = Version(pyproj.__version__) < Version("3")
PYPROJ_GE_31 = Version(pyproj.__version__) >= Version("3.1")
PYPROJ_GE_32 = Version(pyproj.__version__) >= Version("3.2")
+1 -1
View File
@@ -56,7 +56,7 @@ def isna(value):
return True
elif isinstance(value, float) and np.isnan(value):
return True
elif compat.PANDAS_GE_10 and value is pd.NA:
elif value is pd.NA:
return True
else:
return False
+13 -20
View File
@@ -1,4 +1,3 @@
from collections.abc import Iterable
import numbers
import operator
import warnings
@@ -359,24 +358,19 @@ class GeometryArray(ExtensionArray):
if isinstance(idx, numbers.Integral):
return _geom_to_shapely(self.data[idx])
# array-like, slice
if compat.PANDAS_GE_10:
# for pandas >= 1.0, validate and convert IntegerArray/BooleanArray
# to numpy array, pass-through non-array-like indexers
idx = pd.api.indexers.check_array_indexer(self, idx)
return GeometryArray(self.data[idx], crs=self.crs)
else:
if isinstance(idx, (Iterable, slice)):
return GeometryArray(self.data[idx], crs=self.crs)
else:
raise TypeError("Index type not supported", idx)
# validate and convert IntegerArray/BooleanArray
# to numpy array, pass-through non-array-like indexers
idx = pd.api.indexers.check_array_indexer(self, idx)
return GeometryArray(self.data[idx], crs=self.crs)
def __setitem__(self, key, value):
if compat.PANDAS_GE_10:
# for pandas >= 1.0, validate and convert IntegerArray/BooleanArray
# keys to numpy array, pass-through non-array-like indexers
key = pd.api.indexers.check_array_indexer(self, key)
# validate and convert IntegerArray/BooleanArray
# keys to numpy array, pass-through non-array-like indexers
key = pd.api.indexers.check_array_indexer(self, key)
if isinstance(value, pd.Series):
value = value.values
if isinstance(value, pd.DataFrame):
value = value.values.flatten()
if isinstance(value, (list, np.ndarray)):
value = from_shapely(value)
if isinstance(value, GeometryArray):
@@ -1054,11 +1048,10 @@ class GeometryArray(ExtensionArray):
dtype
):
string_values = to_wkt(self)
if compat.PANDAS_GE_10:
pd_dtype = pd.api.types.pandas_dtype(dtype)
if isinstance(pd_dtype, pd.StringDtype):
# ensure to return a pandas string array instead of numpy array
return pd.array(string_values, dtype=pd_dtype)
pd_dtype = pd.api.types.pandas_dtype(dtype)
if isinstance(pd_dtype, pd.StringDtype):
# ensure to return a pandas string array instead of numpy array
return pd.array(string_values, dtype=pd_dtype)
return string_values.astype(dtype, copy=False)
else:
return np.array(self, dtype=dtype, copy=copy)
+1 -6
View File
@@ -1773,11 +1773,6 @@ individually so that features may have different properties
# Overridden to fix GH1870, that return type is not preserved always
# (and where it was, geometry col was not)
if not compat.PANDAS_GE_10:
raise NotImplementedError(
"GeoDataFrame.convert_dtypes requires pandas >= 1.0"
)
return GeoDataFrame(
super().convert_dtypes(*args, **kwargs),
geometry=self.geometry.name,
@@ -2249,5 +2244,5 @@ def _dataframe_set_geometry(self, col, drop=False, inplace=False, crs=None):
DataFrame.set_geometry = _dataframe_set_geometry
if compat.PANDAS_GE_10 and not compat.PANDAS_GE_11: # i.e. on pandas 1.0.x
if not compat.PANDAS_GE_11: # i.e. on pandas 1.0.x
_geodataframe_constructor_with_fallback._from_axes = GeoDataFrame._from_axes
+1 -9
View File
@@ -369,15 +369,7 @@ def _write_postgis(
try:
from geoalchemy2 import Geometry
except ImportError:
raise ImportError("'to_postgis()' requires geoalchemy2 package. ")
if not compat.SHAPELY_GE_17:
raise ImportError(
"'to_postgis()' requires newer version of Shapely "
"(>= '1.7.0').\nYou can update the library using "
"'pip install shapely --upgrade' or using "
"'conda update shapely' if using conda package manager."
)
raise ImportError("'to_postgis()' requires geoalchemy2 package.")
gdf = gdf.copy()
geom_name = gdf.geometry.name
+1 -1
View File
@@ -71,7 +71,7 @@ def _expand_kwargs(kwargs, multiindex):
from typing import Iterable
mpl = Version(matplotlib.__version__)
if mpl >= Version("3.4") or (mpl > Version("3.3.2") and "+" in mpl):
if mpl >= Version("3.4"):
# alpha is supported as array argument with matplotlib 3.4+
scalar_kwargs = ["marker", "path_effects"]
else:
+1 -5
View File
@@ -1,8 +1,6 @@
import subprocess
import sys
from geopandas._compat import PANDAS_GE_10
def test_no_additional_imports():
# test that 'import geopandas' does not import any of the optional or
@@ -20,10 +18,8 @@ def test_no_additional_imports():
"psycopg2",
"geopy",
"geoalchemy2",
"matplotlib",
}
if PANDAS_GE_10:
# pandas > 0.25 stopped importing matplotlib by default
blacklist.add("matplotlib")
code = """
import sys
+27 -16
View File
@@ -144,9 +144,7 @@ def test_from_wkb():
missing_values = [None]
if not compat.USE_PYGEOS:
missing_values.extend([b"", np.nan])
if compat.PANDAS_GE_10:
missing_values.append(pd.NA)
missing_values.append(pd.NA)
res = from_wkb(missing_values)
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
@@ -220,9 +218,7 @@ def test_from_wkt(string_type):
missing_values = [None]
if not compat.USE_PYGEOS:
missing_values.extend([f(""), np.nan])
if compat.PANDAS_GE_10:
missing_values.append(pd.NA)
missing_values.append(pd.NA)
res = from_wkb(missing_values)
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
@@ -436,10 +432,6 @@ def test_binary_geo_scalar(attr):
"is_ring",
marks=[
pytest.mark.filterwarnings("ignore:is_ring:FutureWarning"),
pytest.mark.skipif(
not compat.SHAPELY_GE_17,
reason="is_ring on empty Polygon doesn't work in Shapely 1.6",
),
],
),
],
@@ -482,10 +474,6 @@ def test_unary_predicates(attr):
# for is_ring we raise a warning about the value for Polygon changing
@pytest.mark.filterwarnings("ignore:is_ring:FutureWarning")
@pytest.mark.skipif(
not compat.SHAPELY_GE_17,
reason="is_ring on empty Polygon doesn't work in Shapely 1.6",
)
def test_is_ring():
g = [
shapely.geometry.LinearRing([(0, 0), (1, 1), (1, -1)]),
@@ -766,6 +754,29 @@ def test_getitem():
assert P5.equals(points[1])
@pytest.mark.parametrize(
"item",
[
geopandas.GeoDataFrame(
geometry=[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
),
geopandas.GeoSeries(
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
),
np.array([shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]),
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])],
shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
],
)
def test_setitem(item):
points = [shapely.geometry.Point(i, i) for i in range(10)]
P = from_shapely(points)
P[[0]] = item
assert isinstance(P[0], shapely.geometry.Polygon)
def test_equality_ops():
with pytest.raises(ValueError):
P[:5] == P[:7]
@@ -886,7 +897,6 @@ def test_isna(NA):
assert t1[0] is None
@pytest.mark.skipif(not compat.PANDAS_GE_10, reason="pd.NA introduced in pandas 1.0")
def test_isna_pdNA():
t1 = T.copy()
t1[0] = pd.NA
@@ -922,7 +932,8 @@ class TestEstimateUtmCrs:
self.landmarks.estimate_utm_crs()
else:
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
def test_estimate_utm_crs__projected(self):
+3 -4
View File
@@ -7,7 +7,6 @@ from packaging.version import Version
import numpy as np
import pandas as pd
import pyproj
from pyproj import CRS
from pyproj.exceptions import CRSError
from shapely.geometry import Point, Polygon
@@ -24,7 +23,6 @@ from pandas.testing import assert_frame_equal, assert_index_equal, assert_series
import pytest
PYPROJ_LT_3 = Version(pyproj.__version__) < Version("3")
TEST_NEAREST = compat.PYGEOS_GE_010 and compat.USE_PYGEOS
pandas_133 = Version(pd.__version__) == Version("1.3.3")
@@ -794,12 +792,13 @@ class TestDataFrame:
assert self.df.crs == unpickled.crs
def test_estimate_utm_crs(self):
if PYPROJ_LT_3:
if compat.PYPROJ_LT_3:
with pytest.raises(RuntimeError, match=r"pyproj 3\+ required"):
self.df.estimate_utm_crs()
else:
assert self.df.estimate_utm_crs() == CRS("EPSG:32618")
assert self.df.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
assert self.df.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
def test_to_wkb(self):
wkbs0 = [
+9 -6
View File
@@ -21,7 +21,7 @@ from shapely.geometry import (
from shapely.geometry.base import BaseGeometry
from geopandas import GeoSeries, GeoDataFrame, read_file, datasets, clip
from geopandas._compat import PYPROJ_LT_3, ignore_shapely2_warnings
from geopandas._compat import ignore_shapely2_warnings
from geopandas.array import GeometryArray, GeometryDtype
from geopandas.testing import assert_geoseries_equal
@@ -29,6 +29,8 @@ from geopandas.tests.util import geom_equals
from pandas.testing import assert_series_equal
import pytest
import geopandas._compat as compat
class TestSeries:
def setup_method(self):
@@ -205,27 +207,28 @@ class TestSeries:
self.landmarks.to_crs(crs=None, epsg=None)
def test_estimate_utm_crs__geographic(self):
if PYPROJ_LT_3:
if compat.PYPROJ_LT_3:
with pytest.raises(RuntimeError, match=r"pyproj 3\+ required"):
self.landmarks.estimate_utm_crs()
else:
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
def test_estimate_utm_crs__projected(self):
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == CRS(
"EPSG:32618"
)
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
def test_estimate_utm_crs__out_of_bounds(self):
with pytest.raises(RuntimeError, match="Unable to determine UTM CRS"):
GeoSeries(
[Polygon([(0, 90), (1, 90), (2, 90)])], crs="EPSG:4326"
).estimate_utm_crs()
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
def test_estimate_utm_crs__missing_crs(self):
with pytest.raises(RuntimeError, match="crs must be set"):
GeoSeries([Polygon([(0, 90), (1, 90), (2, 90)])]).estimate_utm_crs()
-6
View File
@@ -263,11 +263,6 @@ def test_astype_invalid_geodataframe():
assert res["a"].dtype == object
@pytest.mark.xfail(
not compat.PANDAS_GE_10,
reason="Convert dtypes new in pandas 1.0",
raises=NotImplementedError,
)
def test_convert_dtypes(df):
# https://github.com/geopandas/geopandas/issues/1870
@@ -662,7 +657,6 @@ def test_df_apply_returning_series(df):
assert_series_equal(result, df["value1"].rename(None))
@pytest.mark.skipif(not compat.PANDAS_GE_10, reason="attrs introduced in pandas 1.0")
def test_preserve_attrs(df):
# https://github.com/geopandas/geopandas/issues/1654
df.attrs["name"] = "my_name"
-8
View File
@@ -1384,9 +1384,6 @@ class TestPlotCollections:
)
def test_points(self):
# failing with matplotlib 1.4.3 (edge stays black even when specified)
pytest.importorskip("matplotlib", "1.5.0")
from geopandas.plotting import _plot_point_collection, plot_point_collection
from matplotlib.collections import PathCollection
@@ -1843,11 +1840,6 @@ def _get_ax(fig, label):
Previously, we did `fig.axes[1]`, but in matplotlib 3.4 the order switched
and the colorbar ax was first and subplot ax second.
"""
if Version(matplotlib.__version__) < Version("3.0.0"):
if label == "<colorbar>":
return fig.axes[1]
elif label == "":
return fig.axes[0]
for ax in fig.axes:
if ax.get_label() == label:
return ax
+7 -7
View File
@@ -1,19 +1,19 @@
# required
fiona>=1.8
pandas>=0.25
pyproj>=2.2.0
shapely>=1.6
pandas>=1.0.0
pyproj>=2.6.1.post1
shapely>=1.7
packaging
# geodatabase access
psycopg2>=2.5.1
SQLAlchemy>=0.8.3
psycopg2>=2.8.0
SQLAlchemy>=1.3
# geocoding
geopy
# plotting
matplotlib>=2.2
matplotlib>=3.2
mapclassify
# testing
@@ -22,7 +22,7 @@ pytest-cov
codecov
# spatial access methods
rtree>=0.8
rtree>=0.9
# styling
black
+3 -3
View File
@@ -30,10 +30,10 @@ if os.environ.get("READTHEDOCS", False) == "True":
INSTALL_REQUIRES = []
else:
INSTALL_REQUIRES = [
"pandas >= 0.25.0",
"shapely >= 1.6",
"pandas >= 1.0.0",
"shapely >= 1.7",
"fiona >= 1.8",
"pyproj >= 2.2.0",
"pyproj >= 2.6.1.post1",
"packaging",
]