PyLearn MAP 01: 地図プログラミングの基礎

pythonで地図を作成する方法についてまとめます。マッピング上のデータ可視化や、最適な施設配置問題を解くオペレーションズリサーチへと発展させることができます。まず、準備として以下をインストールします。

  • pip3 install geocoder
  • pip3 install folium
  • pip3 install geopy

0. 経度・緯度の取得方法

まずは指定した位置の経度、緯度を取得してみます。これには、arcgiaおよびlatlngを利用します。

In [9]:
import geocoder as ge

# 池袋駅
place_ikebukuro_check = ge.arcgis("池袋駅")
place_ikebukuro = place_ikebukuro_check.latlng
print(place_ikebukuro_check)
print(place_ikebukuro)

# 巣鴨駅
place_sugamo_check = ge.arcgis("巣鴨駅")
place_sugamo = place_sugamo_check.latlng
print(place_sugamo_check)
print(place_sugamo)
<[OK] Arcgis - Geocode [池袋駅]>
[35.73140118200007, 139.7082297280001]
<[OK] Arcgis - Geocode [巣鴨駅]>
[35.73397074700006, 139.73792884500006]

1. 距離の算出

経度、緯度を算出した後は、2点間の距離を求めてみます。距離の算出には、geopyのdistanceを利用します。

In [10]:
from geopy import distance
import numpy as np

dist = distance.distance(place_ikebukuro, place_sugamo).km
print("池袋駅から巣鴨駅までの距離は、", np.round(dist, 3), "[km]です。")
池袋駅から巣鴨駅までの距離は、 2.702 [km]です。

2.7kmくらいと出てきました。google mapの徒歩でも、2.7〜2.8kmくらいと出てくるので、まともな値が出てきています。ただし、道を辿った距離ではなく、ユークリッド距離で算出されるので注意してください。

2. 地図の描画

地図の描画にはfoliumのmap関数を利用します。

  • location: 描画したい中心地点の経度・緯度
  • zoom_start: ズーム(1: 小さい〜 20: 大きい)
  • tiles: マッピングのスタイル('openstreetmap'が基本だが、多数あり)

まずは、池袋中心を描画してみます。tilesはたくさんあるので、気に食わなければ自分でいい感じなのを探してください。地図を作った後は、その変数名を入れるだけでjupyter上に表示されます。

In [11]:
import folium as fl
map1 = fl.Map(location = place_ikebukuro,
                        zoom_start = 13,
                        tiles = 'openstreetmap')
map1
Out[11]:

続いて、指定位置にピンを挿してみます。

In [12]:
# 池袋駅
fl.Marker(location = place_ikebukuro, popup = "Ikebukuro station").add_to(map1)

# 巣鴨駅
fl.Marker(location = place_sugamo, popup = "sugamo station").add_to(map1)
map1
Out[12]:

popupに文字を指定すると、クリックした時に文字が表示されるようになります。また、popupにはhtmlタグを入れることができるので、クリック時に画像を表示させたり、リンクを入れたり、文字色を変えたりすることも可能です。

ただしあくまでクリックしたときにしか見えないので、これだと論文化するときなどに文字が見えなくなってしまいます。これに対処するには、iconにhtmlタグを入れることが必要です。

In [13]:
from folium.features import DivIcon

# マーカHTML生成関数
#    border-radiusを0にすると長方形、50%にすると楕円or円
#    ほかはhtmlのdivスタイルを参照
#    文字位置などはpaddingなどで調整
def MakeBox(in_text):
    out_text = '''
            <div style="text-align: center;
            font-size: 10pt;
            color: black;
            padding-top: 5px;
            height: 50px;
            width: 100px;
            background: white;
            border: 2px solid;
            border-radius: 30%;">'''
    out_text = out_text+ in_text + '</div>'
    return out_text

# 地図生成
map2 = fl.Map(location = place_ikebukuro,
                        zoom_start = 13,
                        tiles = 'openstreetmap')

# 池袋
fl.Marker(location=place_ikebukuro,
                  icon=DivIcon(html=MakeBox("Ikebukuro station"))).add_to(map2)

# 巣鴨
fl.Marker(location=place_sugamo,
                  icon=DivIcon(html=MakeBox("sugamo</br>station"))).add_to(map2)
map2
Out[13]:

続いて、2点間に線を引いてみます。

In [31]:
fl.PolyLine(locations=[place_ikebukuro, place_sugamo], color='blue').add_to(map2)
map2
Out[31]:

3. 練習

色々わかったところで、応用寄りのプログラムを書いてみます。

In [65]:
def MakeBox(in_text):
    out_text = '''
            <div style="text-align: center;
            font-size: 5pt;
            color: black;
            padding-top: 1px;
            height: 25px;
            width: 40px;
            background: white;
            border: 2px solid;
            border-radius: 30%;">'''
    out_text = out_text+ in_text + '</div>'
    return out_text

# 経度緯度取得
p1 = ge.arcgis("日本")
p1 = p1.latlng
p2 = ge.arcgis("ロシア")
p2 = p2.latlng
p3 = ge.arcgis("中国")
p3 = p3.latlng
p4 = ge.arcgis("オーストラリア")
p4 = p4.latlng

# マップ生成
map3 = fl.Map(location = p1, zoom_start = 1, tiles = 'openstreetmap')

# マーカ
fl.Marker(location=p1, icon=DivIcon(html=MakeBox("日本"))).add_to(map3)
fl.Marker(location=p2, icon=DivIcon(html=MakeBox("ロシア"))).add_to(map3)
fl.Marker(location=p3, icon=DivIcon(html=MakeBox("中国"))).add_to(map3)
fl.Marker(location=p4, icon=DivIcon(html=MakeBox("オーストラリア"))).add_to(map3)

# 日本と他の国を結ぶ
fl.PolyLine(locations=[p1, p2], color='blue').add_to(map3)
fl.PolyLine(locations=[p1, p3], color='blue').add_to(map3)
fl.PolyLine(locations=[p1, p4], color='blue').add_to(map3)

map3
Out[65]:

謎のネットワークができました。ところで、マーカを打つポイントが増えると、上のプログラムは中々大変になります。リスト構造を理解している人は、for文で一括して書けますね。以下は、上とまったく同じ機能を、for文で記載したものです。

In [66]:
Country_name = ["日本", "ロシア", "中国", "オーストラリア"]

# 経度・緯度取得
Place = []
for i in range(len(Country_name)):
    Place.append(ge.arcgis(Country_name[i]).latlng)

# マップ生成
map4 = fl.Map(location = p1, zoom_start = 1, tiles = 'openstreetmap')

# マーカ
for i in range(len(Country_name)):
    fl.Marker(location=Place[i], icon=DivIcon(html=MakeBox(Country_name[i]))).add_to(map4)

# 日本と他の国を結ぶ
for i in range(len(Country_name)):
    fl.PolyLine(locations=[Place[0], Place[i]], color='blue').add_to(map4)

map4
Out[66]:

なお、作成した地図を保存する場合は、下記の通りに記入します。pngやpdfでは保存できないので、作成した後は、htmlを開きブラウザの印刷機能でpdfにするか、スクリーンショットでpngにすると良いでしょう。

In [67]:
map4.save("maked_map.html")

foliumは今回解説した以外にも様々な応用方法があるので、各自勉強してみるといいでしょう。 Folium Documentation