So I'm trying to animate a line that gradually goes through plot points and I can't seem to figure out a way to do so. I've tried using FuncAnimation with no success and similar questions on Stackoverflow haven't helped me. Any suggestions? Here's my code:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import math
import csv
import sys
import numpy
towns=[['Orleans', '1.750115', '47.980822'],
['Bordeaux', '-0.644905', '44.896839'],
['Bayonne', '-1.380989', '43.470961'],
['Toulouse', '1.376579', '43.662010'],
['Marseille', '5.337151', '43.327276'],
['Nice', '7.265252', '43.745404'],
['Nantes', '-1.650154', '47.385427'],
['Rennes', '-1.430427', '48.197310'],
['Paris', '2.414787', '48.953260'],
['Lille', '3.090447', '50.612962'],
['Dijon', '5.013054', '47.370547'],
['Valence', '4.793327', '44.990153'],
['Aurillac', '2.447746', '44.966838'],
['Clermont-Ferrand', '3.002556', '45.846117'],
['Reims', '4.134148', '49.323421'],
['Strasbourg', '7.506950', '48.580332'],
['Limoges', '1.233757', '45.865246'],
['Troyes', '4.047255', '48.370925'],
['Le Havre', '0.103163', '49.532415'],
['Cherbourg', '-1.495348', '49.667704'],
['Brest', '-4.494615', '48.447500'],
['Niort', '-0.457140', '46.373545']]
order=[0,
8,
17,
14,
9,
18,
19,
7,
6,
21,
1,
2,
3,
12,
13,
16,
11,
4,
5,
10,
15,
20,
0]
fig=plt.figure()
fig.patch.set_facecolor('#ffffff')
fig.patch.set_alpha(0.7)
ax = plt.axes()
ax.set_facecolor('grey')
plt.axis('off')
for i in range(len(order)):
plt.plot(float(towns[order[i]][1]), float(towns[order[i]][2]), 'o', color='black')
try:
line = plt.Line2D((float(towns[order[i]][1]), float(towns[order[i+1]][1])), (float(towns[order[i]][2]), float(towns[order[i+1]][2])), lw=2)
plt.gca().add_line(line)
except IndexError:
pass
towns[order[i]][1]
are x values
towns[order[i]][2]
are y values
How can I make it look like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, 105)
ax.set_ylim(0, 12)
line, = ax.plot(0, 0)
def animation_frame(i):
x_data.append(i * 10)
y_data.append(i)
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig, func=animation_frame, frames=np.arange(0, 10, 0.1), interval=10)
plt.show()
Thank you
There are a few ways to do this, but one option is to expand the data using list comprehension and passed it to a function to show it in a controlled manner. With all the data ready (t
, x
, y
, l
) use the animate
function to place all cities in the graph with ax.plot(x, y,...)
and the cities names with ax.annotate
. Then, pass to the Matplotlib function FuncAnimation
a function (update
in the code below) to call at each frame update to refresh the line segment being displayed.
def animate(t, x, y, l):
fig, ax = plt.subplots()
# fit the labels inside the plot
ax.set_xlim(min(x)-1, max(x)+3)
ax.set_ylim(min(y)-1, max(y)+1)
# place all cities on the graph
cities, = ax.plot(x, y, marker = "o", color = "black", ls = "", markersize = 5)
line_1, = ax.plot([], [], marker = "", color = "blue", ls ="-", lw = 2)
LABEL_OFFSET = 1.003 # text label offset from marker
for i in t:
ax.annotate(f'{l[i]}', xy=(x[i]*LABEL_OFFSET,y[i]*LABEL_OFFSET))
def update(i):
line_1.set_data(x[:i], y[:i])
return line_1
anim = FuncAnimation(fig, update, frames=len(t)+1, interval=300, repeat=True)
return anim
t = range(len(order))
x = [float(towns[order[i]][1]) for i in t]
y = [float(towns[order[i]][2]) for i in t]
l = [towns[order[i]][0] for i in t]
cities_travel = animate(t, x, y, l)
cities_travel.save("cities_travel.gif", writer = PillowWriter(fps = 2))