📍

#2 Pythonで簡単にマップ制作 ~Folium応用編~

2024/12/16に公開

Foliumって他に何ができるの?

Pythonで簡単にマップ制作 ~Folium基本編~で紹介した内容の他にもマップをもっとカスタマイズすることができます。

やれることの一例

  • マップに複数の層を設定することができる
    • 例:年度ごとにデータを分けて表示したい場合に2023年の情報を掲載する層、2024年の情報を掲載する層、、、のように分けて表示することができる。
  • マップに指定した大きさやサイズの円などの図形を書く
  • マップに刺すピンの色やピンのデザインを変える
  • GeoJSON形式データを読み込んでマップに表示することができる
  • ピンをクリックしたときの挙動をHTMLで別途記述する
  • 作成したマップのどこかの地点にクリックしてマップにピンを立てることができる(GEO GUESSR)てきな物もできるかも?
  • マップを既定のマップではなく衛星画像などに変更する

実際に使ってみる

今回は上記の例の中から以下の3つを実装する

  • マップに複数の層を設定することができる
  • マップに指定した大きさやサイズの円などの図形を書く
  • マップに刺すピンの色やピンのデザインを変える

仕様

  • 4個のデータを2つの層にわけて表示
  • データの住所から緯度経度を算出
  • 算出した座標にピンを立てる。ピンのデザインは層で変える
  • ピンを中心として任意の大きさの円をペイントする
  • ピンをクリックしたら情報の詳細を吹き出しで表示

完成イメージ


ソースコード

ソースコード
import folium
import requests


# 住所や名称から緯度経度を取得する
def geo_coding(address):
    url = f"https://msearch.gsi.go.jp/address-search/AddressSearch?q={address}"

    res = requests.get(
        url=url
    )
    data = res.json()

    return data[0]['geometry']['coordinates']


# 掲載する情報一覧
data_list = [
    {
        "発生場所": "各務原市那加西那加町",
        "詳細": "帰宅中の女子学生に対し、男が車で後をつける事案が発生しました。"
    },
    {
        "発生場所": "恵那市長島町正家",
        "詳細": "帰宅中の児童らに対し、男が手を振ったり、女がスマートフォンを向ける事案が発生しました。"
    },
    {
        "発生場所": "岐阜市中1丁目",
        "詳細": "下校中の児童らに対し、男が携帯電話機を向ける事案が発生しました。"
    },
    {
        "発生場所": "岐阜市中西郷",
        "詳細": "下校中の児童らに対し、男が携帯電話機のカメラを向ける事案が発生しました。"
    },
]

map_object = folium.Map(
    location=[35.4096218, 136.754722],
    zoom_start=12
)
# 円の大きさ指定
radius = 50

layer01 = folium.FeatureGroup(
    name="レイヤー1",
    show=False
).add_to(map_object)

data01 = data_list[0]
location = geo_coding(data01["発生場所"])
# ピンを刺す
folium.Marker(
    location=[location[1], location[0]],
    tooltip=data01["詳細"][0:5] + "・・・",
    popup=folium.Popup(data01["詳細"], parse_html=True, max_width=100),
    icon=folium.Icon(color="green")
).add_to(layer01)

# 円を描く
folium.CircleMarker(
    location=[location[1], location[0]],
    radius=radius,
    # 色
    color="cornflowerblue",
    #円の枠線
    stroke=False,
    # 塗りつぶし
    fill=True,
    # 塗りつぶしの不透明度
    fill_opacity=0.6,
).add_to(layer01)



data02 = data_list[1]
location = geo_coding(data02["発生場所"])
folium.Marker(
    location=[location[1], location[0]],
    tooltip=data02["詳細"][0:5] + "・・・",
    # Falseでhtml使える
    popup=folium.Popup(data02["詳細"], parse_html=True, max_width=100),
    icon=folium.Icon(color="green")
).add_to(layer01)

folium.CircleMarker(
    location=[location[1], location[0]],
    radius=radius,
    color="cornflowerblue",
    stroke=False,
    fill=True,
    fill_opacity=0.6,
).add_to(layer01)

layer02 = folium.FeatureGroup(
    name="レイヤー2",
    show=False
).add_to(map_object)

data03 = data_list[2]
location = geo_coding(data03["発生場所"])
folium.Marker(
    location=[location[1], location[0]],
    tooltip=data03["詳細"][0:5] + "・・・",
    popup=folium.Popup(data03["詳細"], parse_html=True, max_width=100),
    icon=folium.Icon(color="blue")
).add_to(layer02)

folium.CircleMarker(
    location=[location[1], location[0]],
    radius=radius,
    color="green",
    stroke=False,
    fill=True,
    fill_opacity=0.6,
).add_to(layer02)

