From 4ed906ece60f2cd907fbe4b53a177cb00252a545 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Sun, 27 Aug 2017 19:19:08 +0200 Subject: [PATCH] VIS: updated plotting defaults (#510) * remove hardcoded defaults * use uniform color by default * for categorical data use default cmap tab10 instead of Set1 * forgot to remove hardcoded markersize * remove no longer used gencolor function * update MultiPolygons test * fix some tests + mixed geom case with cmap * enable more tests * fix for matplotlib 1.4.3 * skip one for 1.4.3 * fix some collection tests * small updates to docstring * undo take squaring of the markersize --- geopandas/plotting.py | 188 +++++++++++++++---------------- geopandas/tests/test_plotting.py | 188 +++++++++++++++---------------- 2 files changed, 180 insertions(+), 196 deletions(-) diff --git a/geopandas/plotting.py b/geopandas/plotting.py index 7f8e01a..a0ead51 100644 --- a/geopandas/plotting.py +++ b/geopandas/plotting.py @@ -1,9 +1,9 @@ from __future__ import print_function +from distutils.version import LooseVersion import warnings import numpy as np -from six import next def _flatten_multi_geoms(geoms, colors=None): @@ -43,9 +43,8 @@ def _flatten_multi_geoms(geoms, colors=None): return components, component_colors -def plot_polygon_collection(ax, geoms, values=None, linewidth=1.0, - edgecolor='black', alpha=0.5, - vmin=None, vmax=None, cmap=None, **kwargs): +def plot_polygon_collection(ax, geoms, values=None, color=None, + cmap=None, vmin=None, vmax=None, **kwargs): """ Plots a collection of Polygon and MultiPolygon geometries to `ax` @@ -90,9 +89,12 @@ def plot_polygon_collection(ax, geoms, values=None, linewidth=1.0, if 'markersize' in kwargs: del kwargs['markersize'] + # color=None overwrites specified facecolor/edgecolor with default color + if color is not None: + kwargs['color'] = color + collection = PatchCollection([PolygonPatch(poly) for poly in geoms], - linewidth=linewidth, edgecolor=edgecolor, - alpha=alpha, **kwargs) + **kwargs) if values is not None: collection.set_array(np.asarray(values)) @@ -105,8 +107,7 @@ def plot_polygon_collection(ax, geoms, values=None, linewidth=1.0, def plot_linestring_collection(ax, geoms, values=None, color=None, - vmin=None, vmax=None, cmap=None, - linewidth=1.0, **kwargs): + cmap=None, vmin=None, vmax=None, **kwargs): """ Plots a collection of LineString and MultiLineString geometries to `ax` @@ -147,7 +148,7 @@ def plot_linestring_collection(ax, geoms, values=None, color=None, kwargs['color'] = color segments = [np.array(linestring)[:, :2] for linestring in geoms] - collection = LineCollection(segments, linewidth=linewidth, **kwargs) + collection = LineCollection(segments, **kwargs) if values is not None: collection.set_array(np.asarray(values)) @@ -160,8 +161,8 @@ def plot_linestring_collection(ax, geoms, values=None, color=None, def plot_point_collection(ax, geoms, values=None, color=None, - vmin=None, vmax=None, cmap=None, - marker='o', markersize=2, **kwargs): + cmap=None, vmin=None, vmax=None, + marker='o', markersize=None, **kwargs): """ Plots a collection of Point geometries to `ax` @@ -177,6 +178,11 @@ def plot_point_collection(ax, geoms, values=None, color=None, Values mapped to colors using vmin, vmax, and cmap. Cannot be specified together with `color`. + markersize : scalar or array-like, optional + Size of the markers. Note that under the hood ``scatter`` is + used, so the specified value will be proportional to the + area of the marker (size in points^2). + Returns ------- collection : matplotlib.collections.Collection that was plotted @@ -187,34 +193,18 @@ def plot_point_collection(ax, geoms, values=None, color=None, x = geoms.x.values y = geoms.y.values - collection = ax.scatter(x, y, s=markersize, c=values, color=color, - vmin=vmin, vmax=vmax, cmap=cmap, + # matplotlib 1.4 does not support c=None, and < 2.0 does not support s=None + if values is not None: + kwargs['c'] = values + if markersize is not None: + kwargs['s'] = markersize + + collection = ax.scatter(x, y, color=color, vmin=vmin, vmax=vmax, cmap=cmap, marker=marker, **kwargs) return collection -def _gencolor(N, colormap='Set1'): - """ - Color generator intended to work with one of the ColorBrewer - qualitative color scales. - - Suggested values of colormap are the following: - - Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3 - - (although any matplotlib colormap will work). - """ - from matplotlib import cm - # don't use more than 9 discrete colors - n_colors = min(N, 9) - cmap = cm.get_cmap(colormap, n_colors) - colors = cmap(range(n_colors)) - for i in range(N): - yield colors[i % n_colors] - - -def plot_series(s, cmap='Set1', color=None, ax=None, linewidth=1.0, - figsize=None, **color_kwds): +def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds): """ Plot a GeoSeries. @@ -228,13 +218,13 @@ def plot_series(s, cmap='Set1', color=None, ax=None, linewidth=1.0, MultiPolygon, LineString, MultiLineString and Point geometries can be plotted. - cmap : str (default 'Set1') + cmap : str (default None) The name of a colormap recognized by matplotlib. Any colormap will work, but categorical colormaps are generally recommended. Examples of useful discrete colormaps include: - Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3 + tab10, tab20, Accent, Dark2, Paired, Pastel1, Set1, Set2 color : str (default None) If specified, all objects will be colored uniformly. @@ -242,42 +232,42 @@ def plot_series(s, cmap='Set1', color=None, ax=None, linewidth=1.0, ax : matplotlib.pyplot.Artist (default None) axes on which to draw the plot - linewidth : float (default 1.0) - Line width for geometries. - figsize : pair of floats (default None) Size of the resulting matplotlib.figure.Figure. If the argument ax is given explicitly, figsize is ignored. - **color_kwds : dict - Color options to be passed on to the actual plot function + **style_kwds : dict + Color options to be passed on to the actual plot function, such + as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``, + ``alpha``. Returns ------- matplotlib axes instance """ - if 'colormap' in color_kwds: + if 'colormap' in style_kwds: warnings.warn("'colormap' is deprecated, please use 'cmap' instead " "(for consistency with matplotlib)", FutureWarning) - cmap = color_kwds.pop('colormap') - if 'axes' in color_kwds: + cmap = style_kwds.pop('colormap') + if 'axes' in style_kwds: warnings.warn("'axes' is deprecated, please use 'ax' instead " "(for consistency with pandas)", FutureWarning) - ax = color_kwds.pop('axes') + ax = style_kwds.pop('axes') import matplotlib.pyplot as plt if ax is None: fig, ax = plt.subplots(figsize=figsize) ax.set_aspect('equal') - # if no color specified, create range of colors based on cmap - num_geoms = len(s.index) - col_seq = False - if color is None: - color_generator = _gencolor(len(s), colormap=cmap) - color = np.array([next(color_generator) for _ in range(num_geoms)]) - col_seq = True + # if cmap is specified, create range of colors based on cmap + values = None + if cmap is not None: + values = np.arange(len(s)) + if hasattr(cmap, 'N'): + values = values % cmap.N + style_kwds['vmin'] = style_kwds.get('vmin', values.min()) + style_kwds['vmax'] = style_kwds.get('vmax', values.max()) geom_types = s.geometry.type poly_idx = np.asarray((geom_types == 'Polygon') @@ -292,43 +282,40 @@ def plot_series(s, cmap='Set1', color=None, ax=None, linewidth=1.0, if not polys.empty: # color overrides both face and edgecolor. As we want people to be # able to use edgecolor as well, pass color to facecolor - facecolor = color_kwds.pop('facecolor', None) - if col_seq: - if not facecolor: - facecolor = color[poly_idx] if col_seq else color - else: + facecolor = style_kwds.pop('facecolor', None) + if color is not None: facecolor = color - plot_polygon_collection(ax, polys, facecolor=facecolor, - linewidth=linewidth, **color_kwds) + values_ = values[poly_idx] if cmap else None + plot_polygon_collection(ax, polys, values_, facecolor=facecolor, + cmap=cmap, **style_kwds) # plot all LineStrings and MultiLineString components in same collection lines = s.geometry[line_idx] if not lines.empty: - color_ = color[line_idx] if col_seq else color - plot_linestring_collection(ax, lines, color=color_, - linewidth=linewidth, **color_kwds) + values_ = values[line_idx] if cmap else None + plot_linestring_collection(ax, lines, values_, color=color, cmap=cmap, + **style_kwds) # plot all Points in the same collection points = s.geometry[point_idx] if not points.empty: - color_ = color[point_idx] if col_seq else color - plot_point_collection(ax, points, color=color_, **color_kwds) + values_ = values[point_idx] if cmap else None + plot_point_collection(ax, points, values_, color=color, cmap=cmap, + **style_kwds) plt.draw() return ax -def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, - categorical=False, legend=False, ax=None, - scheme=None, k=5, vmin=None, vmax=None, figsize=None, - **color_kwds): +def plot_dataframe(df, column=None, cmap=None, color=None, ax=None, + categorical=False, legend=False, scheme=None, k=5, + vmin=None, vmax=None, figsize=None, **style_kwds): """ Plot a GeoDataFrame. Generate a plot of a GeoDataFrame with matplotlib. If a column is specified, the plot coloring will be based on values - in that column. Otherwise, a categorical plot of the - geometries in the `geometry` column will be generated. + in that column. Parameters ---------- @@ -341,39 +328,37 @@ def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, column : str (default None) The name of the column to be plotted. Ignored if `color` is also set. + cmap : str (default None) + The name of a colormap recognized by matplotlib. + categorical : bool (default False) If False, cmap will reflect numerical values of the - column being plotted. For non-numerical columns (or if - column=None), this will be set to True. - - cmap : str (default 'Set1') - The name of a colormap recognized by matplotlib. + column being plotted. For non-numerical columns, this + will be set to True. color : str (default None) If specified, all objects will be colored uniformly. - linewidth : float (default 1.0) - Line width for geometries. - legend : bool (default False) Plot a legend. Ignored if no `column` is given, or if `color` is given. ax : matplotlib.pyplot.Artist (default None) axes on which to draw the plot - scheme : pysal.esda.mapclassify.Map_Classifier - Choropleth classification schemes (requires PySAL) + scheme : str (default None) + Name of a choropleth classification scheme (requires PySAL). + A pysal.esda.mapclassify.Map_Classifier object will be used + under the hood. Supported schemes: 'Equal_interval', 'Quantiles', + 'Fisher_Jenks' k : int (default 5) Number of classes (ignored if scheme is None) vmin : None or float (default None) - Minimum value of cmap. If None, the minimum data value in the column to be plotted is used. vmax : None or float (default None) - Maximum value of cmap. If None, the maximum data value in the column to be plotted is used. @@ -381,33 +366,35 @@ def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, Size of the resulting matplotlib.figure.Figure. If the argument axes is given explicitly, figsize is ignored. - **color_kwds : dict - Color options to be passed on to the actual plot function + **style_kwds : dict + Color options to be passed on to the actual plot function, such + as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``, + ``alpha``. Returns ------- - matplotlib axes instance + """ - if 'colormap' in color_kwds: + if 'colormap' in style_kwds: warnings.warn("'colormap' is deprecated, please use 'cmap' instead " "(for consistency with matplotlib)", FutureWarning) - cmap = color_kwds.pop('colormap') - if 'axes' in color_kwds: + cmap = style_kwds.pop('colormap') + if 'axes' in style_kwds: warnings.warn("'axes' is deprecated, please use 'ax' instead " "(for consistency with pandas)", FutureWarning) - ax = color_kwds.pop('axes') + ax = style_kwds.pop('axes') if column and color: warnings.warn("Only specify one of 'column' or 'color'. Using " "'color'.", UserWarning) column = None + import matplotlib import matplotlib.pyplot as plt if column is None: - return plot_series(df.geometry, cmap=cmap, color=color, - ax=ax, linewidth=linewidth, figsize=figsize, - **color_kwds) + return plot_series(df.geometry, cmap=cmap, color=color, ax=ax, + figsize=figsize, **style_kwds) if df[column].dtype is np.dtype('O'): categorical = True @@ -415,7 +402,10 @@ def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, # Define `values` as a Series if categorical: if cmap is None: - cmap = 'Set1' + if LooseVersion(matplotlib.__version__) >= '2.0': + cmap = 'tab10' + else: + cmap = 'Set1' categories = list(set(df[column].values)) categories.sort() valuemap = dict([(k, v) for (v, k) in enumerate(categories)]) @@ -449,21 +439,19 @@ def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, polys = df.geometry[poly_idx] if not polys.empty: plot_polygon_collection(ax, polys, values[poly_idx], - vmin=mn, vmax=mx, cmap=cmap, - linewidth=linewidth, **color_kwds) + vmin=mn, vmax=mx, cmap=cmap, **style_kwds) # plot all LineStrings and MultiLineString components in same collection lines = df.geometry[line_idx] if not lines.empty: plot_linestring_collection(ax, lines, values[line_idx], - vmin=mn, vmax=mx, cmap=cmap, - linewidth=linewidth, **color_kwds) + vmin=mn, vmax=mx, cmap=cmap, **style_kwds) # plot all Points in the same collection points = df.geometry[point_idx] if not points.empty: plot_point_collection(ax, points, values[point_idx], - vmin=mn, vmax=mx, cmap=cmap, **color_kwds) + vmin=mn, vmax=mx, cmap=cmap, **style_kwds) if legend and not color: from matplotlib.lines import Line2D @@ -477,7 +465,7 @@ def plot_dataframe(df, column=None, cmap=None, color=None, linewidth=1.0, for value, cat in enumerate(categories): patches.append( Line2D([0], [0], linestyle="none", marker="o", - alpha=color_kwds.get('alpha', 0.5), markersize=10, + alpha=style_kwds.get('alpha', 1), markersize=10, markerfacecolor=n_cmap.to_rgba(value))) ax.legend(patches, categories, numpoints=1, loc='best') else: diff --git a/geopandas/tests/test_plotting.py b/geopandas/tests/test_plotting.py index 59a8593..1067a70 100644 --- a/geopandas/tests/test_plotting.py +++ b/geopandas/tests/test_plotting.py @@ -32,10 +32,6 @@ except KeyError: class TestPointPlotting: def setup_method(self): - # scatterplot does not yet accept list of colors in matplotlib 1.4.3 - # if we change the default to uniform, this might work again - pytest.importorskip('matplotlib', '1.5.0') - self.N = 10 self.points = GeoSeries(Point(i, i) for i in range(self.N)) values = np.arange(self.N) @@ -51,45 +47,50 @@ class TestPointPlotting: def test_default_colors(self): - # # without specifying values -> max 9 different colors + # # without specifying values -> uniform color # GeoSeries ax = self.points.plot() - cmap = plt.get_cmap('Set1', 9) - expected_colors = cmap(list(range(9))*2) - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + _check_colors(self.N, ax.collections[0].get_facecolors(), + [MPL_DFT_COLOR] * self.N) - # GeoDataFrame -> uses 'jet' instead of 'Set1' + # GeoDataFrame ax = self.df.plot() - cmap = plt.get_cmap(lut=9) - expected_colors = cmap(list(range(9))*2) - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + _check_colors(self.N, ax.collections[0].get_facecolors(), + [MPL_DFT_COLOR] * self.N) # # with specifying values -> different colors for all 10 values ax = self.df.plot(column='values') cmap = plt.get_cmap() expected_colors = cmap(np.arange(self.N)/(self.N-1)) - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + _check_colors(self.N, ax.collections[0].get_facecolors(), + expected_colors) def test_colormap(self): - # # without specifying values -> max 9 different colors + # without specifying values but cmap specified -> no uniform color + # but different colors for all points # GeoSeries ax = self.points.plot(cmap='RdYlGn') - cmap = plt.get_cmap('RdYlGn', 9) - expected_colors = cmap(list(range(9))*2) - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + cmap = plt.get_cmap('RdYlGn') + exp_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, ax.collections[0].get_facecolors(), exp_colors) - # GeoDataFrame -> same as GeoSeries in this case ax = self.df.plot(cmap='RdYlGn') - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + _check_colors(self.N, ax.collections[0].get_facecolors(), exp_colors) # # with specifying values -> different colors for all 10 values ax = self.df.plot(column='values', cmap='RdYlGn') cmap = plt.get_cmap('RdYlGn') - expected_colors = cmap(np.arange(self.N)/(self.N-1)) - _check_colors(self.N, ax.collections[0].get_facecolors(), expected_colors) + _check_colors(self.N, ax.collections[0].get_facecolors(), exp_colors) + + # when using a cmap with specified lut -> limited number of different + # colors + ax = self.points.plot(cmap=plt.get_cmap('Set1', lut=5)) + cmap = plt.get_cmap('Set1', lut=5) + exp_colors = cmap(list(range(5))*3) + _check_colors(self.N, ax.collections[0].get_facecolors(), exp_colors) def test_single_color(self): @@ -106,7 +107,6 @@ class TestPointPlotting: def test_style_kwargs(self): - # markersize ax = self.points.plot(markersize=10) assert ax.collections[0].get_sizes() == [10] @@ -150,10 +150,6 @@ class TestPointPlotting: class TestPointZPlotting: def setup_method(self): - # scatterplot does not yet accept list of colors in matplotlib 1.4.3 - # if we change the default to uniform, this might work again - pytest.importorskip('matplotlib', '1.5.0') - self.N = 10 self.points = GeoSeries(Point(i, i, i) for i in range(self.N)) values = np.arange(self.N) @@ -189,20 +185,20 @@ class TestLineStringPlotting: def test_style_kwargs(self): - # linestyle + # linestyle (style patterns depend on linewidth, therefore pin to 1) linestyle = 'dashed' - ax = self.lines.plot(linestyle=linestyle) + ax = self.lines.plot(linestyle=linestyle, linewidth=1) exp_ls = _style_to_linestring_onoffseq(linestyle) for ls in ax.collections[0].get_linestyles(): assert ls[0] == exp_ls[0] assert tuple(ls[1]) == exp_ls[1] - ax = self.df.plot(linestyle=linestyle) + ax = self.df.plot(linestyle=linestyle, linewidth=1) for ls in ax.collections[0].get_linestyles(): assert ls[0] == exp_ls[0] assert tuple(ls[1]) == exp_ls[1] - ax = self.df.plot(column='values', linestyle=linestyle) + ax = self.df.plot(column='values', linestyle=linestyle, linewidth=1) for ls in ax.collections[0].get_linestyles(): assert ls[0] == exp_ls[0] assert tuple(ls[1]) == exp_ls[1] @@ -225,21 +221,20 @@ class TestPolygonPlotting: def test_single_color(self): ax = self.polys.plot(color='green') - _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2, alpha=0.5) + _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2) # color only sets facecolor - _check_colors(2, ax.collections[0].get_edgecolors(), ['k'] * 2, alpha=0.5) + _check_colors(2, ax.collections[0].get_edgecolors(), ['k'] * 2) ax = self.df.plot(color='green') - _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2, alpha=0.5) - _check_colors(2, ax.collections[0].get_edgecolors(), ['k'] * 2, alpha=0.5) + _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2) + _check_colors(2, ax.collections[0].get_edgecolors(), ['k'] * 2) with warnings.catch_warnings(record=True) as _: # don't print warning # 'color' overrides 'values' ax = self.df.plot(column='values', color='green') - _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2, alpha=0.5) + _check_colors(2, ax.collections[0].get_facecolors(), ['green']*2) def test_vmin_vmax(self): - # when vmin == vmax, all polygons should be the same color # non-categorical @@ -256,7 +251,7 @@ class TestPolygonPlotting: # facecolor overrides default cmap when color is not set ax = self.polys.plot(facecolor='k') - _check_colors(2, ax.collections[0].get_facecolors(), ['k']*2, alpha=0.5) + _check_colors(2, ax.collections[0].get_facecolors(), ['k']*2) # facecolor overrides more general-purpose color when both are set ax = self.polys.plot(color='red', facecolor='k') @@ -265,30 +260,30 @@ class TestPolygonPlotting: # edgecolor ax = self.polys.plot(edgecolor='red') - np.testing.assert_array_equal([(1, 0, 0, 0.5)], + np.testing.assert_array_equal([(1, 0, 0, 1)], ax.collections[0].get_edgecolors()) ax = self.df.plot('values', edgecolor='red') - np.testing.assert_array_equal([(1, 0, 0, 0.5)], + np.testing.assert_array_equal([(1, 0, 0, 1)], ax.collections[0].get_edgecolors()) + # alpha sets both edge and face + ax = self.polys.plot(facecolor='g', edgecolor='r', alpha=0.4) + _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) + def test_multipolygons(self): # MultiPolygons ax = self.df2.plot() assert len(ax.collections[0].get_paths()) == 4 + _check_colors(4, ax.collections[0].get_facecolors(), [MPL_DFT_COLOR]*4) + + ax = self.df2.plot('values') cmap = plt.get_cmap(lut=2) # colors are repeated for all components within a MultiPolygon expected_colors = [cmap(0), cmap(0), cmap(1), cmap(1)] - # TODO multipolygons don't work yet when values are not specified - # values are flattended, but color not yet (can fix, but we are - # thinking to use uniform coloring by default, which would also fix - # this) - # _check_colors(4, ax.collections[0], expected_colors, alpha=0.5) - - ax = self.df2.plot('values') - # specifying values -> same as without values in this case. - _check_colors(4, ax.collections[0].get_facecolors(), expected_colors, alpha=0.5) + _check_colors(4, ax.collections[0].get_facecolors(), expected_colors) class TestPolygonZPlotting: @@ -321,18 +316,22 @@ class TestNonuniformGeometryPlotting: self.series = GeoSeries([poly, line, point]) self.df = GeoDataFrame({'geometry': self.series, 'values': [1, 2, 3]}) - def test_colormap(self): + def test_colors(self): + # default uniform color + ax = self.series.plot() + _check_colors(1, ax.collections[0].get_facecolors(), [MPL_DFT_COLOR]) + _check_colors(1, ax.collections[1].get_edgecolors(), [MPL_DFT_COLOR]) + _check_colors(1, ax.collections[2].get_facecolors(), [MPL_DFT_COLOR]) + # colormap: different colors ax = self.series.plot(cmap='RdYlGn') - cmap = plt.get_cmap('RdYlGn', 3) - # polygon gets extra alpha. See #266 - _check_colors(1, ax.collections[0].get_facecolors(), [cmap(0)], alpha=0.5) - _check_colors(1, ax.collections[1].get_facecolors(), [cmap(1)], alpha=1) # line - _check_colors(1, ax.collections[2].get_facecolors(), [cmap(2)], alpha=1) # point + cmap = plt.get_cmap('RdYlGn') + exp_colors = cmap(np.arange(3) / (3 - 1)) + _check_colors(1, ax.collections[0].get_facecolors(), [exp_colors[0]]) + _check_colors(1, ax.collections[1].get_edgecolors(), [exp_colors[1]]) + _check_colors(1, ax.collections[2].get_facecolors(), [exp_colors[2]]) def test_style_kwargs(self): - - # markersize -> only the Point gets it ax = self.series.plot(markersize=10) assert ax.collections[2].get_sizes() == [10] ax = self.df.plot(markersize=10) @@ -378,8 +377,7 @@ class TestPlotCollections: for i in range(self.N)]) def test_points(self): - # scatterplot does not yet accept list of colors in matplotlib 1.4.3 - # if we change the default to uniform, this might work again + # 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 @@ -422,12 +420,11 @@ class TestPlotCollections: # default colormap fig, ax = plt.subplots() coll = plot_point_collection(ax, self.points, self.values) + fig.canvas.draw_idle() cmap = plt.get_cmap() - expected_colors = cmap(np.arange(self.N)) - - # not sure why this is failing (gives only a single color, when - # testing outside of pytest, this works perfectly - # _check_colors(self.N, coll.get_facecolors(), expected_colors) + expected_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_facecolors(), expected_colors) + # edgecolor depends on matplotlib version # _check_colors(self.N, coll.get_edgecolors(), expected_colors) def test_linestrings(self): @@ -463,7 +460,8 @@ class TestPlotCollections: ax.cla() # pass through of kwargs - coll = plot_linestring_collection(ax, self.lines, linestyle='--') + coll = plot_linestring_collection(ax, self.lines, linestyle='--', + linewidth=1) exp_ls = _style_to_linestring_onoffseq('dashed') res_ls = coll.get_linestyle()[0] assert res_ls[0] == exp_ls[0] @@ -477,28 +475,28 @@ class TestPlotCollections: # default colormap coll = plot_linestring_collection(ax, self.lines, self.values) + fig.canvas.draw_idle() cmap = plt.get_cmap() - expected_colors = cmap(np.arange(self.N)) - # failing, see above with points - # _check_colors(self.N, coll.get_color(), expected_colors) + expected_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_color(), expected_colors) ax.cla() # specify colormap coll = plot_linestring_collection(ax, self.lines, self.values, cmap='RdBu') + fig.canvas.draw_idle() cmap = plt.get_cmap('RdBu') - expected_colors = cmap(np.arange(self.N)) - # failing, see above with points - # _check_colors(self.N, coll.get_color(), expected_colors) + expected_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_color(), expected_colors) ax.cla() # specify vmin/vmax coll = plot_linestring_collection(ax, self.lines, self.values, vmin=3, vmax=5) + fig.canvas.draw_idle() cmap = plt.get_cmap() expected_colors = cmap([0]) - # failing, see above with points - # _check_colors(self.N, coll.get_color(), expected_colors) + _check_colors(self.N, coll.get_color(), expected_colors) ax.cla() def test_polygons(self): @@ -511,31 +509,28 @@ class TestPlotCollections: ax.cla() # default: single default matplotlib color - # but with default alpha of 0.5 and black edgecolor coll = plot_polygon_collection(ax, self.polygons) - _check_colors(self.N, coll.get_facecolor(), [MPL_DFT_COLOR] * self.N, - alpha=0.5) - _check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N, alpha=0.5) + _check_colors(self.N, coll.get_facecolor(), [MPL_DFT_COLOR] * self.N) + _check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N) ax.cla() # default: color sets both facecolor and edgecolor - # TODO but test fails for edge (still black) coll = plot_polygon_collection(ax, self.polygons, color='g') - _check_colors(self.N, coll.get_facecolor(), ['g'] * self.N, alpha=0.5) - # _check_colors(self.N, coll.get_edgecolor(), ['g'] * self.N, alpha=0.5) + _check_colors(self.N, coll.get_facecolor(), ['g'] * self.N) + _check_colors(self.N, coll.get_edgecolor(), ['g'] * self.N) 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, alpha=0.5) - _check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N, alpha=0.5) + _check_colors(self.N, coll.get_facecolor(), ['g'] * self.N) + _check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N) ax.cla() # custom facecolor and edgecolor coll = plot_polygon_collection(ax, self.polygons, facecolor='g', edgecolor='r') - _check_colors(self.N, coll.get_facecolor(), ['g'] * self.N, alpha=0.5) - _check_colors(self.N, coll.get_edgecolor(), ['r'] * self.N, alpha=0.5) + _check_colors(self.N, coll.get_facecolor(), ['g'] * self.N) + _check_colors(self.N, coll.get_edgecolor(), ['r'] * self.N) ax.cla() def test_polygons_values(self): @@ -545,39 +540,40 @@ class TestPlotCollections: # default colormap, edge is still black by default coll = plot_polygon_collection(ax, self.polygons, self.values) + fig.canvas.draw_idle() cmap = plt.get_cmap() - exp_colors = cmap(np.arange(self.N)) - # failing, see above with points - # _check_colors(self.N, coll.get_facecolor(), exp_colors, alpha=0.5) - _check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N, alpha=0.5) + exp_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_facecolor(), exp_colors) + # edgecolor depends on matplotlib version + #_check_colors(self.N, coll.get_edgecolor(), ['k'] * self.N) ax.cla() # specify colormap coll = plot_polygon_collection(ax, self.polygons, self.values, cmap='RdBu') + fig.canvas.draw_idle() cmap = plt.get_cmap('RdBu') - exp_colors = cmap(np.arange(self.N)) - # failing, see above with points - # _check_colors(self.N, coll.get_facecolor(), exp_colors, alpha=0.5) + exp_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_facecolor(), exp_colors) ax.cla() # specify vmin/vmax coll = plot_polygon_collection(ax, self.polygons, self.values, vmin=3, vmax=5) + fig.canvas.draw_idle() cmap = plt.get_cmap() exp_colors = cmap([0]) - # failing, see above with points - # _check_colors(self.N, coll.get_facecolor(), exp_colors, alpha=0.5) + _check_colors(self.N, coll.get_facecolor(), exp_colors) ax.cla() # override edgecolor coll = plot_polygon_collection(ax, self.polygons, self.values, edgecolor='g') + fig.canvas.draw_idle() cmap = plt.get_cmap() - exp_colors = cmap(np.arange(self.N)) - # failing, see above with points - # _check_colors(self.N, coll.get_facecolor(), exp_colors, alpha=0.5) - _check_colors(self.N, coll.get_edgecolor(), ['g'] * self.N, alpha=0.5) + exp_colors = cmap(np.arange(self.N) / (self.N - 1)) + _check_colors(self.N, coll.get_facecolor(), exp_colors) + _check_colors(self.N, coll.get_edgecolor(), ['g'] * self.N) ax.cla()