What I am trying to do is to plot the data from an orbital simulation using Matplotlib; an example of what I am trying to achieve is attached below.
I have the data of planets as well as the spacecraft in X,Y,Z, time coordinates(or if necessary I can rerun to get it in other coordinate systems) with respect to the Sun.
How can I make a GIF like this?
I tried just using pyplot of x,y, but instead of the moving GIF I only get a flat image.(As expected, because PyPlot can only make static graphs). Any help/pointers to where I can look is greatly appreciated.
One can use matplotlib FuncAnimation
facility to create animations based on data in matplotlib. The idea is to view the animation as a collection of frames and define the graph/contents of each frame based on the frame number (assuming that the frames are placed in ascending order). More details on matplotlib animations can be found on this page on the official site: Matplotlib Animations. Rest of the answer provides some guidance specifically for the question.
Here is a dummy animation that I created using matplotlib FuncAnimation
. It is based around the idea of the question (though with dummy data).
Here is the code to generate the above animation -
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import math
# dummy parameters, replace them with your own
sun_x, sun_y = 0, 0
voyager1_theta, voyager1_speed, voyager1_radius = 0, 0.003*math.pi, 40
planet1_theta, planet1_speed, planet1_radius = 0, 0.002*math.pi, 40
planet2_theta, planet2_speed, planet2_radius = 0, 0.001*math.pi, 90
fps = 10 # frames per second in animation
total_time = 5 # in seconds, total time of animation
time_ratio = 100 # in seconds, how many seconds in reality for every second on animation
# extra calculations
interval = 1/fps # in seconds
total_frames = total_time * fps
voyager_x_data = []
voyager_y_data = []
fig, ax = plt.subplots()
def update(frame):
# this function accepts the frame-number as frame and creates the figure for it
global ax
# dummy calculations, replace them with your own calculations
time = frame*interval*time_ratio # in seconds
voyager_x = (voyager1_radius+time/20) * math.cos(voyager1_speed*time + voyager1_theta)
voyager_y = (voyager1_radius+time/20) * math.sin(voyager1_speed*time + voyager1_theta)
voyager_x_data.append(voyager_x)
voyager_y_data.append(voyager_y)
planet1_x = planet1_radius * math.cos(planet1_speed*time + planet1_theta)
planet1_y = planet1_radius * math.sin(planet1_speed*time + planet1_theta)
planet2_x = planet2_radius * math.cos(planet2_speed*time + planet2_theta)
planet2_y = planet2_radius * math.sin(planet2_speed*time + planet2_theta)
# plotting
ax.clear() # clear the figure (to remove the contents of last frame)
ax.set_ylim(-100,100)
ax.set_xlim(-100,100)
ax.set_aspect('equal')
ax.set_title('Time = {:.3f} sec'.format(time))
ax.scatter(sun_x, sun_y, color = 'yellow', marker = 'o') #, label='Sun')
ax.scatter(planet1_x, planet1_y, color = 'blue', marker = 'o') #, label = 'Planet1')
ax.scatter(planet2_x, planet2_y, color = 'green', marker = 'o') #, label = 'Planet2')
ax.plot(voyager_x_data, voyager_y_data, color = 'red')
ax.scatter(voyager_x, voyager_y, color = 'red', label = 'Voyager')
ax.legend()
anim = FuncAnimation(fig, update, frames = range(total_frames), interval = interval*1000) # multiplying by 1000 to convert sec into milli-sec
anim.save('result.gif', fps = fps) # replace the name of file with your own path and name
#plt.show() # uncomment this line and comment out the second-last line to see in realtime instead of saving
The comments inside the code contain all the assistive information required while reading and working with the code. This section contains overall explanation.
fps
, total_time
, time_ratio
.fig, ax = plt.subplots()
) and then define the update
function which takes frame number as input and creates the figure for that frame number.FuncAnimation
. In the code we did this with FuncAnimation(fig, update, frames = range(total_frames), interval = interval*1000)
. We specify the frames to match the time we want and interval to match the FPS we want in the animation.plt.show()
. To save, call anim.save()
in which, specify the path to the output file along with the filename and the fps to match the FPS we want.Note
- The extension in the filename specified to
anim.save()
is used to decide the export format. Additional formats are supported with the help offfmpeg
. To get support for additional formats, installffmpeg
and specifywriter='ffmpeg'
inanim.save()
.- While viewing the animation in realtime i.e. using
plt.show()
, setframes=None
inFuncAnimation()
since we don't want to specify how long the animation runs. We want it to run as long as we are viewing it.