8000
Skip to content

Conversation

@nkleinbaer
Copy link

Opening a PR to implement feature proposed in #1187. Adds a new function animate_graph_route that is analogous to plot_graph_route, but shows a edge-by-edge animation instead of a static image.

In 59fd139 I simply copied the existing plot_graph_route function and modified it to create an animation. Since this created duplication of code shared by the two funcs (i.e. plotting the graph itself and origin/destination nodes) I then added a 52ffb46 to refactor this out. Wanted to keep that as a separate commit commit so it's easy to drop per your advice (thinking perhaps you may want to avoid touching existing functionality?).

For testing just added a single call to the new function in test_routing, following from what I saw done for plot_graph_route. Can add more tests if desired.

Haven't updated the changelog yet, not sure if I should put this under the 2.0.2 section or a new section.

Example usage (adapted from this example):

import osmnx as ox

place = "Piedmont, California, USA"
G = ox.graph.graph_from_place(place, network_type="drive")

# impute missing edge speeds and calculate free-flow travel times
G = ox.routing.add_edge_speeds(G)
G = ox.routing.add_edge_travel_times(G)

# calculate 3 shortest paths, minimizing travel time
w = "travel_time"
orig, dest = list(G)[10], list(G)[-10]
route1 = ox.routing.shortest_path(G, orig, dest, weight=w)
orig, dest = list(G)[0], list(G)[-1]
route2 = ox.routing.shortest_path(G, orig, dest, weight=w)
orig, dest = list(G)[-100], list(G)[100]
route3 = ox.routing.shortest_path(G, orig, dest, weight=w)

# animate routes
fig, ax = ox.plot.animate_graph_route(G, route1, orig_dest_size=0, node_size=0, save=True, show=True, filepath='images/animation.mp4')
fig, ax = ox.plot.animate_graph_route(G, route2, orig_dest_size=0, node_size=0, save=True, show=True, filepath='images/animation.gif')
fig, ax = ox.plot.animate_graph_route(G, route3, orig_dest_size=0, node_size=0, save=True, show=True, filepath='images/animation.mov')

@nkleinbaer nkleinbaer mentioned this pull request Mar 13, 2025
3 tasks
@codecov
Copy link
codecov bot commented Mar 13, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.46%. Comparing base (c6efebf) to head (d5801e8).
Report is 148 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1285      +/-   ##
==========================================
+ Coverage   98.44%   98.46%   +0.02%     
==========================================
  Files          24       24              
  Lines        2383     2417      +34     
==========================================
+ Hits         2346     2380      +34     
  Misses         37       37              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +380 to +388
G: nx.MultiDiGraph,
route: list[int],
*,
route_color: str = "r",
route_linewidth: float = 4,
route_alpha: float = 0.5,
orig_dest_size: float = 100,
ax: Axes | None = None,
**pg_kwargs: Any, # noqa: ANN401
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the user need more control than this? Animation speed? Repetition? Would fine-grain control be more easily exposed via keyword arguments to pass to matplotlib? Or?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense, IMO it's worth adding the two you mentioned (speed and repetition) as explicitly defined/documented arguments here, then additionally allow passing arbitrary kwargs to matplotlib for more advanced/obscure things.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the function signature/arguments as parsimonious and streamlined as possible. If these are straightforward kwargs to pass directly to matplotlib, that's probably the best solution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I'm running into with this is that we can only gobble up one dict of arbitrary kwargs, but then there are several places in this function where they may need to be passed: some may need to go to plot_graph (by way of _prepare_graph_route_plot), others may need to go to the constructor of FuncAnimation, others may need to go to Animation.save's savefig_kwargs argument, and finally some may need to be passed directly to Animation.save. So I feel like I need to split them all out to avoid passing unexpected kwargs in the wrong spot ... something like this nkleinbaer@904ecda ... but that feels kinda hacky and difficult to maintain (not to mention I haven't tested out that passing any of these kwargs actually works as expected, that's just a draft). What are your thoughts?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nkleinbaer perhaps each of the constant kwarg dicts you define in that commit at lines 404-460 could instead be individual function arguments of type dict. Then the user could pass individual kwarg dicts for each destination function.

@gboeing
Copy link
Owner
gboeing commented Mar 26, 2025

@jabahm you expressed interest in animation in #1187. Do you have any comments/feedback for this PR?

@olivier-be
Copy link
olivier-be commented Jun 7, 2025

Hello, I think this feature should be implemented, it works well and looks good. I have just found one thing that could be improved by adding support for Jupyter Lab, with in the animate_graph_route function:

if pg_kwargs.get("html_animation", True):
   From IPython.display import HTML
    HTML(anim.to_html5_video())

Or return the anim variable so we can do it outside.

Another possible idea is to be able to animate multiple routes at the same time.

Sorry if I'm not using GitHub correctly.

9F53

@gboeing
Copy link
Owner
gboeing commented Jun 24, 2025

@olivier-be thanks for your feedback. I agree we should make this useful for Jupyter users. We won't want to add an ipython dependency to the package to support it, but we could return the animation object to the caller, similar to how the static plotting function return fig, ax tuples to the caller.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0