From 9c2d84c8949e7feccada63dfa40b84fc16f0432c Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 29 Oct 2015 14:57:12 +0100 Subject: [PATCH] ENH: plot with single colors --- geopandas/plotting.py | 45 ++++++++++++++++++---------- tests/test_plotting.py | 67 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/geopandas/plotting.py b/geopandas/plotting.py index 038b62b..fe93e34 100644 --- a/geopandas/plotting.py +++ b/geopandas/plotting.py @@ -49,10 +49,9 @@ def plot_multilinestring(ax, geom, color='red', linewidth=1.0): plot_linestring(ax, line, color=color, linewidth=linewidth) -def plot_point(ax, pt, marker='o', markersize=2, color="black"): +def plot_point(ax, pt, marker='o', markersize=2, color='black'): """ Plot a single Point geometry """ - ax.plot(pt.x, pt.y, marker=marker, markersize=markersize, linewidth=0, - color=color) + ax.plot(pt.x, pt.y, marker=marker, markersize=markersize, color=color) def gencolor(N, colormap='Set1'): @@ -75,7 +74,8 @@ def gencolor(N, colormap='Set1'): yield colors[i % n_colors] -def plot_series(s, cmap='Set1', ax=None, linewidth=1.0, figsize=None, **color_kwds): +def plot_series(s, cmap='Set1', color=None, ax=None, linewidth=1.0, + figsize=None, **color_kwds): """ Plot a GeoSeries Generate a plot of a GeoSeries geometry with matplotlib. @@ -96,6 +96,9 @@ def plot_series(s, cmap='Set1', ax=None, linewidth=1.0, figsize=None, **color_kw Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3 + color : str (default None) + If specified, all objects will be colored uniformly. + ax : matplotlib.pyplot.Artist (default None) axes on which to draw the plot @@ -127,23 +130,26 @@ def plot_series(s, cmap='Set1', ax=None, linewidth=1.0, figsize=None, **color_kw if ax is None: fig, ax = plt.subplots(figsize=figsize) ax.set_aspect('equal') - color = gencolor(len(s), colormap=cmap) + color_generator = gencolor(len(s), colormap=cmap) for geom in s: + if color is None: + col = next(color_generator) + else: + col = color if geom.type == 'Polygon' or geom.type == 'MultiPolygon': - plot_multipolygon(ax, geom, facecolor=next(color), linewidth=linewidth, **color_kwds) + plot_multipolygon(ax, geom, facecolor=col, linewidth=linewidth, **color_kwds) elif geom.type == 'LineString' or geom.type == 'MultiLineString': - plot_multilinestring(ax, geom, color=next(color), linewidth=linewidth) + plot_multilinestring(ax, geom, color=col, linewidth=linewidth) elif geom.type == 'Point': - plot_point(ax, geom, color=next(color)) + plot_point(ax, geom, color=col) plt.draw() return ax -def plot_dataframe(s, column=None, cmap=None, linewidth=1.0, +def plot_dataframe(s, 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 - ): + **color_kwds): """ Plot a GeoDataFrame Generate a plot of a GeoDataFrame with matplotlib. If a @@ -170,6 +176,9 @@ def plot_dataframe(s, column=None, cmap=None, linewidth=1.0, cmap : str (default 'Set1') The name of a colormap recognized by matplotlib. + color : str (default None) + If specified, all objects will be colored uniformly. + linewidth : float (default 1.0) Line width for geometries. @@ -229,7 +238,9 @@ def plot_dataframe(s, column=None, cmap=None, linewidth=1.0, from matplotlib import cm if column is None: - return plot_series(s.geometry, cmap=cmap, ax=ax, linewidth=linewidth, figsize=figsize, **color_kwds) + return plot_series(s.geometry, cmap=cmap, color=color, + ax=ax, linewidth=linewidth, figsize=figsize, + **color_kwds) else: if s[column].dtype is np.dtype('O'): categorical = True @@ -255,12 +266,16 @@ def plot_dataframe(s, column=None, cmap=None, linewidth=1.0, fig, ax = plt.subplots(figsize=figsize) ax.set_aspect('equal') for geom, value in zip(s.geometry, values): + if color is None: + col = cmap.to_rgba(value) + else: + col = color if geom.type == 'Polygon' or geom.type == 'MultiPolygon': - plot_multipolygon(ax, geom, facecolor=cmap.to_rgba(value), linewidth=linewidth, **color_kwds) + plot_multipolygon(ax, geom, facecolor=col, linewidth=linewidth, **color_kwds) elif geom.type == 'LineString' or geom.type == 'MultiLineString': - plot_multilinestring(ax, geom, color=cmap.to_rgba(value), linewidth=linewidth) + plot_multilinestring(ax, geom, color=col, linewidth=linewidth) elif geom.type == 'Point': - plot_point(ax, geom, color=cmap.to_rgba(value)) + plot_point(ax, geom, color=col) if legend: if categorical: patches = [] diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 4863c27..7a0cc93 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -26,7 +26,7 @@ BASELINE_DIR = os.path.join(os.path.dirname(__file__), 'baseline_images', 'test_ TRAVIS = bool(os.environ.get('TRAVIS', False)) -class PlotTests(unittest.TestCase): +class TestImageComparisons(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() @@ -107,6 +107,7 @@ class PlotTests(unittest.TestCase): self._compare_images(ax=ax, filename=filename) + class TestPointPlotting(unittest.TestCase): def setUp(self): @@ -159,9 +160,59 @@ class TestPointPlotting(unittest.TestCase): ax = self.df.plot(column='values', cmap='RdYlGn') cmap = get_cmap('RdYlGn') expected_colors = cmap(np.arange(self.N)/(self.N-1)) - _check_colors(ax.get_lines(), expected_colors) + def test_single_color(self): + + ax = self.points.plot(color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + ax = self.df.plot(color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + ax = self.df.plot(column='values', color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + +class TestLineStringPlotting(unittest.TestCase): + + def setUp(self): + + self.N = 10 + values = np.arange(self.N) + self.lines = GeoSeries([LineString([(0, i), (9, i)]) for i in xrange(self.N)]) + self.df = GeoDataFrame({'geometry': self.lines, 'values': values}) + + def test_single_color(self): + + ax = self.lines.plot(color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + ax = self.df.plot(color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + ax = self.df.plot(column='values', color='green') + _check_colors(ax.get_lines(), ['green']*self.N) + + +class TestPolygonPlotting(unittest.TestCase): + + def test_single_color(self): + + t1 = Polygon([(0, 0), (1, 0), (1, 1)]) + t2 = Polygon([(1, 0), (2, 0), (2, 1)]) + polys = GeoSeries([t1, t2]) + df = GeoDataFrame({'geometry': polys, 'values': [0, 1]}) + + ax = polys.plot(color='green') + _check_colors(ax.patches, ['green']*2, alpha=0.5) + + ax = df.plot(color='green') + _check_colors(ax.patches, ['green']*2, alpha=0.5) + + ax = df.plot(column='values', color='green') + _check_colors(ax.patches, ['green']*2, alpha=0.5) + class TestPySALPlotting(unittest.TestCase): @@ -184,14 +235,20 @@ class TestPySALPlotting(unittest.TestCase): self.assertEqual(labels, expected) -def _check_colors(collection, expected_colors): +def _check_colors(collection, expected_colors, alpha=None): + from matplotlib.lines import Line2D import matplotlib.colors as colors conv = colors.colorConverter for patch, color in zip(collection, expected_colors): - result = patch.get_color() - assert conv.to_rgba(result) == conv.to_rgba(color) + if isinstance(patch, Line2D): + # points/lines + result = patch.get_color() + else: + # polygons + result = patch.get_facecolor() + assert conv.to_rgba(result) == conv.to_rgba(color, alpha=alpha) if __name__ == '__main__':