File size: 4,004 Bytes
36c1c11 25a06ca edf6b35 25a06ca 36c1c11 40bf738 52fda00 bbc3d3d 40bf738 bbc3d3d 36c1c11 37fb541 40bf738 37fb541 edf6b35 40bf738 37fb541 44b349b 860576a 44b349b 37fb541 860576a 44b349b 860576a 44b349b 860576a 37fb541 faf6398 860576a faf6398 860576a f30c2aa faf6398 617fb86 44b349b faf6398 44b349b 40bf738 860576a 37fb541 36c1c11 52fda00 faf6398 860576a 36c1c11 617fb86 36c1c11 25a06ca 44b349b 40bf738 617fb86 40bf738 44b349b 40bf738 52fda00 25a06ca 36c1c11 40bf738 36c1c11 370bf2d 44b349b 37fb541 617fb86 44b349b 37fb541 36c1c11 8827f9c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | import gradio as gr
import math
treasures = [
{"name": "寶藏第1個", "lat": 24.985703, "lon": 121.288680},
{"name": "寶藏第2個", "lat": 24.985900, "lon": 121.289000},
{"name": "寶藏第3個", "lat": 24.956398, "lon": 121.297729},
]
def haversine(lat1, lon1, lat2, lon2):
R = 6371000
phi1, phi2 = math.radians(lat1), math.radians(lat2)
d_phi = math.radians(lat2 - lat1)
d_lambda = math.radians(lon2 - lon1)
a = math.sin(d_phi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(d_lambda/2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return R * c
def check_nearest(lat, lon, found):
if not lat or not lon:
return "❌ 請先取得 GPS", "", "", found
try:
lat = float(lat)
lon = float(lon)
except:
return "❌ 座標格式錯誤", "", "", found
nearest = None
nearest_idx = None
min_distance = float("inf")
for idx, treasure in enumerate(treasures):
dist = haversine(lat, lon, treasure["lat"], treasure["lon"])
if dist < min_distance:
min_distance = dist
nearest = treasure
nearest_idx = idx
dist = haversine(lat, lon, nearest["lat"], nearest["lon"])
gmap = f"https://www.google.com/maps/dir/{lat},{lon}/{nearest['lat']},{nearest['lon']}"
if dist <= 30 and nearest_idx not in found:
found.append(nearest_idx)
status = f"🎯 找到 {nearest['name']}!距離:{dist:.2f} 公尺 ✅"
html = f"""
<h3>{nearest['name']} 🎉 已找到!</h3>
<img src="https://media.giphy.com/media/5GoVLqeAOo6PK/giphy.gif" style="width:100%; max-height:200px;">
"""
elif nearest_idx in found:
status = f"✅ {nearest['name']} 已找到!目前距離:{dist:.2f} 公尺"
html = f'<a href="{gmap}" target="_blank">➡️ 再次導航至 {nearest["name"]}</a>'
else:
status = f"📍 距離 {nearest['name']}:{dist:.2f} 公尺,請再靠近一些"
html = f'<a href="{gmap}" target="_blank">➡️ 點我導航至 {nearest["name"]}</a>'
if len(found) == len(treasures):
html += """
<h2 style="text-align:center;">🎉 所有寶藏已找到!</h2>
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGU4YzZhOTdjYjJmODlmZDEwZmExZDcyOTU4NGY5NGY4MzU3ZDM2YSZjdD1n/26n6WywJyh39n1pBu/giphy.gif" style="width:100%; max-height:300px;">
"""
progress = f"✅ 已找到:{', '.join([treasures[i]['name'] for i in sorted(found)])}" if found else ""
return status, html, progress, found
with gr.Blocks() as demo:
gr.Markdown("# 🗺️ GPS 尋寶遊戲")
gr.Markdown("📍 自動找最近寶藏,進入 30 公尺範圍內顯示動畫 🎉")
gr.HTML("""
<button id="gpsButton" style="padding:10px;font-size:16px;">📍 點我取得 GPS</button>
<script>
document.addEventListener("DOMContentLoaded", function () {
document.getElementById("gpsButton").addEventListener("click", function () {
navigator.geolocation.getCurrentPosition(
function(pos) {
const latbox = document.querySelectorAll("textarea")[0];
const lonbox = document.querySelectorAll("textarea")[1];
latbox.value = pos.coords.latitude;
lonbox.value = pos.coords.longitude;
latbox.dispatchEvent(new Event('input'));
lonbox.dispatchEvent(new Event('input'));
},
function(err) {
alert("⚠️ GPS 錯誤:" + err.message);
}
);
});
});
</script>
""")
with gr.Row():
lat = gr.Textbox(label="Latitude")
lon = gr.Textbox(label="Longitude")
status = gr.Markdown()
link = gr.HTML()
progress = gr.Markdown()
found_state = gr.State([])
btn = gr.Button("🔍 檢查距離 + 導航")
btn.click(fn=check_nearest, inputs=[lat, lon, found_state],
outputs=[status, link, progress, found_state])
demo.launch()
|