見出し画像

Pythonでプロットした地図を出力する

書いている原稿に地図にマークを付けたものを入れたかったのですが,フリーの白地図に自分でマークを入れると位置の調整や,後々の使い勝手や,そもそも権利的によかったっけとかの確認が面倒でした。なので,せっかく勉強しているPythonを使ってできないかと思い調べたら思いのほか手間がかかったのでメモ代わりに。最終的にできたのは次のようなもの。印刷の都合でグレースケールですが,カラーのものも作れます。

画像1

ちなみに作業してから見つけたのですが,PythonではなくQGISというアプリを使えばGUIでもっと簡単にできそうです(よくある)。こちらについてもいつか解説を書きましょうかね。

製作環境
・Mac (10.15.5)
・Python (3.8.0)
・ライブラリ(pandas,geopandas,matplotlib)
・地図データ(japan json)

完成したスクリプトは以下のとおり。

import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt

df = gpd.read_file('/Users/hogehoge/Python/land-master/japan.geojson')

locations = pd.read_csv("sorachi.csv")
locations1 = (locations.query('population >= 10000 & div == "city"'))
locations2 = (locations.query('population >= 10000 & div == "town"'))
locations3 = (locations.query('population < 10000 & div == "city"'))
locations4 = (locations.query('population < 10000 & div == "town"'))

# 表示
with plt.style.context("ggplot"):
  df.plot(figsize=(8,8), edgecolor='#444', facecolor='white', linewidth = 0.5);
  plt.xlim([141,143])
  plt.ylim([42,44])
  plt.scatter(locations1.Longitude, locations1.Latitude, marker='o', s=50, color="green", alpha=0.8, linewidths = 2)
  plt.scatter(locations2.Longitude, locations2.Latitude, marker='v', s=50, color="green", alpha=0.8, linewidths = 2)
  plt.scatter(locations3.Longitude, locations3.Latitude, marker='o', s=50, edgecolor="orange", alpha=0.8, linewidths = 2,facecolor="None")
  plt.scatter(locations4.Longitude, locations4.Latitude, marker='v', s=50, edgecolor="orange", alpha=0.8, linewidths = 2, facecolor="None")

# 保存
plt.savefig("/Users/hogehoge/Python/kibo-300dpi.png", format="png", dpi=300, bbox_inches='tight', pad_inches=0)

plt.show()

以下では中身について説明していきます。

追記 2023/01/04

