Skip to content

Commit

Permalink
Improve routing functions; Add example
Browse files Browse the repository at this point in the history
  • Loading branch information
toruseo committed May 8, 2024
1 parent 6043d95 commit 909373a
Show file tree
Hide file tree
Showing 6 changed files with 888 additions and 19 deletions.
728 changes: 728 additions & 0 deletions demos_and_examples/demo_notebook_07en_optimal_routing.ipynb

Large diffs are not rendered by default.

25 changes: 19 additions & 6 deletions demos_and_examples/uxsim/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,25 @@
from PIL.Image import Resampling
from tqdm.auto import tqdm
from collections import defaultdict as ddict
from importlib.resources import read_binary #according to official doc, this is also not recommended
from importlib.resources import read_binary, files #TODO: according to official doc, `read_binary` is not recommended. To be replaced with `files` in the future after some testing.
import io
from scipy.sparse.csgraph import floyd_warshall

from .utils import *
from .utils import *

plt.rcParams["font.family"] = get_font_for_matplotlib()

def load_font_data_new():
fname = "HackGen-Regular.ttf"
font_data_path = files('uxsim.files').joinpath(fname)
with font_data_path.open('rb') as file:
font_data = file.read()
return font_data

def load_font_data():
font_data = read_binary("uxsim.files", "HackGen-Regular.ttf")
return font_data

class Analyzer:
"""
Class for analyzing and visualizing a simulation result.
Expand Down Expand Up @@ -288,6 +299,7 @@ def print_simple_stats(s, force_print=False):
print(f" average delay of trips:\t {s.average_delay:.1f} s")
print(f" delay ratio:\t\t\t {s.average_delay/s.average_travel_time:.3f}")


def comp_route_travel_time(s, t, route):
pass

Expand Down Expand Up @@ -714,7 +726,8 @@ def network_pillow(s, t=None, detailed=1, minwidth=0.5, maxwidth=12, left_handed

img = Image.new("RGBA", (int(maxx-minx), int(maxy-miny)), (255, 255, 255, 255))
draw = ImageDraw.Draw(img)
font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')

font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
if network_font_size > 0:
font = ImageFont.truetype(font_file_like, int(network_font_size))
Expand Down Expand Up @@ -744,7 +757,7 @@ def flip(y):
draw.text(((n.x)*coef-minx, flip((n.y)*coef-miny)), n.name, font=font, fill="green", anchor="mm")
draw.text(((n.x)*coef-minx, flip((n.y)*coef-miny)), n.name, font=font, fill="green", anchor="mm")

font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
font = ImageFont.truetype(font_file_like, int(30))
draw.text((img.size[0]/2,20), f"t = {t :>8} (s)", font=font, fill="black", anchor="mm")
Expand Down Expand Up @@ -950,7 +963,7 @@ def network_fancy(s, animation_speed_inverse=10, figsize=6, sample_ratio=0.3, in
for t in tqdm(range(int(s.W.TMAX*0), int(s.W.TMAX*1), s.W.DELTAT*speed_coef)):
img = Image.new("RGBA", (int(maxx-minx), int(maxy-miny)), (255, 255, 255, 255))
draw = ImageDraw.Draw(img)
font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
if network_font_size > 0:
font = ImageFont.truetype(font_file_like, int(network_font_size))
Expand All @@ -977,7 +990,7 @@ def flip(y):
draw.ellipse((xs[-1]-size, flip(ys[-1])-size, xs[-1]+size, flip(ys[-1])+size), fill=(int(trace["c"][0]*255), int(trace["c"][1]*255), int(trace["c"][2]*255)))
#draw.line([(x1, flip(y1)), (xmid1, flip(ymid1)), (xmid2, flip(ymid2)), (x2, flip(y2))]

font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
font = ImageFont.truetype(font_file_like, int(30))
draw.text((img.size[0]/2,20), f"t = {t :>8} (s)", font=font, fill="black", anchor="mm")
Expand Down
61 changes: 58 additions & 3 deletions demos_and_examples/uxsim/uxsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
This `uxsim.py` is the core of UXsim. It summarizes the classes and methods that are essential for the simulation.
"""

import numpy as np
import matplotlib.pyplot as plt
import random, csv, time, math, string, warnings
from collections import deque, OrderedDict
from collections import defaultdict as ddict

import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse.csgraph import floyd_warshall
import dill as pickle

from .analyzer import *
from .utils import *
from .utils import *

