如何为geopandas情节制作表格图例

时间:2017-06-16 17:30:25

标签: python-2.7 matplotlib geopandas

我正在使用geopandas绘制一个等值区域地图,我需要绘制一个自定义的表格图例。 This question的答案显示了如何获取contourf图的表格图例。 而且我在下面的代码中使用它:

import pandas as pd
import pysal as ps
import geopandas as gp
import numpy as np
import matplotlib.pyplot as plt

pth = 'outcom.shp'
tracts = gp.GeoDataFrame.from_file(pth)
ax = tracts.plot(column='Density', scheme='QUANTILES')

valeur = np.array([.1,.45,.7])
text=[["Faible","Ng<1,5" ],["Moyenne","1,5<Ng<2,5"],[u"Elevee", "Ng>2,5"]]
colLabels = ["Exposition", u"Densite"]
tab = ax.table(cellText=text, colLabels=colLabels, colWidths = [0.2,0.2],    loc='lower right', cellColours=plt.cm.hot_r(np.c_[valeur,valeur]))
plt.show()

以下是我得到的结果: enter image description here

基本上,正如您所看到的,地图和表格中的类颜色之间没有任何联系。我需要拥有地图中所示表格中的确切颜色。 “NG值”&#39;图例中显示的内容应该从“DENSITY&#39;”栏目中提取出来。我在密谋。

但是,由于我没有从中提取色彩图的等值线图,我对如何链接表格图例和地图颜色感到很遗憾。

2 个答案:

答案 0 :(得分:6)

geopandas图不支持添加图例。它也不提供对其绘图对象的访问权限,只返回形状为多边形的轴。 (它甚至不提供PolyCollection工作)。因此,为这样的情节创造一个正常的传说是很繁琐的工作。

幸运的是,有些工作已经在示例笔记本Choropleth classification with PySAL and GeoPandas - With legend

中完成

因此,我们需要使用此代码并实现来自this answer的自定义表格图例。

以下是完整的代码:

def __pysal_choro(values, scheme, k=5):
    """ Wrapper for choropleth schemes from PySAL for use with plot_dataframe

        Parameters
        ----------

        values
            Series to be plotted

        scheme
            pysal.esda.mapclassify classificatin scheme ['Equal_interval'|'Quantiles'|'Fisher_Jenks']

        k
            number of classes (2 <= k <=9)

        Returns
        -------

        values
            Series with values replaced with class identifier if PySAL is available, otherwise the original values are used
    """

    try:
        from pysal.esda.mapclassify import Quantiles, Equal_Interval, Fisher_Jenks
        schemes = {}
        schemes['equal_interval'] = Equal_Interval
        schemes['quantiles'] = Quantiles
        schemes['fisher_jenks'] = Fisher_Jenks
        s0 = scheme
        scheme = scheme.lower()
        if scheme not in schemes:
            scheme = 'quantiles'
            print('Unrecognized scheme: ', s0)
            print('Using Quantiles instead')
        if k < 2 or k > 9:
            print('Invalid k: ', k)
            print('2<=k<=9, setting k=5 (default)')
            k = 5
        binning = schemes[scheme](values, k)
        values = binning.yb
    except ImportError:
        print('PySAL not installed, setting map to default')

    return binning


def plot_polygon(ax, poly, facecolor='red', edgecolor='black', alpha=0.5, linewidth=1):
    """ Plot a single Polygon geometry """
    from descartes.patch import PolygonPatch
    a = np.asarray(poly.exterior)
    # without Descartes, we could make a Patch of exterior
    ax.add_patch(PolygonPatch(poly, facecolor=facecolor, alpha=alpha))
    ax.plot(a[:, 0], a[:, 1], color=edgecolor, linewidth=linewidth)
    for p in poly.interiors:
        x, y = zip(*p.coords)
        ax.plot(x, y, color=edgecolor, linewidth=linewidth)

def plot_multipolygon(ax, geom, facecolor='red', edgecolor='black', alpha=0.5, linewidth=1):
    """ Can safely call with either Polygon or Multipolygon geometry
    """
    if geom.type == 'Polygon':
        plot_polygon(ax, geom, facecolor=facecolor, edgecolor=edgecolor, alpha=alpha, linewidth=linewidth)
    elif geom.type == 'MultiPolygon':
        for poly in geom.geoms:
            plot_polygon(ax, poly, facecolor=facecolor, edgecolor=edgecolor, alpha=alpha, linewidth=linewidth)