久しぶりに実行したところ次のようにエラーを吐きました。どうもBigSur以降発生しているトラブルのようです(現在の私はMonterey)。

    free = load_dll('c').free
  File "/Users/hogehoge/.pyenv/versions/3.8.1/lib/python3.8/site-packages/shapely/geos.py", line 60, in load_dll
    raise OSError(
OSError: Could not find lib c or load any of its variants [].

/Users/hogehoge/.pyenv/versions/3.8.1/lib/python3.8/site-packages/shapely/geos.py
にあるgeos.pyの一部(私は138行目)を次のように書き換えたら解決しました(元の部分をコメントアウトして残してあります)。

free = CDLL(None).free  # free = load_dll('c').free

解決にあたり次のページが参考になりました。

地図を読み込んで書く

緯度・経度などの入った地図情報をダウンロードします。私はData of Japanのものを使用しました。これはCloneというボタンからDownload ZIPからダウンロード,解凍して好きなところに置いておけばOKです。そして置いた場所を5行目で読み込みます。

df = gpd.read_file('/Users/hogehoge/land-master/japan.geojson')

次のようにすれば地図サイズが決まります。

df.plot(figsize=(8,8));

地図を表示させるには次の命令をします。

plt.show()
スクリーンショット 2020-07-01 20.23.17

境界線や土地の色を調整するには次のように指定します。私が使ったのは以下のものです。
・境界線の色 edgecolor
・土地の色 facecolor
・境界線の太さ linewidth

df.plot(figsize=(8,8), edgecolor='#444', facecolor='white', linewidth = 0.5);

実行結果は以下のとおりです。

スクリーンショット 2020-07-01 20.29.10

必要なところだけ表示する

特定の都道府県だけ表示させるには次のようにします。

df[df['nam_ja'] == '北海道'].plot()
スクリーンショット 2020-07-01 20.37.20

このうち一部だけ表示させたいこともあります。そういうときはGUIで上下左右の矢印を押して,Ctrlを押しながらドラッグすると拡大や移動ができます。ただこれだと保存のときに反映できないので,緯度・経度の情報を入れておくほうがいいでしょう。XとYで範囲を指定します。

plt.xlim([140,142])
plt.ylim([41,43])
スクリーンショット 2020-07-01 20.40.59

地図にマークを付ける(プロットする)

ところで地図に印って付けたいですよね?(私は付けたい)そのためには,あらかじめ印を付ける位置をcsv形式で書いておきます。用意するcsvファイルには場所・緯度・経度の情報を付けておきます。また,単に印をつけるだけでなく,条件によって印を変えるなら,条件についての情報も書いておきます。緯度・経度はgoogle mapで位置を指定するとURLに表示されます(赤線の部分)。

スクリーンショット 2020-07-01 20.47.53

仮に,空知管内で市か町か(div列),人口(population列)の情報を入れます。csvファイルは次のようになります。

スクリーンショット 2020-07-01 21.40.23

csvファイルは下からどうぞ(人口データは北海道のウェブページの平成30年のデータを使用)。

これで,csvを読み込み,条件ごとに別の名前を付けます。

locations = pd.read_csv("sorachi.csv")
locations1 = (locations.query('population >= 10000 & div == "city"'))
locations2 = (locations.query('population >= 10000 & div == "town"'))
locations3 = (locations.query('population < 10000 & div == "city"'))
locations4 = (locations.query('population < 10000 & div == "town"'))

そして,必要な範囲だけを表示するよう範囲指定した上で,条件ごとに異なる図形を描くようにします。

plt.xlim([141,143])
plt.ylim([42,44])
plt.scatter(locations1.Longitude, locations1.Latitude, marker='o', s=50, color="green", alpha=0.8, linewidths = 2)
plt.scatter(locations2.Longitude, locations2.Latitude, marker='v', s=50, color="green", alpha=0.8, linewidths = 2)
plt.scatter(locations3.Longitude, locations3.Latitude, marker='o', s=50, edgecolor="orange", alpha=0.8, linewidths = 2,facecolor="None")
plt.scatter(locations4.Longitude, locations4.Latitude, marker='v', s=50, edgecolor="orange", alpha=0.8, linewidths = 2, facecolor="None")

これで実行すると,次のように,市は○,町は▽,人口が10000人以上ならば緑,10000人未満ならばオレンジの白抜きになります。

スクリーンショット 2020-07-01 21.46.16

これだと海と陸地が分かりにくいので,見栄えをggplotのようにします。これにはwith構文を使用します(陸地を塗る方が楽ですね)。

with plt.style.context("ggplot"):
  df.plot(figsize=(8,8), edgecolor='#444', facecolor='white', linewidth = 0.5);
  plt.xlim([141,143])
  plt.ylim([42,44])
  plt.scatter(locations1.Longitude, locations1.Latitude, marker='o', s=50, color="green", alpha=0.8, linewidths = 2)
  plt.scatter(locations2.Longitude, locations2.Latitude, marker='v', s=50, color="green", alpha=0.8, linewidths = 2)
  plt.scatter(locations3.Longitude, locations3.Latitude, marker='o', s=50, edgecolor="orange", alpha=0.8, linewidths = 2,facecolor="None")
  plt.scatter(locations4.Longitude, locations4.Latitude, marker='v', s=50, edgecolor="orange", alpha=0.8, linewidths = 2, facecolor="None")

これで実行すると次のように海がグレーになります。

スクリーンショット 2020-07-01 21.57.59

地図を表示・保存する

地図を自動的に保存するにはsavegifという命令を入れます。解像度を設定したり,ファイル形式をpngからpdfにしたり,地図の余白を削ることなどもできます。

plt.savefig("/Users/yearman/Desktop/amakusa-200dpi.png", format="png", dpi=200, bbox_inches='tight', pad_inches=0)

できないこと

本当は縮尺が分かりやすいようキロメートルを使ったスケールバーを付けたいのですが,どうやらgeopandasではできないようです(拡大率を計算すればできそうだけど,ちょっと面倒なのでやってません。てへ)。地図を作るにはこの他にcartopyというパッケージを使えばいいのですが,インストールに苦労することがあります。私の場合,最初でしくじってしまい2台のうち1台では入りませんでした。

この記事が気に入ったらサポートをしてみませんか?