乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

プログラミング

今回はアプリをリリースしたので
そちらのご報告です。

解説動画はこちら



アプリについて


今回作成したアプリはこちらです

・チャンクde英会話

iOS
AppStore


Android
Googleplay

良かったら使ってみてください


アプリの作成方法

Flutterを用いて作成しました

Flutterにした理由は
 1.iOSとAndroid両対応であること
 2.学習コストが低い
 3.文献が豊富で有る

以上の理由からFlutterにしてみました。

それ以外にもアプリを作る方法は
いくらでもありますが、今回は
Flutterを使ってみました。

開発言語がDartになるので
1から勉強することになります。

また、コードやデータなどの大半は
ChatGPTを用いて作成していますので
実質ChatGPTに頼れば
アプリの開発は容易かと思います。

学習開発の期間で2ヶ月くらいでした。



リリースについて

コードを実装しシミュレータや
実機でのテストが終わったら
ビルドを行なってアプリを作り
リリース準備ができます。


その前にアプリストアのアカウントがないと
そもそもリリース出来ないので
アプリストアのアカウント取得が必要です。

これもそこそこ手間と時間が掛かります。

リリース登録をしたら審査が行われますが
審査に通らなければビルドからやり直し
審査に通るまでの繰り返しです。

審査に通ったらようやく
リリース、アプリ配信ができる様になります。

リリース作業を始めてから
アカウント登録とアプリリリースまでで
大体1ヶ月くらい掛かっています。



Flutterについて

動画の方では少しだけ解説していますが
FlutterはiOSとAndroidの両方のアプリを
作成する事ができる開発フレームワークです。

Flutterをインストールしたら
VSCodeなどでコードを書き進める事が
できる様になります。

テストやビルドなども
Flutterコマンドを用いて行う形になります。

この辺りも
VSCodeと合わせておくと
開発が楽になるかなと思います。

どんな感じなのかは
動画の方で解説していますので
参考にしていただければと思います。


最後に

これからアプリ開発を行いたい方にとっては
色々な選択肢があると思いますが
Flutterを使ってアプリを開発したい方が
増えていただけたら幸いです。

Python言語の解説と共に
アプリ開発の方も進めていきますので
要望などあれば是非コメントいただければと思います。

それでは。

プログラミング未経験の方のための
プログラミング学習講座を作成しました

その名も
「1時間で学べるPythonプログラミング」


講義動画はこちら




この講座は初学者の方が
短時間でPython言語を学ぶことのできる
プログラミング学習用の講座です

プログラミングが分からないない方は
Python言語を通じて
プログラミングの基礎を学習できます

講座は動画に加えてGoogle Colabを用いて
手元でコードを動かすことのできます
コードがどう動くのかを確認をしながら
進めていってください

資料はここ:
Google Colabの資料


00:00 1.はじめに
02:13 2.導入方法
02:55 3.GoogleColaboratoryの操作方法
06:19 4.Pythonの計算の基礎
27:27 5.Pythonの制御文
42:14 6.Pythonのクラス
49:11 7.Pythonのその他構文
64:30 8.まとめ

なおPythonチートシートを作成しています。

コーディングに迷った際に役に立ち

WEB検索する時間を無くして

作業時間を効率化できます。

note
Pythonチートシート


 

今回は最適化問題を解くことが出来る
OR-Toolsについてです。

解説動画はこちら




OR-Toolsとは


Googleが開発している、組合せ最適化のためのライブラリ

様々な最適化問題を解くためのソルバーが充実していて
C++実装だが、Pythonなどいろいろな言語で使えます。


解ける最適化問題

次のような最適化問題を解くことができます。


1.配送ルート最適化
巡回セールスマン問題 : 1台の車両で最短経路を回る
配送計画問題 : 複数台の車両で分担して配送する

2.スケジューリングと制約プログラミング
決められた制約を全て満たしつつ、最適なスケジュールを組む
従業員のシフト作成
工場のライン計画

3.線形計画・整数計画(LP, MIP)
目的関数を最大化、または最小化する問題を数式で解く手法
生産計画 : 在庫と予算の範囲内で利益の最大化
リソース配分 : 予算内での広告効果最大化の割り当て

4.詰込み・割当問題
ナップサック問題 : 容量制限のあるバッグに価値が最大になるよう詰める
ビンピッキング問題 : サイズの異なる荷物を出来るだけ少ないトラックに詰める

5.ネットワークフロー
最大流問題 : パイプラインで一度に流せる最大量を求める
最小費用問題 : 指定量の輸送で、コストが最も安くなるルートと量の特定


インストール

Google Colab でも無いみたいなので、入れる必要あります。
pip install ortools


OR-Toolsの基本的な使い方

1.ライブラリの読み込み

解く問題に応じて、対応するライブラリを読み込みします
# CP-SATソルバー(制約プログラミング/MIP)
from ortools.sat.python import cp_model

# 線形計画法ソルバー(LP)
from ortools.linear_solver import pywraplp

