今回はGeoPandasとFoliumによる
地理データの取り扱い方です。
解説動画はこちら
Pythonで地理データを取り扱う方法
今回は地理データを取り扱う方法として
2つのライブラリをご紹介します。
GeoPandas
Pandasライブラリに
空間(Geometry)を実現したライブラリ
空間(Geometry)を実現したライブラリ
Pandasの DataFrame を拡張
各行に 1つのジオメトリ(形状) を持つ
GIS(地理情報システム)向けの演算が可能
静的なplotが行える
Folium
Leaflet.js を
Python から操作する描画用ライブラリ
Python から操作する描画用ライブラリ
出力は HTML
OpenStreemMapをタイルレイヤとして表示できる
Jupyter / Web / 静的ファイルで使える
インタラクティブな描画が行える
ということでこの2つを用いて
地理データを描画していきましょう。
GeoPandasを用いた地理描画
最初はGeoPandasを用いた描画です
Google colabで実行できるコードになっていますが
Google colabでは日本語が取り扱えないので
別途ライブラリが必要です。
最初はGeoPandasを用いた描画です
Google colabで実行できるコードになっていますが
Google colabでは日本語が取り扱えないので
別途ライブラリが必要です。
ライブラリのインストール
日本語を使用するため
japanize-matplotlib
をインストールしておきます。pip install japanize-matplotlib
架空のデータを読み込んで描画する
まず初めは、何もない状態から
架空のデータを使っていきます。
pandasに読み込んだ後
GeoPandasに緯度経度として読み込みします。
# -----------------
# 1. データの準備 (架空のオープンデータ)
# -----------------
# (A) 地理データ: 都道府県庁所在地のポイントデータ (GeoJSONを想定)
# 緯度・経度と都道府県名が含まれるGeoDataFrameを作成
data_geo = {
'city': ['Sapporo', 'Sendai', 'Tokyo', 'Nagoya', 'Osaka', 'Fukuoka'],
'prefecture': ['北海道', '宮城', '東京', '愛知', '大阪', '福岡'],
'latitude': [43.06, 38.27, 35.69, 35.18, 34.69, 33.59],
'longitude': [141.35, 140.87, 139.69, 136.91, 135.50, 130.40]
}
df_geo = pd.DataFrame(data_geo)
# GeoDataFrameに変換
# 'geometry'カラムを作成し、緯度・経度からポイント(点)の地理情報を持たせる
gdf_city = gpd.GeoDataFrame(
df_geo,
geometry=gpd.points_from_xy(df_geo.longitude, df_geo.latitude),
crs="EPSG:4326"
)
print("GeoDataFrameの確認:\n", gdf_city.head())GeoDataFrameの確認:
city prefecture latitude longitude geometry
0 Sapporo 北海道 43.06 141.35 POINT (141.35 43.06)
1 Sendai 宮城 38.27 140.87 POINT (140.87 38.27)
2 Tokyo 東京 35.69 139.69 POINT (139.69 35.69)
3 Nagoya 愛知 35.18 136.91 POINT (136.91 35.18)
4 Osaka 大阪 34.69 135.50 POINT (135.5 34.69)
データに少し加工を加え、情報を足します。
# (B) 属性データ: 都道府県ごとの人口データ
# 都道府県ごとの人口属性データフレーム
data_attr = {
'prefecture': ['北海道', '宮城', '東京', '愛知', '大阪', '福岡'],
'population_mil': [5.2, 2.3, 14.0, 7.5, 8.8, 5.1], # 単位: 百万人
'density_class': ['Low', 'Mid', 'High', 'High', 'High', 'Mid']
}
df_attr = pd.DataFrame(data_attr)
# -----------------
# 2. データの結合 (Merge)
# -----------------
# 'prefecture'カラムをキーにしてGeoDataFrameと属性データを結合
gdf_merged = gdf_city.merge(df_attr, on='prefecture')
print("\n結合後のGeoDataFrameの確認:\n", gdf_merged[['prefecture', 'population_mil', 'geometry']].head())結合後のGeoDataFrameの確認:
prefecture population_mil geometry
0 北海道 5.2 POINT (141.35 43.06)
1 宮城 2.3 POINT (140.87 38.27)
2 東京 14.0 POINT (139.69 35.69)
3 愛知 7.5 POINT (136.91 35.18)
4 大阪 8.8 POINT (135.5 34.69)
次のコードで簡易なプロットを行えます。
# 結合したデータを基にGeoPandasで簡易プロット
gdf_merged.plot(column='population_mil', legend=True, figsize=(5, 5),
markersize=gdf_merged['population_mil'] * 10)
plt.title("GeoPandasによる簡易プロット (人口規模)")
plt.show()

