Initial: Mesh-Configs vom 03.09.2026 01:23
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""build_gtfs_db.py — GTFS-Datei in SQLite laden (einmalig).
|
||||
Aufruf: python3 build_gtfs_db.py <gtfs_zip> <out_db> [gtfs_dir]
|
||||
Legt Tabellen stops/trips/st an (Schema siehe gtfs-timetable-routing/references).
|
||||
"""
|
||||
import zipfile, sqlite3, os, sys, time
|
||||
|
||||
def main():
|
||||
gtfs_zip = sys.argv[1]
|
||||
out_db = sys.argv[2]
|
||||
work = sys.argv[3] if len(sys.argv) > 3 else "/tmp/gtfs_x"
|
||||
os.makedirs(work, exist_ok=True)
|
||||
# extrahieren
|
||||
with zipfile.ZipFile(gtfs_zip) as z:
|
||||
z.extractall(work)
|
||||
t0 = time.time()
|
||||
con = sqlite3.connect(out_db)
|
||||
con.execute("PRAGMA journal_mode=MEMORY"); con.execute("PRAGMA temp_store=MEMORY")
|
||||
con.executescript("""
|
||||
CREATE TABLE stops(stop_id TEXT PRIMARY KEY, stop_name TEXT, stop_lat REAL, stop_lon REAL);
|
||||
CREATE TABLE trips(trip_id TEXT PRIMARY KEY, route_id TEXT, service_id TEXT);
|
||||
CREATE TABLE st(stop_id TEXT, trip_id TEXT, seq INT, arr TEXT, dep TEXT);
|
||||
CREATE INDEX idx_st_stop ON st(stop_id);
|
||||
CREATE INDEX idx_st_trip ON st(trip_id);
|
||||
CREATE INDEX idx_st_seq ON st(trip_id, seq);
|
||||
""")
|
||||
# stops.txt: stop_name,parent_station,stop_id,stop_lat,stop_lon,...
|
||||
n=0
|
||||
with open(f"{work}/stops.txt", encoding="utf-8") as f:
|
||||
f.readline()
|
||||
for line in f:
|
||||
p = line.strip().split(",")
|
||||
if len(p) >= 5 and p[3] and p[4]:
|
||||
sid = p[2] if p[2] else p[1] # bevorzugt stop_id, fallback parent_station
|
||||
con.execute("INSERT OR IGNORE INTO stops VALUES(?,?,?,?)", (sid, p[0], p[3], p[4]))
|
||||
n += 1
|
||||
print("stops:", n)
|
||||
# trips.txt: route_id,service_id,trip_id
|
||||
n=0
|
||||
with open(f"{work}/trips.txt", encoding="utf-8") as f:
|
||||
f.readline()
|
||||
for line in f:
|
||||
p = line.strip().split(",")
|
||||
if len(p) >= 3 and p[0]:
|
||||
con.execute("INSERT OR IGNORE INTO trips VALUES(?,?,?)", (p[2], p[0], p[1]))
|
||||
n += 1
|
||||
print("trips:", n)
|
||||
# stop_times.txt: trip_id,arrival_time,departure_time,stop_id,stop_sequence,...
|
||||
n=0; batch=[]
|
||||
with open(f"{work}/stop_times.txt", encoding="utf-8") as f:
|
||||
f.readline()
|
||||
for line in f:
|
||||
p = line.strip().split(",")
|
||||
if len(p) >= 5 and p[0] and p[3]:
|
||||
batch.append((p[3], p[0], int(p[4]) if p[4].isdigit() else 0, p[1], p[2]))
|
||||
if len(batch) >= 50000:
|
||||
con.executemany("INSERT INTO st VALUES(?,?,?,?,?)", batch); n += len(batch); batch = []
|
||||
if batch:
|
||||
con.executemany("INSERT INTO st VALUES(?,?,?,?,?)", batch); n += len(batch)
|
||||
con.commit(); con.close()
|
||||
print("stop_times:", n, "| DB gebaut in %.1fs" % (time.time()-t0))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gtfs_suche.py — echte schnellste Nahverkehrs-Verbindung aus GTFS-SQLite (zeitbasierter Dijkstra).
|
||||
Aufruf: python3 gtfs_suche.py <von_stop_id> <nach_stop_id> [HH:MM]
|
||||
Vorraussetzung: DB aus build_gtfs_db.py. Aktiv-ID-Regel: wenn ein Bahnhof mehrere IDs hat,
|
||||
die mit COUNT(*)>0 in st nehmen.
|
||||
"""
|
||||
import sqlite3, sys, heapq
|
||||
from collections import defaultdict
|
||||
|
||||
DB = "/tmp/gtfs_rt.db"
|
||||
|
||||
def to_sec(t):
|
||||
p = t.split(":")
|
||||
h = int(p[0]); m = int(p[1]) if len(p) > 1 else 0
|
||||
return h*3600 + m*60
|
||||
|
||||
def fmt(t):
|
||||
return f"{t//3600:02d}:{(t%3600)//60:02d}"
|
||||
|
||||
def aktive_id(con, stop_name):
|
||||
"""Findet die stop_id mit st-Vorkommen (Bahnhof kann mehrere IDs haben)."""
|
||||
cands = con.execute("SELECT stop_id FROM stops WHERE stop_name=?", (stop_name,)).fetchall()
|
||||
for (sid,) in cands:
|
||||
if con.execute("SELECT COUNT(*) FROM st WHERE stop_id=?", (sid,)).fetchone()[0] > 0:
|
||||
return sid
|
||||
return None
|
||||
|
||||
def verbinde(von_id, nach_id, start="09:00"):
|
||||
con = sqlite3.connect(DB)
|
||||
con.row_factory = sqlite3.Row
|
||||
dep0 = to_sec(start)
|
||||
deps_by_stop = defaultdict(list); stops_by_trip = defaultdict(list)
|
||||
for r in con.execute("SELECT trip_id, stop_id, seq, arr, dep FROM st"):
|
||||
deps_by_stop[r["stop_id"]].append((to_sec(r["dep"]), r["trip_id"], r["seq"]))
|
||||
stops_by_trip[r["trip_id"]].append((r["seq"], r["stop_id"], to_sec(r["arr"]), to_sec(r["dep"])))
|
||||
con.close()
|
||||
for k in deps_by_stop: deps_by_stop[k].sort()
|
||||
for k in stops_by_trip: stops_by_trip[k].sort()
|
||||
|
||||
INF = float('inf')
|
||||
dist = defaultdict(lambda: INF); dist[von_id] = dep0
|
||||
heap = [(dep0, von_id)]
|
||||
pred = {}
|
||||
while heap:
|
||||
t, stop = heapq.heappop(heap)
|
||||
if t != dist[stop]: continue
|
||||
if stop == nach_id: break
|
||||
for dep_s, tid, seq in deps_by_stop[stop]:
|
||||
if dep_s < t: continue
|
||||
for (sseq, sid, arr_s, dep2) in stops_by_trip[tid]:
|
||||
if sseq <= seq: continue
|
||||
if arr_s < dist[sid]:
|
||||
dist[sid] = arr_s; pred[sid] = (tid, stop)
|
||||
heapq.heappush(heap, (arr_s, sid))
|
||||
if nach_id not in pred and nach_id != von_id:
|
||||
return None
|
||||
if nach_id == von_id:
|
||||
return (0, [], 0, fmt(dep0), fmt(dep0))
|
||||
trips = []; cur = nach_id
|
||||
while cur in pred and cur != von_id:
|
||||
tid, prev = pred[cur]; trips.append(tid); cur = prev
|
||||
trips.reverse()
|
||||
dur = dist[nach_id] - dep0
|
||||
return (dur, trips, len(set(trips)), fmt(dep0), fmt(dist[nach_id]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
von = sys.argv[1]; nach = sys.argv[2]
|
||||
start = sys.argv[3] if len(sys.argv) > 3 else "09:00"
|
||||
r = verbinde(von, nach, start)
|
||||
if r:
|
||||
dur, trips, umst, dep, arr = r
|
||||
print(f"{dur//3600}h {(dur%3600)//60:02d}min | {dep}->{arr} | {umst} Umstieg(e)")
|
||||
else:
|
||||
print("Keine Verbindung")
|
||||
Reference in New Issue
Block a user