2017-06-16 343 views
0

我正在绘制使用geopandas的等值线图,我需要绘制自定义表格图例。 This question的答案显示了如何获取contourf图的表格图例。 而我'在代码波纹管使用它:如何为geopandas绘制表格图例

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值'应该从我正在绘制的'DENSITY'列中提取。

但是,由于我没有从中提取色彩图的等高线图,因此我迷失了如何链接表格图例和地图的颜色。

+0

您正在使用HOT颜色表进行绘制表格。我怀疑地理数据使用另一种颜色地图b默认。尝试为两个操作指定相同的颜色映射表。 –

+0

即使它不起作用,我认为应该修改这一行:text = [[“Faible”,“Ng <1,5”],[“Moyenne”,“1.5 2,5”]]。由于这些NG值应该从我正在绘制的名为'Density'的列中提取。 –

+0

你可以添加一个彩色贴图到你的贴图上,以便像你的图例一样匹配'ax = tracts.plot(column ='Density',scheme ='QUANTILES',cmap ='hot')' –

回答

2

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

幸运一些这方面的工作已经beeing在例如笔记本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

+0

哦超,这就是我要找的,谢谢你的帮助,努力,解释和代码,你救了我的命! –

+0

虽然我很高兴它可以帮助你,但我衷心希望你的生活不依赖于地图传说的一些代码。 ;-) – ImportanceOfBeingErnest