OR 2024, Munich
You should be proficient in:
Simplicity comes at the price of flexibility!
Idea
Create a schematic drawing of metro lines with stations and transfer stations
Input
Output
Packages: networkx, pandas
octilinear representation
Octilinear Representation
Edges in network are restricted to horizontals, verticals and diagonals at 45◦.
Objective
Based on diploma thesis Automated Drawing of Metro Maps by M. Nöllenburg
Installation via PyPI:
Documentation on your selected Mod:
https://gurobi-optimods.readthedocs.io/en/v2.2.0/mods/metromap.html
number,name,posx,posy
1,Aidenbachstraße,11.525277777778,48.097777777778
2,Alte Heide,11.602777777778,48.178611111111
3,Am Hart,11.571944444444,48.196944444444
4,Am Knie,11.471388888889,48.145277777778
5,Arabellapark,11.621388888889,48.153611111111
6,Basler Straße,11.491666666667,48.091111111111
7,Bonner Platz,11.578888888889,48.166666666667
...
source,target
60,84
97,69
48,20
41,33
21,103
72,1
66,77
...
import networkx as nx
# create graph
graph = nx.from_pandas_edgelist(edge_data.reset_index(), create_using=nx.Graph())
# add attribute 'pos' for the geographical position
for number, row in node_data.set_index("number").iterrows():
graph.add_node(number, pos = (row['posx'], row['posy']))
# draw graph with geographical position
pos=nx.get_node_attributes(graph_out, "pos")
nx.draw(graph, pos, with_labels=False, node_size=12, edgecolors="black", node_color="white")
Skip metro lines for now
from gurobi_optimods.metromap import metromap
graph_out, edge_directions = metromap(graph)
# Show the first 4 nodes with their attributes
first_four_nodes_with_attrs = list(graph_out.nodes.data())[:4]
[(60, {'pos': (11.598611111111, 48.135555555556), 'pos_oct': (12.0, 7.0)}),
(84, {'pos': (11.606944444444, 48.139166666667), 'pos_oct': (13.0, 8.0)}),
(97, {'pos': (11.642777777778, 48.094444444444), 'pos_oct': (16.0, 1.0)}),
(69, {'pos': (11.645277777778, 48.088888888889), 'pos_oct': (17.0, 0.0)})]
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")
linename,edge_source,edge_target
U1,77,27
U1,27,104
U1,104,28
U1,28,87
U1,87,56
...
U2,15,36
U2,36,13
U2,13,35
...
linepath_data = pd.read_csv("data/muenchen_ulinepaths.csv")
graph_out, edge_directions = metromap(graph, linepath_data)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")
# default for all penalties in obj is 1, increase penalty for line bends
graph_out, edge_directions = metromap(graph, linepath_data, penalty_line_bends=5)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")
line bend penalty 2
line bend penalty 5 (with the cost of distance)
# default for all penalties in obj is 1, increase penalty for line bends
graph_out, edge_directions = metromap(graph, linepath_data, penalty_line_bends=2, penalty_distance=10, penalty_edge_directions=1)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")
line bend penalty 2
line bend penalty 2 distance penalty 10
time limit can be changed directly:
other solver parameters:
solver_params = {
"NoRelHeurTime": 5,
"Cuts": 3,
"TimeLimit": 5
}
metromap(graph, linepath_data, solver_params=solver_params)
Details for every OptiMod in documentation
Tip
Get involved and share your feedback and ideas!
Thank You!
©️ Gurobi Optimization