Streckenverläufe kompakt speichern und nur bei Bedarf lesen

pfaedle erzeugt für die Schweiz eine 2.6-GB-shapes.txt; die wird jetzt
in shapes.bin (670 MB, mmap) umgewandelt. Halt-Positionen kommen aus
shape_dist_traveled. RAM-Empfehlung im README ergänzt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
This commit is contained in:
Guido
2026-10-21 15:07:14 +02:00
co-authored by Claude Opus 5.5
parent efeba65fb1
commit 0cd2c3951a
5 changed files with 74 additions and 47 deletions
+35
View File
@@ -15,6 +15,7 @@ import shutil
import subprocess
import time
import zipfile
from array import array
from pathlib import Path
import httpx
@@ -168,6 +169,38 @@ def run_pfaedle(work: Path, rail: Path) -> Path | None:
return out
def pack_shapes(d: Path) -> None:
"""shapes.txt ist für die Schweiz ~2.6 GB (56 Mio. Punkte) – viel zu gross für den Speicher.
Umwandeln in shapes.bin (float32: lon, lat, Meter ab Start) + shapes.json (id -> [Start, Anzahl]),
der Server liest daraus nur die Strecken, die er gerade braucht."""
src = d / "shapes.txt"
if not src.exists():
return
index, cur, pts, n = {}, None, [], 0
with open(d / "shapes.bin", "wb") as out:
def flush():
nonlocal n
if cur is not None:
pts.sort()
array("f", [v for p in pts for v in p[1:]]).tofile(out)
index[cur] = [n, len(pts)]
n += len(pts)
with open(src, encoding="utf-8-sig", newline="") as f:
r = csv.reader(f)
h = {k: i for i, k in enumerate(next(r))}
sid, lat, lon, seq = h["shape_id"], h["shape_pt_lat"], h["shape_pt_lon"], h["shape_pt_sequence"]
dist = h["shape_dist_traveled"]
for row in r:
if row[sid] != cur:
flush()
assert row[sid] not in index, "shapes.txt nicht nach shape_id gruppiert"
cur, pts = row[sid], []
pts.append((int(row[seq]), float(row[lon]), float(row[lat]), float(row[dist])))
flush()
(d / "shapes.json").write_text(json.dumps(index, separators=(",", ":")))
src.unlink()
def publish(src: Path, meta: dict) -> None:
(src / "meta.json").write_text(json.dumps(meta))
dest = C.FEEDS_DIR / meta["feed_version"]
@@ -212,6 +245,8 @@ def main() -> None:
shaped = run_pfaedle(work, work / "rail")
meta["shapes"] = shaped is not None
if shaped:
pack_shapes(shaped)
meta["source_file"] = name
meta["prepared_at"] = int(time.time())
publish(shaped or work / "rail", meta)