緯度経度だけを用いて描画していますが
これだけだと、あまり意味がない描画ですね
これをFoliumで描画しなおします。
# Foliumの描画準備 (日本の中心付近にマップを初期化)
map_center = [35.68, 139.69] # 東京の緯度・経度
m = folium.Map(location=map_center, zoom_start=5)
# -----------------
# 1. シンプルなマーカー表示 (庁所在地)
# -----------------
for idx, row in gdf_merged.iterrows():
# ポップアップに表示する情報をHTMLで作成
html = f"""
{row['prefecture']}庁所在地
人口: {row['population_mil']}百万人
密度区分: {row['density_class']}
"""
iframe = folium.IFrame(html)
popup = folium.Popup(iframe, min_width=200, max_width=300)
# Markerを追加
folium.Marker(
location=[row.latitude, row.longitude],
popup=popup,
icon=folium.Icon(color='blue', icon='info-sign')
).add_to(m)
# -----------------
# 2. ヒートマップの追加 (人口密度に応じて色を濃く)
# -----------------
from folium.plugins import HeatMap
# HeatMapに必要なデータ形式: [[緯度, 経度, 強度], ...]
# 強度として人口(百万)を使用
heat_data = [[row.latitude, row.longitude, row.population_mil] for idx, row in gdf_merged.iterrows()]
HeatMap(heat_data).add_to(m)
# -----------------
# 3. GeoPandasとFoliumを組み合わせた応用描画
# Foliumでは、CircleMarkerを使って、データを視覚的に表現できます。
def get_color(density):
if density == 'High': return 'red'
elif density == 'Mid': return 'orange'
else: return 'green'
for idx, row in gdf_merged.iterrows():
folium.CircleMarker(
location=[row.latitude, row.longitude],
radius=row['population_mil'] * 1.5, # 人口規模に応じて円の大きさを変更
color=get_color(row['density_class']),
fill=True,
fill_color=get_color(row['density_class']),
fill_opacity=0.7,
tooltip=f"{row['prefecture']}: {row['population_mil']}M"
).add_to(m)
# マップをHTMLファイルとして保存
m.save("interactive_map.html")
print("\nFoliumマップオブジェクトを作成しました。")
Foliumでは作った地図を
静的なHTMLとして保存ができます。
Google Colabでは
デフォルトのフォルダに
HTMLファイルが出力されると思います。
変数 m を実行すると
中身を描画することもできます。

こんな感じで、架空のデータですが
地図上に描画できました。
Foliumはインタラクティブに操作できるので
色々遊べます。
500キロメートル圏内を描画する
GeoPandasで緯度経度から少し計算して
描画用のデータを作ることができます。
まずは距離を測るためのCRSというデータへ変換します。
# -----------------
# 1. CRSの変換 (距離計算のため)
# -----------------
# 緯度・経度 (4326) からメートル単位のCRS (3857) へ変換
gdf_projected = gdf_merged.to_crs("EPSG:3857")
print("\nCRS変換後のGeoDataFrame (EPSG:3857):\n", gdf_projected.head()) city prefecture latitude longitude geometry \
0 Sapporo 北海道 43.06 141.35 POINT (15735010.024 5321108.922)
1 Sendai 宮城 38.27 140.87 POINT (15681576.668 4617638.286)
2 Tokyo 東京 35.69 139.69 POINT (15550219.669 4258049.263)
3 Nagoya 愛知 35.18 136.91 POINT (15240751.485 4188369.409)
4 Osaka 大阪 34.69 135.50 POINT (15083791.002 4121832.777)
これで計算する用意ができたので
Foliumにデータを加えます。
# -----------------
# 2. バッファリング (空間分析)
# -----------------
# 東京、大阪、名古屋の庁所在地から「500km圏内」のバッファを作成
# 単位はメートルなので 500 * 1000 = 500,000メートル
buffer_distance = 500 * 1000
# GeoPandasのbuffer()メソッドでバッファを作成
# わかりやすさのため、東京の行(index=2)のみを抽出
tokyo_buffer = gdf_projected.iloc[[2]].buffer(buffer_distance)
# バッファを元のCRS (4326) に戻し、Foliumで表示できるようにする
tokyo_buffer_wgs84 = tokyo_buffer.to_crs("EPSG:4326")
# -----------------
# 3. Foliumでの分析結果の可視化
# -----------------
# 新しいFoliumマップを作成
m_analysis = folium.Map(location=map_center, zoom_start=5)
# バッファリング結果 (Polygon) をFoliumに描画
folium.GeoJson(
tokyo_buffer_wgs84.__geo_interface__, # GeoPandasオブジェクトをGeoJSON互換の形式に変換
name='500km Buffer from Tokyo',
style_function=lambda x: {
'fillColor': 'purple',
'color': 'purple',
'weight': 2,
'fillOpacity': 0.3
}
).add_to(m_analysis)
# マーカーとヒートマップも再表示 (任意)
for idx, row in gdf_merged.iterrows():
folium.Marker(
location=[row.latitude, row.longitude],
popup=f"{row['prefecture']} - {row['population_mil']}M"
).add_to(m_analysis)
# マップにレイヤー切り替え機能を追加して見やすくする
folium.LayerControl().add_to(m_analysis)
m_analysis.save("analysis_map.html")
今度は m_analysis という変数の中身を見てみると

