如何为Geopandas绘图制作表格图例

如何为Geopandas绘图制作表格图例

本文介绍了如何为Geopandas绘图制作表格图例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用geopandas绘制一个Choropleth地图,我需要绘制一个定制的表格图例. 此问题的答案说明了如何获取轮廓图的表格图例.我在下面的代码中使用它:

I am plotting a choropleth map using geopandas and I need to plot a customized tabular legend. This question's answer shows how to obtain a tabular legend for a contourf plot.And I'am using it in the code bellow :

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()

这是我得到的结果:

And here's the result i get :

因此,基本上,如您所见,地图和表中的类的颜色之间没有链接.我需要有我在地图上显示的表格中的确切颜色.图例中显示的"NG值"应从我正在绘制的"DENSITY"列中提取.

So basically, as you can see there is no link between the colors of the classes in the map and the table. I need to have the exact colors that i have in the table shown in the map. The 'NG value' shown in the legend should be extracted from the column 'DENSITY' that i am plotting.

但是,由于我没有等高线图可从中提取颜色图,因此我对如何将表格图例和地图颜色链接起来一无所知.

However, since I do not have a contour plot to extract the colormap from, I'm lost on how to link the tabular legend and the map's colors.

推荐答案

注意:此答案已过时.现代geopandas允许通过legend=True参数使用普通图例.不过,我还是将其保留在此处以供参考,以防万一有人想要一个真正的表格状传说.

Note: This answer is outdated. Modern geopandas allows to use a normal legend via legend=True argument. I still keep it here for reference though, or in case someone wants a truely tabular legend.

geopandas图不支持添加图例.它还不提供对其绘图对象的访问,仅返回形状为多边形的轴. (它甚至不提供可使用的PolyCollection).因此,为这样的情节创建一个普通的图例是一件繁琐的工作.

The geopandas plot does not support adding a legend. It also does not provide access to its plotting object and only returns an axes with the shapes as polygons. (It does not even provide a PolyCollection to work with). It is therefore a lot of tedious work to create a normal legend for such a plot.

幸运的是,示例笔记本中的某些工作已经完成了. -带有传奇人物

Fortunately some of this work is already beeing done in the example notebook Choropleth classification with PySAL and GeoPandas - With legend

因此,我们需要采用此代码并实现来自此答案的自定义表格说明.

So we need to take this code and implement the custom tabular legend which comes from 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()

产生以下图像:

这篇关于如何为Geopandas绘图制作表格图例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 02:16