# 配送計画(Routing)
from ortools.constraint_solver import pywrapcp, routing_enums_pb2

2. 最適化モデル構築

次に最適化モデルを作ります。
基本の「4ステップ」があります。

STEP 1: モデルのインスタンス作成
まず、問題を定義するための「箱」を作ります。

from ortools.sat.python import cp_model

model = cp_model.CpModel()


STEP 2: 変数の作成
「何を求めたいか」を変数として定義します。

整数変数: model.NewIntVar(下限, 上限, '変数名')
ブール変数: model.NewBoolVar('変数名')


STEP 3: 制約条件の追加
model.Add(...) を使って、守らなければならないルールを記述します。

等式・不等式: model.Add(x + y <= 10)
論理制約: 「Aの時だけBを適用する」といった
条件付き制約(OnlyEnforceIf)も可能


STEP 4: 目的関数の設定と実行
「何を最大化(または最小化)したいか」を決め
ソルバーを起動します。

# 最小化の場合
model.Minimize(目的の式)

# ソルバーの起動
solver = cp_model.CpSolver()
result = solver.Solve(model)

ここまでのコードを作れば
最適化問題が解ける様になっていると思います。


問題を解いてみる

簡単なつるかめ算をやって
あっているかを確認してみましょう。


問題1:
「つる」と「かめ」が合計で10匹、足の合計は28本です
それぞれ何匹ずついるでしょうか?

コードはこんな感じになります。
from ortools.sat.python import cp_model

def solve_tsurukame():
    # モデルの作成
    model = cp_model.CpModel()

    # 変数の定義 (0匹以上10匹以下)
    tsuru = model.NewIntVar(0, 10, 'tsuru')
    kame = model.NewIntVar(0, 10, 'kame')

    # 制約1: 合計が10匹
    model.Add(tsuru + kame == 10)

    # 制約2: 足の合計が28本
    model.Add(2 * tsuru + 4 * kame == 28)

    # ソルバーの準備と実行
    solver = cp_model.CpSolver()
    result = solver.Solve(model)

    if result == cp_model.OPTIMAL:
        print(f'つる: {solver.Value(tsuru)} 羽')
        print(f'かめ: {solver.Value(kame)} 匹')

solve_tsurukame()
つる: 6 羽
かめ: 4 匹


問題2:
50円切手と80円切手が計20枚、合計で1240円になるとき
それぞれ何枚ずつあるでしょうか?

from ortools.sat.python import cp_model

def solve_stamps():
    model = cp_model.CpModel()

    # 変数の定義 (0枚以上20枚以下)
    s50 = model.NewIntVar(0, 20, '50yen')
    s80 = model.NewIntVar(0, 20, '80yen')

    # 制約1: 合計20枚
    model.Add(s50 + s80 == 20)

    # 制約2: 合計金額が1240円
    model.Add(50 * s50 + 80 * s80 == 1240)

    solver = cp_model.CpSolver()
    result = solver.Solve(model)

    if result == cp_model.OPTIMAL:
        print(f'50円切手: {solver.Value(s50)} 枚')
        print(f'80円切手: {solver.Value(s80)} 枚')

solve_stamps()
50円切手: 12 枚
80円切手: 8 枚



問題3:

動画内ではすこし難しめの
入試問題なんかもやっています。

是非みてみてください。



まとめ


OR-Toolsは最適化問題を解くのに、かなり有効なライブラリです。

実社会の巨大な課題にも応用可能で
「Amazonのような配送ルート作成」や
「学校の複雑な時間割作成」など
他にもいろいろな最適化に応用できるものがあります。

だだし、使いこなすにはちょっとした
数学の知識が必要かもしれません。

使いこなせるとかなり問題を解ける幅が広がるので
実社会の問題解決にかなり有利かもしれません。

是非使ってみてください
それでは。



 

今回はPythonからRustを呼び出して
超高速化する方法についてです。

解説動画はこちら




Pythonは書きやすくて便利だけど
他の言語に比べるとどうしても遅い。


そんな時はRustの高速なネイティブコードを
Pythonから呼び出して計算速度を上げられます。


Python-Rust連携の方法

maturin

Rustで書かれたコードをPythonのパッケージ(wheel形式)
としてビルド・公開するためのビルドツールです。

これを使ってPythonとRustを連携させます。

主な手順は以下です。
1.Rust と maturin をインストール
2.Rust拡張用のプロジェクトを作る
3.Rustコードを作る
4.Cargo.toml を修正する
5.Python用拡張としてビルドする

早速手順を見ていきましょう。

なおGoogle ColabでのRust-Python連携方法になります。
自前のPCだとかだと、少し手順は変わってきます。

1.Rust と maturin をインストール

まず初めはColab内に必要なものをインストールします
# Rust をインストール
!curl https://sh.rustup.rs -sSf | sh -s -- -y -q
import os
os.environ["PATH"] = f"{os.environ['HOME']}/.cargo/bin:" + os.environ["PATH"]
!rustc --version
!cargo --version
!pip install -U maturin
!maturin --version