こんな感じで東京から500キロメートル圏内を
塗ることができました。
CSVファイルを読み込みして描画する
今度は架空ではなく
実際のデータで描画してみましょう。
地震のデータがあるので
これを用います。
地震データの取得先
https://www.data.jma.go.jp/eqdb/data/shindo/
このサイトから検索して
CSVファイルにして
Google Colabのファイル置き場に配置します。
CSVを読み込みします。
緯度経度などのデータが
そのままでは使えないので加工します。
描画を行います。
変数 m を表示してみると

震度ごとに表示を切り替えすることができます。
商圏分析
このサイトから検索して
CSVファイルにして
Google Colabのファイル置き場に配置します。
CSVを読み込みします。
import pandas as pd
import folium
from folium.plugins import MarkerCluster
from io import StringIO
import re
# ----------------------------------------------------
# 1. データ準備
# ----------------------------------------------------
csv_data = """地震の発生日,地震の発生時刻,震央地名,緯度,経度,深さ,M,最大震度
2025/12/10,23:52:24.1,青森県東方沖,40°50.1′N,142°45.3′E,36 km,6.0,震度4
2025/12/09,18:09:45.9,青森県東方沖,41°16.0′N,142°25.1′E,47 km,5.3,震度3
2025/12/09,06:52:42.7,青森県東方沖,40°56.6′N,143°18.0′E,15 km,6.6,震度4
2025/12/09,03:56:29.5,青森県東方沖,40°57.0′N,143°07.6′E,19 km,6.1,震度3
2025/12/08,23:33:39.2,青森県東方沖,40°53.0′N,142°35.2′E,42 km,5.9,震度3
2025/12/08,23:15:10.1,青森県東方沖,40°58.0′N,142°17.2′E,54 km,7.5,震度6強
2025/12/05,12:11:24.1,熊本県阿蘇地方,32°58.5′N,131°06.8′E,4 km,3.3,震度3
"""
# df = pd.read_csv(StringIO(csv_data))
df = pd.read_csv('地震リスト.csv')
df.head(3)<地震の発生日 地震の発生時刻 震央地名 緯度 経度 深さ M 最大震度
0 2025/12/10 23:52:24.1 青森県東方沖 40°50.1′N 142°45.3′E 36 km 6.0 震度4
1 2025/12/09 18:09:45.9 青森県東方沖 41°16.0′N 142°25.1′E 47 km 5.3 震度3
2 2025/12/09 06:52:42.7 青森県東方沖 40°56.6′N 143°18.0′E 15 km 6.6 震度4
緯度経度などのデータが
そのままでは使えないので加工します。
def convert_dms_to_decimal(dms_str):
""" '40°50.1′N' のような文字列を十進数形式に変換する関数 """
match = re.match(r"(\d+)°(\d+\.?\d*)′([NSEW])", dms_str)
if not match:
return None
degrees = float(match.group(1))
minutes = float(match.group(2))
direction = match.group(3)
decimal = degrees + minutes / 60
# 南緯(S)と西経(W)はマイナスにする
if direction in ('S', 'W'):
decimal *= -1
return decimal
# 緯度と経度を数値に変換
df['緯度_dec'] = df['緯度'].apply(convert_dms_to_decimal)
df['経度_dec'] = df['経度'].apply(convert_dms_to_decimal)
# 震度を強度順に数値化するための辞書
shindo_mapping = {
'震度1': 10,
'震度2': 20,
'震度3': 30,
'震度4': 40,
'震度5弱': 51,
'震度5強': 55,
'震度6弱': 61,
'震度6強': 65,
'震度7': 70,
}
# 震度を数値に変換
df['shindo_value'] = df['最大震度'].map(shindo_mapping)
def get_shindo_color(shindo):
# 震度値 (30, 40, ..., 70) を利用
if shindo >= 65: # 震度6強, 震度7
return 'darkred'
elif shindo >= 55: # 震度5強, 震度6弱
return 'red'
elif shindo >= 51: # 震度5弱
return 'orange'
elif shindo >= 40: # 震度4
return 'lightred'
elif shindo >= 30: # 震度3
return 'green'
else:
return 'gray' # その他
描画を行います。
# ----------------------------------------------------
# 1. Folium マップの作成
# ----------------------------------------------------
# 日本の中心付近を初期位置とする
map_center = [df['緯度_dec'].mean(), df['経度_dec'].mean()]
m = folium.Map(location=map_center, zoom_start=5)
# ----------------------------------------------------
# 2. 震度ごとに FeatureGroup (レイヤー) を作成
# ----------------------------------------------------
# 震度レベルごとの FeatureGroup を格納する辞書
shindo_layers = {}
# 震度マッピングのキー(例: '震度7', '震度6強')を降順でソート
sorted_shindo_levels = sorted(shindo_mapping.keys(), key=lambda x: shindo_mapping[x], reverse=True)
for shindo_level in sorted_shindo_levels:
# レイヤー名を設定 (LayerControlに表示される名前)
layer_name = f"最大震度: {shindo_level}"
# FeatureGroupを作成し、マップに追加
fg = folium.FeatureGroup(name=layer_name, show=True).add_to(m)
shindo_layers[shindo_level] = fg
# ----------------------------------------------------
# 3. データの反復処理と適切なレイヤーへのマーカー追加
# ----------------------------------------------------
for idx, row in df.iterrows():
datetime_full = f"{row['地震の発生日']} {row['地震の発生時刻'][:8]}"
# ポップアップ情報
popup_html = f"""
**最大震度: {row['最大震度']}**
発生日時: {datetime_full}
震央地名: {row['震央地名']}
深さ: {row['深さ']}
マグニチュード(M): {row['M']}
"""
marker_color = get_shindo_color(row['shindo_value'])
current_shindo_level = row['最大震度']
# 該当する震度の FeatureGroup を取得
target_fg = shindo_layers.get(current_shindo_level)
if target_fg is not None:
# マーカーを対応する FeatureGroup に追加
folium.Marker(
location=[row['緯度_dec'], row['経度_dec']],
popup=popup_html,
icon=folium.Icon(color=marker_color, icon='flash', prefix='fa')
).add_to(target_fg)
# ----------------------------------------------------
# 4. LayerControl (表示切り替えボックス) を追加
# ----------------------------------------------------
folium.LayerControl(collapsed=False).add_to(m)
# ----------------------------------------------------
# 5. マップの保存と表示
# ----------------------------------------------------
m.save("earthquake_layer_control_map.html")
print("震度別表示切り替え機能付きFoliumマップの作成が完了しました。")
変数 m を表示してみると

震度ごとに表示を切り替えすることができます。
まとめ
GeoPandasで計算して、Foliumで描画すると
以下のような分析が簡単に行えます。
商圏分析
人口ヒートマップ
配送ルート可視化
不動産マップ
災害・危険区域表示
これらの分析を行う際に
このライブラリの組み合わせは
非常に捗ります。
かなり便利なので
使ってみると面白いと思います
それでは。
これらの分析を行う際に
このライブラリの組み合わせは
非常に捗ります。
かなり便利なので
使ってみると面白いと思います
それでは。

