Introductie
FAQ
Verbinden met ZeroMQ
De meeste datafeeds van het NDOV Loket zijn ontsloten via het ZeroMQ protocol. Dit protocol maakt het mogelijk om een PubSub verbinding op te zetten, zodat je data automatisch gepusht krijgt zonder constant zelf nieuwe data op te hoeven halen. Voor deze basis tutorial zullen we gebruik maken van de programmeertaal Python met de PyZMQ library.
Allereerst moeten we er voor zorgen dat Python en PyZMQ geïnstalleerd zijn.
In deze tutorial doen we dit via de package manager uv, maar dit kan ook met een andere package manager naar keuze.
-
Volg de installatiestappen van uv voor jouw besturingssysteem.
-
Installeer Python via uv en maak een project:
uv python install --default
uv init ndovzeromq
cd ndovzeromq
- Installeer vervolgens de PyZMQ library:
uv add pyzmq
- Open
main.pyin een code editor naar keuze. Vervang de standaard code met onderstaande code-snippet:
#!/usr/bin/env python3
# Imports
from gzip import GzipFile
from io import BytesIO
import time
import zmq
# Maak de nieuwe subscriber socket aan
context = zmq.Context()
subscriber = context.socket(zmq.SUB)
# Verbind met NDOV Loket
subscriber.connect("{SERVER}")
# Vertel de server welke data je allemaal wil ontvangen.
# `/` is een wildcard die er voor zorgt dat je alle data ontvangt.
subscriber.setsockopt_string(zmq.SUBSCRIBE, "/")
# Ontvang alle data en loop door de nieuwe berichten
while True:
multipart = subscriber.recv_multipart()
# Berichten ontstaan uit 2 delen, het adres en de inhoud
address = multipart[0].decode('UTF-8')
# Om dataverkeer te besparen wordt alle inhoud gecomprimeerd met gzip,
# dit moeten we dus handmatig decomprimeren
try:
contents = b''.join(multipart[1:])
contents = GzipFile('','r',0,BytesIO(contents)).read()
contents = contents.decode('UTF-8')
# Print de tijd, het adres en de inhoud naar je terminal
print(int(time.time()), address, contents)
except UnicodeDecodeError:
print("Error bij decoderen")
raise
pass
except:
print("Error bij decomprimeren")
raise
pass
# Sluit de verbinding netjes af
subscriber.close()
context.term()
- Vervang
{SERVER}door de gewenste ZeroMQ server:
| Datafeed | Adres | Doel |
|---|---|---|
| BISON KV6 / KV15 / KV17 | tcp://pubsub.besteffort.ndovloket.nl:7658 | Verschillende bus-tram-metro data |
| BISON KV7/8 Turbo | tcp://pubsub.besteffort.ndovloket.nl:7817 | Geplande en actuele reisinformatie bus-tram-metro |
| NS InfoPlus | tcp://pubsub.besteffort.ndovloket.nl:7664 | Verschillende trein data |
| SIRI | tcp://pubsub.besteffort.ndovloket.nl:7666 | Nieuwe standaard voor geplande en actuele reisinformatie |
Voor gebruikers die deze databronnen in productie willen gebruiken is een aparte versie beschikbaar met een SLA. Neem contact op met het loket om hier toegang tot te krijgen.
Let op: voor NDOV gebruikers is maximaal één actieve verbinding per datafeed toegestaan. Om meerdere projecten te ontsluiten kunnen datafeeds lokaal herdistribueert worden via universal-sub-pubsub.
- Start het Python script:
uv run main.py
Als de verbinding succesvol is ontvang je nu alle berichten op de gekozen ZeroMQ server.
Krijg je fouten in je terminal? In onze Discord kunnen we je vast verder helpen.
Verbinden met MQTT
Dynamische Data
BISON
KV6
KV7/8 Turbo
KV15
KV17
NS InfoPlus
Dynamische VertrekStraat
Documentatie: pdf
RitInfo
Treinposities
Verstoringsinformatie
Vrije tekst
Dienstregeling
NeTEx
Wat is NeTEx
Network Timetable Exchange is een Europese technische specificatie om infrastructuur, dienstregelingen en tarieven uit te wisselen. Als je niet bij NS of EBS werkt spreek je het uit als Net-tex, als je daar wel werkt: welkom bij onze club. De standaard is gebaseerd op Transmodel, een conceptueel datamodel dat beschrijft hoe concepten zich naar elkaar verhouden, maar geen zelfstandige implementatie is. Alle objecten uit NeTEx staan in Transmodel, maar Transmodel beschrijft niet welke attributen een object moet hebben. NeTEx is geimplementeerd in XML Schema, een manier om de structuur van een XML bestand te beschrijven, af te dwingen en te kunnen valideren.
Als standaard is NeTEx het beste te beschrijven als een ontologie, een woordenboek met grammaticaregels. NeTEx beschrijft dat er een relatie kan zijn tussen een rit en een voertuigsoort en met welk attribuut die relatie moet worden gemaakt, maar dat leidt niet automatisch tot software dat in staat is om automatisch naar die foreign key toe te gaan om dat element te benaderen.
NeTEx in de Nederlandse Context
NeTEx is extreem uitgebreid het is om meerdere reden ondoenlijk om een "volledige NeTEx implementatie te maken". Binnen NeTEx kun je hetzelfde probleem op meerdere manieren oplossen. Er zijn afspraken gemaakt hoe een bepaald concept wordt beschreven. We noemen die afspraken: een profiel. In Nederland is er een Nederlands NeTEx profiel, in Frankrijk het Franse Profiel, in Italie is het Italiaanse Profiel, in Zwitserland het Zwitserse Profiel, in Noorwegen en Zweden het Nordic Profiel en in Duitsland: VDV462.

