16

我的目标是用 Python 创建加拿大的等值线地图。假设我有一本字典,其中的值指的是每个加拿大省/地区:

myvalues={'Alberta': 1.0,
 'British Columbia': 2.0,
 'Manitoba': 3.0,
 'New Brunswick': 4.0,
 'Newfoundland and Labrador': 5.0,
 'Northwest Territories': 6.0,
 'Nova Scotia': 7.0,
 'Nunavut': 8.0,
 'Ontario': 9.0,
 'Prince Edward Island': 10.0,
 'Quebec': 11.0,
 'Saskatchewan': 12.0,
 'Yukon': 13.0}

现在我想myvalues使用连续的颜色图(例如,红色阴影)根据 中的相应值为每个省着色。怎么做?

到目前为止,我只能在 matplotlib 中绘制加拿大各省/地区,但它们的形状以独特的颜色出现,我不知道如何根据中的数字来改变它myvalues(也许我需要玩,patches但我不知道如何)。

您可以在此处找到 shapefile:http ://www.filedropper.com/canadm1_1

这是我迄今为止的代码:

import shapefile
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
#   -- input --
sf = shapefile.Reader("myfolder\CAN_adm1.shp")
recs    = sf.records()
shapes  = sf.shapes()
Nshp    = len(shapes)
cns     = []
for nshp in xrange(Nshp):
    cns.append(recs[nshp][1])
cns = array(cns)
cm    = get_cmap('Dark2')
cccol = cm(1.*arange(Nshp)/Nshp)
#   -- plot --
fig     = plt.figure()
ax      = fig.add_subplot(111)
for nshp in xrange(Nshp):
    ptchs   = []
    pts     = array(shapes[nshp].points)
    prt     = shapes[nshp].parts
    par     = list(prt) + [pts.shape[0]]
    for pij in xrange(len(prt)):
     ptchs.append(Polygon(pts[par[pij]:par[pij+1]]))
    ax.add_collection(PatchCollection(ptchs,facecolor=None,edgecolor='k', linewidths=.5))
ax.set_xlim(-160,-40)
ax.set_ylim(40,90)

这是我到目前为止得到的图像:

在此处输入图像描述

编辑

我得到的解决方案必须是以下几行:

cm    = get_cmap('OrRd')
cccol = cm(1.*arange(Nshp)/Nshp)

上面的脚本创建了一个cccol实际上具有这种形状的数组:

array([[ 1.        ,  0.96862745,  0.9254902 ,  1.        ],
       [ 0.99766244,  0.93356402,  0.84133796,  1.        ],
       [ 0.99520185,  0.89227221,  0.74749713,  1.        ],
       [ 0.99274125,  0.84306037,  0.64415227,  1.        ],
       [ 0.99215686,  0.78754327,  0.5740254 ,  1.        ],
       [ 0.99186467,  0.71989237,  0.50508269,  1.        ],
       [ 0.98940408,  0.60670514,  0.39927722,  1.        ],
       [ 0.97304114,  0.50618995,  0.32915034,  1.        ],
       [ 0.94105344,  0.40776625,  0.28732027,  1.        ],
       [ 0.88521339,  0.28115341,  0.19344868,  1.        ],
       [ 0.8220992 ,  0.16018455,  0.10345252,  1.        ],
       [ 0.73351789,  0.04207613,  0.02717416,  1.        ],
       [ 0.61959248,  0.        ,  0.        ,  1.        ]])

我不知道为什么它有 4 列,但我想如果我能以某种方式将此数组的值链接到valuesdict 中指定的值,我就可以解决问题。有任何想法吗?

编辑 2

我发现“诀窍”在cccol = cm(). 为了将其与各省联系起来,我试图分配 cccol = cm(myvalues.values(i) for i in myvalues.keys())

这样(至少在我看来)每种颜色都是根据相关键分配的,并且没有错位。问题是我得到一个错误:

TypeError: Cannot cast array data from dtype('O') to dtype('int32') according to the rule 'safe'.

如何解决这个问题?

4

3 回答 3

7

这并不能直接回答您的问题,但希望能同样解决您的问题。你看过GeoPandas吗?它提供了一个简单的 API 用于处理和绘制 shapefile。您只需几行即可复制您的代码,包括绘制等值线:

import geopandas as gpd
canada = gpd.read_file('CAN_adm1.shp')
canada.plot('myvalues', cmap='OrRd')

此示例假定您的 shapefile 在每个省份都有一个包含您要绘制的值的属性,该属性称为“myvalues”。如果值未存储在 shapefile 中,您可以使用canada.mergevalues地图合并到 GeoDataframe。

一个警告:此时 GeoPandas 没有一种简单的方法来绘制等值线颜色的图例。(此处报告的问题

于 2016-06-21T17:03:12.393 回答
2

您提到了关于cccol成为列表列表的困惑。它是 RGBA 元组(红色、绿色、蓝色、alpha 透明度)的列表。这些代表从橙色到红色的 13 种“等距”颜色。

在您的情况下,您不需要等间距的颜色,而是对应于myvalues. 做这个:

cmap = matplotlib.cm.get_cmap('OrRd')
norm = matplotlib.colors.Normalize(min(myvalues.values()), max(myvalues.values()))
color_producer = matplotlib.cm.ScalarMappable(norm=norm, cmap=cmap)

现在color_producer有一个方法to_rgba可以从中获取值myvalues并将它们转换为正确的颜色。将Normalize最小和最大范围设置myvalues为红橙色颜色图的极端颜色。

现在,当您创建每个省的 时PatchCollection,您可以将其设置facecolor为由返回的 RGBA 元组color_producer

# Change the province name passed as you iterate through provinces.
rgba = color_producer.to_rgba(myvalues['Manitoba'])
PatchCollection(ptchs, facecolor=rgba, edgecolor='k', linewidths=.5)
于 2016-06-21T16:48:31.647 回答
2

请求:请将您的values字典重命名为其他名称。这个名字使写这个答案变得更加困难。:)

尚未对此进行测试,但请尝试:

color_numbers = values.values()
    # assumes the provinces are listed in the same order in values as 
    # they are in the shape file
for nshp in xrange(Nshp):
    ptchs   = []
    # ... code omitted ...
    the_facecolor = [(color_numbers[nshp]-1)/(Nshp-1), 0, 0];   #1..13 -> 0..1, then add G=B=0.
        # change the computation if the values in the values dictionary are no longer 1..13
    ax.add_collection(PatchCollection(ptchs, facecolor=the_facecolor, edgecolor='k', linewidths=.5))

您获得的输出具有所有蓝色补丁,或[0,0,1]. 由于该行不在 中cccol,我认为这不是cccol问题。此外,您添加的代码在创建后从未真正引用过cccol!(请将链接添加到您开始的代码示例!:))

无论如何,facecolor据我所知,设置应该有所帮助。将values条目转换为 0..1 范围,然后制作 [R,G,B] 颜色条目,应该会给你红色的阴影。

于 2016-06-21T16:45:50.567 回答