Update visualizer to remove none used links
This commit is contained in:
parent
6c5bae5839
commit
50d7fbaca2
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,4 +8,4 @@ input_files/*
|
||||||
|
|
||||||
# Ignore all files inside the 'output_files' directory
|
# Ignore all files inside the 'output_files' directory
|
||||||
output_files/*
|
output_files/*
|
||||||
output/*
|
output_files/output/*
|
Binary file not shown.
Binary file not shown.
6
main.py
6
main.py
|
@ -25,10 +25,12 @@ try:
|
||||||
ConstructionFactory(construction_format, city).enrich()
|
ConstructionFactory(construction_format, city).enrich()
|
||||||
UsageFactory(usage_format, city).enrich()
|
UsageFactory(usage_format, city).enrich()
|
||||||
|
|
||||||
Matsim(city, 'output_files').export()
|
# Matsim(city, 'output_files').export()
|
||||||
MatSimEngine('output_files/Montreal_config.xml').run()
|
# MatSimEngine('output_files/Montreal_config.xml').run()
|
||||||
|
# MatSimEngine('equil/config.xml').run()
|
||||||
|
|
||||||
visualizer = MatsimVisualizer('output_files/Montreal/output_network.xml.gz', 'output_files/Montreal/output_events.xml.gz', 'output_files')
|
visualizer = MatsimVisualizer('output_files/Montreal/output_network.xml.gz', 'output_files/Montreal/output_events.xml.gz', 'output_files')
|
||||||
|
# visualizer = MatsimVisualizer('output/output_network.xml.gz', 'output/output_events.xml.gz', 'output_files')
|
||||||
visualizer.visualize()
|
visualizer.visualize()
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import gzip
|
import gzip
|
||||||
|
|
||||||
import xmltodict
|
from lxml import etree
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.animation import FuncAnimation
|
from matplotlib.animation import FuncAnimation
|
||||||
|
@ -22,61 +22,74 @@ class MatsimVisualizer():
|
||||||
|
|
||||||
self._G = nx.Graph()
|
self._G = nx.Graph()
|
||||||
self._traffic_per_tick = defaultdict(lambda: defaultdict(int))
|
self._traffic_per_tick = defaultdict(lambda: defaultdict(int))
|
||||||
self._cumulative_traffic = defaultdict(lambda: defaultdict(int))
|
|
||||||
self._cmap = cm.viridis
|
self._cmap = cm.viridis
|
||||||
|
|
||||||
|
self._tick = 0
|
||||||
|
self._max_traffic = 0
|
||||||
|
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
# Load network data
|
# ====== LOAD NETWORK ====== #
|
||||||
with gzip.open(self._network_file_path, 'rb') as file:
|
with gzip.open(self._network_file_path, 'rb') as file:
|
||||||
network_doc = xmltodict.parse(file.read().decode('utf-8'))
|
network_doc = etree.parse(file)
|
||||||
|
|
||||||
# Parse nodes
|
self._nodes = {node.get('id'): (float(node.get('x')), float(node.get('y')))
|
||||||
self._nodes = {node['@id']: (float(node['@x']), float(node['@y'])) for node in
|
for node in network_doc.xpath('/network/nodes/node')}
|
||||||
network_doc['network']['nodes']['node']}
|
|
||||||
|
|
||||||
# Parse links
|
|
||||||
self._links = [{
|
self._links = [{
|
||||||
'id': link['@id'],
|
'id': link.get('id'),
|
||||||
'from': link['@from'],
|
'from': link.get('from'),
|
||||||
'to': link['@to']
|
'to': link.get('to'),
|
||||||
} for link in network_doc['network']['links']['link']]
|
'vehicles': 0,
|
||||||
|
} for link in network_doc.xpath('/network/links/link')]
|
||||||
|
seen_links = set()
|
||||||
|
cumulative_traffic = defaultdict(int)
|
||||||
|
|
||||||
link_state = defaultdict(list)
|
# ====== LOAD EVENTS ====== #
|
||||||
|
|
||||||
# Load and parse the events file
|
|
||||||
with gzip.open(self._events_file_path, 'rb') as file:
|
with gzip.open(self._events_file_path, 'rb') as file:
|
||||||
events_doc = xmltodict.parse(file.read().decode('utf-8'))
|
events_doc = etree.parse(file)
|
||||||
|
|
||||||
for event in events_doc['events']['event']:
|
self._tick = 0
|
||||||
link_id = event.get('@link')
|
last_time = None
|
||||||
event_type = event.get('@type')
|
for event in events_doc.xpath('/events/event'):
|
||||||
tick = float(event.get('@time'))
|
link_id = event.get('link')
|
||||||
vehicle_id = event.get('@vehicle')
|
event_type = event.get('type')
|
||||||
|
time = float(event.get('time'))
|
||||||
|
vehicle_id = event.get('vehicle')
|
||||||
|
ticked = False
|
||||||
|
|
||||||
if link_id is not None and event_type is not None and tick is not None:
|
if link_id is not None and event_type is not None and time is not None:
|
||||||
if event_type == 'entered link' or event_type == 'vehicle enters traffic':
|
if event_type in ['entered link', 'vehicle enters traffic']:
|
||||||
self._traffic_per_tick[tick][link_id] += 1
|
self._traffic_per_tick[self._tick][link_id] += 1
|
||||||
link_state[link_id].append(vehicle_id)
|
seen_links.add(link_id)
|
||||||
elif event_type == 'left link' or event_type == 'vehicle leaves traffic':
|
cumulative_traffic[link_id] += 1
|
||||||
self._traffic_per_tick[tick][link_id] -= 1
|
if self._max_traffic < cumulative_traffic[link_id]:
|
||||||
link_state[link_id].remove(vehicle_id)
|
self._max_traffic = cumulative_traffic[link_id]
|
||||||
|
ticked = True
|
||||||
|
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 and last_time is not None and last_time != time:
|
||||||
|
self._tick += 1
|
||||||
|
|
||||||
|
last_time = time
|
||||||
|
|
||||||
|
# ====== FILTER LINKS ====== #
|
||||||
|
unique_links = []
|
||||||
for link in self._links:
|
for link in self._links:
|
||||||
self._cumulative_traffic[0][link['id']] = 0
|
if link['id'] in seen_links:
|
||||||
|
unique_links.append(link)
|
||||||
|
self._links = unique_links
|
||||||
|
|
||||||
# Accumulate the counts to get the total number of vehicles on each link up to each tick
|
# ====== FILTER NODES ====== #
|
||||||
actual_tick = 0
|
seen_nodes = set()
|
||||||
sorted_ticks = sorted(self._traffic_per_tick.keys())
|
for link in self._links:
|
||||||
for tick in sorted_ticks:
|
seen_nodes.add(link['from'])
|
||||||
if actual_tick not in self._cumulative_traffic:
|
seen_nodes.add(link['to'])
|
||||||
# Start with the vehicle counts of the previous tick
|
|
||||||
self._cumulative_traffic[actual_tick] = defaultdict(int, self._cumulative_traffic.get(actual_tick - 1, {}))
|
|
||||||
|
|
||||||
# Apply the changes recorded for the current tick
|
filtered_nodes = {node_id: self._nodes[node_id] for node_id in seen_nodes if node_id in self._nodes}
|
||||||
for link_id, change in self._traffic_per_tick[tick].items():
|
self._nodes = filtered_nodes
|
||||||
self._cumulative_traffic[actual_tick][link_id] += change
|
|
||||||
|
|
||||||
actual_tick += 1 # Move to the next tick
|
|
||||||
|
|
||||||
def create_graph(self):
|
def create_graph(self):
|
||||||
for node_id, coords in self._nodes.items():
|
for node_id, coords in self._nodes.items():
|
||||||
|
@ -86,23 +99,30 @@ class MatsimVisualizer():
|
||||||
self._pos = nx.get_node_attributes(self._G, 'pos')
|
self._pos = nx.get_node_attributes(self._G, 'pos')
|
||||||
|
|
||||||
def setup_color_mapping(self):
|
def setup_color_mapping(self):
|
||||||
# Find max traffic to setup the normalization instance
|
self.norm = colors.Normalize(vmin=0, vmax=self._max_traffic)
|
||||||
max_traffic = max(max(self._cumulative_traffic[tick].values()) for tick in self._cumulative_traffic)
|
|
||||||
self.norm = colors.Normalize(vmin=0, vmax=max_traffic)
|
|
||||||
|
|
||||||
def update(self, frame_number):
|
def update(self, frame_number):
|
||||||
tick = sorted(self._cumulative_traffic.keys())[frame_number]
|
traffic_change = self._traffic_per_tick[self._tick]
|
||||||
traffic_data = self._cumulative_traffic[tick]
|
|
||||||
|
|
||||||
edge_colors = [self._cmap(self.norm(traffic_data.get(link['id'], 0))) for link in self._links]
|
edge_colors = []
|
||||||
edge_widths = [1 + self.norm(traffic_data.get(link['id'], 0)) * 10 for link in self._links]
|
edge_widths = []
|
||||||
|
|
||||||
|
for link in self._links:
|
||||||
|
for link_id, change in traffic_change.items():
|
||||||
|
if link_id == link['id']:
|
||||||
|
link['vehicles'] += change
|
||||||
|
break
|
||||||
|
|
||||||
|
edge_colors.append(self._cmap(link['vehicles']))
|
||||||
|
edge_widths.append(1 + self.norm(link['vehicles'])*10)
|
||||||
|
|
||||||
plt.cla()
|
plt.cla()
|
||||||
nx.draw(self._G, self._pos, node_size=0, node_color='blue', width=edge_widths, edge_color=edge_colors,
|
nx.draw(self._G, self._pos, node_size=0, node_color='blue', width=edge_widths, edge_color=edge_colors,
|
||||||
with_labels=False,
|
with_labels=False,
|
||||||
edge_cmap=self._cmap)
|
edge_cmap=self._cmap)
|
||||||
|
|
||||||
plt.title(f"Time: {tick}")
|
plt.title(f"Time: {self._tick}")
|
||||||
|
self._tick += 1
|
||||||
|
|
||||||
def visualize(self):
|
def visualize(self):
|
||||||
self.load_data()
|
self.load_data()
|
||||||
|
@ -115,6 +135,7 @@ class MatsimVisualizer():
|
||||||
sm.set_array([])
|
sm.set_array([])
|
||||||
plt.colorbar(sm, ax=ax, label='Traffic Density')
|
plt.colorbar(sm, ax=ax, label='Traffic Density')
|
||||||
|
|
||||||
ani = FuncAnimation(fig, self.update, frames=len(self._cumulative_traffic), repeat=False)
|
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)
|
ani.save(f"{self._output_file_path}/traffic_animation.gif", writer='ffmpeg', fps=5)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
|
@ -3,6 +3,5 @@ geopandas~=0.14.2
|
||||||
shapely~=2.0.2
|
shapely~=2.0.2
|
||||||
lxml~=5.1.0
|
lxml~=5.1.0
|
||||||
pathlib~=1.0.1
|
pathlib~=1.0.1
|
||||||
xmltodict~=0.13.0
|
|
||||||
networkx~=3.2.1
|
networkx~=3.2.1
|
||||||
matplotlib~=3.8.2
|
matplotlib~=3.8.2
|
||||||
|
|
Loading…
Reference in New Issue
Block a user