Frankreich, Niederlande, Belgien als weitere Quellen (noch im Test)

- Quellen fr (SNCF), nl (OVapi), be (GTFS.be), Kacheln für alle 5 Länder
- Landesgrenzen für 5 Länder (borders.json), Gitter-Cache für die Prüfung
- Echtzeit-Abgleich auch über Haltestellen-ID (FR, NL), über den Bahnhof
  bei Gleiswechsel
- SNCF: Gattung aus der Trip-ID, Zugnummer aus dem Headsign; NL-Linien gekürzt
- calendar.txt optional (SNCF hat nur calendar_dates)
- OSM je Land zugeschnitten vor pfaedle; beim Zusammenführen je Objekt
  nur die neueste Version (time-filter)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
This commit is contained in:
Claude
2026-10-22 07:06:47 +02:00
parent 76a8fc0806
commit d339da5da2
12 changed files with 203 additions and 68 deletions
+18 -8
View File
@@ -21,6 +21,7 @@ from pathlib import Path
import httpx
from . import border
from . import config as C
log = logging.getLogger("prepare")
@@ -189,8 +190,12 @@ def ensure_osm() -> Path:
"-o", str(part), "--overwrite"], check=True)
raw.unlink() # spart Platz
parts.append(str(part))
tmp = d / "rail.tmp.osm.pbf"
subprocess.run([C.OSMIUM, "merge", *parts, "-o", str(tmp), "--overwrite"], check=True)
merged, tmp = d / "rail.merged.osm.pbf", d / "rail.tmp.osm.pbf"
subprocess.run([C.OSMIUM, "merge", *parts, "-o", str(merged), "--overwrite"], check=True)
# Die Länder-Dateien sind unterschiedlich alt: Objekte an den Grenzen stecken dann in zwei
# Versionen drin, woran osmium extract scheitert. time-filter behält je Objekt die neueste.
subprocess.run([C.OSMIUM, "time-filter", str(merged), "-o", str(tmp), "--overwrite"], check=True)
merged.unlink()
tmp.rename(rail)
urls_file.write_text(wanted)
for p in parts:
@@ -200,19 +205,24 @@ def ensure_osm() -> Path:
return rail
def run_pfaedle(work: Path, rail: Path) -> Path | None:
def run_pfaedle(work: Path, rail: Path, source: str) -> Path | None:
if not shutil.which(C.PFAEDLE):
log.warning("pfaedle nicht gefunden – Züge fahren Luftlinie zwischen den Haltestellen")
return None
osm = ensure_osm()
log.info("pfaedle rechnet Streckenverläufe …")
# Netz aufs Land (+ Rand) zuschneiden: die Fahrpläne enthalten Züge bis Paris oder Hamburg,
# pfaedle würde sonst das Netz aller Länder laden. Ausserhalb zeigt ohnehin der Nachbar-Fahrplan.
osm = work / "region.osm.pbf"
subprocess.run([C.OSMIUM, "extract", "-b", border.bbox(source), str(ensure_osm()), "-o", str(osm),
"--overwrite"], check=True)
log.info("[%s] pfaedle rechnet Streckenverläufe …", source)
t = time.time()
res = subprocess.run([C.PFAEDLE, "-D", "-m", "rail", "-x", str(osm), str(rail)], cwd=work)
osm.unlink()
out = work / "gtfs-out"
if res.returncode != 0 or not (out / "shapes.txt").exists():
log.error("pfaedle fehlgeschlagen (Code %s) – nutze Fahrplan ohne Streckenverläufe", res.returncode)
log.error("[%s] pfaedle fehlgeschlagen (Code %s) – nutze Fahrplan ohne Streckenverläufe", source, res.returncode)
return None
log.info("pfaedle fertig in %.0f s", time.time() - t)
log.info("[%s] pfaedle fertig in %.0f s", source, time.time() - t)
return out
@@ -306,7 +316,7 @@ def prepare(source: str, state: dict, zip_path: Path | None = None, force: bool
if zip_path.parent == work:
zip_path.unlink()
shaped = run_pfaedle(work, work / "rail")
shaped = run_pfaedle(work, work / "rail", source)
meta["shapes"] = shaped is not None
if shaped:
pack_shapes(shaped)