Zum Inhalt

bahnhofgraph

Classes:

BahnhofElement


              flowchart TD
              stskit.model.bahnhofgraph.BahnhofElement[BahnhofElement]

              

              click stskit.model.bahnhofgraph.BahnhofElement href "" "stskit.model.bahnhofgraph.BahnhofElement"
            

Vollständige Bahnhofelementbezeichnung (Typ und Name)

Bahnhofelemente sind alle benannten Gleise, Bahnsteige, etc., die im Bahnhofgraph als Knoten vorkommen. Dazu gehören also in Erweiterung des üblichen Sprachgebrauchs ausdrücklich auch Anschlussgleise und Haltepunkte.

Eine Bahnhofelementbezeichnung enthält den Typ und den Namen des Elements, die auch als Property im BahnsteigGraphNode vorkommen. Typ und Namen werden verwendet, weil Anschlussgleise und Bahnhofgleise den gleichen Namen tragen können.

Attributes:

  • typ (str) –

    Bahnhofelementtyp. Stringliteral aus BAHNHOFELEMENT_TYPEN.

  • name (str) –

    Bahnhofelementname. Name des Elements gemäss Simulator.

Methods:

  • __str__

    Benutzerfreundliche Bezeichnung, wird im UI verwendet.

  • from_string

    Bahnhofelement aus Stringdarstellung

__str__

__str__()

Benutzerfreundliche Bezeichnung, wird im UI verwendet.

Source code in stskit/model/bahnhofgraph.py
def __str__(self):
    """
    Benutzerfreundliche Bezeichnung, wird im UI verwendet.
    """
    return f"{self.typ} {self.name}"

from_string classmethod

from_string(s: str) -> BahnhofElement

Bahnhofelement aus Stringdarstellung

Raises:

  • ValueError

    Fehlerhaftes Format oder unbekannter Elementtyp. Überprüft nicht, ob das Bahnhofelement in der Anlage existiert.

Source code in stskit/model/bahnhofgraph.py
@classmethod
def from_string(cls, s: str) -> 'BahnhofElement':
    """
    Bahnhofelement aus Stringdarstellung

    Raises: 
        ValueError: Fehlerhaftes Format oder unbekannter Elementtyp.
            Überprüft nicht, ob das Bahnhofelement in der Anlage existiert.
    """

    typ, name = s.split(" ", 1)
    if typ not in BAHNHOFELEMENT_TYPEN:
        raise ValueError(f"Unbekannter Bahnhofelementtyp {typ} in {s}")
    if not name:
        raise ValueError(f"Undefinierter Bahnhofelementname {name} in {s}")
    return BahnhofElement(typ, name)

BahnhofGraph

BahnhofGraph(incoming_graph_data=None, **attr)

              flowchart TD
              stskit.model.bahnhofgraph.BahnhofGraph[BahnhofGraph]

              

              click stskit.model.bahnhofgraph.BahnhofGraph href "" "stskit.model.bahnhofgraph.BahnhofGraph"
            

Bahnhöfe und ihre Gleishierarchie

Der Bahnhofgraph stellt die Gleishierarchie der Bahnhöfe dar und ordnet Bahnhöfe, Bahnhofteile, Bahnsteige und Gleise einander zu.

Die Attribute der Knoten haben die Klasse BahnsteigGraphNode, die Kanten BahnsteigGraphEdge.

Der Graph ist gerichtet, die Kanten zeigen von Bahnhöfen zu Gleisen. Die ungerichtete Variante ist der BahnsteigGraph.

Attributes:

  • ziel_gleis (Dict[Union[int, str], BahnhofLabelType]) –

    Ordnet jedem Gleisnamen und jeder Anschlussnummer das entsprechende Graphlabel zu.

Methods:

Source code in stskit/model/bahnhofgraph.py
def __init__(self, incoming_graph_data=None, **attr):
    super().__init__(incoming_graph_data, **attr)
    self.ziel_gleis: Dict[Union[int, str], BahnhofLabelType] = {}
    self.gleisschema = Gleisschema()

anschlussgleise

anschlussgleise(anst: str) -> Iterable[str]

Listet die zu einer Anschlussstelle gehörenden Gleise auf.

Nur für Anschlussgleise.

Parameters:

  • anst

    (str) –

    Name der Anschlussstelle.

Returns:

Raises:

  • KeyError

    Wenn die Anst nicht gefunden wird.

Source code in stskit/model/bahnhofgraph.py
def anschlussgleise(self, anst: str) -> Iterable[str]:
    """
    Listet die zu einer Anschlussstelle gehörenden Gleise auf.

    Nur für Anschlussgleise.

    Args:
        anst: Name der Anschlussstelle.

    Returns:
        Iterator von Gleisnamen.

    Raises:
        KeyError: Wenn die Anst nicht gefunden wird.
    """

    try:
        for parent, child in nx.dfs_edges(self, BahnhofLabelType('Anst', anst)):
            if child.typ == 'Agl':
                yield child.name
    except nx.NetworkXError as e:
        logger.exception(e)
        raise KeyError(f"Anst {anst} ist im Bahnhofgraph nicht verzeichnet.")

anschlussstelle

anschlussstelle(gleis: str) -> str

Zu Anschlussgleis zugeordnete Anschlussstelle nachschlagen.

Nur für Anschlussgleise.

Parameters:

  • gleis

    (str) –

    Anschlussgleisname wie im STS.

Returns:

  • str

    Name der Anschlussstelle.

Raises:

Source code in stskit/model/bahnhofgraph.py
def anschlussstelle(self, gleis: str) -> str:
    """
    Zu Anschlussgleis zugeordnete Anschlussstelle nachschlagen.

    Nur für Anschlussgleise.

    Args:
        gleis: Anschlussgleisname wie im STS.

    Returns:
        Name der Anschlussstelle.

    Raises:
        KeyError: Wenn nicht gefunden.
    """

    anst = self.find_superior(BahnhofLabelType('Agl', gleis), {'Anst'})
    return anst.name

anschlussstellen

anschlussstellen() -> Iterable[str]

Listet alle Anschlussstellen auf.

Returns:

Source code in stskit/model/bahnhofgraph.py
def anschlussstellen(self) -> Iterable[str]:
    """
    Listet alle Anschlussstellen auf.

    Returns:
        Iterator von Anschlussstellennamen
    """

    for node in self.list_children(BahnhofLabelType('Bst', 'Anst'), {'Anst'}):
        yield node.name

