foliumの機能拡張:プラグイン編

FOSS4G Advent Calendar 2021 | 10日目

この記事は1年以上前に書かれました。
内容が古くなっている可能性がありますのでご注意下さい。


foliumは地理空間データをPythonで可視化するためのオープンソースのライブラリです。JavaScriptのWeb地図ライブラリ・Leafletを利用するHTMLを出力することにより、インタラクティブなマップ表現を実現することができます。

foliumはpipコマンドでインストールします。

pip install folium

では早速、foliumを使ってみましょう。以下のプログラム(sample1.py)は、日本経緯度原点を中心とした地図を表示し、そこにマーカーを1つ表示します。マーカーをクリックすると「日本経緯度原点」とポップアップ表示します。
本記事中のプログラムはGitHubでも公開しています。

# -*- coding: utf-8 -*-

import webbrowser
import folium

def main():
    f_map = folium.Map(
        location = [35.658099, 139.741357],
        zoom_start = 12)

    folium.Marker(
        [35.658099, 139.741357],
        popup="日本経緯度原点",
    ).add_to(f_map)

    f_map.save('map.html')
    webbrowser.open('map.html')

if __name__ == "__main__":
    main()

はじめに、地図を作成します。

f_map = folium.Map(
    location = [35.658099, 139.741357],
    zoom_start = 12)

地図の中心とズームレベルを指定します。地図タイルを指定することもできます。省略するとOpenStreetMapが使用されます。
次に、マーカーを追加します。

folium.Marker(
    [35.658099, 139.741357],
    popup="日本経緯度原点",
).add_to(f_map)

最後に、HTMLファイルを保存し、Webブラウザで表示させます。

f_map.save('map.html')
webbrowser.open('map.html')

実行するとWebブラウザに以下のような地図が表示されます。

このように、foliumはとても簡単に使うことができますが、foliumと標準のfoliumプラグインだけでは、機能的に少し物足りなく思うことがあります。そのような場合、豊富なLeafletプラグインを活用してfoliumを機能拡張することができます。

前置きが長くなりましたが、今回はLeafletプラグインをラップするfoliumプラグインを独自に作成する方法を紹介します。
使用するプラグインは、地図の中央の緯度経度を表示するLeaflet.MapCenterCoordです。

作成したプラグイン(map_center_coord.py)の核心部分のプログラムは以下の通りです。Python用のテンプレートエンジンjinja2を使用して、LeafletプラグインMapCenterCoordのオブジェクトを作ってマップに追加するJavaScriptのコードを生成します。その際、Pythonプログラムから渡されたオプションをJSON形式に変換して渡しています。

_template = Template("""
    {% macro script(this, kwargs) %}
        L.control.mapCenterCoord(
            {{ this.options|tojson }}
        ).addTo({{ this._parent.get_name() }});
    {% endmacro %}
""")

Leafletプラグインを使用するには、JavaScriptとCSSのファイルを読み込ませる必要があります。そのために、default_jsとdefault_cssを定義します。

default_js = [
    ('MapCenterCoord_js',
     'L.Control.MapCenterCoord.js')
]
default_css = [
    ('MapCenterCoord_css',
     'L.Control.MapCenterCoord.css')
]

それぞれ、タプルのリスト形式です。タプルの1つめは任意の名前、2つめはファイルのURLです。このサンプルでは、Pythonプログラム、生成するHTMLファイル、Leafletプラグインのすべてが同じフォルダにあるという前提としているため、URLではなく単にファイル名になっています。ちなみに、folium本体や標準のfuliumプラグインでは、Leaflet本体やLeafletプラグインをCDNから読み込んでいます。

作成したmap_center_coordプラグインをインポートし、マップに追加するサンプル(一部抜粋)は以下の通りです(sample2.py)。

from map_center_coord import MapCenterCoord

def main():
    f_map = folium.Map(
        location = [35.658099, 139.741357],
        zoom_start = 12)

    folium.Marker(
        [35.658099, 139.741357],
        popup="日本経緯度原点",
    ).add_to(f_map)

    MapCenterCoord().add_to(f_map)

    f_map.save('map.html')
    webbrowser.open('map.html')

実行すると、左下に地図の中心座標が表示されます。

次に、同じLeafletプラグインを利用して、地図の中心の地質を表示するプラグインを作成します(map_center_geo.py)。地質の情報取得には、産業技術総合研究所地質調査総合センターの20万分の1日本シームレス地質図V2 Web APIを利用します。

JavaScriptプログラムを生成する部分のコードは以下のようになります。

_template = Template("""
    {% macro script(this, kwargs) %}
        $.ajaxSetup({async: false});
        var url = 'https://gbank.gsj.jp/seamless/v2/api/1.0/legend.json?point=';
        var geoFormatter = function (lat, lng) { 
            var geoinfo = '';
            var req = url +
                      {{ this._parent.get_name() }}.getCenter().lat + ',' + 
                      {{ this._parent.get_name() }}.getCenter().lng;

            $.getJSON(req, function(data) {
                if(0 != Object.keys(data).length) {
                    geoinfo = data.group_ja + ' : ' + data.lithology_ja ;
                }
            })
            return geoinfo;
        };
        var options = {{ this.options|tojson }};
        options.latLngFormatter = geoFormatter;
        L.control.mapCenterCoord(
            options
        ).addTo({{ this._parent.get_name() }});
    {% endmacro %}
""")

Leaflet.MapCenterCoordは、緯度経度の表示文字列を作成する関数をlatLngFormatterというオプションで指定することができます。そこで、緯度経度ではなく地質情報の文字列を返す関数geoFormatter()を作成してlatLngFormatterとして登録します。
geoFormatter()関数には引数で緯度経度が渡されますので、それを用いて産総研のAPIを呼び出し、JSON形式で返された結果から地質情報の文字列を作成して返します。

map_center_geoプラグインを利用するサンプル(sample3.py)を実行した結果は以下のようになります。

今回の例では、部品として比較的独立した機能を追加するため、foliumプラグインとして実装しました。機能拡張の内容によっては、folium(またはfoliumプラグイン)の既存のクラスを派生したクラスの中でLeafletプラグインを利用した方が良い場合もありますので、またの機会に紹介します。