File size: 5,893 Bytes
d600364 7ea91b8 e3f741a 29f2775 d600364 7f43dee 5056326 29f2775 4919331 5056326 7ea91b8 d600364 432e03d 5056326 d600364 5056326 e3f741a 7ea91b8 7f43dee 9831b1b 7f43dee 5056326 7f43dee e3f741a 7f43dee 5056326 9831b1b 7f43dee 5056326 d600364 d6e0895 9a51b1c d84fb96 2cbf59b d6e0895 29f2775 d6e0895 a575f4e d6e0895 a575f4e 29f2775 7f43dee 29f2775 5056326 2af3d93 e11f617 d84fb96 2af3d93 2cbf59b 5056326 7f43dee d6e0895 a575f4e 7f43dee 5056326 7f43dee 5056326 cfbb7e9 5056326 29f2775 b366683 29f2775 5056326 b366683 29f2775 5056326 29f2775 5056326 29f2775 5056326 29f2775 b366683 5056326 29f2775 5056326 b366683 29f2775 b366683 5056326 29f2775 b366683 5056326 b366683 29f2775 b366683 29f2775 b366683 29f2775 b366683 29f2775 b366683 29f2775 b366683 29f2775 b366683 29f2775 b366683 5056326 29f2775 5056326 b366683 2af3d93 e11f617 d600364 2af3d93 | 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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | import streamlit as st
import pandas as pd
from sqlalchemy import create_engine, text
from sshtunnel import SSHTunnelForwarder
import os
import tempfile
from datetime import datetime, timedelta
# --- Config ---
st.set_page_config(page_title="GPS Viewer", layout="wide")
st.title("🚀 GPS Track on Google Maps")
# --- Secrets ---
SSH_KEY = st.secrets["SSH_KEY"]
SSH_USER = st.secrets["SSH_USER"]
SSH_HOST = st.secrets["DB_HOST"]
REMOTE_BIND_HOST = st.secrets["REMOTE_BIND_HOST"]
LOCAL_BIND_HOST = st.secrets["LOCAL_BIND_HOST"]
MYSQL_USER = st.secrets["MYSQL_USER"]
MYSQL_PASSWORD = st.secrets["MYSQL_PASSWORD"]
MYSQL_DB = st.secrets["DB_NAME"]
MYSQL_PORT = int(st.secrets["MYSQL_PORT"])
GOOGLE_MAPS_API_KEY = st.secrets["GOOGLE_MAPS_API_KEY"]
# --- Write SSH key to temp file ---
with tempfile.NamedTemporaryFile(delete=False, suffix=".pem") as temp_file:
temp_file.write(SSH_KEY.encode())
temp_file_path = temp_file.name
os.chmod(temp_file_path, 0o600)
# --- SSH Tunnel ---
@st.cache_resource
def start_ssh_tunnel():
tunnel = SSHTunnelForwarder(
(SSH_HOST, 22),
ssh_username=SSH_USER,
ssh_pkey=temp_file_path,
remote_bind_address=(REMOTE_BIND_HOST, MYSQL_PORT),
local_bind_address=(LOCAL_BIND_HOST, MYSQL_PORT)
)
tunnel.start()
return tunnel
server = start_ssh_tunnel()
if not server.is_active:
server.start()
# --- SQLAlchemy Engine ---
MYSQL_URL = f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{LOCAL_BIND_HOST}:{MYSQL_PORT}/{MYSQL_DB}"
engine = create_engine(MYSQL_URL, pool_pre_ping=True, pool_recycle=3600)
# --- Fetch timestamp range from DB ---
def get_timestamp_range():
try:
with engine.connect() as conn:
result = conn.execute(text("SELECT MIN(timestamp) as min_ts, MAX(timestamp) as max_ts FROM telemetry_data")).fetchone()
return result[0], result[1] # min_ts, max_ts
except Exception as e:
st.error(f"Error fetching timestamp range: {e}")
return None, None
min_ts, max_ts = get_timestamp_range()
# --- Time range selection ---
st.sidebar.header("Filter by Time Frame")
if min_ts and max_ts:
start_datetime = st.sidebar.slider("Start and End Time", min_value=min_ts, max_value=max_ts,
value=(min_ts, max_ts), format="YYYY-MM-DD HH:mm:ss")
start_ts, end_ts = start_datetime
else:
start_ts = end_ts = None
# --- Load GPS Data ---
def load_gps_data(from_ts, to_ts):
try:
query = text("""
SELECT latitude, longitude, device_id
FROM telemetry_data
WHERE timestamp BETWEEN :from_ts AND :to_ts
ORDER BY timestamp
""")
with engine.connect() as conn:
df = pd.read_sql(query, conn, params={"from_ts": from_ts, "to_ts": to_ts})
return df
except Exception as e:
st.error(f"Error loading GPS data: {e}")
return pd.DataFrame()
gps_data = pd.DataFrame()
if start_ts and end_ts:
gps_data = load_gps_data(start_ts, end_ts)
# --- Display on Google Maps ---
if not gps_data.empty:
points = gps_data[["latitude", "longitude"]].to_dict(orient="records")
js_points = ",\n".join([f"{{ lat: {p['latitude']}, lng: {p['longitude']} }}" for p in points])
st.components.v1.html(f"""
<div id=\"map\" style=\"height: 80vh; width: 100%;\"></div>
<div style=\"text-align: center; margin: 10px;\">
<button onclick=\"startReplay()\">▶️ Start</button>
<button onclick=\"pauseReplay()\">⏸ Pause</button>
<button onclick=\"resetReplay()\">🔄 Reset</button>
<button onclick=\"stepBack()\">⏪ Back</button>
<button onclick=\"stepForward()\">⏩ Forward</button>
</div>
<script src=\"https://maps.googleapis.com/maps/api/js?key={GOOGLE_MAPS_API_KEY}\"></script>
<script>
const pathCoords = [{js_points}];
let map, marker, interval, index = 0, playing = false;
function initMap() {{
map = new google.maps.Map(document.getElementById(\"map\"), {{
zoom: 14,
center: pathCoords[0],
mapTypeId: \"roadmap\"
}});
new google.maps.Polyline({{
path: pathCoords,
geodesic: true,
strokeColor: \"#FF0000\",
strokeOpacity: 1.0,
strokeWeight: 2
}}).setMap(map);
marker = new google.maps.Marker({{
position: pathCoords[0],
map: map,
icon: {{
path: google.maps.SymbolPath.CIRCLE,
scale: 6,
fillColor: \"#0000FF\",
fillOpacity: 1.0,
strokeWeight: 1
}}
}});
}}
function updateMarker() {{
marker.setPosition(pathCoords[index]);
map.panTo(pathCoords[index]);
}}
function playStep() {{
if (index < pathCoords.length - 1) {{
index++;
updateMarker();
}} else {{
clearInterval(interval);
playing = false;
}}
}}
function startReplay() {{
if (!playing) {{
interval = setInterval(playStep, 200);
playing = true;
}}
}}
function pauseReplay() {{
clearInterval(interval);
playing = false;
}}
function resetReplay() {{
pauseReplay();
index = 0;
updateMarker();
}}
function stepBack() {{
pauseReplay();
if (index > 0) {{
index--;
updateMarker();
}}
}}
function stepForward() {{
pauseReplay();
if (index < pathCoords.length - 1) {{
index++;
updateMarker();
}}
}}
window.onload = initMap;
</script>
""", height=750)
st.subheader("📊 Telemetry Data")
st.dataframe(gps_data[["device_id"]])
else:
st.info("No GPS data available for selected time frame.") |