From 50d7fbaca27a0b86fb4fffb7120a9e0ce8648ab9 Mon Sep 17 00:00:00 2001 From: Ruben1729 Date: Thu, 15 Feb 2024 14:32:49 -0500 Subject: [PATCH] Update visualizer to remove none used links --- .gitignore | 2 +- __pycache__/matsim.cpython-39.pyc | Bin 9122 -> 11174 bytes __pycache__/matsim_visualizer.cpython-39.pyc | Bin 4970 -> 4812 bytes main.py | 6 +- matsim_visualizer.py | 121 +++++++++++-------- requirements.txt | 1 - 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 8b315b2..e7ce587 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ input_files/* # Ignore all files inside the 'output_files' directory output_files/* -output/* \ No newline at end of file +output_files/output/* \ No newline at end of file diff --git a/__pycache__/matsim.cpython-39.pyc b/__pycache__/matsim.cpython-39.pyc index 105f345b0040ec7977ddf5f03b4a1acb507bf3ad..23725b71602c03666e039e6b58501c55104454f7 100644 GIT binary patch delta 2771 zcmbtWNpBoQ6z=Ys@oe6*kq8H;oJ2}2IgS$!AR{1=@TTxCXjhqZDPL^PH3LDN;3&3)$D4>= z^gS*DMAaq=;$6gC;h}&oQI2g?_qc;aP??)Oat`f7Q_P}nKw);AP*<=zqr5$irjE@{ z!P9w9m$Z^2&tgH#u8u2=wtCba3q8K{VmHmd6RhXkzm81X|=?=vQXBcY#_0 zHVNhEbhtoWG>2WQL0gE|U_9htW{+CwU6?tQU!)IahV`|=yPqG`+c~f`-}3;ykC>|YSJ7=T`|wm?w;XDJy~ zf}?H54t*wi$hcPB24)8;0E^LGUN%Nz6#`QqCR_i!Ej|!2nbV zh3GjqJq1J(6g03;6^}_A#W1VC7hcu%nbjAIb)z~a;TjUI5dBvEs$BxKa?4R@$7EjzGtIf zA9y%PvY$YsC#da1GSx%ZQg9N5K=MFrLyk~LezXouZR$XbsWeKG2#&!@oEY(3F_WB> zcb6N?YABy}M7F_gUTA|KA#n(alOqx60%|Ny&X8BX_o}2jIk|-GFgdEG|9F`H6>hy8jb3!@Y|y20Y8^RQi_%>{X~!^0TTs4&kOL#7K1tYdDnjmN z1rVlk0duH?BS`VWsn2O38)d>wM@Gk%xGHySRP03E=)LM4$;n=zNGnF4p{YH3sPS%M ztmH1i!vf~pNN&Xt+9i$TFB?}!SMrzgw{%j79vd3a^~fCBwW$l%r&bEWxFt^&x55J= zx;C_>t>?A8b~&eOZvO!-xIG3>5BYZ<{?EdLDxdPdt zB5V<^%BYEc9(t?#sSFzVM9|97&7G*Sb*)&d5zngCq7Qa{)U_Kn^2O-+T?57CQZLl? Gp+5mS*IT{- delta 722 zcmX|;L2DC16vyYy>~1ER-EERuQ%$WkEp4`l2ny=0R7!h@Ye6tFo|I9FBv7^~df1Rd zBYM$V2Sfxh@t_A!vg%Ew7Y~B;B6twLfH%K^Z+3$_?0f%t|KAKV^WHRHulhGUuR!oy zzVQ8)I=AOf^`7EP5d|$GvkVf_Ih8ccOtLgv(#A|%PCF-akg>d-rvym5AVpeXEw}AS z549zIWy>+;$l@wdImP=#@f|8lDz6HCBFjoB7xR&E>p$#?DB)?h(i?!jZ(XyE4SLbOBaRw}V<3>cve#+4ayCqs_+v;8^0%p& zF~o);s9th^qyP`~LvKU>^{xx!Y?X}-Nz;FuZBP%B%cTdPf0hqK#{f|VydQtY(hVA6 zGOX-5$(iY|AodOMDI=_z049p2;9}sw^W5QU+~t?_@`>}6>|W6$^a2{$n4)P6db@U3 zYyz&zuA)EGt`5I){!BgPcJksl5G;X3c4)D zW}=={v`afACW`IQ-BVpFwg#ZAApkKdrvDWf)x4>A>Ezo zIvqB!Vmo$*bVQ5#F(5jqSJU2b{X2Mf@`X9oWO^zN7B#;M1srv1feU4bR?rM+7zT&+ d=f(Z`8cy-a%s|=@E|NHy-y=P8ljk_+)qk?^isb+R diff --git a/__pycache__/matsim_visualizer.cpython-39.pyc b/__pycache__/matsim_visualizer.cpython-39.pyc index fdc48a06929f3b6540ebc818b8a42b448bed4431..bab6254e6d71dc1d010fa55a58b2a61370e65fac 100644 GIT binary patch literal 4812 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 LrlIlH>F4|pnEa)U literal 4970 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 F{{veI#gza6 diff --git a/main.py b/main.py index a2a0fee..60638f7 100644 --- a/main.py +++ b/main.py @@ -25,10 +25,12 @@ try: ConstructionFactory(construction_format, city).enrich() UsageFactory(usage_format, city).enrich() - Matsim(city, 'output_files').export() - MatSimEngine('output_files/Montreal_config.xml').run() + # Matsim(city, 'output_files').export() + # 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/output_network.xml.gz', 'output/output_events.xml.gz', 'output_files') visualizer.visualize() except Exception as ex: diff --git a/matsim_visualizer.py b/matsim_visualizer.py index 1a11b8c..96fbcfb 100644 --- a/matsim_visualizer.py +++ b/matsim_visualizer.py @@ -1,6 +1,6 @@ import gzip -import xmltodict +from lxml import etree import networkx as nx import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation @@ -22,61 +22,74 @@ class MatsimVisualizer(): self._G = nx.Graph() self._traffic_per_tick = defaultdict(lambda: defaultdict(int)) - self._cumulative_traffic = defaultdict(lambda: defaultdict(int)) self._cmap = cm.viridis + self._tick = 0 + self._max_traffic = 0 + def load_data(self): - # Load network data + # ====== LOAD NETWORK ====== # 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['@id']: (float(node['@x']), float(node['@y'])) for node in - network_doc['network']['nodes']['node']} + self._nodes = {node.get('id'): (float(node.get('x')), float(node.get('y'))) + for node in network_doc.xpath('/network/nodes/node')} - # Parse links self._links = [{ - 'id': link['@id'], - 'from': link['@from'], - 'to': link['@to'] - } for link in network_doc['network']['links']['link']] + 'id': link.get('id'), + 'from': link.get('from'), + 'to': link.get('to'), + 'vehicles': 0, + } for link in network_doc.xpath('/network/links/link')] + seen_links = set() + cumulative_traffic = defaultdict(int) - link_state = defaultdict(list) - - # Load and parse the events file + # ====== LOAD EVENTS ====== # 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']: - link_id = event.get('@link') - event_type = event.get('@type') - tick = float(event.get('@time')) - vehicle_id = event.get('@vehicle') + self._tick = 0 + last_time = None + for event in events_doc.xpath('/events/event'): + link_id = event.get('link') + 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 event_type == 'entered link' or event_type == 'vehicle enters traffic': - self._traffic_per_tick[tick][link_id] += 1 - link_state[link_id].append(vehicle_id) - elif event_type == 'left link' or event_type == 'vehicle leaves traffic': - self._traffic_per_tick[tick][link_id] -= 1 - link_state[link_id].remove(vehicle_id) + 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 + seen_links.add(link_id) + cumulative_traffic[link_id] += 1 + if self._max_traffic < cumulative_traffic[link_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: - 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 - actual_tick = 0 - sorted_ticks = sorted(self._traffic_per_tick.keys()) - for tick in sorted_ticks: - if actual_tick not in self._cumulative_traffic: - # Start with the vehicle counts of the previous tick - self._cumulative_traffic[actual_tick] = defaultdict(int, self._cumulative_traffic.get(actual_tick - 1, {})) + # ====== FILTER NODES ====== # + seen_nodes = set() + for link in self._links: + seen_nodes.add(link['from']) + seen_nodes.add(link['to']) - # Apply the changes recorded for the current tick - for link_id, change in self._traffic_per_tick[tick].items(): - self._cumulative_traffic[actual_tick][link_id] += change - - actual_tick += 1 # Move to the next tick + filtered_nodes = {node_id: self._nodes[node_id] for node_id in seen_nodes if node_id in self._nodes} + self._nodes = filtered_nodes def create_graph(self): for node_id, coords in self._nodes.items(): @@ -86,23 +99,30 @@ class MatsimVisualizer(): self._pos = nx.get_node_attributes(self._G, 'pos') def setup_color_mapping(self): - # Find max traffic to setup the normalization instance - max_traffic = max(max(self._cumulative_traffic[tick].values()) for tick in self._cumulative_traffic) - self.norm = colors.Normalize(vmin=0, vmax=max_traffic) + self.norm = colors.Normalize(vmin=0, vmax=self._max_traffic) def update(self, frame_number): - tick = sorted(self._cumulative_traffic.keys())[frame_number] - traffic_data = self._cumulative_traffic[tick] + traffic_change = self._traffic_per_tick[self._tick] - edge_colors = [self._cmap(self.norm(traffic_data.get(link['id'], 0))) for link in self._links] - edge_widths = [1 + self.norm(traffic_data.get(link['id'], 0)) * 10 for link in self._links] + 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 + break + + 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: {tick}") + plt.title(f"Time: {self._tick}") + self._tick += 1 def visualize(self): self.load_data() @@ -115,6 +135,7 @@ class MatsimVisualizer(): sm.set_array([]) 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) plt.show() diff --git a/requirements.txt b/requirements.txt index 04c221e..f54edaf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,5 @@ geopandas~=0.14.2 shapely~=2.0.2 lxml~=5.1.0 pathlib~=1.0.1 -xmltodict~=0.13.0 networkx~=3.2.1 matplotlib~=3.8.2