bahnhoefe

bahnhoefe() -> Iterable[str]

Listet alle Bahnhöfe auf.

Returns:

Source code in stskit/model/bahnhofgraph.py
def bahnhoefe(self) -> Iterable[str]:
    """
    Listet alle Bahnhöfe auf.

    Returns:
        Iterator von Bahnhofnamen
    """

    for node in self.list_children(BahnhofLabelType('Bst', 'Bf'), {'Bf'}):
        yield node.name

bahnhofgleise

bahnhofgleise(bahnhof: str) -> Iterable[str]

Listet die zu einem Bahnhof gehörenden Gleise auf.

Nur für Bahnhofgleise.

Parameters:

  • bahnhof

    (str) –

    Name des Bahnhofs.

Returns:

Raises:

  • KeyError

    Wenn der Bahnhof nicht gefunden wird.

Source code in stskit/model/bahnhofgraph.py
def bahnhofgleise(self, bahnhof: str) -> Iterable[str]:
    """
    Listet die zu einem Bahnhof gehörenden Gleise auf.

    Nur für Bahnhofgleise.

    Args:
        bahnhof: Name des Bahnhofs.

    Returns:
        Iterator von Gleisnamen.

    Raises:
        KeyError: Wenn der Bahnhof nicht gefunden wird.
    """

    try:
        for parent, child in nx.dfs_edges(self, ('Bf', bahnhof)):
            if child.typ == 'Gl':
                yield child.name
    except nx.NetworkXError as e:
        logger.exception(e)
        raise KeyError(f"Bf {bahnhof} ist im Bahnhofgraph nicht verzeichnet.")

bahnhofteilgleise

bahnhofteilgleise(bahnhofteil: str) -> Iterable[str]

Listet die zu einem Bahnhofteil gehörenden Gleise auf.

Nur für Bahnhofgleise.

Parameters:

  • bahnhofteil

    (str) –

    Name des Bahnhofteils.

Returns:

Raises:

  • KeyError

    Wenn der Bahnhofteil nicht gefunden wird.

Source code in stskit/model/bahnhofgraph.py
def bahnhofteilgleise(self, bahnhofteil: str) -> Iterable[str]:
    """
    Listet die zu einem Bahnhofteil gehörenden Gleise auf.

    Nur für Bahnhofgleise.

    Args:
        bahnhofteil: Name des Bahnhofteils.

    Returns:
        Iterator von Gleisnamen.

    Raises:
        KeyError: Wenn der Bahnhofteil nicht gefunden wird.
    """

    try:
        for parent, child in nx.dfs_edges(self, BahnhofLabelType('Bft', bahnhofteil)):
            if child.typ == 'Gl':
                yield child.name
    except nx.NetworkXError as e:
        logger.exception(e)
        raise KeyError(f"Bft {bahnhofteil} ist im Bahnhofgraph nicht verzeichnet.")

export_konfiguration

export_konfiguration() -> Sequence[Dict[str, Union[str, int, float, bool]]]

Bahnhofgraph exportieren für Konfigurationsdatei

Returns:

Source code in stskit/model/bahnhofgraph.py
def export_konfiguration(self) -> Sequence[Dict[str, Union[str, int, float, bool]]]:
    """
    Bahnhofgraph exportieren für Konfigurationsdatei

    Returns:
        Liste von Bahnhof-Elementen für das `elemente`-Array gemäss `config.schema3.json`.
    """

    elemente = {}
    for e1, e2 in self.edges():
        # e1 ist der Stammknoten
        if e1.typ == 'Stw':
            continue
        data1: BahnsteigGraphNode = self.nodes[e1]
        data2: BahnsteigGraphNode = self.nodes[e2]
        element = {'name': e2.name,
                   'typ': e2.typ,
                   'auto': data2.auto and data1.auto,
                   'ordnung': data2.get('ordnung', 0),
                   'sichtbar': True,
                   'flags': ''}
        if e1.typ != 'Bst':
            element['stamm'] = e1.name
        if data2.get('sperrung'):
            element['flags'] = 'S'
            element['auto'] = False
        if element['typ'] in {'Gl', 'Bs', 'Agl'}:
            element['gleise'] = data2.get('gleise', 1)
        elif element['typ'] in {'Bf', 'Anst'}:
            if stil := data2.get('linienstil'):
                element['linienstil'] = stil
        elemente[e2] = element

    return list(elemente.values())

find_gleis_enr

find_gleis_enr(name_enr: Union[int, str]) -> Optional[BahnhofLabelType]

Gleis nach Namen oder Anschlussgleis nach enr suchen.

Der Zielgraph enthält für Anschlussgleise die enr. Mit dieser Methode kann sie in ein Label des Bahnhofgraphs überführt werden. Die Gleise sind indiziert in ziel_gleis. Dieser Dictionary kann alternativ verwendet werden.

Parameters:

  • name_enr

    (Union[int, str]) –

    Gleisname oder Elementnummer (enr) aus Signalgraph

Returns:

  • Optional[BahnhofLabelType]

    Gleislabel (typ, name) oder None, wenn nicht gefunden

Source code in stskit/model/bahnhofgraph.py
def find_gleis_enr(self, name_enr: Union[int, str]) -> Optional[BahnhofLabelType]:
    """
    Gleis nach Namen oder Anschlussgleis nach `enr` suchen.

    Der Zielgraph enthält für Anschlussgleise die `enr`.
    Mit dieser Methode kann sie in ein Label des Bahnhofgraphs überführt werden.
    Die Gleise sind indiziert in `ziel_gleis`.
    Dieser Dictionary kann alternativ verwendet werden.

    Args:
        name_enr: Gleisname oder Elementnummer (enr) aus Signalgraph

    Returns:            
        Gleislabel (typ, name) oder None, wenn nicht gefunden
    """

    try:
        return self.ziel_gleis[name_enr]
    except KeyError:
        return None

find_name

find_name(name: str) -> Optional[BahnhofLabelType]

Betriebsstelle nach Namen suchen.

Wenn der Typ nicht bekannt ist. Sucht zuerst in den Bahnhöfen und Anschlussstellen, dann in der weiteren Hierarchie.

Parameters:

  • name

    (str) –

    Name der Betriebsstelle

Returns:

  • Optional[BahnhofLabelType]

    Label (Typ und Name) der Betriebsstelle oder None

