驚くほど簡単な技術系健忘録

驚くほど簡単な技術系健忘録

アプリやWebサービス、RPAを作る上での健忘録を書いていきます。

GeoPandasで市区町村別と県別の境界をそれぞれ描く

【手順①:Shapeファイルの用意】
GeoPandasで市区町村別と県別の境界をそれぞれ書くには以下のデータがまず必要となる。
①市区町村別のShapeファイル
②県月のShapeファイル
ということでまず、それぞれのデータをダウンロードしておく

①市区町村別のShapeファイル
「国土数値情報ダウンロード」のデータがやはりいちばん利用しやすい。
以下のURLでデータ形式を「GML(JPGIS2.1)シェープファイル」にして「行政区域(ポリゴン)」から、全国データをダウンロードすると良い。
nlftp.mlit.go.jp
※ファイルはいろいろなところにあるので別のものを使っても大丈夫

②県別のShapeファイル
こちらのものが一番使いやすかった
www.okadajp.org
f:id:nade_nadegata:20200824225009p:plain
この日本全図というのを利用すると日本全国が一気に利用できるので簡単だが東京の小笠原諸島がどうやっても除外できなかったりので、各県のデータをそれぞれダウンロードしてPythonのfor文で読み込むことにした。
※ファイルはいろいろなところにあるので別のものを使っても大丈夫


【手順②:市区町村別に境界を描く】
全部ここのサイトを参考にした
qiita.com

「参考コード」

import numpy as np
import geopandas as gpd
import random
import matplotlib.colors
import matplotlib.pyplot as plt

filename = "/content/drive/My Drive/Colab Notebooks/pref47.geojson"  #自分の場合ここをShapeファイルにしている
df = gpd.read_file(filename, encoding='SHIFT-JIS')

df["target"] = [random.random() for i in range(df.shape[0])]

def colors_scale(arr):
    n_min = min(arr)
    n_max = max(arr)
    cmap = plt.cm.rainbow
    norm = matplotlib.colors.Normalize(vmin=n_min, vmax=n_max)

    arr = [cmap(norm(r)) for r in arr]
    return arr, cmap, norm


mini_df = df[df["N03_001"].isin(["愛媛県", "徳島県", "香川県", "高知県"])]

num_color, cmap, norm = colors_scale(mini_df["num"])

mini_df.plot(color=num_color, figsize=(10,6))
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
plt.colorbar(sm)
plt.show()


【手順③:県別の境界を上から描く】
データで色付けしたり市区町村の境界の上に県の境界を別の色で書く方法がすぐに分からなかったのでこれも備忘録として記載する。

gdf.boundary.plot();

これはGeoPandasの公式ページで見つけた。


【手順④:市区町村名をプロット】

#市区町村の重心を取得
gdf['centroid'] = gdf['geometry'].centroid
gdf["x"] = gdf.centroid.x
gdf["y"] = gdf.centroid.y

#重複しないように市区町村名でグループ化してプロット
name_df = gdf.groupby(['N03_003', 'N03_004'], as_index=False).mean().loc[:, ['N03_003', 'N03_004', 'x', 'y']]
mylist = list(name_df.index)
for l in mylist:
    if name_df['N03_004'][l] == "NO":
        plt.text(name_df['x'][l], name_df['y'][l], name_df['N03_003'][l], fontname="MS Gothic", fontsize=25)        
    else:
        plt.text(name_df['x'][l]-0.02, name_df['y'][l], name_df['N03_004'][l], fontname="MS Gothic", fontsize=25)

これで完成!!!
ということで全体的なソースコードを以下に記載。(多分エラー出ないはず)

import numpy as np
import geopandas as gpd
import pandas as pd
import matplotlib.colors
import matplotlib.pyplot as plt
import glob

#市区町村のshapeデータ
filename = "../data/N03-14_140401.shp"
gdf = gpd.read_file(filename, encoding='SHIFT-JIS')

#県別のshapeデータ
filename = "../data/tokyohk.shp"
pgdf = gpd.read_file(filename, encoding="SHIFT-JIS")
l = glob.glob("../data/*shp")
for filename in l:
    pgdf_temp = gpd.read_file(filename, encoding="SHIFT-JIS")
    pgdf = pgdf.append(pgdf_temp, ignore_index = False)
#重複行を削除す
pgdf.drop_duplicates()

#市区町村の重心を取得
gdf['centroid'] = gdf['geometry'].centroid
gdf["x"] = gdf.centroid.x
gdf["y"] = gdf.centroid.y

#ランダムな数値をカラムに入れる
gdf["target"] = [random.random() for i in range(df.shape[0])]

#色定義
def colors_scale(arr):
    n_min = min(arr)
    n_max = max(arr)
    cmap = plt.cm.rainbow
    norm = matplotlib.colors.Normalize(vmin=n_min, vmax=n_max)
    arr = [cmap(norm(r)) for r in arr]
    return arr, cmap, norm

#特定の県に絞って市区町村境の描画
mini_df = gdf[gdf['N03_001'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df = mini_df[~mini_df['N03_004'].str.contains("小笠原村|三宅村|八丈町|青ケ島村|御蔵島村|神津島|新島村|利島村|大島町|所属未定地", na=False)]    #島を除外
base = mini_df.plot(color='white', edgecolor='black', figsize=(20,20))  #ベースを白、境界を黒で描画

#県境の描画
mini_df = pgdf[pgdf['PREF'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df.boundary.plot(ax=base, linewidth=2, edgecolor='red')

#市区町村名をプロット
mini_df = gdf[gdf['N03_001'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df = mini_df[~mini_df['N03_004'].str.contains("小笠原村|三宅村|八丈町|青ケ島村|御蔵島村|神津島|新島村|利島村|大島町|所属未定地", na=False)]
name_df = mini_df.groupby(['N03_003', 'N03_004'], as_index=False).mean().loc[:, ['N03_003', 'N03_004', 'x', 'y']]
mylist = list(name_df.index)
for l in mylist:
    if name_df['N03_004'][l] == "NO":
        plt.text(name_df['x'][l], name_df['y'][l], name_df['N03_003'][l], fontname="MS Gothic", fontsize=25)        
    else:
        plt.text(name_df['x'][l]-0.02, name_df['y'][l], name_df['N03_004'][l], fontname="MS Gothic", fontsize=25)

#色を描画
num_color, cmap, norm = colors_scale(mini_df["target"])

#カラーバーを記載
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
fig = base.get_figure()
cax = fig.add_axes([base.get_position().x1+0.01, base.get_position().y0, 0.02,
base.get_position().height])
cbar = fig.colorbar(sm, cax=cax)
cbar.ax.tick_params(labelsize=15)

plt.show()