2. Rust拡張用のプロジェクトを作る
デフォルトのディレクトリはcontentになっているので
その中にプロジェクトを作り移動します。
!maturin init fastcalc
%cd /content/fastcalc

UIのフォルダマークからディレクトリが
作成されていると思います。


3. Rustコードを作る

すでにあるコードを上書きする形で
コードを書き換えます。
ここでは簡単な計算を行うコードを指定して
関数を作っています。

%%writefile src/lib.rs
use pyo3::prelude::*;

#[pyfunction]
fn sum_squares(n: u64) -> u64 {
    let mut s = 0;
    for i in 0..n {
        s += i * i;
    }
    s
}

#[pymodule]
fn fastcalc(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum_squares, m)?)?;
    Ok(())
}

4. Cargo.toml を修正

設定用のファイルも上書きします。

%%writefile Cargo.toml
[package]
name = "fastcalc"
version = "0.1.0"
edition = "2021"

[lib]
name = "fastcalc"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.21", features = ["extension-module"] }

5. Python用拡張としてビルド

RustコードをPythonライブラリとしてビルドします。

# Python環境から Rust を見えるように PATH を追加
!export PATH="$HOME/.cargo/bin:$PATH"

# wheel を作る
!maturin build --release

# wheel を pip install
!pip install target/wheels/*.whl

6.Pythonから Rust 関数を呼ぶ

1-5までが出来ていたら
ライブラリを呼び出して、Rustの関数が実行できます。

import fastcalc
fastcalc.sum_squares(100_000_000)

7.Python版と速度比較

これで速度比較が出せると思います。

import time

def sum_squares_py(n):
    s = 0
    for i in range(n):
        s += i * i
    return s

N = 100_000_000

t0 = time.time()
sum_squares_py(N)
t1 = time.time()
python_time = t1 - t0

t2 = time.time()
fastcalc.sum_squares(N)
t3 = time.time()

rust_time = t3 - t2
print("Python: {:.8f} sec".format(python_time))
print("Rust  : {:.8f} sec".format(rust_time))
print("Rust vs Python : {:.10f}".format(python_time / rust_time))

どれくらいの速度差になるかは
是非動画の方を見てみてください。

動画内ではもう一つの
シミュレーションも行っていますので
参考になると思います。



まとめ

maturinを使ったRustコードのPython拡張を作って実行できるようにすると
実行速度が高速な関数を呼び出せるようになります。


Pythonの簡単さはそのままに、重い計算はRustに任せる
これが Python × Rust 連携の最強パターンです。


最近はPolarsなどの大量データ処理や
深層学習モデルの構築や推論なども
Rust製ライブラリが増えてきています。

無いものは自分で作れると
自作の処理の時短になって
めちゃくちゃ捗ります。

今日はここまでです
それでは。

今回はGeoPandasとFoliumによる
地理データの取り扱い方です。


解説動画はこちら


 
Pythonで地理データを取り扱う方法


今回は地理データを取り扱う方法として
2つのライブラリをご紹介します。


GeoPandas

Pandasライブラリに
空間(Geometry)を実現したライブラリ

Pandasの DataFrame を拡張
各行に 1つのジオメトリ(形状) を持つ
GIS(地理情報システム)向けの演算が可能
静的なplotが行える


Folium

Leaflet.js を
Python から操作する描画用ライブラリ

出力は HTML
OpenStreemMapをタイルレイヤとして表示できる
Jupyter / Web / 静的ファイルで使える
インタラクティブな描画が行える

ということでこの2つを用いて
地理データを描画していきましょう。



GeoPandasを用いた地理描画

最初は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()
スクリーンショット 2025-12-13 17.24.29

緯度経度だけを用いて描画していますが
これだけだと、あまり意味がない描画ですね

これを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 を実行すると
中身を描画することもできます。

スクリーンショット 2025-12-13 17.21.28

こんな感じで、架空のデータですが
地図上に描画できました。

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 という変数の中身を見てみると
スクリーンショット 2025-12-13 17.22.35

こんな感じで東京から500キロメートル圏内を
塗ることができました。



CSVファイルを読み込みして描画する


今度は架空ではなく
実際のデータで描画してみましょう。

地震のデータがあるので
これを用います。

地震データの取得先
https://www.data.jma.go.jp/eqdb/data/shindo/

このサイトから検索して
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)<
地震の発生日 地震の発生時刻 震央地名 緯度 経度 深さ 最大震度
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 を表示してみると
スクリーンショット 2025-12-13 17.21.43


震度ごとに表示を切り替えすることができます。





まとめ

GeoPandasで計算して、Foliumで描画すると
以下のような分析が簡単に行えます。

商圏分析
人口ヒートマップ
配送ルート可視化
不動産マップ
災害・危険区域表示

これらの分析を行う際に
このライブラリの組み合わせは
非常に捗ります。

かなり便利なので
使ってみると面白いと思います
それでは。


このページのトップヘ