Corin1998 commited on
Commit
f971a80
·
verified ·
1 Parent(s): 4b02f53

Create costs.py

Browse files
Files changed (1) hide show
  1. services/costs.py +106 -0
services/costs.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from typing import Optional, Union, Dict, Any
3
+ import re
4
+
5
+ Number = Union[int, float]
6
+
7
+ def _parse_fare_hint(fare_hint: Any) -> Optional[float]:
8
+ """
9
+ Navitia等からの運賃ヒントをなるべく数値(JPY)にして返す。
10
+ - dict: {"total": {"value": 420, "currency": "JPY"}} などを優先
11
+ {"amount": 420, "currency": "JPY"} にも対応
12
+ - str : "¥420", "JPY 420", "420 JPY" 等から数値を抽出
13
+ - それ以外: None
14
+ """
15
+ # dictパターン
16
+ if isinstance(fare_hint, dict):
17
+ # Navitia: fare.total.value / fare.total.currency
18
+ total = fare_hint.get("total") if isinstance(fare_hint.get("total"), dict) else None
19
+ if total and isinstance(total.get("value"), (int, float)):
20
+ cur = str(total.get("currency") or "JPY").upper()
21
+ if cur in ("JPY", "YEN", "¥", "JPY "):
22
+ return float(total["value"])
23
+ else:
24
+ # 通貨が別の時はここでは換算せず None(過小評価を避ける)
25
+ return None
26
+ # amount/currency 形式
27
+ amt = fare_hint.get("amount")
28
+ cur = str(fare_hint.get("currency") or "JPY").upper()
29
+ if isinstance(amt, (int, float)) and cur in ("JPY", "YEN", "¥"):
30
+ return float(amt)
31
+ # 別のキーでも value を持っていればそれを採用
32
+ for k in ("value", "price", "fare"):
33
+ v = fare_hint.get(k)
34
+ if isinstance(v, (int, float)):
35
+ return float(v)
36
+
37
+ # 文字列パターン
38
+ if isinstance(fare_hint, str):
39
+ m = re.search(r"([\d,]+(?:\.\d+)?)", fare_hint)
40
+ if m:
41
+ try:
42
+ return float(m.group(1).replace(",", ""))
43
+ except Exception:
44
+ pass
45
+
46
+ return None
47
+
48
+
49
+ def estimate_leg_cost(
50
+ mode: str,
51
+ distance_km: Number,
52
+ duration_min: Number,
53
+ fare_hint: Optional[Union[dict, str, Number]] = None,
54
+ ) -> float:
55
+ """
56
+ 区間の推定交通費(JPY)を返す簡易関数。
57
+ - mode: "Walk" | "Transit" | "Drive/Taxi"
58
+ - fare_hint: Navitia等の運賃情報がある場合はそれを優先
59
+ - 返り値: 四捨五入して10円単位
60
+ """
61
+ # ヒントがあれば最優先(数値化できればそのまま採用)
62
+ if isinstance(fare_hint, (int, float)):
63
+ return float(round(fare_hint / 10.0) * 10)
64
+ hint = _parse_fare_hint(fare_hint)
65
+ if hint is not None:
66
+ return float(round(hint / 10.0) * 10)
67
+
68
+ d = max(0.0, float(distance_km))
69
+ t = max(0.0, float(duration_min))
70
+ mode = (mode or "Walk").strip()
71
+
72
+ # 徒歩は無料
73
+ if mode.lower().startswith("walk"):
74
+ return 0.0
75
+
76
+ # 公共交通:ざっくり距離比例(東京近郊の初乗り含むイメージ)
77
+ if mode.lower().startswith("transit"):
78
+ base = 180.0 # 初乗りイメージ
79
+ per_km = 22.0 # 距離係数
80
+ est = base + per_km * d
81
+ # 長距離で上限(空港連絡等は別だが、概算としてのキャップ)
82
+ est = min(est, 1200.0)
83
+ return float(round(est / 10.0) * 10)
84
+
85
+ # タクシー/自動車:ざっくり距離+低速時の時間加算
86
+ # 東京の実勢に近い概算:初乗り500円 + 420円/km + 渋滞待機 45円/分(低速時のみ)
87
+ if mode.lower().startswith("drive"):
88
+ flag_fall = 500.0
89
+ per_km = 420.0
90
+ est = flag_fall + per_km * d
91
+
92
+ # 低速(12km/h未満)なら待機料金を加算(概算)
93
+ speed_kmh = (d / (t / 60.0)) if t > 0 else 999.0
94
+ if speed_kmh < 12.0:
95
+ wait_fee_per_min = 45.0
96
+ # 12km/h で同距離を走るのに要する時間との差を「渋滞/待機」とみなす
97
+ ideal_min = (d / 12.0) * 60.0
98
+ extra_min = max(0.0, t - ideal_min)
99
+ est += wait_fee_per_min * extra_min
100
+
101
+ # 短距離の過小評価を避ける最低料金
102
+ est = max(est, 680.0)
103
+ return float(round(est / 10.0) * 10)
104
+
105
+ # 未知のモードは0円にしておく(上流で弾かれる想定)
106
+ return 0.0