class Node:
"""
Expand Down Expand Up @@ -1071,6 +1073,28 @@ def add_dest(s, dest, order=-1):
else:
raise ValueError(f"Vehicle {s.name} is not in taxi mode. Cannot add destination.")

def set_links_prefer(s, links):
"""
Set the links the vehicle prefers.
Parameters
----------
links : list of str
The list of link names the vehicle prefers.
"""
s.links_prefer = [s.W.get_link(l) for l in links]

def set_links_avoid(s, links):
"""
Set the links the vehicle avoids.
Parameters
----------
links : list of str
The list of link names the vehicle avoids.
"""
s.links_avoid = [s.W.get_link(l) for l in links]

def add_dests(s, dests):
"""
Add multiple destinations to the vehicle's destination list.
Expand Down Expand Up @@ -2008,6 +2032,17 @@ def show_network(W, width=1, left_handed=1, figsize=(6,6), network_font_size=10,
plt.show()
else:
plt.close("all")

def copy(W):
"""
Copy the World object.
Returns
-------
World object
The copied World object.
"""
return pickle.loads(pickle.dumps(W))


class Route:
Expand All @@ -2026,6 +2061,17 @@ def __init__(s, W, links, name="", trust_input=False):
List of name of links. The contents are str.
trust_input : bool
True if you trust the `links` in order to reduce the computation cost by omitting verification.
Examples
--------
>>> route = Route(W, ["l1", "l2", "l3"])
... vehicle_object.links_prefer = route
This will enforce the vehicle to travel the route if the route covers the entire links between the OD nodes of the vehicle.
>>> route = Route(W, ["l1", "l2", "l3"])
... for link in route:
... print(link)
This will print the links in the route.
"""
s.W = W
s.name = name
Expand All @@ -2049,6 +2095,15 @@ def __init__(s, W, links, name="", trust_input=False):
def __repr__(s):
return f"<Route {s.name}: {s.links}>"

def __iter__(s):
"""
Override `iter()` function. Iterate the links of the route.
"""
return iter(s.links)

def __len__(s):
return len(s.links)

def __eq__(self, other):
"""
Override `==` operator. If the links of two route are the same, then the routes are the same.
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ classifiers = [
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering",
]
Expand All @@ -23,7 +27,8 @@ dependencies = [
"tqdm>=4.64.1",
"scipy>=1.9.1",
"pandas>=1.4.4",
"PyQt5>=5.15.7"
"PyQt5>=5.15.7",
"dill>=0.3.8"
]
dynamic = ["version"] # Version is read from uxsim/__init__.py

Expand Down
25 changes: 19 additions & 6 deletions uxsim/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,25 @@
from PIL.Image import Resampling
from tqdm.auto import tqdm
from collections import defaultdict as ddict
from importlib.resources import read_binary #according to official doc, this is also not recommended
from importlib.resources import read_binary, files #TODO: according to official doc, `read_binary` is not recommended. To be replaced with `files` in the future after some testing.
import io
from scipy.sparse.csgraph import floyd_warshall

from .utils import *
from .utils import *

plt.rcParams["font.family"] = get_font_for_matplotlib()

def load_font_data_new():
fname = "HackGen-Regular.ttf"
font_data_path = files('uxsim.files').joinpath(fname)
with font_data_path.open('rb') as file:
font_data = file.read()
return font_data

def load_font_data():
font_data = read_binary("uxsim.files", "HackGen-Regular.ttf")
return font_data

class Analyzer:
"""
Class for analyzing and visualizing a simulation result.
Expand Down Expand Up @@ -288,6 +299,7 @@ def print_simple_stats(s, force_print=False):
print(f" average delay of trips:\t {s.average_delay:.1f} s")
print(f" delay ratio:\t\t\t {s.average_delay/s.average_travel_time:.3f}")


def comp_route_travel_time(s, t, route):
pass

Expand Down Expand Up @@ -714,7 +726,8 @@ def network_pillow(s, t=None, detailed=1, minwidth=0.5, maxwidth=12, left_handed

img = Image.new("RGBA", (int(maxx-minx), int(maxy-miny)), (255, 255, 255, 255))
draw = ImageDraw.Draw(img)
font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')

font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
if network_font_size > 0:
font = ImageFont.truetype(font_file_like, int(network_font_size))
Expand Down Expand Up @@ -744,7 +757,7 @@ def flip(y):
draw.text(((n.x)*coef-minx, flip((n.y)*coef-miny)), n.name, font=font, fill="green", anchor="mm")
draw.text(((n.x)*coef-minx, flip((n.y)*coef-miny)), n.name, font=font, fill="green", anchor="mm")

font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
font = ImageFont.truetype(font_file_like, int(30))
draw.text((img.size[0]/2,20), f"t = {t :>8} (s)", font=font, fill="black", anchor="mm")
Expand Down Expand Up @@ -950,7 +963,7 @@ def network_fancy(s, animation_speed_inverse=10, figsize=6, sample_ratio=0.3, in
for t in tqdm(range(int(s.W.TMAX*0), int(s.W.TMAX*1), s.W.DELTAT*speed_coef)):
img = Image.new("RGBA", (int(maxx-minx), int(maxy-miny)), (255, 255, 255, 255))
draw = ImageDraw.Draw(img)
font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
if network_font_size > 0:
font = ImageFont.truetype(font_file_like, int(network_font_size))
Expand All @@ -977,7 +990,7 @@ def flip(y):
draw.ellipse((xs[-1]-size, flip(ys[-1])-size, xs[-1]+size, flip(ys[-1])+size), fill=(int(trace["c"][0]*255), int(trace["c"][1]*255), int(trace["c"][2]*255)))
#draw.line([(x1, flip(y1)), (xmid1, flip(ymid1)), (xmid2, flip(ymid2)), (x2, flip(y2))]

font_data = read_binary('uxsim.files', 'HackGen-Regular.ttf')
font_data = load_font_data()
font_file_like = io.BytesIO(font_data)
font = ImageFont.truetype(font_file_like, int(30))
draw.text((img.size[0]/2,20), f"t = {t :>8} (s)", font=font, fill="black", anchor="mm")
Expand Down
61 changes: 58 additions & 3 deletions uxsim/uxsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
This `uxsim.py` is the core of UXsim. It summarizes the classes and methods that are essential for the simulation.
"""

import numpy as np
import matplotlib.pyplot as plt
import random, csv, time, math, string, warnings
from collections import deque, OrderedDict
from collections import defaultdict as ddict

import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse.csgraph import floyd_warshall
import dill as pickle

from .analyzer import *
from .utils import *
from .utils import *

class Node:
"""
Expand Down Expand Up @@ -1071,6 +1073,28 @@ def add_dest(s, dest, order=-1):
else:
raise ValueError(f"Vehicle {s.name} is not in taxi mode. Cannot add destination.")

def set_links_prefer(s, links):
"""
Set the links the vehicle prefers.
Parameters
----------
links : list of str
The list of link names the vehicle prefers.
"""
s.links_prefer = [s.W.get_link(l) for l in links]

def set_links_avoid(s, links):
"""
Set the links the vehicle avoids.
Parameters
----------
links : list of str
The list of link names the vehicle avoids.
"""
s.links_avoid = [s.W.get_link(l) for l in links]

def add_dests(s, dests):
"""
Add multiple destinations to the vehicle's destination list.
Expand Down Expand Up @@ -2008,6 +2032,17 @@ def show_network(W, width=1, left_handed=1, figsize=(6,6), network_font_size=10,
plt.show()
else:
plt.close("all")

def copy(W):
"""
Copy the World object.
Returns
-------
World object
The copied World object.
"""
return pickle.loads(pickle.dumps(W))


class Route:
Expand All @@ -2026,6 +2061,17 @@ def __init__(s, W, links, name="", trust_input=False):
List of name of links. The contents are str.
trust_input : bool
True if you trust the `links` in order to reduce the computation cost by omitting verification.
Examples
--------
>>> route = Route(W, ["l1", "l2", "l3"])
... vehicle_object.links_prefer = route
This will enforce the vehicle to travel the route if the route covers the entire links between the OD nodes of the vehicle.
>>> route = Route(W, ["l1", "l2", "l3"])
... for link in route:
... print(link)
This will print the links in the route.
"""
s.W = W
s.name = name
Expand All @@ -2049,6 +2095,15 @@ def __init__(s, W, links, name="", trust_input=False):
def __repr__(s):
return f"<Route {s.name}: {s.links}>"

def __iter__(s):
"""
Override `iter()` function. Iterate the links of the route.
"""
return iter(s.links)

def __len__(s):
return len(s.links)

def __eq__(self, other):
"""
Override `==` operator. If the links of two route are the same, then the routes are the same.
Expand Down

0 comments on commit 909373a

Please sign in to comment.