...binnen Europa hebben we trouwens het European Passenger Information Profile (EPIP) en het European Passenger Information Accessibility Profile (EPIAP).
Geen van de voorgenoemde profielen kan worden verwerkt met software die een ander profiel geeft geimplementeerd. Daarom hebben we in Nederland dus ook eigen documentatie.
Programmeren met een XML Schema als basis
XML Schema geeft de mogelijkheid om code te genereren.
Dit kan code zijn om een document te parsen en binnen de programmeertaal rechtstreeks in objecten te benaderen.
Goede resultaten zijn te halen met Java en Python.
Ondanks dat C# een zeer efficiente manier heeft om XML te verwerken, blijkt veelvuldig dat het XML Schema van NeTEx (extreem) complex is en dat het model genereren vanuit het XML Schema niet lukt.
De andere optie: vanuit een bestaand XML-bestand werkt wel, maar dat is valsspelen.
De complexiteit ontstaat door workarounds in NeTEx zodat middels XML Schema toch multiple inheritance toegepast kan worden.
Een tweede complexiteit ligt in het fenomeen repeated compound fields.
In een dienstregeling willen we een rit laten lopen langs haltes en andere belangrijke punten die de tijd kennen.
Een halte heeft de klasse ScheduledStopPoint, een belangrijkpunt is een TimingPoint.
Het afdwingen dat ScheduledStopPoint of TimingPoint in een lijst kunnen voorkomen, leidt tot een definitie als list[ScheduledStopPoint|TimingPoint].
Sommige programmeertalen kunnen deze definitie niet maken en moeten daarom terugvallen tot list[Any].
Daarmee moet je zowel tijdens het programmeren als in runtime gaan aftasten wat je binnen krijgt, dat is zeer ongewenst en leidt tot fouten.
Wat moet je zeker niet moet doen
XML zou je nooit met de hand moeten willen parsen. Kom niet in de verleiding om zelf een parser te schrijven, of met hier en daar een XPath-query elementen te selecteren. De kracht van een XML Schema is nu juist dat je binnen een programmeertaal de object definities hebt en kunt toepassen alsof je een object bewerkt.
Wat werkt wel
Na ruim tien jaar NeTEx ontwikkeling kan ik ook zeggen dat het, zonder superieure C# XML technieken, nooit gaat lukken om een XML van meerdere gigabytes groot in een keer te openen en te verwachten dat je daar binnen het genot van random-access hebt. Maar misschien heb jij inmiddels wel een systeem met een hoeveelheid RAM, waar dat geen pijn meer doet om zo'n XML bestand in een Document Object Model te openen. Mijn ervaring is dat je voor het lezen van NeTEx het beste uit kan gaan van een SAX-parser waar je op basis van bekende elementnamen objecten incrementeel omzet. Zo'n incrementele manier zou ook met XPath toegepast kunnen worden, waar het dan aan schort is overerving. Bepaalde bovenliggende attributen kunnen iets zeggen over iets wat niet er onder is gematerialiseerd. Middels SAX loop je over alle elementen heen, middels XPath selecteer je alleen de voor jou relevante elementen.
IFF
De IFF standaard wordt gebruikt voor de uitwisseling van spoorse dienstregelingen door Diensten Centrum Reisinformatie (DCRI). Het is een historisch formaat, lang geleden door de afdeling van CVI van NS ontwikkeld. Het ligt in de lijn der verwachting dat NS op ook NeTEx zal gaan publiceren, geef daarom deze standaard geen prioriteit bij nieuwe ontwikkelingen.
De historische en actuele documentatie is beschikbaar. Er zijn verschillende (open source) implementaties om IFF in te lezen en zelfs om het naar NeTEx te converteren.