Source code in stskit/model/bahnhofgraph.py
def find_name(self, name: str) -> Optional[BahnhofLabelType]:
    """
    Betriebsstelle nach Namen suchen.

    Wenn der Typ nicht bekannt ist.
    Sucht zuerst in den Bahnhöfen und Anschlussstellen, dann in der weiteren Hierarchie.

    Args:
        name: Name der Betriebsstelle

    Returns:
        Label (Typ und Name) der Betriebsstelle oder None
    """

    for u, v in nx.bfs_edges(self, ('Bst', 'Bf')):
        if v.name == name:
            return v

    for u, v in nx.bfs_edges(self, ('Bst', 'Anst')):
        if v.name == name:
            return v

    return None

find_superior

find_superior(label: BahnhofLabelType, typen: Set[str]) -> BahnhofLabelType

Übergeordnetes Element suchen.

Parameters:

  • label

    (BahnhofLabelType) –

    Label des Ausgangselements.

  • typen

    (Set[str]) –

    Set von Elementtypen, z.B. {'Anst', 'Bf'}.

Returns:

  • BahnhofLabelType

    Label des gefundenen Elements.

Raises:

Source code in stskit/model/bahnhofgraph.py
def find_superior(self, label: BahnhofLabelType, typen: Set[str]) -> BahnhofLabelType:
    """
    Übergeordnetes Element suchen.

    Args:
        label: Label des Ausgangselements.
        typen: Set von Elementtypen, z.B. {'Anst', 'Bf'}.

    Returns:
        Label des gefundenen Elements.

    Raises:
        KeyError: Wenn nicht gefunden.
    """

    try:
        for node in nx.ancestors(self, label):
            if node.typ in typen:
                return node
        else:
            raise KeyError(f"{label} ist keinem übergeordnetem {typen} zugeordnet")
    except nx.NetworkXError:
        raise KeyError(f"Element {label} ist im Bahnhofgraph nicht verzeichnet.")

gleis_bahnhof

gleis_bahnhof(gleis: str) -> str

Zu Gleis zugeordneten Bahnhof nachschlagen.

Nur für Bahnhofgleise.

Parameters:

  • gleis

    (str) –

    Gleisname wie im STS.

Returns:

  • str

    Bahnhofname.

Raises:

Source code in stskit/model/bahnhofgraph.py
def gleis_bahnhof(self, gleis: str) -> str:
    """
    Zu Gleis zugeordneten Bahnhof nachschlagen.

    Nur für Bahnhofgleise.

    Args:
        gleis: Gleisname wie im STS.

    Returns:
        Bahnhofname.

    Raises:
        KeyError: Wenn nicht gefunden.
    """

    bf = self.find_superior(BahnhofLabelType('Gl', gleis), {'Bf'})
    return bf.name

gleis_bahnhofteil

gleis_bahnhofteil(gleis: str) -> str

Zu Gleis zugeordneten Bahnhofteil nachschlagen.

Nur für Bahnhofgleise.

Parameters:

  • gleis

    (str) –

    Gleisname wie im STS.

Returns:

  • str

    Bahnhofteilname.

Raises:

Source code in stskit/model/bahnhofgraph.py
def gleis_bahnhofteil(self, gleis: str) -> str:
    """
    Zu Gleis zugeordneten Bahnhofteil nachschlagen.

    Nur für Bahnhofgleise.

    Args:
        gleis: Gleisname wie im STS.

    Returns:
        Bahnhofteilname.

    Raises:
        KeyError: Wenn nicht gefunden.
    """

    bft = self.find_superior(BahnhofLabelType('Gl', gleis), {'Bft'})
    return bft.name

gleis_bahnsteig

gleis_bahnsteig(gleis: str) -> str

Zu Gleis zugeordneten Bahnsteig nachschlagen.

Nur für Bahnhofgleise.

Parameters:

  • gleis

    (str) –

    Gleisname wie im STS.

Returns:

  • str

    Bahnsteigname.

Raises:

Source code in stskit/model/bahnhofgraph.py
def gleis_bahnsteig(self, gleis: str) -> str:
    """
    Zu Gleis zugeordneten Bahnsteig nachschlagen.

    Nur für Bahnhofgleise.

    Args:
        gleis: Gleisname wie im STS.

    Returns:
        Bahnsteigname.

    Raises:
        KeyError: Wenn nicht gefunden.
    """

    bs = self.find_superior(BahnhofLabelType('Gl', gleis), {'Bs'})
    return bs.name

gleis_parents

gleis_parents() -> Dict[BahnhofLabelType, Dict[str, BahnhofLabelType]]

Generates a dictionary of parents for each Gl and Agl node in the graph.

This method traverses the graph in reverse breadth-first search (BFS) to find all parent nodes of each Gl and Agl node. It organizes these parent nodes into a nested dictionary where the keys are the Gl or Agl nodes, and the values are dictionaries mapping child node types to their corresponding child nodes.

Returns:

  • Dict[BahnhofLabelType, Dict[str, BahnhofLabelType]]

    A dictionary containing parent nodes for each Gl and Agl node.

Source code in stskit/model/bahnhofgraph.py
def gleis_parents(self) -> Dict[BahnhofLabelType, Dict[str, BahnhofLabelType]]:
    """
    Generates a dictionary of parents for each Gl and Agl node in the graph.

    This method traverses the graph in reverse breadth-first search (BFS) to find all parent nodes of each Gl and Agl node.
    It organizes these parent nodes into a nested dictionary where the keys are the Gl or Agl nodes,
    and the values are dictionaries mapping child node types to their corresponding child nodes.

    Returns:
        A dictionary containing parent nodes for each Gl and Agl node.
    """

    result = {}
    for gl in self.list_by_type({'Gl', 'Agl'}):
        for parent, child in nx.bfs_edges(self, gl, reverse=True):
            if gl in result:
                result[gl][child.typ] = child
            else:
                result[gl] = {child.typ: child}

    return result

hierarchical_index

hierarchical_index(elements: Iterable[BahnhofElement]) -> Dict[BahnhofElement, Tuple[Union[int, str], ...]]

Erstellt einen hierarchischen Sortierindex von Gleisen.

Der Index ist ein Dictionary mit folgenden Eigenschaften:

Die Schlüssel sind Tupel mit den Namen aller Elemente im Pfad vom Root-Element zum jeweiligen Gleis.

Namen, die numerische Elemente enthalten, werden zusätzlich aufgespalten, damit Gleisnummern numerisch sortiert werden. Die Aufspaltung wird vom Gleisschema durchgeführt.

