[Git][debian-gis-team/python-geopandas][upstream] New upstream version 0.6.2
Bas Couwenberg
gitlab at salsa.debian.org
Tue Nov 19 04:47:02 GMT 2019
Bas Couwenberg pushed to branch upstream at Debian GIS Project / python-geopandas
Commits:
0246bb2c by Bas Couwenberg at 2019-11-19T04:25:41Z
New upstream version 0.6.2
- - - - -
11 changed files:
- CHANGELOG.md
- geopandas/_compat.py
- geopandas/_version.py
- geopandas/array.py
- geopandas/geodataframe.py
- geopandas/plotting.py
- geopandas/tests/test_array.py
- geopandas/tests/test_geodataframe.py
- geopandas/tests/test_geom_methods.py
- geopandas/tests/test_pandas_methods.py
- geopandas/tests/test_plotting.py
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -1,8 +1,22 @@
Changes
=======
-Version 0.6.1 (July 11, 2019)
------------------------------
+
+Version 0.6.2 (November 18, 2019)
+---------------------------------
+
+Small bug-fix release fixing a few regressions:
+
+- Fix a regression in passing an array of RRB(A) tuples to the ``.plot()``
+ method (#1178, #1211).
+- Fix the ``bounds`` and ``total_bounds`` attributes for empty GeoSeries, which
+ also fixes the repr of an empty or all-NA GeoSeries (#1184, #1195).
+- Fix filtering of a GeoDataFrame to preserve the index type when ending up
+ with an empty result (#1190).
+
+
+Version 0.6.1 (October 12, 2019)
+--------------------------------
Small bug-fix release fixing a few regressions:
=====================================
geopandas/_compat.py
=====================================
@@ -8,6 +8,7 @@ import six
# -----------------------------------------------------------------------------
PANDAS_GE_024 = str(pd.__version__) >= LooseVersion("0.24.0")
+PANDAS_GE_025 = str(pd.__version__) >= LooseVersion("0.25.0")
# -----------------------------------------------------------------------------
=====================================
geopandas/_version.py
=====================================
@@ -22,8 +22,8 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (tag: v0.6.1)"
- git_full = "7df43d1375fee9174eb9314567c2464ed2d7e7a1"
+ git_refnames = " (tag: v0.6.2, 0.6.x)"
+ git_full = "34fe94357b8131afb49da76b1e5b14d9477f9e37"
keywords = {"refnames": git_refnames, "full": git_full}
return keywords
=====================================
geopandas/array.py
=====================================
@@ -732,6 +732,9 @@ class GeometryArray(ExtensionArray):
@property
def bounds(self):
+ # ensure that for empty arrays, the result has the correct shape
+ if len(self) == 0:
+ return np.empty((0, 4), dtype="float64")
# need to explicitly check for empty (in addition to missing) geometries,
# as those return an empty tuple, not resulting in a 2D array
bounds = np.array(
@@ -746,6 +749,10 @@ class GeometryArray(ExtensionArray):
@property
def total_bounds(self):
+ if len(self) == 0:
+ # numpy 'min' cannot handle empty arrays
+ # TODO with numpy >= 1.15, the 'initial' argument can be used
+ return np.array([np.nan, np.nan, np.nan, np.nan])
b = self.bounds
return np.array(
(
=====================================
geopandas/geodataframe.py
=====================================
@@ -72,11 +72,17 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
# TODO do we want to raise / return normal DataFrame in this case?
if geometry is None and "geometry" in self.columns:
# only if we have actual geometry values -> call set_geometry
+ index = self.index
try:
self["geometry"] = _ensure_geometry(self["geometry"].values)
except TypeError:
pass
else:
+ if self.index is not index:
+ # With pandas < 1.0 and an empty frame (no rows), the index
+ # gets reset to a default RangeIndex -> set back the original
+ # index if needed
+ self.index = index
geometry = "geometry"
if geometry is not None:
@@ -173,7 +179,12 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
# Check that we are using a listlike of geometries
level = _ensure_geometry(level)
+ index = frame.index
frame[geo_column_name] = level
+ if frame.index is not index and len(frame.index) == len(index):
+ # With pandas < 1.0 and an empty frame (no rows), the index gets reset
+ # to a default RangeIndex -> set back the original index if needed
+ frame.index = index
frame._geometry_column_name = geo_column_name
frame.crs = crs
frame._invalidate_sindex()
=====================================
geopandas/plotting.py
=====================================
@@ -90,25 +90,36 @@ def plot_polygon_collection(
"The descartes package is required for plotting polygons in geopandas."
)
from matplotlib.collections import PatchCollection
+ from matplotlib.colors import is_color_like
geoms, multiindex = _flatten_multi_geoms(geoms, range(len(geoms)))
if values is not None:
- values = np.take(values, multiindex)
+ values = np.take(values, multiindex, axis=0)
# PatchCollection does not accept some kwargs.
if "markersize" in kwargs:
del kwargs["markersize"]
if color is not None:
- kwargs["color"] = color
- if pd.api.types.is_list_like(color):
- kwargs["color"] = np.take(color, multiindex)
- else:
+ if is_color_like(color):
kwargs["color"] = color
+ elif pd.api.types.is_list_like(color):
+ kwargs["color"] = np.take(color, multiindex, axis=0)
+ else:
+ raise TypeError(
+ "Color attribute has to be a single color or sequence of colors."
+ )
+
else:
for att in ["facecolor", "edgecolor"]:
if att in kwargs:
- if pd.api.types.is_list_like(kwargs[att]):
- kwargs[att] = np.take(kwargs[att], multiindex)
+ if not is_color_like(kwargs[att]):
+ if pd.api.types.is_list_like(kwargs[att]):
+ kwargs[att] = np.take(kwargs[att], multiindex, axis=0)
+ elif kwargs[att] is not None:
+ raise TypeError(
+ "Color attribute has to be a single color or sequence "
+ "of colors."
+ )
collection = PatchCollection([PolygonPatch(poly) for poly in geoms], **kwargs)
@@ -152,10 +163,11 @@ def plot_linestring_collection(
"""
from matplotlib.collections import LineCollection
+ from matplotlib.colors import is_color_like
geoms, multiindex = _flatten_multi_geoms(geoms, range(len(geoms)))
if values is not None:
- values = np.take(values, multiindex)
+ values = np.take(values, multiindex, axis=0)
# LineCollection does not accept some kwargs.
if "markersize" in kwargs:
@@ -163,10 +175,14 @@ def plot_linestring_collection(
# color=None gives black instead of default color cycle
if color is not None:
- if pd.api.types.is_list_like(color):
- kwargs["color"] = np.take(color, multiindex)
- else:
+ if is_color_like(color):
kwargs["color"] = color
+ elif pd.api.types.is_list_like(color):
+ kwargs["color"] = np.take(color, multiindex, axis=0)
+ else:
+ raise TypeError(
+ "Color attribute has to be a single color or sequence of colors."
+ )
segments = [np.array(linestring)[:, :2] for linestring in geoms]
collection = LineCollection(segments, **kwargs)
@@ -215,12 +231,14 @@ def plot_point_collection(
-------
collection : matplotlib.collections.Collection that was plotted
"""
+ from matplotlib.colors import is_color_like
+
if values is not None and color is not None:
raise ValueError("Can only specify one of 'values' and 'color' kwargs")
geoms, multiindex = _flatten_multi_geoms(geoms, range(len(geoms)))
if values is not None:
- values = np.take(values, multiindex)
+ values = np.take(values, multiindex, axis=0)
x = [p.x for p in geoms]
y = [p.y for p in geoms]
@@ -232,8 +250,13 @@ def plot_point_collection(
kwargs["s"] = markersize
if color is not None:
- if pd.api.types.is_list_like(color):
- color = np.take(color, multiindex)
+ if not is_color_like(color):
+ if pd.api.types.is_list_like(color):
+ color = np.take(color, multiindex, axis=0)
+ else:
+ raise TypeError(
+ "Color attribute has to be a single color or sequence of colors."
+ )
if "norm" not in kwargs:
collection = ax.scatter(
=====================================
geopandas/tests/test_array.py
=====================================
@@ -9,6 +9,7 @@ import shapely.affinity
import shapely.geometry
from shapely.geometry.base import CAP_STYLE, JOIN_STYLE
import shapely.wkb
+from shapely._buildcfg import geos_version
import geopandas
from geopandas.array import (
@@ -394,8 +395,8 @@ def test_binary_geo_scalar(attr):
)
def test_unary_predicates(attr):
na_value = False
- if attr == "is_simple":
- # poly.is_simple raises an error for empty polygon
+ if attr == "is_simple" and geos_version < (3, 8):
+ # poly.is_simple raises an error for empty polygon for GEOS < 3.8
with pytest.raises(Exception):
T.is_simple
vals = triangle_no_missing
@@ -616,6 +617,36 @@ def test_bounds():
assert result.dtype == "float64"
np.testing.assert_allclose(result, np.array([[np.nan] * 4]))
+ # empty array (https://github.com/geopandas/geopandas/issues/1195)
+ E = from_shapely([])
+ result = E.bounds
+ assert result.shape == (0, 4)
+ assert result.dtype == "float64"
+
+
+def test_total_bounds():
+ result = T.total_bounds
+ bounds = np.array(
+ [t.bounds if not (t is None or t.is_empty) else [np.nan] * 4 for t in triangles]
+ )
+ expected = np.array(
+ [
+ bounds[:, 0].min(), # minx
+ bounds[:, 1].min(), # miny
+ bounds[:, 2].max(), # maxx
+ bounds[:, 3].max(), # maxy
+ ]
+ )
+ np.testing.assert_allclose(result, expected)
+
+ # additional check for empty array or one empty / missing
+ for geoms in [[], [None], [shapely.geometry.Polygon()]]:
+ E = from_shapely(geoms)
+ result = E.total_bounds
+ assert result.ndim == 1
+ assert result.dtype == "float64"
+ np.testing.assert_allclose(result, np.array([np.nan] * 4))
+
def test_getitem():
points = [shapely.geometry.Point(i, i) for i in range(10)]
=====================================
geopandas/tests/test_geodataframe.py
=====================================
@@ -287,6 +287,12 @@ class TestDataFrame:
assert i == r["geometry"].x
assert i == r["geometry"].y
+ def test_set_geometry_empty(self):
+ df = pd.DataFrame(columns=["a", "geometry"], index=pd.DatetimeIndex([]))
+ result = df.set_geometry("geometry")
+ assert isinstance(result, GeoDataFrame)
+ assert isinstance(result.index, pd.DatetimeIndex)
+
def test_align(self):
df = self.df2
=====================================
geopandas/tests/test_geom_methods.py
=====================================
@@ -299,6 +299,16 @@ class TestGeomMethods:
result = gdf.bounds
assert_frame_equal(expected, result)
+ def test_bounds_empty(self):
+ # test bounds of empty GeoSeries
+ # https://github.com/geopandas/geopandas/issues/1195
+ s = GeoSeries([])
+ result = s.bounds
+ expected = DataFrame(
+ columns=["minx", "miny", "maxx", "maxy"], index=s.index, dtype="float64"
+ )
+ assert_frame_equal(result, expected)
+
def test_unary_union(self):
p1 = self.t1
p2 = Polygon([(2, 0), (3, 0), (3, 1)])
=====================================
geopandas/tests/test_pandas_methods.py
=====================================
@@ -12,10 +12,10 @@ from shapely.geometry import Point, GeometryCollection
import geopandas
from geopandas import GeoDataFrame, GeoSeries
-from geopandas._compat import PANDAS_GE_024
+from geopandas._compat import PANDAS_GE_024, PANDAS_GE_025
from geopandas.array import from_shapely
-from geopandas.tests.util import assert_geoseries_equal
+from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
from pandas.util.testing import assert_frame_equal, assert_series_equal
import pytest
@@ -39,6 +39,7 @@ def df():
def test_repr(s, df):
assert "POINT" in repr(s)
assert "POINT" in repr(df)
+ assert "POINT" in df._repr_html_()
@pytest.mark.skipif(
@@ -64,6 +65,29 @@ def test_repr_boxed_display_precision():
assert "POINT (10.123456789 50.123456789)" in repr(s1)
+def test_repr_all_missing():
+ # https://github.com/geopandas/geopandas/issues/1195
+ s = GeoSeries([None, None, None])
+ assert "None" in repr(s)
+ df = GeoDataFrame({"a": [1, 2, 3], "geometry": s})
+ assert "None" in repr(df)
+ assert "geometry" in df._repr_html_()
+
+
+def test_repr_empty():
+ # https://github.com/geopandas/geopandas/issues/1195
+ s = GeoSeries([])
+ if PANDAS_GE_025:
+ # repr with correct name fixed in pandas 0.25
+ assert repr(s) == "GeoSeries([], dtype: geometry)"
+ else:
+ assert repr(s) == "Series([], dtype: geometry)"
+ df = GeoDataFrame({"a": [], "geometry": s})
+ assert "Empty GeoDataFrame" in repr(df)
+ # https://github.com/geopandas/geopandas/issues/1184
+ assert "geometry" in df._repr_html_()
+
+
def test_indexing(s, df):
# accessing scalar from the geometry (colunm)
@@ -121,6 +145,54 @@ def test_reindex(s, df):
# should it return DataFrame instead ?
+def test_take(s, df):
+ inds = np.array([0, 2])
+
+ # GeoSeries take
+ result = s.take(inds)
+ expected = s.iloc[[0, 2]]
+ assert isinstance(result, GeoSeries)
+ assert_geoseries_equal(result, expected)
+
+ # GeoDataFrame take axis 0
+ result = df.take(inds, axis=0)
+ expected = df.iloc[[0, 2], :]
+ assert isinstance(result, GeoDataFrame)
+ assert_geodataframe_equal(result, expected)
+
+ # GeoDataFrame take axis 1
+ df = df.reindex(columns=["value1", "value2", "geometry"]) # ensure consistent order
+ result = df.take(inds, axis=1)
+ expected = df[["value1", "geometry"]]
+ assert isinstance(result, GeoDataFrame)
+ assert_geodataframe_equal(result, expected)
+
+ result = df.take(np.array([0, 1]), axis=1)
+ expected = df[["value1", "value2"]]
+ assert isinstance(result, pd.DataFrame)
+ assert_frame_equal(result, expected)
+
+
+def test_take_empty(s, df):
+ # ensure that index type is preserved in an empty take
+ # https://github.com/geopandas/geopandas/issues/1190
+ inds = np.array([], dtype="int64")
+
+ # use non-default index
+ df.index = pd.date_range("2012-01-01", periods=len(df))
+
+ result = df.take(inds, axis=0)
+ assert isinstance(result, GeoDataFrame)
+ assert result.shape == (0, 3)
+ assert isinstance(result.index, pd.DatetimeIndex)
+
+ # the original bug report was an empty boolean mask
+ for result in [df.loc[df["value1"] > 100], df[df["value1"] > 100]]:
+ assert isinstance(result, GeoDataFrame)
+ assert result.shape == (0, 3)
+ assert isinstance(result.index, pd.DatetimeIndex)
+
+
def test_assignment(s, df):
exp = GeoSeries([Point(10, 10), Point(1, 1), Point(2, 2)])
=====================================
geopandas/tests/test_plotting.py
=====================================
@@ -119,6 +119,18 @@ class TestPointPlotting:
ax = self.df.plot(color="green")
_check_colors(self.N, ax.collections[0].get_facecolors(), ["green"] * self.N)
+ # check rgba tuple GH1178
+ ax = self.df.plot(color=(0.5, 0.5, 0.5))
+ _check_colors(
+ self.N, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5)] * self.N
+ )
+ ax = self.df.plot(color=(0.5, 0.5, 0.5, 0.5))
+ _check_colors(
+ self.N, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5, 0.5)] * self.N
+ )
+ with pytest.raises(TypeError):
+ self.df.plot(color="not color")
+
with warnings.catch_warnings(record=True) as _: # don't print warning
# 'color' overrides 'column'
ax = self.df.plot(column="values", color="green")
@@ -268,6 +280,18 @@ class TestLineStringPlotting:
ax = self.df.plot(color="green")
_check_colors(self.N, ax.collections[0].get_colors(), ["green"] * self.N)
+ # check rgba tuple GH1178
+ ax = self.df.plot(color=(0.5, 0.5, 0.5, 0.5))
+ _check_colors(
+ self.N, ax.collections[0].get_colors(), [(0.5, 0.5, 0.5, 0.5)] * self.N
+ )
+ ax = self.df.plot(color=(0.5, 0.5, 0.5, 0.5))
+ _check_colors(
+ self.N, ax.collections[0].get_colors(), [(0.5, 0.5, 0.5, 0.5)] * self.N
+ )
+ with pytest.raises(TypeError):
+ self.df.plot(color="not color")
+
with warnings.catch_warnings(record=True) as _: # don't print warning
# 'color' overrides 'column'
ax = self.df.plot(column="values", color="green")
@@ -354,6 +378,14 @@ class TestPolygonPlotting:
_check_colors(2, ax.collections[0].get_facecolors(), ["green"] * 2)
_check_colors(2, ax.collections[0].get_edgecolors(), ["k"] * 2)
+ # check rgba tuple GH1178
+ ax = self.df.plot(color=(0.5, 0.5, 0.5))
+ _check_colors(2, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5)] * 2)
+ ax = self.df.plot(color=(0.5, 0.5, 0.5, 0.5))
+ _check_colors(2, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5, 0.5)] * 2)
+ with pytest.raises(TypeError):
+ self.df.plot(color="not color")
+
with warnings.catch_warnings(record=True) as _: # don't print warning
# 'color' overrides 'values'
ax = self.df.plot(column="values", color="green")
@@ -404,6 +436,17 @@ class TestPolygonPlotting:
_check_colors(2, ax.collections[0].get_facecolors(), ["g"] * 2, alpha=0.4)
_check_colors(2, ax.collections[0].get_edgecolors(), ["r"] * 2, alpha=0.4)
+ # check rgba tuple GH1178 for face and edge
+ ax = self.df.plot(facecolor=(0.5, 0.5, 0.5), edgecolor=(0.4, 0.5, 0.6))
+ _check_colors(2, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5)] * 2)
+ _check_colors(2, ax.collections[0].get_edgecolors(), [(0.4, 0.5, 0.6)] * 2)
+
+ ax = self.df.plot(
+ facecolor=(0.5, 0.5, 0.5, 0.5), edgecolor=(0.4, 0.5, 0.6, 0.5)
+ )
+ _check_colors(2, ax.collections[0].get_facecolors(), [(0.5, 0.5, 0.5, 0.5)] * 2)
+ _check_colors(2, ax.collections[0].get_edgecolors(), [(0.4, 0.5, 0.6, 0.5)] * 2)
+
def test_legend_kwargs(self):
ax = self.df.plot(
@@ -667,6 +710,27 @@ class TestPlotCollections:
_check_colors(self.N, coll.get_edgecolors(), ["r", "g", "b"])
ax.cla()
+ coll = plot_point_collection(
+ ax,
+ self.points,
+ color=[(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ _check_colors(
+ self.N,
+ coll.get_facecolors(),
+ [(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ _check_colors(
+ self.N,
+ coll.get_edgecolors(),
+ [(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ ax.cla()
+
+ # not a color
+ with pytest.raises(TypeError):
+ plot_point_collection(ax, self.points, color="not color")
+
def test_points_values(self):
from geopandas.plotting import plot_point_collection
@@ -710,6 +774,18 @@ class TestPlotCollections:
_check_colors(self.N, coll.get_colors(), ["r", "g", "b"])
ax.cla()
+ coll = plot_linestring_collection(
+ ax,
+ self.lines,
+ color=[(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ _check_colors(
+ self.N,
+ coll.get_colors(),
+ [(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ ax.cla()
+
# pass through of kwargs
coll = plot_linestring_collection(ax, self.lines, linestyle="--", linewidth=1)
exp_ls = _style_to_linestring_onoffseq("dashed", 1)
@@ -718,6 +794,10 @@ class TestPlotCollections:
assert res_ls[1] == exp_ls[1]
ax.cla()
+ # not a color
+ with pytest.raises(TypeError):
+ plot_linestring_collection(ax, self.lines, color="not color")
+
def test_linestrings_values(self):
from geopandas.plotting import plot_linestring_collection
@@ -774,6 +854,23 @@ class TestPlotCollections:
_check_colors(self.N, coll.get_edgecolor(), ["g", "b", "r"])
ax.cla()
+ coll = plot_polygon_collection(
+ ax,
+ self.polygons,
+ color=[(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ _check_colors(
+ self.N,
+ coll.get_facecolor(),
+ [(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ _check_colors(
+ self.N,
+ coll.get_edgecolor(),
+ [(0.5, 0.5, 0.5, 0.5), (0.1, 0.2, 0.3, 0.5), (0.4, 0.5, 0.6, 0.5)],
+ )
+ ax.cla()
+
# only setting facecolor keeps default for edgecolor
coll = plot_polygon_collection(ax, self.polygons, facecolor="g")
_check_colors(self.N, coll.get_facecolor(), ["g"] * self.N)
@@ -786,6 +883,10 @@ class TestPlotCollections:
_check_colors(self.N, coll.get_edgecolor(), ["r"] * self.N)
ax.cla()
+ # not a color
+ with pytest.raises(TypeError):
+ plot_polygon_collection(ax, self.polygons, color="not color")
+
def test_polygons_values(self):
from geopandas.plotting import plot_polygon_collection
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-geopandas/commit/0246bb2c367e653ec6ca42c8249a6ea2b4252bfc
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-geopandas/commit/0246bb2c367e653ec6ca42c8249a6ea2b4252bfc
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20191119/19e8ebde/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list