要旨
folium を使うことで、Python で柔軟な地図可視化が出来る。しかもグリグリ動かせる
folium は日本語チュートリアル が少ないので、簡単なものを網羅的にまとめる
バージョンの更新で結構使い方が異なっているので、公式ドキュメント以外を参考すると上手くいかないことがある
概要
Folium は javascript ライブラリの leaflet を、Python で使えるようにしたモジュール。これを使うことで Python で柔軟な地図可視化の操作ができるようになります。
ただ、日本語版チュートリアル が少なく、またバージョンが 0.1 とか 0.2 の頃と現在とではマップにピンを置く関数名や使い方が大きく異なっています。余談だと、2017年時点では version0.5 だったのが、2018年に入ってから version0.6 となり、2018年11月に見たら version0.7 になっていました。
こういった事情で、公式ドキュメントを見る必要があるが、そうするとますます日本語ドキュメントの数が少なくなったり。そこで、簡単なものだけでも網羅的に使い方をまとめたいと思った次第です。
実装
Install
インストールは pip でも何でも。
$ pip install folium
$ pip install folium -U
Getting started
ここからのコードの実行は基本的には jupyter 上で行われていると想定してください。実行後、即表示されるのでオススメです。
そうでなければ、一度htmlで保存する必要があります。
import numpy as np
import pandas as pd
import folium
from folium import plugins
print ( "folium version is {}" .format(folium.__version__) )
folium version is 0.7.0 と出てきます。
使用するデータ
今回使用するデータは、Kaggle のリクルート コンペの hpg_store_info.csv です。緯度経度が入っていて、適当な大きさのデータだったためです。
Recruit Restaurant Visitor Forecasting | Kaggle
中身は、以下のような感じです。
hpg_store_id
hpg_genre_name
hpg_area_name
latitude
longitude
0
hpg_6622b62385aec8bf
Japanese style
Tōkyō-to Setagaya-ku Taishidō
35.643675
139.668221
1
hpg_e9e068dd49c5fa00
Japanese style
Tōkyō-to Setagaya-ku Taishidō
35.643675
139.668221
2
hpg_2976f7acb4b3a3bc
Japanese style
Tōkyō-to Setagaya-ku Taishidō
35.643675
139.668221
適当な1つの緯度経度地点が欲しいので、以下のように定義しておきます。
LAT = data.latitude.iloc[0 ]
LNG = data.longitude.iloc[0 ]
地図を表示する
まずは、地図を表示させます。
そのためには、以下のようにするだけです。
m = folium.Map(location=[LAT, LNG])
m
map_quickstart
location 引数は、表示する地図の中心点となります。
HTMLで保存
ちなみに、HTML 保存する場合も以下のように簡単にできます。
filename = 'output/map.html'
m.save(filename)
Tile
Tile とは地図のベースとなるものです。
デフォルトは Open Street Map というオープンライセンスのマップタイルが利用されます。
Tile については、説明するより見比べると理解しやすいと思います。
stamen-toner
m = folium.Map(location=[LAT, LNG], tiles="Stamen Toner" , zoom_start=13 )
m
map_tile_stamen-toner
stamen-terrain
m = folium.Map(location=[LAT, LNG], zoom_start=12 , tiles="Stamen Terrain" )
m
map_tile_stamen-terrain
tile 引数で指定できます。かっこいい感じになりますね。
ちなみに、zoom_start 引数で最初のズームレベルを指定できます。こちらは指定しないと、「いい感じ」にしてくれるようです。ズームレベルは、1~20の整数値で指定し、1が最もズームアウトした状態、20が最もズームインした状態です。
あと、普段使いませんが、Mapbox も API Key を渡して使用することができるようです。
Markers
Simple Marker
最もシンプルなMarker。
ホバーやクリックで情報を表示する設定もできます。
m = folium.Map(location=[LAT, LNG])
folium.Marker(
location=[data.latitude.iloc[100 ], data.longitude.iloc[100 ]],
popup="This is Simple Marker" ,
).add_to(m)
m
引数 popup で、クリックしたときに表示する情報を追加できます。
folium.Marker() でマーカーを作るだけでは地図に表示されず、これを add_to(m) することで、地図に情報を追加できます。このあたりの書き方が最初慣れず使いにくく感じました。
map_simple-marker
Marker with icon
工夫したい場合は、Marker の色を変えたり、アイコンを設定できたりできます。(Leaflet.R でいうところの AwesomeMarker です)
m = folium.Map(location=[LAT, LNG])
folium.Marker(
location=[data.latitude.iloc[100 ], data.longitude.iloc[100 ]],
popup=data.hpg_genre_name.iloc[100 ],
tooltip="<i>" + data.hpg_area_name.iloc[100 ] + "</i>" ,
icon=folium.Icon(color="red" , icon="info-sign" ),
).add_to(m)
m
map_marker-icon
Circle and Circle Marker
ピンのマーカーではなく、円をマーカー代わりにできます。
m = folium.Map(
location=[data.latitude.iloc[0 ], data.longitude.iloc[0 ]],
tiles="Stamen Toner" ,
zoom_start=10 ,
)
folium.Circle(
radius=500 ,
location=[data.latitude.iloc[100 ], data.longitude.iloc[100 ]],
popup="<b>" + data.hpg_genre_name.iloc[100 ] + "</b>" ,
color="crimson" ,
fill=False ,
).add_to(m)
folium.CircleMarker(
location=[data.latitude.iloc[200 ], data.longitude.iloc[200 ]],
radius=50 ,
popup=data.hpg_genre_name.iloc[200 ],
color="#3186cc" ,
fill=True ,
fill_color="#3186cc" ,
).add_to(m)
m
map_circle
その他:lat/lng popovers
クリックした点の緯度経度をポップアップ表示します。
m = folium.Map(location=[46.1991 , -122.1889 ], tiles="Stamen Terrain" , zoom_start=13 )
m = m.add_child(folium.LatLngPopup())
m
map_popover
その他:click-for-marker
クリックした点にMarkerを生成します。
m = folium.Map(location=[46.8527 , -121.7649 ], tiles="Stamen Terrain" , zoom_start=13 )
folium.Marker(location=[46.8354 , -121.7325 ], popup="Camp Muir" ).add_to(m)
m = m.add_child(folium.ClickForMarker(popup="Waypoint" ))
m
map_click-for-marker
MarkerCluster
Leaflet.R と異なり、Folium.plugins.MarkerCluster で使える点に注意。ドキュメント探すときは、plugins の項目内にあるので探します。
folium.plugins.FeatureGroupSubGroup でクラスタ ーをグループ分けできる
今までは、1点のみをマーカーを貼っていたが、複数の地点をマークしたい場合は、numpy の array 形式で渡してあげます。
locations = np.array([data.latitude, data.longitude]).T
m = folium.Map([LAT, LNG], zoom_start=4 )
plugins.MarkerCluster(locations).add_to(m)
m
map_marker-cluster
FeatureGroup
Creation, International cuisine, Japanese style の3つのジャンルがデータ中で最も多いジャンル名だったため、これらの地図を作成することにする。
def create_feature_group (genre):
data_genre = data[data.hpg_genre_name == genre]
arr_latlon = np.array([data_genre.latitude, data_genre.longitude]).T
feature_group = folium.FeatureGroup(name=genre)
plugins.MarkerCluster(arr_latlon).add_to(feature_group)
return feature_group
m = folium.Map(location=[LAT, LNG], zoom_start=12 )
genres = ["Creation" , "International cuisine" , "Japanese style" ]
for genre in genres:
fg = create_feature_group(genre)
fg.add_to(m)
folium.LayerControl().add_to(m)
m
map_feature-group
DataFrame 内の全要素をプロットする
m = folium.Map(location=[LAT, LNG], zoom_start=10 )
data_nodup = data.drop_duplicates(subset=["latitude" , "longitude" ])
for lat, lon in zip (data_nodup.latitude, data_nodup.longitude):
folium.Marker(
location=[lat, lon],
popup="/" .join([str (lat), str (lon)]),
tooltip=str (lat) + "_" + str (lon),
).add_to(m)
m
map_df
その他
感覚的には、ポップアップをつけたMarkerを5000以上プロットしようとすると、固まるイメージ。 Vincent/Vega and Altair/VegaLite Markers を使用する際も、渡す地図情報のサイズを重すぎないようにすることが大事かも。 (具体的にどれくらいメモリを使っているのか、どのように処理が進んでいるのかは調べていない)
LeafletRでは、マップオブジェクトにパイプ( %>%
)で付加情報を足していって地図を生成する Folium の場合だと、Mapオブジェクトをはじめに定義して、その後各種要素を作って add_to(m)
していく
ただ、クラスタ ーに関しては、LeafletR ではaddMarkerの引数でオプションを指定したが、 Folium ではMarkerCluster オブジェクトを別で作成して add_to(m)
する必要がある
LeafletR は、kazutan さんのサイト を参照するのがいいと思います
github.com