Der Dictionary kann in der sorted-Funktion verwendet werden: gleise = sorted(result.keys(), key=result.get)

Parameters:

  • elements

    (Iterable[BahnhofElement]) –

    Liste von Gleisen (Bahnhofelemente vom Typ Gl oder Agl) Wenn leer oder None, werden alle Gleise und Anschlussgleise inkludiert.

Returns:

Source code in stskit/model/bahnhofgraph.py
def hierarchical_index(self, elements: Iterable[BahnhofElement]) -> Dict[BahnhofElement, Tuple[Union[int, str], ...]]:
    """
    Erstellt einen hierarchischen Sortierindex von Gleisen.

    Der Index ist ein Dictionary mit folgenden Eigenschaften:

    Die Schlüssel sind Tupel mit den Namen aller Elemente im Pfad vom Root-Element zum jeweiligen Gleis.

    Namen, die numerische Elemente enthalten, werden zusätzlich aufgespalten,
    damit Gleisnummern numerisch sortiert werden.
    Die Aufspaltung wird vom Gleisschema durchgeführt.

    Der Dictionary kann in der sorted-Funktion verwendet werden:
    `gleise = sorted(result.keys(), key=result.get)`

    Args:
        elements: Liste von Gleisen (Bahnhofelemente vom Typ Gl oder Agl)
            Wenn leer oder None, werden alle Gleise und Anschlussgleise inkludiert.

    Returns:
        Zuordnung Gleis zu Sortierschlüssel.
    """

    if not elements:
        elements = self.list_by_type({'Gl', 'Agl'})

    sortierung = {}
    for be in elements:
        parents = [be] + list(self.list_parents(be))
        keys = []
        for e in reversed(parents):
            node = self.nodes[e]
            key = self.gleisschema.gleisname_sortkey(node.name)
            keys.append(node.get('ordnung', 0))
            keys.extend(key)
        sortierung[be] = tuple(keys)
    return sortierung

import_anlageninfo

import_anlageninfo(anlageninfo: AnlagenInfo)

Importiert die Anlageninformation in den Stellwerksknoten.

Source code in stskit/model/bahnhofgraph.py
def import_anlageninfo(self, anlageninfo: AnlagenInfo):
    """
    Importiert die Anlageninformation in den Stellwerksknoten.
    """

    anl_label = BahnhofLabelType('Stw', anlageninfo.name)
    self.add_node(anl_label, typ=anl_label.typ, name=anl_label.name, auto=True, aid=anlageninfo.aid,
                  region=anlageninfo.region, build=anlageninfo.build, online=anlageninfo.online)
    self.gleisschema = Gleisschema.regionsschema(anlageninfo.name, anlageninfo.region)

import_bahnsteiggraph

import_bahnsteiggraph(bahnsteiggraph: BahnsteigGraph, gleisschema: Gleisschema)

Importiert Gleise und Bahnsteige aus einem Bahnsteiggraphen.

Source code in stskit/model/bahnhofgraph.py
def import_bahnsteiggraph(self,
                          bahnsteiggraph: BahnsteigGraph,
                          gleisschema: Gleisschema):
    """
    Importiert Gleise und Bahnsteige aus einem Bahnsteiggraphen.
    """

    anl_label = self.root()
    bf_label = BahnhofLabelType('Bst', 'Bf')
    self.add_node(bf_label, typ=bf_label.typ, name=bf_label.name, auto=True)
    self.add_edge(anl_label, bf_label, typ=anl_label.typ, auto=True)

    for comp in nx.connected_components(bahnsteiggraph):
        gleis = min(comp, key=len)
        bft = gleisschema.bahnsteigname(gleis)
        bf = gleisschema.bahnhofname(bft)
        self.add_node(BahnhofLabelType('Bf', bf), name=bf, typ='Bf', auto=True)
        self.add_edge(bf_label, BahnhofLabelType('Bf', bf), typ=bf_label.typ, auto=True)
        self.add_node(BahnhofLabelType('Bft', bft), name=bft, typ='Bft', auto=True)
        self.add_edge(BahnhofLabelType('Bf', bf), BahnhofLabelType('Bft', bft), typ='Bf', auto=True)

        for gleis in comp:
            bs = gleisschema.bahnsteigname(gleis)
            self.add_node(BahnhofLabelType('Bs', bs), name=bs, typ='Bs', gleise=1, auto=True)
            self.add_node(BahnhofLabelType('Gl', gleis), name=gleis, typ='Gl', gleise=1, auto=True)
            self.ziel_gleis[gleis] = BahnhofLabelType('Gl', gleis)
            self.add_edge(BahnhofLabelType('Bft', bft), BahnhofLabelType('Bs', bs), typ='Bft', auto=True)
            self.add_edge(BahnhofLabelType('Bs', bs), BahnhofLabelType('Gl', gleis), typ='Bs', auto=True)

import_konfiguration

import_konfiguration(elemente: Iterable[Dict[str, Any]])

Konfiguration importieren

Strategie
  1. Alle Knoten von bestehendem Graph importieren. Kanten nicht importieren, sondern Vorfahr im stamm-Attribut verzeichnen.
  2. Knoten mit Attributen aus Elementen importieren, bestehende überschreiben. Vorfahr im stamm-Attribut verzeichnen. In der Anlage nicht vorkommende Gleise nicht übernehmen.
  3. Kanten gem. stamm-Attribut schichtweise zur Vorfahrebene konstruieren, beginnend mit Gl und Agl.
  4. Kantenlose Knoten entfernen: Alle ohne eingehende Kanten, alle nicht-Gleise ohne ausgehende Kanten. Gl und Agl haben keine ausgehenden Kanten und müssen beibehalten werden.