import numpy as np
from geopandas.plotting import (plot_linestring, plot_point, norm_cmap)


def plot_dataframe(s, column=None, colormap=None, alpha=0.5,
                   categorical=False, legend=False, axes=None, scheme=None,
                   k=5, linewidth=1):
    """ 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.

        Parameters
        ----------

        GeoDataFrame
            The GeoDataFrame to be plotted.  Currently Polygon,
            MultiPolygon, LineString, MultiLineString and Point
            geometries can be plotted.

        column : str (default None)
            The name of the column to be plotted.

        categorical : bool (default False)
            If False, colormap will reflect numerical values of the
            column being plotted.  For non-numerical columns (or if
            column=None), this will be set to True.

        colormap : str (default 'Set1')
            The name of a colormap recognized by matplotlib.

        alpha : float (default 0.5)
            Alpha value for polygon fill regions.  Has no effect for
            lines or points.

        legend : bool (default False)
            Plot a legend (Experimental; currently for categorical
            plots only)

        axes : matplotlib.pyplot.Artist (default None)
            axes on which to draw the plot

        scheme : pysal.esda.mapclassify.Map_Classifier
            Choropleth classification schemes

        k   : int (default 5)
            Number of classes (ignored if scheme is None)


        Returns
        -------

        matplotlib axes instance
    """
    import matplotlib.pyplot as plt
    from matplotlib.lines import Line2D
    from matplotlib.colors import Normalize
    from matplotlib import cm

    if column is None:
        raise NotImplementedError
        #return plot_series(s.geometry, colormap=colormap, alpha=alpha, axes=axes)
    else:
        if s[column].dtype is np.dtype('O'):
            categorical = True
        if categorical:
            if colormap is None:
                colormap = 'Set1'
            categories = list(set(s[column].values))
            categories.sort()
            valuemap = dict([(j, v) for (v, j) in enumerate(categories)])
            values = [valuemap[j] for j in s[column]]
        else:
            values = s[column]
        if scheme is not None:
            binning = __pysal_choro(values, scheme, k=k)
            values = binning.yb
            # set categorical to True for creating the legend
            categorical = True
            binedges = [binning.yb.min()] + binning.bins.tolist()
            categories = ['{0:.2f} - {1:.2f}'.format(binedges[i], binedges[i+1]) for i in range(len(binedges)-1)]
        cmap = norm_cmap(values, colormap, Normalize, cm)
        if axes == None:
            fig = plt.gcf()
            fig.add_subplot(111, aspect='equal')
            ax = plt.gca()
        else:
            ax = axes
        for geom, value in zip(s.geometry, values):
            if geom.type == 'Polygon' or geom.type == 'MultiPolygon':
                plot_multipolygon(ax, geom, facecolor=cmap.to_rgba(value), alpha=alpha, linewidth=linewidth)
            elif geom.type == 'LineString' or geom.type == 'MultiLineString':
                raise NotImplementedError
                #plot_multilinestring(ax, geom, color=cmap.to_rgba(value))
            # TODO: color point geometries
            elif geom.type == 'Point':
                raise NotImplementedError
                #plot_point(ax, geom, color=cmap.to_rgba(value))
        if legend:
            if categorical:
                rowtitle = ["Moyenne"] * len(categories)
                rowtitle[0] = "Faible"; rowtitle[-1] = u"Elevée"
                text=zip(rowtitle, categories)
                colors = []
                for i in range(len(categories)):                    
                    color = list(cmap.to_rgba(i))
                    color[3] = alpha
                    colors.append(color)
                colLabels = ["Exposition", u"Densité"]

                tab=plt.table(cellText=text, colLabels=colLabels, 
                                    colWidths = [0.2,0.2], loc='upper left', 
                                    cellColours=zip(colors, colors))
            else:
                # TODO: show a colorbar
                raise NotImplementedError

    plt.draw()
    return ax




if __name__ == "__main__":
    import pysal as ps 
    import geopandas as gp
    import matplotlib.pyplot as plt 

    pth = ps.examples.get_path("columbus.shp")
    tracts = gp.GeoDataFrame.from_file(pth)

    ax = plot_dataframe(tracts, column='CRIME', scheme='QUANTILES', k=5, colormap='OrRd', legend=True)
    plt.show()

产生以下图像:

enter image description here

答案 1 :(得分:0)

您的问题出在cmap中:

ax = tracts.plot(......scheme='QUANTILES',cmap='jet')

和:

tab = ...... cellColours=plt.cm.jet(np.c_[valeur,valeur]))