1. Introduction to GeNet¶
This section goes through basic usage examples. Available as a jupyter notebook and a wiki page.
Network data structure¶

Main schema:
- nodes: exist uniquely, hold spatial information.
- e.g. A
- edges: directed pairs of
fromandtonodes, often more than one link exists for one(from, to)nodes pair. Sometimes unique.- e.g. A-B has 3 links: Bus, Car and a combined Walk+Bike
- links: single edge between a
(from, to)nodes pair. Always unique.- e.g. the A-B Bus link (Bus lane going from A to B)
Data is stored on nodes and edges, where:
- nodes hold spatial information
- each edge/link holds information such as mode of transport allowed, speed, capacity, length, OSM-inherited tags

Schedule consists of:
- List of
Services, where eachServicehas:- List of
Routes, where eachRoutehas:- List of
Stops (The order ofStops characterises theRouteobject), where eachStophas:- Spatial information
- For a multimodal transport network each
Stopshould reference a link on the network
- Dictionary of trips which share that route (with time at first stop and vehicle ID)
- List of offsets for arrival and departure from
Stops
- List of
- List of
minimal_transfer_timesbetweenStops, optional.vehicles: dictionary of vehicle IDs from Route objects, mapping them to vehicle types in vehicle_types. Looks like this:{veh_id : {'type': 'bus'}}. Defaults to None and generates itself from the vehicles IDs in Routes, maps to the mode of the Route. Checks if those modes are defined in thevehicle_types.vehicle types: dictionary of vehicle types and their specification. Indexed by the vehicle type that vehicles in thevehiclesattribute are referring to. E.g.
{'bus' : {
'capacity': {'seats': {'persons': '70'}, 'standingRoom': {'persons': '0'}},
'length': {'meter': '18.0'},
'width': {'meter': '2.5'},
'accessTime': {'secondsPerPerson': '0.5'},
'egressTime': {'secondsPerPerson': '0.5'},
'doorOperation': {'mode': 'serial'},
'passengerCarEquivalents': {'pce': '2.8'}}}
Setting-up a Network object¶
from genet import Network
To initialise an empty Network, you need a coordinate system. We've gone with the British National Grid.
n = Network(epsg="epsg:27700")
You can get quick stats on the Network by calling n.print() method or just running the cell with network object.
n
<Network instance at 140419464489296: with graph: Name: Network graph Type: MultiDiGraph Number of nodes: 0 Number of edges: 0 and schedule Schedule: Number of services: 0 Number of routes: 0 Number of stops: 0
n.print()
Graph info: Name: Network graph Type: MultiDiGraph Number of nodes: 0 Number of edges: 0 Schedule info: Schedule: Number of services: 0 Number of routes: 0 Number of stops: 0
Right now the Network is empty. You can fill it in with MATSim network data, using Open Street Map (OSM) data (Please refer to notebooks on reading data) or we can add links ourselves. A single link or a few links at once.
link_id = n.add_link(link_id="1", u="A", v="B", attribs={"modes": ["car", "walk"]})
2022-07-14 15:15:47,508 - Added Link with index 1, from node:A to node:B, under multi-index:0, and data={'modes': ['car', 'walk'], 'from': 'A', 'to': 'B', 'id': '1'}
link_id
'1'
Even though you specify a link id in this method, this id can change if there already exists a link with that id in the Network. This is why this method returns the link id under which the link was added. Let's try to add the same link again.
link_id = n.add_link(link_id="1", u="A", v="B", attribs={"modes": ["car", "walk"]})
2022-07-14 15:15:47,548 - Generated 1 link ids.
2022-07-14 15:15:47,563 - Generated link id 0.
2022-07-14 15:15:47,565 - `1` already exists. Generated a new unique_index: `0`
2022-07-14 15:15:47,582 - Added Link with index 0, from node:A to node:B, under multi-index:1, and data={'modes': ['car', 'walk'], 'from': 'A', 'to': 'B', 'id': '0'}
link_id
'0'
If you are adding many edges between the same two nodes you can also specify which multi index you want a link to use
link_id = n.add_link(
link_id="1", u="A", v="B", attribs={"modes": ["car", "walk"]}, multi_edge_idx=5
)
2022-07-14 15:15:47,638 - Generated 1 link ids.
2022-07-14 15:15:47,642 - Generated link id 2.
2022-07-14 15:15:47,643 - `1` already exists. Generated a new unique_index: `2`
2022-07-14 15:15:47,650 - Added Link with index 2, from node:A to node:B, under multi-index:5, and data={'modes': ['car', 'walk'], 'from': 'A', 'to': 'B', 'id': '2'}
To add several links it is faster to use the add_links method. This expects a dictionary with keys referring to desired link ids and the values being attribute dictionaries saved on those links. At the minimum each attribute dictionary needs a 'from' and 'to' key referring to from and to nodes the link is connecting.
reindexing_dict, links_and_attributes = n.add_links(
links_and_attributes={
"1": {"from": "A", "to": "B", "modes": ["bike"]},
"10": {"from": "B", "to": "A", "modes": ["bike"]},
}
)
2022-07-14 15:15:47,690 - Generated 1 link ids. 2022-07-14 15:15:47,699 - Added 2 links
This method also checks for links with clashing indices and returns a dictionary showing which ids we're reindex and what their new indices are, as well as an updated links_and_attributes dictionary showing final link attributes added to the Network.
reindexing_dict
{'1': '3'}
links_and_attributes
{'3': {'from': 'A', 'to': 'B', 'modes': ['bike'], 'id': '3'},
'10': {'from': 'B', 'to': 'A', 'modes': ['bike'], 'id': '10'}}
Each node should have a spatial reference. For now we worked with nodes A and B, which dont have this information. To check what information is saved under nodes or links
n.node_attribute_summary(data=False)
attribute
n.link_attribute_summary(data=True)
attribute ├── modes: ['walk', 'car', 'bike'] ├── from: ['B', 'A'] ├── to: ['B', 'A'] └── id: ['1', '2', '3', '0', '10']
To add spatial information to nodes we can use apply_attributes_to_node or apply_attributes_to_nodes methods. We have two nodes so let's use the latter. GeNet expects values x and y in the coordinate system declared at the time of initiating the Network.
n.apply_attributes_to_nodes(
new_attributes={
"A": {"x": 528704.1425925883, "y": 182068.78193707118},
"B": {"x": 528835.203274008, "y": 182006.27331298392},
}
)
2022-07-14 15:15:47,798 - Changed Node attributes for 2 nodes
n.node_attribute_summary(data=False)
attribute ├── x └── y
Now that we have spatial information for the nodes, we can do a quick plot of the Network.
# n.plot()
The plots get much more interesting the more links you have in the Network. Any additions and changes we made are recorded in the Networks changelog.
n.change_log.head(10)
| timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2022-07-14 15:15:47 | add | link | None | 1 | None | {'modes': ['car', 'walk'], 'from': 'A', 'to': ... | [(add, , [('modes', ['car', 'walk']), ('from',... |
| 1 | 2022-07-14 15:15:47 | add | link | None | 0 | None | {'modes': ['car', 'walk'], 'from': 'A', 'to': ... | [(add, , [('modes', ['car', 'walk']), ('from',... |
| 2 | 2022-07-14 15:15:47 | add | link | None | 2 | None | {'modes': ['car', 'walk'], 'from': 'A', 'to': ... | [(add, , [('modes', ['car', 'walk']), ('from',... |
| 3 | 2022-07-14 15:15:47 | add | link | None | 3 | None | {'from': 'A', 'to': 'B', 'modes': ['bike'], 'i... | [(add, , [('from', 'A'), ('to', 'B'), ('modes'... |
| 4 | 2022-07-14 15:15:47 | add | link | None | 10 | None | {'from': 'B', 'to': 'A', 'modes': ['bike'], 'i... | [(add, , [('from', 'B'), ('to', 'A'), ('modes'... |
| 5 | 2022-07-14 15:15:47 | modify | node | A | A | {} | {'x': 528704.1425925883, 'y': 182068.78193707118} | [(add, , [('x', 528704.1425925883), ('y', 1820... |
| 6 | 2022-07-14 15:15:47 | modify | node | B | B | {} | {'x': 528835.203274008, 'y': 182006.27331298392} | [(add, , [('x', 528835.203274008), ('y', 18200... |
Another important part of the Network is the Schedule element describing public transit.
from genet import Schedule
n.schedule.print()
Schedule: Number of services: 0 Number of routes: 0 Number of stops: 0
It is initiated empty with a Network. Right now, GeNet does not have nice methods to add and change Schedules. You can generate a Schedule using different Schedule elements: Service, Route and Stop, or by reading GTFS data (Please refer to notebooks on reading data).
from genet import Route, Service, Stop
Each Schedule consists of Services. A Service corresponds to a specific transit line, for example the Piccadilly London Underground line. Each Service will have a number of Routes which are characterised by an ordered sequence of Stops. For a network to be a valid multimodal network each Route needs to have a valid reference to Network links. Let's create a Schedule with a bus Service.
s = Schedule(
epsg="epsg:27700",
services=[
Service(
id="service1",
routes=[
Route(
id="1",
route_short_name="route1",
mode="bus",
stops=[
Stop(
id="0",
x=529455.7452394223,
y=182401.37630677427,
epsg="epsg:27700",
linkRefId="0",
),
Stop(
id="1",
x=529350.7866124967,
y=182388.0201078112,
epsg="epsg:27700",
linkRefId="1",
),
],
trips={
"trip_id": ["route1_04:40:00"],
"trip_departure_time": ["04:40:00"],
"vehicle_id": ["veh_bus_0"],
},
arrival_offsets=["00:00:00", "00:02:00"],
departure_offsets=["00:00:00", "00:02:00"],
route=["0", "1"],
),
Route(
id="2",
route_short_name="route2",
mode="bus",
stops=[
Stop(
id="1",
x=529455.7452394223,
y=182401.37630677427,
epsg="epsg:27700",
linkRefId="1",
),
Stop(
id="2",
x=529350.7866124967,
y=182388.0201078112,
epsg="epsg:27700",
linkRefId="2",
),
],
trips={
"trip_id": ["route2_05:40:00"],
"trip_departure_time": ["05:40:00"],
"vehicle_id": ["veh_bus_1"],
},
arrival_offsets=["00:00:00", "00:03:00"],
departure_offsets=["00:00:00", "00:05:00"],
route=["1", "2"],
),
],
)
],
)
s.print()
Schedule: Number of services: 1 Number of routes: 2 Number of stops: 3
# s.plot()
You can replace the Network schedule by your new Schedule.
n.schedule = s
n.print()
Graph info: Name: Network graph Type: MultiDiGraph Number of nodes: 2 Number of edges: 5 Average in degree: 2.5000 Average out degree: 2.5000 Schedule info: Schedule: Number of services: 1 Number of routes: 2 Number of stops: 3