Source code in stskit/model/bahnhofgraph.py
def import_konfiguration(self, elemente: Iterable[Dict[str, Any]]):
    """
    Konfiguration importieren

    Strategie:
        1. Alle Knoten von bestehendem Graph importieren.
            Kanten _nicht_ importieren, sondern Vorfahr im `stamm`-Attribut verzeichnen.
        2. Knoten mit Attributen aus Elementen importieren, bestehende überschreiben.
            Vorfahr im `stamm`-Attribut verzeichnen.
            In der Anlage nicht vorkommende Gleise nicht übernehmen.
        3. Kanten gem. `stamm`-Attribut schichtweise zur Vorfahrebene konstruieren,
            beginnend mit Gl und Agl.
        4. Kantenlose Knoten entfernen:
            Alle ohne eingehende Kanten, alle nicht-Gleise ohne ausgehende Kanten.
            Gl und Agl haben keine ausgehenden Kanten und müssen beibehalten werden.
    """

    # Schritt 1: Default-Graph importieren
    new_graph = BahnhofGraph()
    for node, data in self.nodes(data=True):
        data.auto = True
        try:
            parent_node = next(self.list_parents(node), BahnhofLabelType(None, None))
            data.stamm = parent_node.name
        except KeyError:
            continue
        new_graph.add_node(node, **data)

    # Schritt 2: Konfiguration importieren, wo auto==False
    konfig_graph = BahnhofGraph()
    for element in elemente:
        node = BahnhofElement(element['typ'], element['name'])
        auto = element.get('auto', True)
        stamm = element.get('stamm', None)

        data = {"auto": auto, "typ": element['typ'], "name": element['name'], "stamm": stamm}
        if "sichtbar" in element:
            data["sichtbar"] = element['sichtbar']
        if "ordnung" in element:
            data["ordnung"] = element['ordnung']
        if "gleise" in element:
            data["gleise"] = element['gleise']
        if "flags" in element:
            data["sperrung"] = "S" in element['flags']
        if "linienstil" in element:
            data["linienstil"] = element['linienstil']

        if element['typ'] in {'Gl', 'Agl'}:
            if node not in self:
                logger.warning(f"Gleis {node} existiert nicht im Simulator")
                continue

        # element einfuegen oder attribute aktualisieren
        konfig_graph.add_node(node, **data)

    original_count = len(list(new_graph.list_by_type({'Gl', 'Agl'})))
    konfig_count = len(list(konfig_graph.list_by_type({'Gl', 'Agl'})))
    logger.info(f"Konfiguration importieren. Original {original_count} Gleise, Konfiguration {konfig_count} Gleise")

    # Schritt 2.5: reine auto-Knoten filtern
    for node, data in konfig_graph.nodes(data=True):
        parent_node = BahnhofElement(BAHNHOFELEMENT_HIERARCHIE[data['typ']], data['stamm'])
        parent_auto = not konfig_graph.has_node(parent_node) or konfig_graph.nodes[parent_node]['auto']
        if node not in new_graph or not data.auto or not parent_auto:
            new_graph.add_node(node, **data)
        else:
            logger.debug(f"BahnhofGraph.import_konfiguration: Ignoriere Auto-Element {node}.")

    # Schritt 3: Kanten erstellen gem. stamm-Attributen
    TYPEN_FOLGE = ['Gl', 'Agl', 'Bs', 'Bft', 'Bf', 'Anst']
    for typ in TYPEN_FOLGE:
        for node in new_graph.list_by_type({typ}):
            data = new_graph.nodes[node]
            try:
                if typ == "Bf":
                    parent_node = BahnhofElement("Bst", "Bf")
                elif typ == "Anst":
                    parent_node = BahnhofElement("Bst", "Anst")
                else:
                    parent_node = BahnhofElement(BAHNHOFELEMENT_HIERARCHIE[data['typ']], data['stamm'])
            except KeyError:
                continue
            if parent_node in new_graph:
                new_graph.add_edge(parent_node, node, auto=data.get('auto', True))
            else:
                logger.warning(f"BahnhofGraph.import_konfiguration: Fehlendes Stammelement {parent_node} zu {node}.")

    new_graph.add_edge(self.root(), BahnhofElement('Bst', 'Bf'))
    new_graph.add_edge(self.root(), BahnhofElement('Bst', 'Anst'))

    # Schritt 4: kinder- und elternlose Knoten entfernen
    for typ in TYPEN_FOLGE:
        to_remove = set()
        for node in new_graph.list_by_type({typ}):
            if new_graph.in_degree(node) == 0:
                to_remove.add(node)
            if typ in {'Gl', 'Agl'}:
                continue
            if new_graph.out_degree(node) == 0:
                to_remove.add(node)
        for node in to_remove:
            new_graph.remove_node(node)

    # Schritt 5: Import abschliessen
    self.clear()
    self.update(new_graph)
    self.validate()

import_signalgraph

import_signalgraph(signalgraph: SignalGraph, gleisschema: Gleisschema)

Importiert Anschlussgleise aus einem Signalgraphen.

Source code in stskit/model/bahnhofgraph.py
def import_signalgraph(self,
                       signalgraph: SignalGraph,
                       gleisschema: Gleisschema):
    """
    Importiert Anschlussgleise aus einem Signalgraphen.
    """

    anl_label = self.root()
    bst_label = BahnhofLabelType('Bst', 'Anst')
    self.add_node(bst_label, typ=bst_label.typ, name=bst_label.name, auto=True)
    self.add_edge(anl_label, bst_label, typ=anl_label.typ, auto=True)

    agl_gleise = {}
    for anschluss, data in signalgraph.nodes(data=True):
        if data.typ in {Knoten.Typ.EINFAHRT, Knoten.Typ.AUSFAHRT}:
            agl = data.name
            agl_label = BahnhofLabelType('Agl', agl)
            try:
                agl_data = self.nodes[agl_label]
            except KeyError:
                agl_data = BahnsteigGraphNode(name=agl, typ='Agl', enr=data.enr, gleise=0, auto=True)
                agl_gleise[agl_label] = 0.

            if data.typ == Knoten.Typ.EINFAHRT:
                agl_data.einfahrt = True
                if agl_label in agl_gleise:
                    agl_gleise[agl_label] += 0.5
            if data.typ == Knoten.Typ.AUSFAHRT:
                agl_data.ausfahrt = True
                if agl_label in agl_gleise:
                    agl_gleise[agl_label] += 0.5

            anst = gleisschema.anschlussname(agl)
            anst_label = BahnhofLabelType('Anst', anst)
            self.add_node(agl_label, **agl_data)
            self.add_edge(bst_label, anst_label, typ=bst_label.typ, auto=True)
            self.ziel_gleis[data.enr] = agl_label
            self.add_node(anst_label, name=anst, typ='Anst', auto=True)
            self.add_edge(anst_label, agl_label, typ='Anst', auto=True)

    for agl_label, gleise in agl_gleise.items():
        self.nodes[agl_label]['gleise'] = int(gleise + 0.5)

label staticmethod