data04 = data_list[3]
location = geo_coding(data04["発生場所"])
folium.Marker(
    location=[location[1], location[0]],
    tooltip=data04["詳細"][0:5] + "・・・",
    popup=folium.Popup(data04["詳細"], parse_html=True, max_width=100),
    icon=folium.Icon(color="blue")
).add_to(layer02)

folium.CircleMarker(
    location=[location[1], location[0]],
    radius=radius,
    color="green",
    stroke=False,
    fill=True,
    fill_opacity=0.6,
).add_to(layer02)

folium.LayerControl().add_to(map_object)

# 生成したマップを保存する
map_object.save("sample.html")

コード解説

  1. 必要なライブラリのインポート
    folium:マップの作成に使用するライブラリ
    requests:APIを使う(通信するため)のライブラリ
import folium
import requests
  1. 住所から緯度経度を算出する関数の準備
    関数の説明は以前の記事でしているため省略
def geo_coding(address):
    url = f"https://msearch.gsi.go.jp/address-search/AddressSearch?q={address}"

    res = requests.get(
        url=url
    )
    data = res.json()

    return data[0]['geometry']['coordinates']
  1. 掲載する情報をリストの中に辞書型を用いて用意
    data_listのインデックス0と1はレイヤ1に2~3はレイヤ2に表示させる
data_list = [
    {
        "発生場所": "各務原市那加西那加町",
        "詳細": "帰宅中の女子学生に対し、男が車で後をつける事案が発生しました。"
    },
    {
        "発生場所": "恵那市長島町正家",
        "詳細": "帰宅中の児童らに対し、男が手を振ったり、女がスマートフォンを向ける事案が発生しました。"
    },
    {
        "発生場所": "岐阜市中1丁目",
        "詳細": "下校中の児童らに対し、男が携帯電話機を向ける事案が発生しました。"
    },
    {
        "発生場所": "岐阜市中西郷",
        "詳細": "下校中の児童らに対し、男が携帯電話機のカメラを向ける事案が発生しました。"
    },
  1. マップオブジェクトの生成
    マップを表示させたときの中心座標をlocationに、初期の表示倍率をzoom_startに指定する。
    radiusで描写する円の大きさを指定する
map_object = folium.Map(
    location=[35.4096218, 136.754722],
    zoom_start=12
)
radius = 50
  1. 1つ目の層(レイヤー1)を生成
    .FeatureGroupでレイヤを生成する。
    nameでレイヤの表示名、showでマップを開いたときに最初から表示させるかを指定する。
layer01 = folium.FeatureGroup(
    name="レイヤー1",
    show=False
).add_to(map_object)
  1. レイヤー1にピンを刺す
    .Markerでマップにピンを指すことができます。
    location ピンの座標
    tooltip ピンにカーソルを合わせたときに表示させる文字を指定。今回はポップアップメッセージの5文字目までを表示させるようにしています。
    popup ピンをクリックしたときに表示させる文字。さらに表示方法も別途指定が可能でparse_htmlをFalseにすることでポップアップメッセージをHTMLで記述することができます。max_widthでポップアップメッセージの最大サイズを指定できます。
    icon ピンのデザインを指定する。ピンの種類や色は別途指定することが可能
folium.Marker(
    location=[location[1], location[0]],
    tooltip=data01["詳細"][0:5] + "・・・",
    popup=folium.Popup(data01["詳細"], parse_html=True, max_width=100),
    icon=folium.Icon(color="green")
).add_to(layer01)
  1. レイヤー1に円を描く
    CircleMarkerでマップに円を描くことができます。
    基本的なパラメータはピンを刺すのと同じですが今回使用したものは以下のものがあります。
    radius:円の大きさ
    stroke:円の枠線の有無を指定(今回は無し)
    fill:円の塗りつぶしの有無
    fill_opacity:円の不透明度の指定。opacityで円の枠線の不透明度の設定も可能

この7~8の作業をレイヤ2でも同様に行います

folium.CircleMarker(
    location=[location[1], location[0]],
    radius=radius,
    color="cornflowerblue",
    stroke=False,
    fill=True,
    fill_opacity=0.6,
).add_to(layer01)
  1. レイヤーをまとめてマップオブジェクトに追加する
    これを忘れると一生マップにレイヤが表示されません(経験談)
folium.LayerControl().add_to(map_object)
  1. 生成したマップを保存する
map_object.save("sample.html")

最後に

もうできる事多すぎて正直意味わからん!!!
もう少し公式のドキュメントを読み込んでいつか理解を深めたい

Discussion