foliumは、Pythonのコードから地図ライブラリLeafletのHTMLを生成し、拡大・縮小やクリックができる地図を作るライブラリです。2026年10月時点の安定版は0.20.0で、7月には1.0.0の候補版(rc1)が公開されました。この記事では、インストールとマーカー表示、GeoJSONの塗り分け、2026年9月に始まったCartoDBタイルのAPIキー必須化への対処、1万点を描いたときのHTMLサイズ、FlaskやStreamlitへの埋め込みまでを、そのまま動かせるコード付きで整理します。
まとめ:foliumは0.20系で組み、タイルの規約とデータ量で用途を決める
foliumで地図を出すまでの手順は短く、pip install foliumのあとに数行書けばHTMLファイルが手に入ります。つまずくのはコードではなく、背景に敷くタイルの扱いです。2026年9月4日のxyzservices 2026.9.0からCartoDBのタイルはAPIキーが必要になり、2026年初めまでの解説記事にあるtiles="CartoDB positron"の書き方は警告が出る状態です。
本番の判断は2つに絞れます。社内向けの分析レポートや数千点規模の可視化ならfoliumで十分です。地図上で編集や権限管理が要る、あるいは数十万点を更新し続ける用途なら、foliumを入口にとどめ、地図を扱う業務システムとして設計し直してください。1.0.0rc1は互換性を壊す変更が少ない一方、本番は0.20.0に固定しておくのが無難です。
foliumの仕組みと0.20系の環境構築からHTML出力までの作業手順
まず、foliumが何を出力しているのかを押さえてから手を動かします。
LeafletのJavaScriptを生成するfoliumの仕組みと同梱版1.9.3
foliumは地図そのものを描画しません。Pythonで組み立てた地図オブジェクトを、Jinja2のテンプレートでLeafletを呼び出すHTMLとJavaScriptに変換するだけです。描画はブラウザ側のLeafletが担い、foliumの0.20.0と1.0.0rc1はどちらもCDNから[email protected]を読み込みます。
この構造から、2つの性質が出てきます。出力したHTMLはサーバーなしで開ける一方、表示にはCDNと背景タイルへのネット接続が必要です。そして、Pythonで扱ったデータはHTMLの中にそのまま埋め込まれるため、データ量がそのままファイルサイズになります。後者は1万点の章で数字を示します。
pip installでのfolium導入とPython 3.9以上という動作要件
PyPIのfoliumページによると、0.20.0は2025年6月16日の公開で、動作要件はPython 3.9以上、ライセンスはMITです。依存パッケージはbranca、jinja2、numpy、requests、xyzservicesの5つで、pipが一緒に入れます。
# 安定版を固定して入れる(pipは候補版を既定で選ばない)
pip install folium==0.20.0
# 1.0.0rc1を試す場合だけ --pre を付ける
pip install --pre folium
pipは--preを付けない限り候補版を選ばないため、pip install foliumだけでも0.20.0が入ります。ただし、要件ファイルにはバージョンを固定して書いておくと、1.0.0の正式版が出た日にCIの結果が変わる事態を防げます。
東京駅にマーカーと半径500mの円を置いてHTMLへ保存するコード
最初の地図は、中心座標とズームを決めてマーカーを1つ置くだけです。座標は[緯度, 経度]の順で渡します。GeoJSONは経度が先なので、取り違えると地図の外へ点が飛びます。
import folium
# 中心座標は [緯度, 経度] の順。zoom_start の既定値は10
m = folium.Map(location=[35.681236, 139.767125], zoom_start=15)
folium.Marker(
location=[35.681236, 139.767125],
popup="東京駅",
tooltip="クリックで詳細",
icon=folium.Icon(color="red", icon="info-sign"),
).add_to(m)
# radius はメートル単位
folium.Circle([35.681236, 139.767125], radius=500, color="blue", fill=True).add_to(m)
m.save("map_basic.html")
Jupyter Notebookなら、最後の行をmにするとセルの下に地図が出ます。背景を指定しない場合はOpenStreetMapの標準タイルが使われます。
GeoJSONの塗り分けとGeoPandas連携で業務データを地図に重ねる手順
点だけでなく、エリアごとの数値を色で見せるのがfoliumの主な使い道です。
style_functionで売上に応じて色を変えるGeoJsonの塗り分け例
GeoJSONをfolium.GeoJsonに渡し、style_functionで属性に応じた色を返します。ツールチップにはGeoJsonTooltipで属性名と表示名を並べます。
import folium
geojson = {
"type": "FeatureCollection",
"features": [
{"type": "Feature", "properties": {"name": "エリアA", "sales": 120},
"geometry": {"type": "Polygon", "coordinates": [[[139.70, 35.66], [139.73, 35.66],
[139.73, 35.69], [139.70, 35.69], [139.70, 35.66]]]}},
{"type": "Feature", "properties": {"name": "エリアB", "sales": 45},
"geometry": {"type": "Polygon", "coordinates": [[[139.73, 35.66], [139.76, 35.66],
[139.76, 35.69], [139.73, 35.69], [139.73, 35.66]]]}},
],
}
def style(feature):
sales = feature["properties"]["sales"]
return {"fillColor": "#d7301f" if sales >= 100 else "#fdcc8a",
"color": "#555", "weight": 1, "fillOpacity": 0.6}
m = folium.Map(location=[35.675, 139.73], zoom_start=13)
folium.GeoJson(
geojson,
name="売上エリア",
style_function=style,
tooltip=folium.GeoJsonTooltip(fields=["name", "sales"], aliases=["エリア", "売上(万円)"]),
).add_to(m)
folium.LayerControl().add_to(m)
m.save("map_geojson.html")
GeoJSONの座標は[経度, 緯度]の順です。前章のマーカーと逆なので、自前でポリゴンを組むときはここを最初に疑ってください。
GeoDataFrameを渡すとEPSG:4326へ自動変換される仕様と注意点
公式のGeoPandas連携ガイドのとおり、GeoPandasで読んだGeoDataFrameはfolium.GeoJsonにそのまま渡せます。0.20.0のソースを見ると、__geo_interface__とto_crsを持つデータはEPSG:4326(緯度経度)へ変換してからJSON化する仕様です。平面直角座標系で配られる自治体のデータも、再投影を書かずに重なります。
注意したいのは、CRSが未設定のデータです。変換元が分からないため正しく再投影されません。gdf.crsがNoneなら、元データの仕様書で座標系を確かめてset_crsで付けてから渡します。
ChoroplethとGeoJsonのどちらで塗り分けるかを決める判断基準
folium.Choroplethは、pandasのデータフレームとGeoJSONをキーで結合し、凡例付きの段階色を自動で作ります。統計値をそのまま色に落とすだけなら、こちらが短く書けます。
一方、しきい値を業務ルールで決めたい、ツールチップに複数の項目を出したい、クリック時の挙動を変えたいといった要件があるなら、前述のGeoJsonとstyle_functionで組むほうが後から手を入れやすくなります。社内で使い続ける地図は、最初からGeoJsonで書くと決めておけば迷いません。
背景タイルの選び方と2026年9月のCartoDB APIキー必須化への対処
foliumの地図は、背景のタイルを外部のサーバーから読み込みます。ここは利用規約の問題でもあります。
xyzservices 2026.9.0でCartoDBにAPIキーが必要になった変更
foliumはtiles="CartoDB positron"のような名前を、依存パッケージxyzservicesのタイル定義から引いています。2026年9月4日公開の2026.9.0で、CartoDBの定義はURLにkey={apikey}を含む形へ変わりました。同日の2026.9.1で例外は警告に緩められたものの、キーを渡さないと「CartoDB tiles now require an API key.」という警告が出ます。公式ガイドのサンプルにもtiles="cartodbpositron"が残っているため、写して使う場合は書き換えが要ります。
キーはCARTOのBasemaps APIキー取得ページで発行し、環境変数から渡します。
import os
import folium
import xyzservices.providers as xyz
# キーはコードに直書きせず環境変数から読む
positron = xyz.CartoDB.Positron(apikey=os.environ["CARTO_API_KEY"])
m = folium.Map(location=[35.681236, 139.767125], zoom_start=13, tiles=positron)
m.save("map_carto.html")
出力したHTMLにはキーがURLとして残ります。社外に配るファイルにキーが入る点は、発行元の利用条件と合わせて判断してください。
OpenStreetMap標準タイルを商用サービスで使うときの利用規約
既定のOpenStreetMapタイルは無料で使えますが、運営団体のTile Usage Policyには条件があります。地図上に著作権表示を出すこと、表示していない範囲のタイルを先読み・一括取得しないこと、アプリ名が分かるUser-Agentを送ること、各タイルを最低7日キャッシュすることです。商用サービスや寄付を募るサービスは、提供がいつ止められてもおかしくないと明記されています。
社内の分析レポートなら標準タイルで構いません。顧客に提供するサービスに組み込むなら、有償の地図サービスを選ぶ前提で費用を見積もってください。AWS上で地図・検索・ルート計算まで揃える選択肢はAmazon Location Serviceとは?地図・検索・ルート・追跡の機能と料金・Google Mapsとの違い【2026】で比較しています。
国内の業務地図で地理院タイルを背景に使う設定と出典表記の書き方
国内だけを扱う地図なら、国土地理院の地理院タイルも候補になります。地理院タイル一覧によると、Webサイトやソフトウェアでリアルタイムに表示する用途は、出典を明示すれば申請は要りません。表記は「国土地理院」や「地理院タイル」とし、一覧ページへのリンクを付けます。
import folium
m = folium.Map(location=[35.681236, 139.767125], zoom_start=15, tiles=None)
folium.TileLayer(
tiles="https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png",
attr='<a href="https://maps.gsi.go.jp/development/ichiran.html">地理院タイル</a>',
name="地理院タイル(淡色)",
max_zoom=18,
).add_to(m)
m.save("map_gsi.html")
URLで外部のタイルを指定するときは、attrを空にできません。foliumは出典がないとエラーにします。淡色地図(pale)は塗り分けの色が背景に埋もれにくく、GeoJSONを重ねる用途と相性がよい地図です。
1万点の位置データを描くときのHTMLサイズ比較とプラグインの選び方
点の数が増えると、foliumのHTMLは急に重くなります。描き方でどれだけ差が出るかを数字で示します。
1万点でMarkerClusterとFastMarkerClusterの出力サイズを比較
東京周辺に乱数で置いた1万点を、3通りの方法で描いてHTMLのサイズを比べました(folium 0.20.0、2026年10月3日計測)。1.0.0rc1でも差は数バイトでした。
| 描き方 | HTMLサイズ | 仕組み |
|---|---|---|
| CircleMarkerを1万個 | 約4.97MB | 点ごとにJavaScriptの記述を生成 |
| MarkerCluster+Markerを1万個 | 約2.29MB | 点ごとの記述+ズームでまとめる |
| FastMarkerCluster | 約0.41MB | 座標を配列で渡しブラウザ側で生成 |
差を生むのは、点ごとにJavaScriptを書き出すかどうかです。FastMarkerClusterは座標の配列だけを埋め込み、マーカーはブラウザ側で作ります。ポップアップを点ごとに細かく変える必要がなければ、数千点を超えた時点でこちらへ切り替えてください。
import random
import folium
from folium.plugins import FastMarkerCluster, HeatMap
random.seed(0)
points = [[35.6 + random.random() * 0.2, 139.6 + random.random() * 0.3] for _ in range(10000)]
m = folium.Map(location=[35.7, 139.75], zoom_start=11, prefer_canvas=True)
FastMarkerCluster(points, name="店舗(クラスタ)").add_to(m)
HeatMap(points, name="密度", radius=12, show=False).add_to(m)
folium.LayerControl().add_to(m)
m.save("map_many.html")
密度を伝えたい場面でHeatMapへ切り替える判断とLayerControl
1つずつの地点より「どこに集中しているか」を伝えたいなら、マーカーよりHeatMapが向いています。上のコードではshow=Falseで最初は非表示にし、右上のLayerControlからクラスタ表示と切り替えられるようにしました。
サイズの上限を見積もる目安として、配布するHTMLは数MBに収めると、メール添付や社内ポータルへのアップロードで止まりにくくなります。10万点を超えるなら、HTMLにデータを埋め込む方式そのものが合っていません。判断章で扱う、タイルやAPIからデータを読み込む作りへ移る合図です。
FlaskとStreamlitにfoliumの地図を埋め込んでWebアプリにする方法
ファイルとして配るだけでなく、Webアプリの画面に地図を出す方法も2通り押さえておきます。
Flaskでget_root().render()を返す全画面表示とiframe埋め込み
公式のFlaskガイドは、全画面で返す、iframeで埋め込む、ヘッダ・本体・スクリプトに分けて既存テンプレートへ差し込む、の3通りを示しています。最小構成は次のとおりです。
import folium
from flask import Flask
app = Flask(__name__)
@app.route("/")
def fullscreen():
m = folium.Map(location=[35.681236, 139.767125], zoom_start=13)
return m.get_root().render()
@app.route("/iframe")
def iframe():
m = folium.Map(location=[35.681236, 139.767125], zoom_start=13)
m.get_root().width = "800px"
m.get_root().height = "600px"
return m.get_root()._repr_html_()
既存の画面にヘッダやメニューがあるなら、iframe方式がCSSの衝突を起こしにくい選択です。ポップアップに利用者の入力値を出す場合は、HTMLとして解釈されないようエスケープしてから渡してください。
streamlit-foliumのst_foliumでクリック地点をPythonに返す例
Streamlitで使うなら、streamlit-foliumのst_foliumが定番です。表示するだけでなく、クリックした地点や表示範囲を辞書でPython側へ返します。Streamlit自体の書き方はStreamlitとは?できること・使い方・料金を実例コードで解説【Python】を参照してください。
import folium
import streamlit as st
from streamlit_folium import st_folium
m = folium.Map(location=[35.681236, 139.767125], zoom_start=13)
folium.Marker([35.681236, 139.767125], tooltip="東京駅").add_to(m)
# returned_objects を絞ると、地図を動かすたびの再実行を減らせる
data = st_folium(m, height=500, use_container_width=True,
returned_objects=["last_clicked", "last_object_clicked"])
st.write(data)
2026年8月3日公開の0.27.4では、returned_objectsを指定しないと地図のあらゆる操作でアプリ全体が再実行されます。クリック地点だけ使うなら、上のように受け取る項目を絞ってください。
1.0.0rc1の変更点と0.20系から上げる前に確認する互換性の論点
1.0.0は正式版の前段階にあります。何が変わり、いつ上げるかを整理します。
2026年7月26日公開の1.0.0rc1で入ったLayerGroupと3D地球儀
GitHubのリリースノート(v1.0.0-pre.1)に並ぶ変更は、大半が不具合修正とドキュメント追加です。機能の追加として目立つのは、LeafletのLayerGroupへの対応、WebGL Earthによる3D地球儀プラグイン、マーカーのアイコン背景への16進カラーコード指定の3つです。
修正のほうでは、整数型のnumpy配列を重みに渡したHeatMapの異常終了、ポップアップ文字列のバックスラッシュの扱い、幅や高さを%で指定したときの表示崩れが直っています。公式ドキュメントの「latest」はすでに1.0.0rc1の内容に切り替わっているため、0.20.0で動かす場合は引数の記述に差がないか確かめてから写してください。
save()を2回呼ぶとHTMLが重複する0.20系の不具合と回避策
1.0.0rc1で直った不具合のうち、実務で踏みやすいのがsave()の重複です。同じ地図オブジェクトでsave()を2回呼ぶと、0.20.0では2回目のHTMLに記述が重なり、手元の計測では3,524バイトから3,639バイトへ増えました。1.0.0rc1では2回とも3,527バイトで同じです。
0.20.0のまま運用するなら、地図を作る処理を関数にまとめ、保存のたびに新しいfolium.Mapを作る形にしておけば影響を受けません。本番の更新は1.0.0の正式版を待ち、それまでは要件ファイルで0.20.0に固定する方針を勧めます。
foliumで足りる地図の用途と業務システムとして作り直すべき条件
最後に、foliumをどこまで使い、どこから別の作りにするかの線を引きます。
分析レポートや社内共有ならfoliumで済ませてよい2つの条件
次の2つを満たすなら、foliumで完結させて問題ありません。1つ目は、地図が「見るだけ」で、利用者が地図上で点を追加・編集しないこと。2つ目は、点の数が数千から数万で、データの更新が日次程度の作り直しで間に合うことです。
この範囲なら、Pythonで集計した結果をHTMLにして配るか、Streamlitの画面に載せるだけで用が足ります。地図の専用サーバーやデータベースを用意するより、立ち上げも保守も安く済みます。
編集・権限・数十万点の更新が要るならfoliumを採用しない判断
反対に、次のいずれかに当たるならfoliumを本体に据えません。現場の担当者が地図上で設備や顧客の位置を登録・修正する。部署や取引先ごとに見える範囲を変える。数十万点以上の位置情報を分単位で更新する。どれも、データを都度HTMLに焼き込むfoliumの方式とは合わない要件です。
この場合は、位置情報をPostGISなどのデータベースで管理し、地図はLeafletやMapLibreをフロントエンドから直接使う構成にします。GISを業務システムへ組み込むときの考え方はGISとは?地理情報システムの仕組み・種類と業務システムへの組み込み判断で整理しました。地図を中心にした業務システムを要件定義から作る場合は、一創のフルスクラッチ開発でPythonやGoでの設計・開発を承っています。foliumで作った試作の地図を、そのまま要件のたたき台として持ち込んでいただく形でも構いません。
よくある質問
foliumを使い始めるときに挙がりやすい質問をまとめます。
foliumとLeafletの違いは何ですか?
LeafletはブラウザでJavaScriptから使う地図ライブラリで、foliumはそのLeafletを呼び出すHTMLをPythonから生成するライブラリです。foliumの0.20.0はLeaflet 1.9.3を読み込みます。Pythonの中で地図を作って配るならfolium、Webアプリのフロントエンドで地図を細かく制御するならLeafletを直接書く、という分担になります。
foliumの地図をPNG画像で保存できますか?
標準の機能では、保存できるのはHTMLです。内部用のメソッド_to_png()はありますが、seleniumとヘッドレスのFirefoxでHTMLを開いて撮影する仕組みで、既定で3秒待ってから撮ります。タイルの読み込みが間に合わないと背景が欠けるため、報告書用の画像が必要なら、待ち時間を延ばすか、静止画に向いた別のライブラリで描くほうが確実です。
foliumは商用利用できますか?
folium自体はMITライセンスのため、商用のシステムに組み込めます。注意が要るのは背景のタイルです。OpenStreetMapの標準タイルは商用サービスへの提供が保証されておらず、CartoDBは2026年9月からAPIキーが必要になりました。地理院タイルは出典の明示で使えます。配布先と規模に合わせて、タイルの利用条件を個別に確かめてください。
foliumで住所から緯度経度を求められますか?
folium本体にはPythonから住所を座標に変える機能はありません。地図に検索窓を付けるGeocoderプラグインはありますが、既定ではOpenStreetMapのNominatimを使い、ソースのコメントでもNominatimの利用ポリシーを守るよう注記されています。大量の住所を変換する用途では、ジオコーディングのAPIで事前に座標を付けてからfoliumに渡してください。
foliumとPlotlyの地図はどう使い分けますか?
グラフと地図を同じ画面で連動させたい、あるいはDashでダッシュボードを組むなら、Plotlyの地図機能が向いています。GeoJSONの塗り分けやタイルの切り替え、プラグインによるクラスタ表示など、地図そのものを作り込むならfoliumです。Plotlyの基本はPlotlyとは?Pythonでインタラクティブなグラフを作る基本と使い方で確認できます。
関連記事
- GISソフトの比較|無料・商用・クラウドの選定軸と受託開発への切替点:foliumで足りない要件が出たときに、GISソフトと受託開発のどちらへ進むかを比べられます
- Pandasとは?できること・使い方とPandas 3.0の変更点を解説:地図に載せる前の集計とデータ整形の基本を確認できます
- ZENRIN Maps APIの料金と無料枠の実際|機能一覧とGoogle Maps Platform比較【2026年版】:商用サービスで有償の地図を使う場合の費用感が分かります
- Streamlitとは?できること・使い方・料金を実例コードで解説【Python】:st_foliumで地図を載せるアプリの土台を作れます