label(typ: str, name: str) -> BahnhofLabelType

Das Label besteht aus Typ und Namen des BahnsteigGraphNode.

Source code in stskit/model/bahnhofgraph.py
@staticmethod
def label(typ: str, name: str) -> BahnhofLabelType:
    """
    Das Label besteht aus Typ und Namen des BahnsteigGraphNode.
    """
    return BahnhofLabelType(typ, name)

leere_gruppen_entfernen

leere_gruppen_entfernen()

Gruppen ohne Elemente aus dem Graphen entfernen.

Bei der Bearbeitung mittels replace_parent kann es vorkommen, dass leere Gruppen (Bahnhöfe etc.) zurückbleiben. Diese Methode räumt sie auf.

Source code in stskit/model/bahnhofgraph.py
def leere_gruppen_entfernen(self):
    """
    Gruppen ohne Elemente aus dem Graphen entfernen.

    Bei der Bearbeitung mittels replace_parent kann es vorkommen, dass leere Gruppen (Bahnhöfe etc.) zurückbleiben.
    Diese Methode räumt sie auf.
    """

    # die reihenfolge und getrennte behandlung der ebenen ist wichtig:
    for typ in ['Bs', 'Bft', 'Bf', 'Anst']:
        entfernen = []
        for n in self.nodes():
            if n.typ == typ and self.out_degree[n] == 0:
                entfernen.append(n)
        for n in entfernen:
            self.remove_node(n)

list_by_type

list_by_type(typen: Set[str]) -> Generator[BahnhofLabelType, None, None]

Listet die alle Elemente bestimmter Typen auf.

Source code in stskit/model/bahnhofgraph.py
def list_by_type(self, typen: Set[str]) -> Generator[BahnhofLabelType, None, None]:
    """
    Listet die alle Elemente bestimmter Typen auf.
    """

    for label in self.nodes:
        if label.typ in typen:
            yield label

list_children

list_children(label: BahnhofLabelType, typen: Set[str]) -> Generator[BahnhofLabelType, None, None]

Listet die untergeordneten Elemente bestimmter Typen auf.

Die Suchreihenfolge ist Breadth First.

Parameters:

  • label

    (BahnhofLabelType) –

    Label des Ausgangselements.

  • typen

    (Set[str]) –

    Set von Elementtypen, z.B. {'Anst', 'Bf'}.

Returns:

  • None

    Iterator über gefundene Elemente.

Raises:

  • KeyError

    Wenn label nicht gefunden wird.

Source code in stskit/model/bahnhofgraph.py
def list_children(self, label: BahnhofLabelType, typen: Set[str]) -> Generator[BahnhofLabelType, None, None]:
    """
        Listet die untergeordneten Elemente bestimmter Typen auf.

        Die Suchreihenfolge ist Breadth First.

        Args:
            label: Label des Ausgangselements.
            typen: Set von Elementtypen, z.B. {'Anst', 'Bf'}.

        Returns:
            Iterator über gefundene Elemente.

        Raises:
            KeyError: Wenn label nicht gefunden wird.
        """

    try:
        for parent, child in nx.bfs_edges(self, label):
            if child.typ in typen:
                yield child
    except nx.NetworkXError as e:
        logger.exception(e)
        raise KeyError(f"Element {label} ist im Bahnhofgraph nicht verzeichnet.")

list_parents

list_parents(label: BahnhofLabelType) -> Generator[BahnhofLabelType, None, None]

Übergeordnete Bahnhofelemente zu einem Gleis.

Parameters:

  • label

    (BahnhofLabelType) –

    Gleislabel (typ, name).

Returns:

  • None

    Generator von übergeordneten Elementen (typ, name) von unten nach oben.

  • None

    Das Ausgangselement wird nicht geliefert.

Raises:

  • KeyError

    Wenn das Gleis nicht existiert.

Source code in stskit/model/bahnhofgraph.py
def list_parents(self, label: BahnhofLabelType) -> Generator[BahnhofLabelType, None, None]:
    """
    Übergeordnete Bahnhofelemente zu einem Gleis.

    Args:
        label: Gleislabel (typ, name).

    Returns:
        Generator von übergeordneten Elementen (typ, name) von unten nach oben.
        Das Ausgangselement wird nicht geliefert.

    Raises:
        KeyError: Wenn das Gleis nicht existiert.
    """

    if self.has_node(label):
        for parent, child in nx.bfs_edges(self, label, reverse=True):
            yield child
    else:
        raise KeyError(f"Element {label} ist im Bahnhofgraph nicht verzeichnet.")

list_siblings

list_siblings(label: BahnhofLabelType) -> Generator[BahnhofLabelType, None, None]

Listet die Geschwisterelemente eines Bahnhofelements auf.

Geschwisterelemente haben den gleichen Typ und das gleiche Elternelement. Das Originalelement ist enthalten.

Source code in stskit/model/bahnhofgraph.py
def list_siblings(self, label: BahnhofLabelType) -> Generator[BahnhofLabelType, None, None]:
    """
    Listet die Geschwisterelemente eines Bahnhofelements auf.

    Geschwisterelemente haben den gleichen Typ und das gleiche Elternelement.
    Das Originalelement ist enthalten.
    """

    parent = self.find_superior(label, {BAHNHOFELEMENT_HIERARCHIE[label.typ]})
    yield from self.list_children(parent, {label.typ})

map_from_other_graph

Bahnhofelement von einem anderen Graphen übersetzen

Wenn zwei Graphen dieselbe Anlage abbilden, übersetzt diese Methode ein Bahnhofelement des anderen Graphen anhand eines repräsentativen Gleises in ein Bahnhofelement des eigenen Graphen. Beide Graphen müssen dieselben Gleise enthalten.

Parameters:

  • original_element

    (BahnhofElement) –

    BahnhofElement im anderen Graphen.

  • original_graph

    (BahnhofGraph) –

    anderer BahnhofGraph, der original_element enthält.

Source code in stskit/model/bahnhofgraph.py
def map_from_other_graph(self, original_element: BahnhofElement, original_graph: 'BahnhofGraph') -> Optional[BahnhofElement]:
    """
    Bahnhofelement von einem anderen Graphen übersetzen

    Wenn zwei Graphen dieselbe Anlage abbilden, übersetzt diese Methode ein Bahnhofelement des anderen Graphen
    anhand eines repräsentativen Gleises in ein Bahnhofelement des eigenen Graphen.
    Beide Graphen müssen dieselben Gleise enthalten.

    Args:
        original_element: BahnhofElement im anderen Graphen.
        original_graph: anderer BahnhofGraph, der original_element enthält.
    """

    if original_element.typ in {'Gl', 'Agl'}:
        return original_element
    else:
        try:
            gl = next(original_graph.list_children(original_element, {'Gl', 'Agl'}))
            result = self.find_superior(gl, {original_element.typ})
            return result
        except KeyError:
            return None

