web-dev-qa-db-ja.com

Python:カナダのシェープファイルからコロプレスマップを作成する方法は?

ここでの私の目標は、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で遊ぶ必要がありますが、方法がわかりません)。

ここでシェープファイルを見つけることができます: 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)
_

これは私がこれまでに得ている画像です:

enter image description here

[〜#〜]編集[〜#〜]

解決策は次の行にある必要があります。

_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列あるのかわかりませんが、この配列の値をvalues dictで指定された値にリンクできれば、問題を解決できると思います。何か案は?

編集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'

これを回避する方法は?

16
FaCoffee

これはあなたの質問に直接答えることはありませんが、うまくいけば同じようにあなたの問題を解決します。 GeoPandas を見たことがありますか?シェープファイルを操作およびプロットするためのシンプルなAPIを提供します。コロプレスのプロットを含め、コードを数行で複製できます。

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

この例では、シェープファイルにプロットする値を含む各州の属性があり、その属性は「myvalues」と呼ばれていると想定しています。値がシェープファイルに保存されていない場合は、canada.mergeを使用してvaluesマップをGeoDataframeにマージできます。

注意点:現時点では、GeoPandasにはコロプレスカラーの凡例をプロットする簡単な方法がありません。 ( ここで報告された問題

6
Jeff G

cccolがリストのリストであるという混乱についておっしゃいました。 RGBAタプル(赤、緑、青、アルファ透明度)のリストです。これらは、オレンジから赤までの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にはmyvaluesから値を取得して正しい色に変換するメソッドto_rgbaがあります。 Normalizeは、myvaluesの最小範囲と最大範囲を赤オレンジカラーマップの極端な色に設定します。

これで、各州のPatchCollectionを作成するときに、そのfacecolorcolor_producerによって返されるRGBAタプルに設定できます。

# 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)
2
Jeff G

リクエスト: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]カラーエントリを作成すると、赤の色合いが得られます。

2
cxw