はじめに
子どもの数が減ってきて、学校の統廃合が現実に議論されるようになってきました。
また、公共施設の在り方(取り壊す、残す)についても議論が及んできています。
そこで、各校区の在籍人数を地図上にマッピングをしたいと思い、試行錯誤して何とか表示できた過程の記録として残します。
使用したJupyterファイルなどはこちらに置いてあります。
https://github.com/SGyutan/folium_kuki
なお、小学校の校区の境界データは
国土数値情報 小学校区データ
からダウンロードしてください。
行いたいこと
(1)学校の位置を地図上に示す
→経度緯度と学校名のリストがあればよい
(2)学校の校区を示す
→ポリゴン操作が必要
(3)各学校の在籍人数を示す
→階層プロットが必要
なお対象地域は、埼玉県久喜市とします。
環境
Win10
Anaconda
python3.7
モジュール
Folium、geopandas
GISデータを取り扱う上での最低限のイメージ
GEOデータマッピングで必要な知識
ジオメトリタイプ一覧をながめる
マッピングの基本種類
POINT:単一の点。
LINESTRING:単一の折れ線。
POLYGON:単一のポリゴン。リングは複数あっても良い。
POLYHEDRALSURFACE:多面体サーフェス。
MULTIPOINT:複数の点からなるジオメトリ
MULTILINESTRING:複数の折れ線からなるジオメトリ
MULTIPOLYGON:複数のポリゴンからなるジオメトリ
データをマッピングする方法(ソフト)
・QGIS(GISソフト)
・Folium(Python、OpenStreetMap)
(Folium は javascript ライブラリの leafletを、Python で使えるようにしたモジュール。)
など
GISエンジンおよびサービスのまとめ
に詳しく書かれている
今回は、Foliumを利用します。
データの入手
ベースになる地図(日本)
Open Street Map というオープンライセンスのソフト
(QGIS3,Foliumではデフォルトで使えます。)
基盤地図情報サイト国土地理院
必要な地図をダウンロードする。
境界データ
国土交通省が提供している国土数値情報 ダウンロードサービスからダウンロード
ファイル形式
GeoJSON形式
Shape形式
CSV形式(データが入っているもの)
その他にもありますが今回私が利用したファイル形式。
データの前処理
データの整形ソフト・モジュール
Excel(最終的にはCSVで保存する)
Pandas(python)
GeoPandas(python)
GeoPandasの使い方
geopandasの使い方をマスターしよう ~Shapeファイルの読込・作成、GeoDataFrameの扱い方まで~
geopandasを使ってShapeファイルを作成しよう! ~Airbnbのデータを可視化してみよう~
geopandasやそれ以外のツールやファイル変換など
Pythonを用いたshapefileやgeojsonの読込および描画
Foliumのドキュメント
必要なモジュールのインストールはこちらを参照してください。
anacondaに入っているモジュールの他に、
folium、geopandasなど
folium(位置情報の可視化)の使い方メモ
Folium: Python で地図可視化
【folium】Pythonで位置情報の可視化
すごく参考になった記事
python folium で、都内の公園にまつわる情報を地図上に描画する
python folium を使い、都道府県の夫婦年齢差をプロットする
##(0)Foliumで地図を表示してみます。
埼玉県久喜市を例にします。
久喜市の市役所:大字下早見85-3
緯度・経度 36.06205900,139.66683800
#必要なモジュールのインポート
import json
from pathlib import Path
import folium
from folium import plugins
print( "folium version is {}".format(folium.__version__) )
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
# ベースマップの作製
map_center = [36.06205900,139.66683800] #久喜市に設定
m_kuki = folium.Map(location=map_center, tiles='openstreetmap', zoom_start=13)
m_kuki.save('kuki.html')
市役所を中心とした地図が描かれます。
参考:
【珈琲たいむ】Pythonでコロプレス図を描く
※コロプレス図 (choropleth map)日本語では「階級区分図」
に記載されている内容
locationで地図の中心を指定、
zoom_startで縮尺のレベルを指定(デフォルト値は10)、
tilesでベースマップの種類を指定します(デフォルトはopenstreetmap)。なお、コロプレス図が見やすいのはcartodbpositron。
foliumにビルトインされているベースマップのうち、API_keyがなくても利用できるのは以下の8個
openstreetmap,
mapboxbright,
cartodbdark_matter,
cartodbpositron,
mapboxcontrolroom,
stamenterrain,
stamentoner,
stamenwatercolor
##(1)小学校の位置をプロット
Open Data Saitama
にて検索して
組織 久喜市 【久喜市】公共施設情報 【H30】公共施設情報 のCSVファイルを取得します。
https://opendata.pref.saitama.lg.jp/data/dataset/0703b822-dee1-4cf5-943b-7cbc837108ba/resource/f200c959-b425-4d1f-81cb-75c1780dd2fa/download/inet-file01.inet-kuki.localpublic01010500.votiro02011001h30sisetu-kuki.csv
このデータには、久喜市の公共施設の住所、名称、経度緯度などの情報が記載されています。
※ダウンロードしたファイル名が長いので、「kukikokyoshisetsu.csv」に変更します。
# 日本語CSVファイルの読み込みのために engine = "python"としています。
df_kuki = pd.read_csv("kukikokyoshisetsu.csv", engine = "python")
#元のデータから学校関係を抽出します。
df_school=df_kuki[df_kuki["種別"]=="小中学校・給食センター"]
# 小学校だけ抽出
df_esh=df_school.loc[117:139]
df_esh.head()
#地図に位置をマッピングします。
map_center = [36.06205900,139.66683800] #久喜市に設定
m2_kuki = folium.Map(location=map_center, tiles='openstreetmap', zoom_start=13)
#Forで回します。
for i, dt in df_esh.iterrows():
folium.Marker(location=[dt['施設_緯度'],dt['施設_経度']], popup='{},{}'.format(dt['名称'],dt['施設_電話番号'])).add_to(m2_kuki)
# print(dt['施設_緯度'], dt['施設_経度'], dt['名称'])
m2_kuki.save('kuki_e_school.html')
##(2)久喜市の小学校の校区をプロット
データの入手先
国土数値情報 小学校区データ
埼玉県を指定
データ形式:
①「GML形式=高度な内容を表現できる世界標準規格。普及はこれから
②「シェープファイル形式」=現在の殆どのGISソフトで読み書きでき普及している規格
このデータは上記の形式で提供されています。
データの属性
範囲、通学区域の範囲面型(GM_Surface)
市区町村コード(A27_005):小学校が属する行政コード、コードリスト「行政コード」
設置主体(A27_006):小学校の設置主体、文字列型(CharacterString)
名称(A27_007):小学校の名称、文字列型(CharacterString)
所在地(A27_008):小学校の設置所在地、文字列型(CharacterString)
#GeopandasでShapeファイルを読み込む
#埼玉県の校区
#ファイル読込(encoding='SHIFT-JIS'とすることで日本語データにも対応)
shdf = gpd.read_file('A27-16_11.shp',encoding='SHIFT-JIS')
#久喜市だけ取り出します。
#A27_005には市町村コード番号が入っているので、この値でセレクトしてもよい。
kuki_esh=shdf[shdf['A27_006']=='久喜市立']
#この後の処理のために学校を識別するためにidをつけます。ここではid2としています。
#このままだと一番左のインデックス番号がid番号になります。
kuki_esh['id2']=[x for x in range(0,23)]
# id2は数値ではなく文字列にしておく必要があるため型変換をします。
kuki_esh['id2'] = kuki_esh['id2'].astype('str')
map_center = [36.06205900,139.66683800] #久喜市に設定
m_kuki_sh = folium.Map(location=map_center, tiles='cartodbpositron', zoom_start=13)
folium.Choropleth(geo_data=kuki_esh.to_json()).add_to(m_kuki_sh)
for i, dt in df_esh.iterrows():
folium.Marker(location=[dt['施設_緯度'],dt['施設_経度']], popup='{}'.format(dt['名称'])).add_to(m_kuki_sh)
folium.LayerControl().add_to(m_kuki_sh)
m_kuki_sh.save('kuki_e_sh_koku.html')
#geo_data=kuki_esh.to_json() dfのデータをJSON形式にして渡します。
##(3)各小学校の在校者数をプロット
各小学校の在籍者のデータを取ってきます。
教育要覧(久喜市の教育)
このHPにある「令和元年度久喜市の教育」
分割版2(学校教育、13ページから38ページまで)(PDF:30,337KB)
をダウンロードします。
この19ページ目に年度による生徒数がまとめられています。
これを数値化します。私がやったのは、PDFをWebサービスでWordファイルにして、そのあといろいろとExcelでやって、CSVファイルにしました。
前処理したファイルを「児童生徒数推移.csv」とします。
なお先ほど、境界データで付加したIDと同じ番号を同じ小学校に付加しています。
df_num=pd.read_csv('児童生徒数推移.csv', engine = "python")
# この後にgeojsonのデータと結びつけます。
# geojsonの値は、基本文字列なので、その文字列と結びつけるためにIDをIntからStringに変換しています。
df_num['id'] = df_num['id'].astype('str')
児童数の推移のデータを見てみます。
100人を切っている小学校がある一方、急激に増加している小学校もあります。
全体としては右肩下がりです。
# 最新の令和元年度のマップを作ってみます。
map_center = [36.06205900,139.66683800] #久喜市に設定
m_kuki_sh = folium.Map(location=map_center, tiles='cartodbpositron', zoom_start=13)
folium.Choropleth(geo_data=kuki_esh.to_json()).add_to(m_kuki_sh)
m_kuki_sh.choropleth(geo_data=kuki_esh.to_json(),data=df_num,
columns=['id', 'R01'],
key_on='feature.properties.id2',
fill_color='OrRd',
threshold_scale=[0, 100, 200, 300, 400, 500, 600, 700, 800, 900],
fill_opacity=0.7,
line_opacity=0.2,
legend_name='生徒数'
)
# folium.LayerControl().add_to(m_kuki_sh)
for i, dt in df_esh.iterrows():
folium.Marker(location=[dt['施設_緯度'],dt['施設_経度']], popup='{}'.format(dt['名称'])).add_to(m_kuki_sh)
# m_saitama
m_kuki_sh.save('kuki_esh_r1.html')
# 一番古いH22年度のマップを作ってみます。
map_center = [36.06205900,139.66683800] #久喜市に設定
m_kuki_sh = folium.Map(location=map_center, tiles='cartodbpositron', zoom_start=13)
folium.Choropleth(geo_data=kuki_esh.to_json()).add_to(m_kuki_sh)
m_kuki_sh.choropleth(geo_data=kuki_esh.to_json(),data=df_num,
columns=['id', 'H22'],
key_on='feature.properties.id2',
fill_color='OrRd',
threshold_scale=[0, 100, 200, 300, 400, 500, 600, 700, 800, 900],
fill_opacity=0.7,
line_opacity=0.2,
legend_name='生徒数'
)
# folium.LayerControl().add_to(m_kuki_sh)
for i, dt in df_esh.iterrows():
folium.Marker(location=[dt['施設_緯度'],dt['施設_経度']], popup='{}'.format(dt['名称'])).add_to(m_kuki_sh)
# m_saitama
m_kuki_sh.save('kuki_esh_H22.html')
fill_color を変更することで色を選べます。
Colerのサンプル
color mapの一覧をheatmapで(160個くらい画像があるので注意)
key_on の設定について
python folium を使い、都道府県の夫婦年齢差をプロットする
Geojson 上に feature はキー値として存在しませんが、features は存在しており、且つ、配列です。key_onの指定がfeature.properties.id なので、features > 1要素 feature としての解釈は folium 側でうまいことやってくれています。
geojsonファイルは以下のようになっていて、今回 id としたいのは地域コードで、properties の下にぶらさがっており、on_key に feature.properties.id2 を指定しています。
##感想
いろいろと突っ込みどころ満載だと思いますが、何とか当初の目的の校区ごとの在籍者の可視化ができました。
少子化でどんどん子どもが減っていく一方、在籍者の偏りが地域で激しくなってきているのがわかりました。現在増加しているところも10年もすると途端に人数が減ってくるのがわかります。
それにしてもPDFのデータは何とかならないのでしょうか。