Zum Hauptinhalt springen
Data Connectors · Öffentlicher Nahverkehr

GTFS-RT

Der Node lädt einen GTFS-Realtime-Feed (Protocol Buffers) in einem Worker-Thread, dekodiert ihn streamend, filtert die Entitäten anhand der Haltestellen-, Linien- und Fahrt-IDs, die er vom GTFS-Node gelernt hat, und gibt Fahrtaktualisierungen (TripUpdates), Fahrzeugpositionen (VehiclePositions) und Störungsmeldungen (Alerts) in Batches über drei Ausgänge aus. Er ist für sehr große, deutschlandweite Feeds ausgelegt.

  • Datenquelle
  • Triggerbasiert
  • GTFS-Realtime
Node-Typ
gtfs-realtime
Paket
@htag/node-red-contrib-hypertegrity-urbanmobility
Eingänge / Ausgänge
1 / 3
Datenquelle
Beliebiger GTFS-Realtime-Feed als Protobuf über HTTP(S) (Standard: realtime.gtfs.de)

Wofür Sie den Node verwenden​

GTFS-Realtime ergänzt den statischen GTFS-Fahrplan um Echtzeitinformationen. Ein Feed ist eine binäre Protobuf-Nachricht (FeedMessage) mit drei Arten von Entitäten: TripUpdates mit Verspätungen und geänderten Ankunfts- und Abfahrtszeiten je Halt, VehiclePositions mit GPS-Position, Fahrtrichtung und Geschwindigkeit der Fahrzeuge sowie Alerts mit Störungsmeldungen und deren Ursache und Auswirkung. Deutschlandweite Feeds enthalten Zehntausende Entitäten je Abruf. Der GTFS-RT-Node reduziert diese Menge auf die Haltestellen, Linien und Fahrten Ihres Gebiets, deren IDs er aus den Ausgängen des GTFS-Nodes übernimmt.

Typische Einsatzfälle sind:

  • Abfahrtsmonitore mit aktuellen Verspätungen für die Haltestellen einer Kommune.
  • Live-Karten mit Fahrzeugpositionen der lokalen Bus- und Bahnlinien.
  • Störungshinweise auf Dashboards oder in Benachrichtigungsflows.

So funktioniert der Node​

1

Filter lernen

Nachrichten mit GtfsStop-, GtfsRoute- oder GtfsTrip-Entitäten vom GTFS-Node ersetzen die jeweilige Filterliste. Sie lösen keinen Abruf aus und werden auf der Festplatte gesichert.

2

Auslösen

Jede andere Nachricht startet den Abruf, sofern mindestens eine Filterliste oder msg.stopIds vorhanden ist.

3

Streamend dekodieren

Ein Worker-Thread lädt den Feed (gzip und deflate werden entpackt) und dekodiert die Protobuf-Entitäten Stück für Stück, ohne die Node-RED-Ereignisschleife zu blockieren.

4

Filtern und ausgeben

Entitäten, die zu einer gelernten Fahrt-, Linien- oder Haltestellen-ID passen, werden in Batches an die Ausgänge TripUpdates, VehiclePositions und Alerts gesendet.

Ergebnis: Drei Ausgänge mit Fahrtaktualisierungen, Fahrzeugpositionen und Störungsmeldungen für Ihr Gebiet, in Batches konfigurierbarer Größe.

Der Node ist triggerbasiert. Ein Inject-Node mit einem Intervall von ein bis fünf Minuten ist üblich; viele Feeds werden alle 30 bis 60 Sekunden aktualisiert. Die gelernten Filterlisten liegen als Datei im temporären Verzeichnis des Betriebssystems unter node-red-gtfs-realtime/<Node-ID>/filters.json, überleben Deploys und Neustarts und werden gelöscht, wenn der Node aus dem Flow entfernt wird.

Konfiguration​

Der Bearbeitungsdialog hat keine Registerkarten.

FeldTypStandardBedeutung
NameTextleerAnzeigename des Nodes im Flow.
Feed URLTextsiehe untenAdresse der GTFS-Realtime-Protobuf-Datei. http:// und https:// werden unterstützt, Weiterleitungen befolgt.
Batch-GrößeZahl1000Maximale Anzahl Entitäten je Ausgangsnachricht und Ausgang. Sobald ein Ausgangstyp diese Zahl erreicht, werden alle drei Batches gesendet.

Standardwerte der URL-Felder:

Feed URL: https://realtime.gtfs.de/realtime-free.pb

