徒歩時間計算の理論と実装 - シンプルな方法で十分という結論

多くのWebサービスで目にする「駅から徒歩○分」という表示。最近、待ち合わせ駅を自動提案するサービスを開発しました。その際、待ち合わせにかかる時間を最寄り駅までの移動時間を考慮する仕様にするため、その時間を求める必要がありました。最初は外部APIで道のりの予測を取得する必要があると思い込んでいましたが、要件によってはシンプルな計算で十分だということが分かりました。

アプリの実装はTypeScriptですが、検討段階ではPythonで計算を行いました。

## 理論的背景

徒歩などで実際に移動する距離を道路距離という場合があるようです。

道路を移動する場合、空き地を横切って移動するより、曲がり角を経由する方が遠いことは、誰もが知っています。　誰だったか忘れましたが、昔の文豪が、数学とはそういう当たり前のことを大真面目に研究するものというようなことを書いたという話を思い出し...ということで数学的に求める方法を検索しました。

（[その他｜名城大学　理工学部数学教室　鈴木研究室](https://ccmath.meijo-u.ac.jp/~suzukin/other.html)）に、２点間の直線距離に対応する道路距離の理論値について解説されていました（[道路距離(解説)_1019.pdf](https://ccmath.meijo-u.ac.jp/~suzukin/dl/%E9%81%93%E8%B7%AF%E8%B7%9D%E9%9B%A2(%E8%A7%A3%E8%AA%AC)_1019.pdf)）。

それによると、直線距離と実際の道路距離の関係については、理論値として4/π（≈1.2732...）という数値が知られています。この値は以下のような考え方で導出されます：

- 2点間の移動において、道のりはcos θ + sin θという形で表現できる
- θは0からπ/2の範囲で分布する
- すべての角度が均等に出現すると仮定した場合の平均値

実際の測定でも、この理論値に近い値が得られています：

- 複数都市での測定例：1.141 ～ 1.466
- 総合的な平均値：1.275

最近のGPSデータを用いた研究（20万件以上のデータ分析）でも：

- 平均値：1.3035
- 最小値：1.1260
- 最大値：1.9235

という結果が報告されていることが紹介されていました。

## 実装アプローチの検討

### 1. 距離計算

まず、地球上の2点間の距離計算について。徒歩圏内（2km程度まで）であれば、地球が平らと考えても誤差は数メートル以内であることを示します。球面上の厳密な計算は不要で、平面として近似して問題ありません。

```python
from dataclasses import dataclass
import math
from geopy.distance import geodesic

@dataclass
class Location:
    lat: float
    lng: float
    name: str = ""  # 場所の名前（オプション）

def simple_distance(start: Location, end: Location) -> float:
    """平面近似による距離計算（メートル）"""
    # 東京付近（約35度）での換算値
    LAT_PER_METER = 111000  # 緯度1度あたりの距離(m)
    LNG_PER_METER = 91000   # 経度1度あたりの距離(m)

    lat_diff = (end.lat - start.lat) * LAT_PER_METER
    lng_diff = (end.lng - start.lng) * LNG_PER_METER

    return math.sqrt(lat_diff**2 + lng_diff**2)

def geopy_distance(start: Location, end: Location) -> float:
    """geopyによる厳密な距離計算（メートル）"""
    return geodesic(
        (start.lat, start.lng),
        (end.lat, end.lng)
    ).meters

# テストケース（東京駅を中心とした様々な距離のポイント）
tokyo_station = Location(35.681236, 139.767125, "東京駅")
test_points = [
    Location(35.681236, 139.769125, "東200m地点"),  # 約200m東
    Location(35.681236, 139.772125, "東500m地点"),  # 約500m東
    Location(35.681236, 139.777125, "東1km地点"),   # 約1km東
    Location(35.681236, 139.787125, "東2km地点"),   # 約2km東
    Location(35.671236, 139.767125, "南1km地点"),   # 約1km南
    Location(35.691236, 139.767125, "北1km地点"),   # 約1km北
    # 斜め方向のテスト
    Location(35.687236, 139.773125, "北東1km地点"),  # 約1km北東
]

print("検証結果:")
print("-" * 70)
print(f"{'地点':12} | {'平面近似(m)':>10} | {'geopy(m)':>10} | {'誤差(m)':>8} | {'誤差率(%)':>8}")
print("-" * 70)

for point in test_points:
    simple_dist = simple_distance(tokyo_station, point)
    geopy_dist = geopy_distance(tokyo_station, point)

    diff = abs(simple_dist - geopy_dist)
    diff_percent = (diff / geopy_dist) * 100

    print(f"{point.name:12} | {simple_dist:10.1f} | {geopy_dist:10.1f} | {diff:8.1f} | {diff_percent:8.2f}")

検証結果:
----------------------------------------------------------------------
地点           |    平面近似(m) |   geopy(m) |    誤差(m) |   誤差率(%)
----------------------------------------------------------------------
東200m地点      |      182.0 |      181.1 |      0.9 |     0.52
東500m地点      |      455.0 |      452.6 |      2.4 |     0.52
東1km地点       |      910.0 |      905.3 |      4.7 |     0.52
東2km地点       |     1820.0 |     1810.5 |      9.5 |     0.52
南1km地点       |     1110.0 |     1109.5 |      0.5 |     0.04
北1km地点       |     1110.0 |     1109.5 |      0.5 |     0.04
北東1km地点      |      861.2 |      859.2 |      2.0 |     0.24
```

```python
def calculate_distance(start_lat, start_lng, end_lat, end_lng) -> float:
    """2地点間の直線距離を計算（メートル）"""
    # 緯度経度の差分をメートルに換算
    # 東京付近（約35度）での換算値
    LAT_PER_METER = 111000  # 緯度1度あたりの距離(m)
    LNG_PER_METER = 91000   # 経度1度あたりの距離(m)

    lat_diff = (end_lat - start_lat) * LAT_PER_METER
    lng_diff = (end_lng - start_lng) * LNG_PER_METER

    # 三平方の定理で距離を算出
    return math.sqrt(lat_diff**2 + lng_diff**2)
```

### 2. 徒歩時間の計算方法の比較

複雑な実装と簡易実装を比較してみました：

#### 複雑な実装のアプローチ

- 遠ければ幹線道路沿いで信号の少ない歩きやすい道を選ぶと考え、距離に応じて歩行速度を変える（500m以内は分速80m、1km以内は83m、2km以内は87m）
- 理論値（4/π）を使用して道路距離を補正

#### 簡易実装のアプローチ

- 直線距離×1.3÷歩行速度（80m/分）
- シンプルな計算式

### 3. 比較結果

実際に両者を比較してみると：

```bash
距離(m) | 簡易計算(分) | 複雑計算(分) | 差分(秒)
--------------------------------------------------
  273.0 |         4.4 |         4.3 |      5.5
  910.0 |        14.8 |        14.0 |     49.7
 1820.0 |        29.6 |        26.6 |    176.4
```

誤差は10%以内であり、実用上十分な精度であることが分かりました。

## 結論：シンプルな実装で十分

分析の結果、以下の実装で十分という結論に達しました：

```python
def walking_time_minutes(distance_meters: float) -> int:
    """
    歩行時間を簡易計算（分単位、切り上げ）
    Args:
        distance_meters: 直線距離（メートル）
    Returns:
        徒歩所要時間（分）
    """
    minutes = (distance_meters * 1.3) / 80  # 距離×1.3÷歩行速度
    return max(1, round(minutes))  # 1分未満は1分に切り上げ
```

結果として、理論的な裏付けがある近似値の計算方法があり、実装は意外にもシンプルなもので十分という結論になりました。

自家用車での出張経費の概算にもご活用いただけます。