matsim-proto/matsim_visualizer.py
2024-02-21 12:44:08 -05:00

157 lines
4.8 KiB
Python

import gzip
from lxml import etree
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from collections import defaultdict
import matplotlib.cm as cm
import matplotlib.colors as colors
class MatsimVisualizer():
def __init__(self, network_file_path, events_file_path, output_file_path, viewport):
self._nodes = {}
self._links = []
self._pos = None
self.norm = None
self._max_x = None
self._min_x = None
self._max_y = None
self._min_y = None
self._viewport = viewport
self._output_file_path = output_file_path
self._network_file_path = network_file_path
self._events_file_path = events_file_path
self._G = nx.Graph()
self._traffic_per_tick = defaultdict(lambda: defaultdict(int))
self._cmap = cm.viridis
self._tick = 0
self._max_traffic = 0
self._max_traffic_link = None
self._max_traffic_tick = None
def load_data(self):
# ====== LOAD NETWORK ====== #
with gzip.open(self._network_file_path, 'rb') as file:
network_doc = etree.parse(file)
for node in network_doc.xpath('/network/nodes/node'):
x = float(node.get('x'))
y = float(node.get('y'))
if self._max_x is None or x > self._max_x:
self._max_x = x
if self._min_x is None or x < self._min_x:
self._min_x = x
if self._max_y is None or y > self._max_y:
self._max_y = y
if self._min_y is None or y < self._min_y:
self._min_y = y
if self._viewport[0] < x < self._viewport[2] and self._viewport[1] > y > self._viewport[3]:
self._nodes[node.get('id')] = (x, y)
for link in network_doc.xpath('/network/links/link'):
start_node = link.get('from')
end_node = link.get('to')
id = link.get('id')
if start_node in self._nodes and end_node in self._nodes:
self._links.append({
'id': id,
'from': start_node,
'to': end_node,
'vehicles': 0,
})
cumulative_traffic = defaultdict(int)
# ====== LOAD EVENTS ====== #
with gzip.open(self._events_file_path, 'rb') as file:
events_doc = etree.parse(file)
self._tick = 0
for event in events_doc.xpath('/events/event'):
link_id = event.get('link')
event_type = event.get('type')
time = float(event.get('time'))
ticked = False
if link_id is not None and event_type is not None and time is not None:
if event_type in ['entered link', 'vehicle enters traffic']:
self._traffic_per_tick[self._tick][link_id] += 1
ticked = True
cumulative_traffic[link_id] += 1
# We need to find the maximum value for traffic at any given point for the heatmap
if self._max_traffic < cumulative_traffic[link_id]:
self._max_traffic = cumulative_traffic[link_id]
self._max_traffic_link = link_id
self._max_traffic_tick = self._tick
elif event_type in ['left link', 'vehicle leaves traffic']:
self._traffic_per_tick[self._tick][link_id] -= 1
cumulative_traffic[link_id] -= 1
ticked = True
if ticked:
self._tick += 1
def create_graph(self):
for node_id, coords in self._nodes.items():
self._G.add_node(node_id, pos=coords)
for link in self._links:
self._G.add_edge(link['from'], link['to'])
self._pos = nx.get_node_attributes(self._G, 'pos')
def setup_color_mapping(self):
self.norm = colors.Normalize(vmin=0, vmax=self._max_traffic)
def update(self, frame):
traffic_change = self._traffic_per_tick[self._tick]
edge_colors = []
edge_widths = []
for link in self._links:
for link_id, change in traffic_change.items():
if link_id == link['id']:
link['vehicles'] += change
edge_colors.append(self._cmap(link['vehicles']))
edge_widths.append(1 + self.norm(link['vehicles']) * 10)
plt.cla()
nx.draw(self._G, self._pos, node_size=0, node_color='blue', width=edge_widths, edge_color=edge_colors,
with_labels=False,
edge_cmap=self._cmap)
plt.title(f"Time: {self._tick}")
self._tick += 1
def visualize(self):
self.load_data()
self.create_graph()
self.setup_color_mapping()
fig, ax = plt.subplots()
ax.set_aspect((self._max_x - self._min_x) / (self._max_y - self._min_y))
sm = plt.cm.ScalarMappable(cmap=self._cmap, norm=self.norm)
sm.set_array([])
plt.colorbar(sm, ax=ax, label='Traffic Density')
self._tick = 0
ani = FuncAnimation(fig, self.update, frames=len(self._traffic_per_tick), repeat=False)
ani.save(f"{self._output_file_path}/traffic_animation.gif", writer='ffmpeg', fps=5)
plt.show()
print(f"Largest amount of vehicles ({self._max_traffic}) seen at {self._max_traffic_tick} on link {self._max_traffic_link}")