replace_parent

replace_parent(gleis: BahnhofLabelType, new_parent: BahnhofLabelType, new_data: Optional[BahnsteigGraphNode] = None, del_old_parent: bool = False, dry_run: bool = False) -> bool

Ersetzt den Elternknoten eines Gleises.

Dies ist nützlich, wenn ein Gleis von einem anderen Bahnhof übernommen wird. Der Elternknoten kann ein beliebiger übergeordneter Knoten des Gleises sein. Der Elternknoten kann existieren oder wird neu erstellt. Der alte Knoten wird gelöscht, wenn der Parameter del_old_parent auf True gesetzt ist und er keine Kinder mehr hat.

Hinweis

Es können leere Gruppen zurückbleiben. Am Ende der Bearbeitung daher ggf. leere_gruppen_entfernen aufrufen!

Parameters:

  • gleis

    (BahnhofLabelType) –

    Das Gleis, dessen Elternknoten ersetzt werden soll. Kann auch ein Element einer höheren Ebene sein, so lange die Ebene tiefer ist als die von new_parent.

  • new_parent

    (BahnhofLabelType) –

    Der neue Elternknoten des Gleises. Gibt den Typ und Namen des neuen Elternknotens an. Der Knoten kann existieren oder wird aus einer Kopie des alten neu erstellt.

  • new_data

    (Optional[BahnsteigGraphNode], default: None ) –

    Daten des neuen Elternknotens. Wenn None, werden die alten Daten verwendet.

  • del_old_parent

    (bool, default: False ) –

    Alten Knoten löschen, wenn er keine Kinder hat. Standardmäßig False.

  • dry_run

    (bool, default: False ) –

    True = prüfen, ob die Aktion möglich ist, Graph aber nicht verändern. Bei einem Fehler wird ein ValueError gemeldet.

Returns:

  • bool

    True, wenn erfolgreich.

Raises:

  • ValueError

    Wenn der Stammknoten nicht gefunden wird oder das Gleis bereits zum neuen Stamm gehört.

Source code in stskit/model/bahnhofgraph.py
def replace_parent(self, gleis: BahnhofLabelType,
                   new_parent: BahnhofLabelType,
                   new_data: Optional[BahnsteigGraphNode] = None,
                   del_old_parent: bool = False,
                   dry_run: bool = False) -> bool:
    """
    Ersetzt den Elternknoten eines Gleises.

    Dies ist nützlich, wenn ein Gleis von einem anderen Bahnhof übernommen wird.
    Der Elternknoten kann ein beliebiger übergeordneter Knoten des Gleises sein.
    Der Elternknoten kann existieren oder wird neu erstellt.
    Der alte Knoten wird gelöscht, wenn der Parameter `del_old_parent` auf True gesetzt ist und er keine Kinder mehr hat.

    Hinweis:
        Es können leere Gruppen zurückbleiben. Am Ende der Bearbeitung daher ggf.
        `leere_gruppen_entfernen` aufrufen!

    Args:
        gleis: Das Gleis, dessen Elternknoten ersetzt werden soll. Kann auch ein Element
            einer höheren Ebene sein, so lange die Ebene tiefer ist als die von `new_parent`.
        new_parent: Der neue Elternknoten des Gleises. Gibt den Typ und Namen des neuen
            Elternknotens an. Der Knoten kann existieren oder wird aus einer Kopie des alten
            neu erstellt.
        new_data: Daten des neuen Elternknotens. Wenn None, werden die alten Daten verwendet.
        del_old_parent: Alten Knoten löschen, wenn er keine Kinder hat. Standardmäßig False.
        dry_run: True = prüfen, ob die Aktion möglich ist, Graph aber nicht verändern.
            Bei einem Fehler wird ein ValueError gemeldet.

    Returns:
        True, wenn erfolgreich.

    Raises:
        ValueError: Wenn der Stammknoten nicht gefunden wird oder das Gleis bereits zum neuen
            Stamm gehört.
    """

    old_path, old_parent, old_data = self._find_parent_to_replace(gleis, new_parent.typ)
    if old_parent is None:
        raise ValueError(f"Gleis {gleis} hat keinen Stammknoten vom Typ {new_parent.typ}!")
    if new_parent == old_parent:
        raise ValueError(f"Gleis {gleis} ist schon ein Mitglied von {new_parent}!")

    if new_data is None and not self.has_node(new_parent):
        new_data = old_data.copy()
    if new_data is not None:
        new_data['name'] = new_parent.name
        new_data['typ'] = new_parent.typ
        if not dry_run:
            self.add_node(new_parent, **new_data)

    for element in old_path:
        if self.has_edge(old_parent, element):
            data = self.get_edge_data(old_parent, element)
            if not dry_run:
                self.add_edge(new_parent, element, **data)
                self.nodes[element]['auto'] = False
                self.remove_edge(old_parent, element)
        if self.has_edge(element, old_parent):
            data = self.get_edge_data(element, old_parent)
            if not dry_run:
                self.add_edge(element, new_parent, **data)

    if not dry_run and del_old_parent and not any(self.successors(old_parent)):
        self.remove_node(old_parent)

    return dry_run or self.has_edge(new_parent, gleis)

root

root() -> BahnhofLabelType

Label des höchsten Knotens

Der höchste Knoten ist das Stellwerk.

Returns:

  • BahnhofLabelType

    Label ('Stw', Stellwerkname)

Source code in stskit/model/bahnhofgraph.py
def root(self) -> BahnhofLabelType:
    """
    Label des höchsten Knotens

    Der höchste Knoten ist das Stellwerk.

    Returns:
        Label `('Stw', Stellwerkname)`
    """
    for node in self.nodes():
        if node.typ == 'Stw':
            return node
    else:
        raise KeyError('Bahnhofgraph enthält kein Anlagenelement.')

validate

validate()

Vollständigkeit des Bahnhofgraphen prüfen.

  • Hat jedes Gleis eine zugeordnete Bst?
  • Hat jedes Element genau ein Stammelement?
