File size: 7,198 Bytes
ec5ee81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""

Builds a schematic model of central Tunis.



  Avenue Habib Bourguiba (main corridor, west -> east):

    Bab Bhar -- [B1 Rue de Rome] -- [B2 Theatre crossing] -- [B3 Av. de Paris /

    Av. de Carthage] -- [B4 Place du 14 Janvier, Av. Mohamed V] -- Tunis Marine

  Avenue Farhat Hached (parallel, south) with junctions F1, F3, F4.



IMPORTANT: this is a schematic built from the main axes of the city centre.

Distances and lane counts are approximate and some side streets are left out.

For the exact street geometry, run scripts/import_osm.py on your own machine

(it downloads OpenStreetMap data); every other script works on either network.



Usage:  python scripts/build_network.py

"""
import os
import subprocess

import sys

import sumo

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from traffix.core import config  # noqa: E402
import traffix.demand.generator  # noqa: E402,F401

NET_DIR = config.path("data", "networks")
YELLOW = config.agents()["safety"]["yellow"]

BIN = os.path.join(os.environ.get("SUMO_HOME", sumo.SUMO_HOME), "bin")

# id: (x, y, type, name)   x = metres east, y = metres north
NODES = {
    "B1": (0, 0, "traffic_light", "Rue de Rome x Av. de France / Bourguiba"),
    "B2": (300, 0, "traffic_light", "Theatre municipal pedestrian crossing"),
    "B3": (550, 0, "traffic_light", "Av. de Paris / Av. de Carthage x Bourguiba"),
    "B4": (950, 0, "traffic_light", "Place du 14 Janvier 2011 (Av. Mohamed V)"),
    "F1": (0, -400, "traffic_light", "Rue de Rome x Av. Farhat Hached"),
    "F3": (550, -400, "traffic_light", "Av. de Carthage x Av. Farhat Hached"),
    "F4": (950, -400, "traffic_light", "Av. Mohamed V (south) x Av. Farhat Hached"),
    # entry / exit points at the edge of the model
    "bab_bhar": (-300, 0, "priority", "Bab Bhar"),
    "tunis_marine": (1350, 0, "priority", "Tunis Marine"),
    "rome_n": (0, 300, "priority", "Rue de Rome (north)"),
    "paris_n": (550, 300, "priority", "Av. de Paris (north)"),
    "mv_n": (950, 300, "priority", "Av. Mohamed V (north)"),
    "fh_w": (-300, -400, "priority", "Av. Farhat Hached (west)"),
    "fh_e": (1350, -400, "priority", "Av. Farhat Hached (east)"),
    "rome_s": (0, -700, "priority", "Rue de Rome (south)"),
    "carthage_s": (550, -700, "priority", "Av. de Carthage (south)"),
    "mv_s": (950, -700, "priority", "Av. Mohamed V (south)"),
}

# (from, to, lanes, street name). Each road is built in both directions.
ROADS = [
    ("bab_bhar", "B1", 2, "Avenue de France"),
    ("B1", "B2", 2, "Avenue Habib Bourguiba"),
    ("B2", "B3", 2, "Avenue Habib Bourguiba"),
    ("B3", "B4", 2, "Avenue Habib Bourguiba"),
    ("B4", "tunis_marine", 2, "Avenue Habib Bourguiba"),
    ("fh_w", "F1", 2, "Avenue Farhat Hached"),
    ("F1", "F3", 2, "Avenue Farhat Hached"),
    ("F3", "F4", 2, "Avenue Farhat Hached"),
    ("F4", "fh_e", 2, "Avenue Farhat Hached"),
    ("rome_n", "B1", 1, "Rue de Rome"),
    ("B1", "F1", 1, "Rue de Rome"),
    ("F1", "rome_s", 1, "Rue de Rome"),
    ("paris_n", "B3", 2, "Avenue de Paris"),
    ("B3", "F3", 2, "Avenue de Carthage"),
    ("F3", "carthage_s", 2, "Avenue de Carthage"),
    ("mv_n", "B4", 3, "Avenue Mohamed V"),
    ("B4", "F4", 2, "Avenue Mohamed V"),
    ("F4", "mv_s", 2, "Avenue Mohamed V"),
]
SPEED = 13.89  # 50 km/h urban limit


def write_plain_files():
    os.makedirs(NET_DIR, exist_ok=True)
    with open(os.path.join(NET_DIR, "tunis_centre.nod.xml"), "w") as f:
        f.write("<nodes>\n")
        for nid, (x, y, typ, name) in NODES.items():
            f.write(f'  <node id="{nid}" x="{x}" y="{y}" type="{typ}">'
                    f'<param key="name" value="{name}"/></node>\n')
        f.write("</nodes>\n")
    with open(os.path.join(NET_DIR, "tunis_centre.edg.xml"), "w") as f:
        f.write("<edges>\n")
        bus_lanes = set(config.simulation().get("bus_lanes") or [])
        for a, b, lanes, name in ROADS:
            for u, v in ((a, b), (b, a)):
                eid = f"{u}_{v}"
                if eid in bus_lanes and lanes >= 2:       # right lane reserved for buses
                    f.write(f'  <edge id="{eid}" from="{u}" to="{v}" numLanes="{lanes}" speed="{SPEED}" '
                            f'name="{name}"><lane index="0" allow="bus emergency"/></edge>\n')
                else:
                    f.write(f'  <edge id="{eid}" from="{u}" to="{v}" '
                            f'numLanes="{lanes}" speed="{SPEED}" name="{name}"/>\n')
        f.write("</edges>\n")


def netconvert(tls_type, out):
    cmd = [
        os.path.join(BIN, "netconvert"),
        "-n", os.path.join(NET_DIR, "tunis_centre.nod.xml"),
        "-e", os.path.join(NET_DIR, "tunis_centre.edg.xml"),
        "--sidewalks.guess", "true",       # pavements on every road
        "--crossings.guess", "true",       # zebra crossings at junctions
        "--tls.default-type", tls_type,
        "--tls.yellow.time", str(YELLOW),
        "--no-turnarounds", "true",
        "--junctions.corner-detail", "5",
        "-o", out,
    ]
    print(" ".join(cmd))
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL)


def write_bus_stops(net_file):
    """One stop on each bus line, just before every signalised junction it reaches."""
    import sumolib
    net = sumolib.net.readNet(net_file)
    stops = {}
    for line, first, last in config.bus_lines():
        if not (net.hasEdge(first) and net.hasEdge(last)):
            continue
        path, _ = net.getShortestPath(net.getEdge(first), net.getEdge(last), vClass="bus")
        for e in path or []:
            if e.getToNode().getType() != "traffic_light":
                continue
            lane = next(l for l in e.getLanes() if l.allows("bus"))
            end = max(10.0, lane.getLength() - 30)
            st = stops.setdefault(e.getID(), {"lane": lane.getID(), "start": max(0.0, end - 20), "end": end,
                                              "junction": e.getToNode().getID(), "lines": []})
            st["lines"].append(line)
    out = net_file.replace(".net.xml", ".stops.xml")
    with open(out, "w") as f:
        f.write("<additional>\n")
        for edge, st in stops.items():
            name = NODES.get(st["junction"], (0, 0, 0, st["junction"]))[3]
            f.write(f'  <busStop id="stop_{edge}" lane="{st["lane"]}" startPos="{st["start"]:.1f}" '
                    f'endPos="{st["end"]:.1f}" lines="{" ".join(st["lines"])}" name="{name}">'
                    f'<param key="junction" value="{st["junction"]}"/></busStop>\n')
        f.write("</additional>\n")
    print(f"Wrote {len(stops)} bus stops to {out}")


if __name__ == "__main__":
    os.environ["TRAFFIX_NETWORK"] = "tunis_centre"   # this script builds the schematic network only
    write_plain_files()
    static, actuated = (os.path.join(NET_DIR, "tunis_centre.net.xml"),
                        os.path.join(NET_DIR, "tunis_centre_actuated.net.xml"))
    netconvert("static", static)
    netconvert("actuated", actuated)
    write_bus_stops(static)
    print("Built", static, "and", actuated)