はじめに
folium は Python から地図を簡単に生成できる便利なライブラリです。
基本的な使い方については以下の記事で紹介しています
Pythonで地図可視化 ― foliumの概要・インストール・基本的な使い方
m.save("map.html")
とすれば、1つの HTML ファイルが出力されて、そのままブラウザで開けば地図が表示されます。
このとき生成される HTML には、地図の構造だけでなく、JavaScript や CSS もすべて埋め込まれています。
そのため 「1ファイル完結」 という点では便利ですが、内容が増えてくると見通しが悪くなることもあります。
そこで出力を少し整理して、HTML / JavaScript / CSS を分けて保存してみます。
そうすることで次のようなメリットが得られます。
- ファイルの中身が役割ごとに分かれて理解しやすい
- Git などで差分管理しやすい
- Flask / Django / 静的サイトなど、他の環境・フレームワークにもそのまま展開できる
この記事では、folium の出力を
index.html
(構造) / app.js
(振る舞い) / style.css
(見た目)
の3つに分ける方法を紹介します。
folium 標準の出力形式
folium で地図を生成し、m.save("map.html")
を実行すると、1つの HTML ファイルが出力されます。
このファイルをブラウザで開くだけで地図が表示されるので、とても手軽です。
中身を見てみると、次のような要素がすべて 1ファイルにまとまっています。
-
HTML(構造部分)
地図を表示するための<div>
要素や、外部ライブラリ(Leaflet など)の読み込みリンク。 -
JavaScript(振る舞い)
Leaflet を使って地図を初期化したり、マーカーやポリゴンを描画するコードが<script>...</script>
に埋め込まれています。 -
CSS(見た目)
地図コンテナやコントロールのデザインを調整するスタイルが<style>...</style>
に埋め込まれています。
このように folium の出力は「構造・振る舞い・見た目が全部入りのHTML」という形式になっています。
すぐに動かせる点では非常に便利ですが、ファイルが大きくなると編集や管理がしにくくなります。
もう一歩工夫する:3ファイルに整理
folium が出力する HTML は 1ファイルにすべてがまとまっているため、
そのまま開けばすぐに地図が動作します。
この「1ファイル完結」という点は folium の大きな利点です。
一方で、内容が増えてくると次のような場面では扱いにくさを感じることがあります。
- 中身が長くなり、どこに何が書かれているのか見通しにくい
- Git などで差分を確認しづらい
そこで、HTML / JavaScript / CSS をそれぞれ別ファイルに分けて保存してみます。
分割後の構成イメージ
projects_root/
├─ templates/
│ └─ index.html ← HTML(構造部分)
└─ assets/
├─ app.js ← JavaScript(振る舞い)
└─ style.css ← CSS(見た目)
この構成は、前回の記事
【入門】StreamlitでHTML/JS/CSSを表示してみる
でも紹介したものと同じです。
そのため、folium で生成した地図も 同じ構成に整理しておけば、
Streamlit や Flask/Django など他のフレームワークや静的サイトにも自然に展開できます。
foliumの出力を自動分割する方法
folium が出力する HTML を手作業で編集して JavaScript や CSS を抜き出すのは大変です。
そこで Python スクリプトを用意して、自動的に分割して保存できるようにしてみます。
以下は標準ライブラリの re
を使った最小構成の実装例です。
from pathlib import Path
import re
import folium
PROJECT_ROOT = Path.cwd()
TEMPLATES_DIR = PROJECT_ROOT / "templates"
ASSETS_DIR = PROJECT_ROOT / "assets"
def save_folium_separated(m) -> None:
TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
ASSETS_DIR.mkdir(parents=True, exist_ok=True)
html = m.get_root().render()
# CSS を抽出
css_chunks = re.findall(r"<style.*?>(.*?)</style>", html, flags=re.S)
(ASSETS_DIR / "style.css").write_text("\n\n".join(css_chunks), encoding="utf-8")
html = re.sub(r"<style.*?>.*?</style>", "", html, flags=re.S)
# JS を抽出(srcなしの <script>)
js_chunks = re.findall(r"<script(?![^>]*src).*?>(.*?)</script>", html, flags=re.S)
(ASSETS_DIR / "app.js").write_text("\n;\n".join(j.strip() for j in js_chunks if j.strip()), encoding="utf-8")
html = re.sub(r"<script(?![^>]*src).*?>.*?</script>", "", html, flags=re.S)
# 外部参照を追加
html = re.sub(r"</head>", r' <link rel="stylesheet" href="/assets/style.css">\n</head>', html, count=1)
html = re.sub(r"</body>", r' <script src="/assets/app.js"></script>\n</body>', html, count=1)
(TEMPLATES_DIR / "index.html").write_text(html, encoding="utf-8")
if __name__ == "__main__":
# 1. folium の通常の使い方(地図を作成して保存)
m = folium.Map(location=[35.68, 139.76], zoom_start=12)
m.save("map_original.html") # ← この1行で通常どおりHTMLが出力されます
# 2. 自作関数を使って、同じ地図を index.html / app.js / style.css に分割保存
save_folium_separated(m)
print("分割完了: templates/index.html, assets/app.js, assets/style.css")
このスクリプトを実行すると、次のようなフォルダ・ファイルが自動で生成されます。
projects_root/
├─ map_original.html ← 通常の出力(比較用)
├─ templates/
│ └─ index.html ← HTML(構造)
└─ assets/
├─ app.js ← JavaScript(振る舞い)
└─ style.css ← CSS(見た目)
補足:BeautifulSoupを使う場合
もし HTML をより堅牢に扱いたい場合は、beautifulsoup4 を使う方法もあります。
pip install beautifulsoup4
BeautifulSoup を使うと次のようなメリットがあります。
-
<style>
や<script>
タグを属性付きでも安全に処理できる -
<head>
や<body>
が存在しない場合でも正しく追加できる - 将来的に
<meta>
タグを操作したり、特定の要素だけを削除するといった拡張にも対応しやすい
規模が大きくなったり、HTML の構造を細かく調整したい場合は、こちらを使うと安心です。
他の環境でもそのまま使える
分割した index.html
/ app.js
/ style.css
は、
Streamlit だけでなく Flask や Django、さらには静的サイトにもそのまま展開できます。
静的サイト(GitHub Pages / Vercel など)
もっともシンプルな使い方は、生成した index.html
と assets/
をそのまま配置する方法です。
site_root/
├─ index.html ← templates/index.html をコピー or 移動
└─ assets/
├─ app.js
└─ style.css
この形にしておけば、GitHub Pages や Vercel などのホスティングサービスにアップロードするだけで公開できます。
Flask で利用する場合
Flask では、templates/
と assets/
をそのまま利用できます。
index.html
は完全な静的HTMLのままで書き換え不要です。
from flask import Flask, send_from_directory
app = Flask(__name__, static_folder="assets", static_url_path="/assets")
@app.route("/")
def root():
return send_from_directory("templates", "index.html")
if __name__ == "__main__":
app.run(debug=True) # http://127.0.0.1:5000/
python app_flask.py
起動時に以下のような警告が表示されますが、これは開発サーバであることを知らせているだけなので無視して問題ありません。
WARNING: This is a development server. Do not use it in a production deployment.
Django で利用する場合
Django でも同じファイル構成を使えます。
pip install django
django-admin startproject mysite .
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"], # index.html を置く
"APP_DIRS": True,
},
]
STATIC_URL = "/assets/"
STATICFILES_DIRS = [BASE_DIR / "assets"]
from django.urls import path
from django.http import FileResponse
from django.conf import settings
from django.conf.urls.static import static
from pathlib import Path
def root(request):
return FileResponse(open(Path(settings.BASE_DIR, "templates/index.html"), "rb"))
urlpatterns = [
path("", root),
] + static(settings.STATIC_URL, document_root=Path(settings.BASE_DIR, "assets"))
python manage.py runserver
起動時に以下の警告が出ますが、これは開発用の注意なので無視して構いません。
WARNING: This is a development server. Do not use it in a production setting.
Use a production WSGI or ASGI server instead.
まとめ
folium は標準のままでも便利に地図を出力できます。
1ファイル完結で動くため、そのままでも十分実用的です。
ただし、内容が増えてくると見通しが悪くなったり、共同開発や他の環境に展開する際に扱いづらさを感じることがあります。
そこで今回は、出力を
-
index.html
(構造部分) -
app.js
(振る舞い) -
style.css
(見た目)
の3ファイルに分割して保存する方法を紹介しました。
このように分けておくと、
- ファイルの役割が明確になり、理解しやすい
- Git などで差分管理しやすい
- Streamlit / Flask / Django / 静的サイトなどにそのまま展開できる
といったメリットがあります。
もちろん folium の標準出力(1ファイルHTML)でも動作します。
ですが、「あとで他の環境に展開したい」「チームで開発したい」といった場合には、
最初から分割して整理しておくと後からの変更や展開が楽になります。
標準でも便利、でも少し工夫するともっと扱いやすくなる
── そんな工夫の一つとして参考にしていただければ幸いです。