Source code in stskit/model/bahnhofgraph.py
def validate(self):
    """
    Vollständigkeit des Bahnhofgraphen prüfen.

    - Hat jedes Gleis eine zugeordnete Bst?
    - Hat jedes Element genau ein Stammelement?
    """

    logger.debug("Validating BahnhofGraph")
    for gl in self.list_by_type({'Gl', 'Agl'}):
        if gl.typ == 'Gl':
            check = {'Gl', 'Bs', 'Bft', 'Bf', 'Bst', 'Stw'}
        elif gl.typ == 'Agl':
            check = {'Agl', 'Anst', 'Bst', 'Stw'}
        else:
            raise ValueError(f"Ungueltiger Gleistyp {gl}")

        try:
            for be in self.list_parents(gl):
                check -= {be.typ}
            if not check:
                logger.error(f"{gl} hat fehlende Eltern {check}")
        except KeyError as e:
            logger.exception(f"{gl} nicht im bahnhofgraph", exc_info=e)

    for node, data in self.nodes(data=True):
        try:
            if self.in_degree(node) > 1:
                path = [str(node) for node in nx.ancestors(self.graph, node)]
                logger.error(f"{node} hat mehrere Eltern. {path}")
        except KeyError as e:
            logger.exception(f"{gl} nicht im bahnhofgraph", exc_info=e)

BahnsteigGraph


              flowchart TD
              stskit.model.bahnhofgraph.BahnsteigGraph[BahnsteigGraph]

              

              click stskit.model.bahnhofgraph.BahnsteigGraph href "" "stskit.model.bahnhofgraph.BahnsteigGraph"
            

Bahnsteige

Der Bahnsteiggraph enthält alle Bahnsteige aus der Bahnsteigliste der Plugin-Schnittstelle als Knoten. Kanten werden entsprechend der Nachbarrelationen gesetzt. Der Graph ist ungerichtet, da die Nachbarbeziehung als reziprok aufgefasst wird.

Vom Simulator werden nur die Gleisbezeichnungen der untersten Hierarchie sowie ihre Nachbarbeziehungen angegeben. Die Gruppierung in Bahnhofteile, Bahnhöfe und Anschlussstellen wird von der Klasse unterstützt, muss aber vom Besitzer gemacht werden.

Methods:

bahnsteige_importieren

bahnsteige_importieren(bahnsteige: Iterable[BahnsteigInfo])

Bahnsteiggraph aus Plugindaten erstellen.

Parameters:

Source code in stskit/model/bahnhofgraph.py
def bahnsteige_importieren(self, bahnsteige: Iterable[BahnsteigInfo]):
    """
    Bahnsteiggraph aus Plugindaten erstellen.

    Args:
        bahnsteige: Iterable von stsobj.BahnsteigInfo vom PluginClient
    """

    self.clear()

    for bs1 in bahnsteige:
        self.add_node(bs1.name, name=bs1.name, typ='Gl')
        for bs2 in bs1.nachbarn.values():
            self.add_edge(bs1.name, bs2.name, typ='Nachbar', distanz=0)

BahnsteigGraphEdge


              flowchart TD
              stskit.model.bahnhofgraph.BahnsteigGraphEdge[BahnsteigGraphEdge]

              

              click stskit.model.bahnhofgraph.BahnsteigGraphEdge href "" "stskit.model.bahnhofgraph.BahnsteigGraphEdge"
            

Klasse der Kantenattribute von BahnsteigGraph und BahnhofGraph

Attributes:

  • typ

    Beziehungstyp

    Wert Bedeutung
    Nachbar Nachbarbeziehung gemäss Simulator.
    Hierarchie Von StsDispo definierte Hierarchiebeziehung.
  • distanz

    Länge (Anzahl Knoten) des kürzesten Pfades zwischen den Knoten.

BahnsteigGraphNode


              flowchart TD
              stskit.model.bahnhofgraph.BahnsteigGraphNode[BahnsteigGraphNode]

              

              click stskit.model.bahnhofgraph.BahnsteigGraphNode href "" "stskit.model.bahnhofgraph.BahnsteigGraphNode"
            

Klasse der Knotenattribute von BahnsteigGraph und BahnhofGraph

Attributes:

  • name

    Name

  • enr

    Elementnummer bei Anschlussgleisen. Nur für Agl definiert.

  • typ

    Bahnhofelementtyp

    Wert Bedeutung
    Gl Gleis- oder Haltepunktbezeichnung, wie sie in den Fahrplänen vorkommt. Vom Sim deklariert.
    Bs Bahnsteigbezeichnung, fasst Gleissektoren bzw. Haltepunkte zusammen.
    Bft Bahnhofteil, fasst Bahnsteige zusammen, auf die ein Zug umdisponiert werden kann.
    Bf Bahnhof für grafische Darstellung und Fahrzeitauswertung.
    Agl Anschluss- oder Übergabegleis. Vom Sim deklariert.
    Anst Anschluss- oder Übergabestelle, fasst Anschlussgleise zusammen auf die ein Zug umdisponiert werden kann.
    Bst Betriebsstelle. Entweder 'Bf' oder 'Anst'.
    Stw Stellwerk/Anlage.
  • auto

    True bei automatischer, False bei manueller Konfiguration.

  • stamm

    Name des übergeordneten Knoten. Wird im Konfigurationsimport und -export verwendet und ist ansonsten undefiniert. Der Typ erschliesst sich aus BAHNHOFELEMENT_HIERARCHIE[typ].

  • ordnung

    Sortierordnung innerhalb der Gruppe. Bahnhofelemente innerhalb der Gruppe werden gemäss dem Tupel (ordnung, name) sortiert.

  • gleise

    Anzahl Gleise mit dem gleichen Namen. Normalerweise 1, ausser z.B. bei Haltestellen oder Anschlussgleisen ohne Gleisnummer. In diesem Fall wird bei Mehrfachbelegung kein Konflikt angezeigt.
    Nur bei Gl, Bs und Agl. Im Moment wird der Wert nur bei Agl automatisch aus dem Signalgraphen ausgewertet.

  • einfahrt

    True, wenn das Gleis eine Einfahrt ist. Nur für Agl definiert.

  • ausfahrt

    True, wenn das Gleis eine Ausfahrt ist. Nur für Agl definiert.

  • sperrung

    Gleissperrung

  • linienstil

    Linienstil für die Darstellung der Station.