Die Filterung erfolgt über IDs, die vom GTFS-Node geliefert oder in msg.stopIds übergeben werden.

Eingang​

Der Node unterscheidet zwei Arten von Nachrichten anhand des Payloads.

EigenschaftTypBedeutung
msg.payloadArray von Entitäten mit id beginnend mit urn:ngsi-ld:GtfsFilterdaten vom GTFS-Node. Je nach type (GtfsStop, GtfsRoute, GtfsTrip) ersetzt der Inhalt vollständig die Liste der Haltestellen-, Linien- oder Fahrt-IDs. Agency- und StopTime-Entitäten werden ignoriert. Es wird kein Abruf ausgelöst.
msg.payloadbeliebiger anderer WertLöst den Abruf des Feeds aus.
msg.stopIdsArray von TextenZusätzliche Haltestellen-IDs, die mit der gelernten Liste vereinigt werden. Erlaubt einen Abruf auch ohne verbundenen GTFS-Node.
msg.topicTextWird in die Ausgangsnachrichten übernommen. Die Ausgangsnachrichten enthalten topic, payload, feedTimestamp und totalEntities.

Die IDs werden beim Vergleich wie im GTFS-Node bereinigt (alle Zeichen außer Buchstaben, Ziffern, _ und - werden zu _), sodass eine Fahrt-ID de:vrn:12-345:67 aus dem Feed zur gelernten ID de_vrn_12-345_67 passt.

Ausgang​

Jeder Ausgang liefert Nachrichten mit einem Array im Payload sowie msg.feedTimestamp (Zeitstempel des Feeds, ISO 8601) und msg.totalEntities (bisher gelesene Entitäten des Feeds). Ein Ausgang erhält nur dann eine Nachricht, wenn der Batch für ihn mindestens eine Entität enthält. Zeitstempel werden aus Unix-Sekunden in ISO 8601 umgerechnet, Aufzählungswerte in ihre Namen übersetzt.

Ausgang 1: TripUpdates​

Eine Fahrtaktualisierung passt, wenn ihre Fahrt-ID, ihre Linien-ID oder eine Haltestellen-ID in stopTimeUpdate zu den Filterlisten gehört.

[
{
"id": "tu-20261001-0815-105",
"type": "GtfsRtTripUpdate",
"trip": {"tripId": "de:vrn:12-345:67", "routeId": "de:vrn:12-345", "directionId": 0, "startTime": "08:15:00", "startDate": "20261001", "scheduleRelationship": "SCHEDULED"},
"vehicle": {"id": "4711", "label": "105", "licensePlate": null},
"stopTimeUpdate": [
{"stopSequence": 5, "stopId": "de:07312:1001", "scheduleRelationship": "SCHEDULED",
"arrival": {"delay": 120, "time": "2026-10-01T06:19:00.000Z", "uncertainty": 0},
"departure": {"delay": 120, "time": "2026-10-01T06:20:00.000Z", "uncertainty": 0}}
],
"timestamp": "2026-10-01T06:17:40.000Z",
"delay": 120
}
]
FeldBedeutung
id, typeEntitäts-ID aus dem Feed und immer GtfsRtTripUpdate.
tripFahrt-ID, Linien-ID, Richtung, Startzeit und Startdatum (YYYYMMDD) sowie scheduleRelationship (SCHEDULED, ADDED, CANCELED, …).
vehicleFahrzeug-ID, Anzeigename und Kennzeichen, falls im Feed enthalten, sonst null.
stopTimeUpdateJe Halt: Reihenfolge, Haltestellen-ID, Status sowie arrival und departure mit Verspätung in Sekunden, Zeitpunkt und Unsicherheit.
timestamp, delayZeitpunkt der Meldung und Gesamtverspätung der Fahrt in Sekunden.

Ausgang 2: VehiclePositions​

Eine Fahrzeugposition passt, wenn ihre Fahrt-ID, Linien-ID oder aktuelle Haltestellen-ID zu den Filterlisten gehört.

[
{
"id": "vp-4711",
"type": "GtfsRtVehiclePosition",
"trip": {"tripId": "de:vrn:12-345:67", "routeId": "de:vrn:12-345", "directionId": 0, "startTime": "08:15:00", "startDate": "20261001", "scheduleRelationship": "SCHEDULED"},
"vehicle": {"id": "4711", "label": "105", "licensePlate": null},
"position": {"latitude": 49.4447, "longitude": 7.7689, "bearing": 270, "speed": 8.3},
"location": {"type": "Point", "coordinates": [7.7689, 49.4447]},
"currentStopSequence": 5,
"stopId": "de:07312:1001",
"currentStatus": "IN_TRANSIT_TO",
"timestamp": "2026-10-01T06:18:10.000Z",
"congestionLevel": "UNKNOWN"
}
]
FeldBedeutung
positionBreite, Länge, Fahrtrichtung in Grad und Geschwindigkeit in Metern pro Sekunde (laut GTFS-RT-Spezifikation).
locationDieselbe Position als GeoJSON-Punkt [Länge, Breite].
currentStopSequence, stopId, currentStatusNächster beziehungsweise aktueller Halt und Status INCOMING_AT, STOPPED_AT oder IN_TRANSIT_TO.
congestionLevelVerkehrslage von UNKNOWN bis SEVERE_CONGESTION.
trip, vehicle, timestampWie bei TripUpdates.

Ausgang 3: Alerts​

Eine Störungsmeldung passt, wenn eine ihrer betroffenen Entitäten (informedEntity) eine gelernte Haltestellen-, Linien- oder Fahrt-ID nennt.

[
{
"id": "alert-2026-1042",
"type": "GtfsRtAlert",
"activePeriod": [{"start": "2026-10-01T04:00:00.000Z", "end": "2026-10-03T20:00:00.000Z"}],
"informedEntity": [{"routeId": "de:vrn:12-345"}],
"cause": "CONSTRUCTION",
"effect": "DETOUR",
"url": "https://www.beispielverkehr.de/stoerungen/1042",
"headerText": "Umleitung Linie 105",
"descriptionText": "Wegen Bauarbeiten wird die Haltestelle Marktplatz nicht bedient."
}
]
FeldBedeutung
activePeriodGültigkeitszeiträume der Meldung.
informedEntityBetroffene Unternehmen, Linien, Verkehrsmittel, Haltestellen oder Fahrten.
cause, effectUrsache (STRIKE, WEATHER, CONSTRUCTION, …) und Auswirkung (NO_SERVICE, DETOUR, SIGNIFICANT_DELAYS, …).
url, headerText, descriptionTextLink, Überschrift und Beschreibung in der ersten im Feed enthaltenen Sprache.

Beispiel-Flow​

Das Paket bringt einen Beispiel-Flow mit. Sie importieren ihn in Node-RED über Menü → Importieren → Beispiele → @htag/node-red-contrib-hypertegrity-urbanmobility → gtfs-realtime. Alternativ laden Sie die Datei gtfs-realtime.json herunter und importieren sie über Menü → Importieren. Der Beispiel-Flow enthält zwei GTFS-Nodes mit dem Grenzpolygon von Kaiserslautern; in der Kurzfassung unten ist ein Node mit Platzhalter dargestellt.

Schritt 1

GTFS lernt die IDs

Ein täglicher Inject-Node löst den GTFS-Node aus; dessen Ausgänge Stops, Routes und Trips sind mit dem Eingang des GTFS-RT-Nodes verbunden.

Schritt 2

GTFS-RT ruft alle 5 Minuten ab

Ein zweiter Inject-Node löst den Abruf des Echtzeit-Feeds von realtime.gtfs.de aus.

Schritt 3

Drei Debug-Nodes

Je ein Debug-Node zeigt TripUpdates, VehiclePositions und Alerts in der Seitenleiste an.

Beispiel-Flow als JSON anzeigen
[
{"id": "tab1", "type": "tab", "label": "gtfs-realtime", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "01:00 daily", "props": [{"p": "payload"}], "repeat": "", "crontab": "00 01 * * *", "once": false, "onceDelay": "30", "topic": "", "payload": "", "payloadType": "date", "x": 190, "y": 200, "wires": [["gtfs1"]]},
{"id": "gtfs1", "type": "gtfs-data", "z": "tab1", "name": "gtfs.de", "url": "https://download.gtfs.de/germany/free/latest.zip", "refreshInterval": "23", "stopTimesBatchSize": 50000, "areas": "[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[\"<POLYGON-KOORDINATEN>\"]]},\"properties\":{\"display_name\":\"Kaiserslautern, Rheinland-Pfalz, Deutschland\",\"osm_id\":62652,\"osm_type\":\"relation\"}}]", "x": 330, "y": 200, "wires": [[], ["rt1"], ["rt1"], ["rt1"], []]},
{"id": "inject2", "type": "inject", "z": "tab1", "name": "5 min", "props": [{"p": "payload"}], "repeat": "300", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 170, "y": 340, "wires": [["rt1"]]},
{"id": "rt1", "type": "gtfs-realtime", "z": "tab1", "name": "", "url": "https://realtime.gtfs.de/realtime-free.pb", "batchSize": 1000, "x": 500, "y": 340, "wires": [["debug1"], ["debug2"], ["debug3"]]},
{"id": "debug1", "type": "debug", "z": "tab1", "name": "TripUpdates", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 720, "y": 300, "wires": []},
{"id": "debug2", "type": "debug", "z": "tab1", "name": "VehiclePositions", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 730, "y": 340, "wires": []},
{"id": "debug3", "type": "debug", "z": "tab1", "name": "Alerts", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 700, "y": 380, "wires": []}
]

Status und Fehler​

AnzeigeBedeutung
Gelber Ring „Worker wird gestartet...“Der Worker-Thread lädt das Protobuf-Schema. Auslöser in dieser Zeit werden zurückgestellt und nach dem Start ausgeführt.
Grauer Ring „Filter: 412 Haltestellen, 38 Linien, 9850 Fahrten“Ruhezustand mit Übersicht der gelernten Filterlisten. Ohne Filterlisten bleibt der Status leer.
Blauer Punkt „412 Haltestellen geladen“, „38 Linien geladen“, „9850 Fahrten geladen“Eine Filterliste wurde vom GTFS-Node übernommen und gespeichert.
Gelber Ring „Streaming: 25000“Der Feed wird gelesen; die Zahl nennt die bisher dekodierten Entitäten.
Grüner Punkt „Bereit (312 Entitäten)“Der Abruf ist abgeschlossen; die Zahl nennt die ausgegebenen Entitäten aller drei Ausgänge.
Gelber Ring „Kein Filter geladen – zuerst einen gtfs-data Node verbinden“Es gibt weder gelernte Filterlisten noch msg.stopIds; der Abruf wird verweigert und als Warnung gemeldet.
Warnung „Abruf läuft bereits...“Ein Abruf ist noch aktiv; der neue Auslöser wird verworfen.
Roter Ring „Fehler“Der Abruf ist fehlgeschlagen, zum Beispiel „HTTP 404“, „Request timeout“ (nach 60 Sekunden), „Proto schema not loaded yet“ oder ein Dekodierfehler.
Warnung „Worker exited with code 1“Der Worker-Thread wurde unerwartet beendet; der Node startet ihn nach 2 Sekunden neu.
Warnung „Filter cache load failed: …“ oder „Filter cache save failed: …“Die Filterdatei konnte nicht gelesen oder geschrieben werden.

Fehler werden über node.error gemeldet und können mit einem Catch-Node abgefangen werden.

Hinweise​

  • Zusammenspiel mit dem GTFS-Node: Verbinden Sie die Ausgänge 2 (Stops), 3 (Routes) und 4 (Trips) des GTFS-Nodes mit dem Eingang dieses Nodes. Jede Lieferung ersetzt die jeweilige Liste vollständig; es genügt, wenn der GTFS-Node einmal täglich läuft. Da die Listen auf der Festplatte gesichert werden, ist nach einem Neustart kein erneuter GTFS-Lauf nötig.
  • Rate Limits: Der Standard-Feed von gtfs.de ist frei zugänglich; beachten Sie die dort genannten Nutzungsbedingungen und Abrufintervalle. Jeder Abruf lädt den vollständigen Feed, deutschlandweit mehrere Megabyte.
  • Große Datenmengen: Verwenden Sie bei Debug-Nodes nicht die Einstellung „vollständiges Nachrichtenobjekt“ und begrenzen Sie die Anzeige; Batches mit 1000 Entitäten können den Browser belasten. Die Batch-Größe steuert, wie viele Entitäten je Nachricht gesendet werden.
  • Einheiten: Verspätungen in Sekunden, Geschwindigkeit in Metern pro Sekunde, Fahrtrichtung in Grad, Koordinaten in Dezimalgrad (WGS 84).
  • Personenbezogene Daten: Fahrzeugpositionen und die Felder vehicle.id, vehicle.label und vehicle.licensePlate (Kennzeichen) erlauben eine Zuordnung zu einzelnen Fahrzeugen und damit mittelbar zum Fahrpersonal. Speichern Sie solche Daten nur so lange wie nötig, verzichten Sie bei Veröffentlichungen auf Kennzeichen und prüfen Sie, ob die Fahrzeugkennungen entfernt oder pseudonymisiert werden sollten.

Weiterführende Themen​

Referenzen​