Plotting animation on basemap - python

I'm trying to animate a plot that moves across my map (field of regard of an airplane). I need it to iterate through my coordinates, so I tried using a for loop. When I run this, I get two frames, the first is empty, and the second is just an empty map.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.basemap import Basemap
#map of Puerto Rico
map=Basemap(projection='merc', llcrnrlat=17.5,urcrnrlat=19.,llcrnrlon=-67.5, urcrnrlon=-65, epsg=4139)
map.arcgisimage(service='World_Shaded_Relief', xpixels = 2000)
plt.title("Flight Path of sbet 0059")
#sample coordinates
lon=[-63,-64,-65,-66]
lon=[int(l) for l in lon]
lat=[17., 17.5, 18., 18.5]
lat=[int(l) for l in lat]
time=[1, 3, 5, 7]
time=[int(l) for l in time]
fig=plt.figure()
for a1,b1 in zip(lon,lat):
def init():
fig
return(fig),
def animate(i):
x,y=map(a1,b1)
map.plot(x,y, linewidth = 1,color = 'm')
return(map),
anim=animation.FuncAnimation(fig, animate, frames=len(time), interval=1000)
plt.show()
Does anyone know what the problem is and how I get the plot to move across the map? Thanks!

Your code had quite a few problems. Maybe I just list them here and then you can ask questions if there is something you don't understand:
If no figure has been opened yet, the call to Basemap will generate a new figure. This makes the extra call to plt.figure() (kind of) redundant. I usually call plt.figure anyway, but before the call to Basemap.
When you use FuncAnimation, you do not have to loop through the values you want to animate -- FuncAnimation is doing this for you. The only thing that you need to provide is a function that updates the current plot according to the integer argument that FuncAnimation will provide to this function. In other words, the entire for loop you wrote is not the way to go.
Only call plt.show() once. Right now you call it every time you run through your for loop.
Don't call the plot command every time you want to update your plot. If everything else in your code would be correct, you would get many lines instead of just one. Instead update the data of the line object that is returned by plot. See the code below how this is done.
The amount of data points you provide and the amount of frames to want to animate do not match (4 vs 1000), which means that the animation will run "empty" for a long time.
Most of the coordinates you provide are outside of the map region that you display.
Don't call the Basemap object map, even though they do so in the tutorials. map is a reserved word in python. Rather use m or bmap or something.
Below I tried to correct the problems with your code, but it might be altered too much for your needs, so please ask if there is anything unclear:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.basemap import Basemap
fig=plt.figure()
#map of Puerto Rico
bmap=Basemap(projection='merc', llcrnrlat=17.5,urcrnrlat=19.,llcrnrlon=-67.5, urcrnrlon=-65, epsg=4139)
bmap.arcgisimage(service='World_Shaded_Relief', xpixels = 2000)
plt.title("Flight Path of sbet 0059")
#sample coordinates
##lon=[-63,-64,-65,-66]
##lon=[int(l) for l in lon]
##lat=[17., 17.5, 18., 18.5]
##lat=[int(l) for l in lat]
##time=[1, 3, 5, 7]
##time=[int(l) for l in time]
#generate the flight coordinates (just a straight line with 100 points)
N = 100
lon = np.linspace(-64, -68, N)
lat = np.linspace(17, 19.5, N)
#only convert the coordinates once
x,y = bmap(lon, lat)
#generate the original line object with only one point
line, = bmap.plot(x[0], y[0], linewidth = 1, color = 'm')
def animate(i):
#this is doing all the magic. Every time animate is called, the line
#will become longer by one point:
line.set_data(x[:i],y[:i])
return line
anim=animation.FuncAnimation(fig, animate, frames=N, interval=N)
plt.show()

Related

Plots not visible when using a line plot

I am new to python and I am trying to plot x and y (both have a large number of data) but when I use a plt.plot there is not plot visible on the output.
The code I have been using is
for i in range(len(a)):
plt.plot(a[i],b[i])
plt.figure()
plt.show()
when I tried a scatter plot
for i in range(len(a)):
plt.scatter(a[i],b[i])
plt.figure()
plt.show()
I am not able to understand the reason for missing the line plot and even when I try seaborn it showing me an error ValueError: If using all scalar values, you must pass an index
import numpy as np
import matplotlib.pyplot as plt
a = np.linspace(0,5,100)
b = np.linspace(0,10,100)
plt.plot(a,b)
plt.show()
I think this answers your question. I have taken sample values of a and b. The matplotlib line plots are not required to run in loops
A line is created between two points. If you are plotting single values, a line can't be constructed.
Well, you might say "but I am plotting many points," which already contains part of the answer (points). Actually, matplotlib.plot() plots line-objects. So every time, you call plot, it creates a new one (no matter if you are calling it on the same or on a new axis). The reason why you don't get lines is that only single points are plotted. The reason why you're not even seeing the these points is that plot() does not indicate the points with markers per default. If you add marker='o' to plot(), you will end up with the same figure as with scatter.
A scatter-plot on the other hand is an unordered collection of points. There characteristic is that there are no lines between these points because they are usually not a sequence. Nonetheless, because there are no lines between them, you can plot them all at once. Per default, they have all the same color but you can even specify a color vector so that you can encode a third information in it.
import matplotlib.pyplot as plt
import numpy as np
# create random data
a = np.random.rand(10)
b = np.random.rand(10)
# open figure + axes
fig,axs = plt.subplots(1,2)
# standard scatter-plot
axs[0].scatter(a,b)
axs[0].set_title("scatter plot")
# standard line-plot
axs[1].plot(a,b)
axs[1].set_title("line plot")

Create single animation from programme

This programme fills a figure with square patches. The y axis limit is set so that it will be seen that there is only one patch in one position. It plots this filling process. I want to record the filling as an animation and am trying to do so with 'matplotlib.animation'. I turn the plotting part of the programme into a function (def filler(b):) so that I can pass this function to the animation lines at the bottom. When I run the programme I get an error right at the end of the plotting saying Python has stopped working. Please could somebody explain why. Thanks.
Note that I don't know what the b in the function argument is meant to represent. I include it because without it the programme doesn't run, asking for a positional argument.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import numpy as np
startx = 0
endx = 10
blocks = 100
points = np.random.randint(startx,endx,size=blocks)
y = [-1]*int(endx-startx)
fig = plt.figure(figsize=(5,6))
ax = fig.add_subplot(111,aspect='equal')
ax.set_xlim(startx,endx)
ax.set_ylim(0,5)
def filler(b):
for i in range(blocks):
z = 5
a = patches.Rectangle((points[i],z),1,1,ec='k',fc=(1-i/blocks,i/(2*blocks),i/blocks))
ax.add_patch(a)
while z>y[int(points[i])]+1:
z=z-1
plt.pause(0.001)
a.set_y(z)
y[int(points[i])]=z
filler_ani = animation.FuncAnimation(fig, filler,interval=50, repeat=False, blit=True)
filler_ani.save('filler.mp4')
The code in the question mixes two different types of animations. Using a loop and plt.draw(), and a FuncAnimation. This will lead to chaos, as essentially the complete animation on screen is done during the first frame of the FuncAnimation, at the end of that first frame the animation fails.
So, one has to decide. Since it seems you want to do a FuncAnimation here, in order to be able to save it, one needs to get rid of the plt.draw. Then the problem is that there is a for loop and a while loop. This makes it hard to use a framenumber based animation.
Instead one may use a generator based animation.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import numpy as np
startx = 0
endx = 10
blocks = 101
points = np.random.randint(startx,endx,size=blocks)
y = [-1]*int(endx-startx)
fig = plt.figure(figsize=(5,6))
ax = fig.add_subplot(111,aspect='equal')
ax.set_xlim(startx,endx)
ax.set_ylim(0,5)
def filler():
yield None
for i in range(blocks):
z = 5
a = patches.Rectangle((points[i],z),1,1,ec='k',fc="r")
ax.add_patch(a)
while z>y[int(points[i])]+1:
z=z-1
a.set_y(z)
yield a
y[int(points[i])]=z
filler_ani = animation.FuncAnimation(fig, lambda x: None, frames=filler,
interval=50, blit=False, repeat=False)
plt.show()
This is kind of hacky, but stays most closely to your initial code.

Animate a circle whose radius and evolution times are provided as arrays

How do I animate a circle when I have its radius as a function of time (I have them both as arrays) in Python? That is, I want the frames to follow the times given in the time array.
I did the following which works but does not make use of FuncAnimation() (which is a problem as I need to save it as a GIF or mp4).
import matplotlib.pyplot as plt
import numpy as np
time = np.array([0.0,0.1,0.122,0.124,0.4,0.45,0.5,1.2,1.4,1.5])
r = np.array([100.0,99.0,90.0,80.0,78.0,50.0,40.0,30.0,10.0,5.0])
fig, ax = plt.subplots()
ax.set_xlim(-max(r),max(r))
ax.set_ylim(-max(r),max(r))
for j in range(len(time)):
ax.add_artist(plt.Circle((0, 0), r[j], color='r'))
ax.set_title(str(time[j]))
fig.canvas.draw()
if j+1==len(time):
break
plt.pause((time[j+1]-time[j]))
plt.gca().cla()
plt.show()
Is there a way to do it using FuncAnimation() and save it?

Animating with matplotlib without animation function

Is there a way to animate a graph in matplotlib without resorting to the built in animation functions? I find them extremely awkward to use and feel it would be much simpler to just plot a point, wipe the graph, then plot the next point.
I envision something such as:
def f():
# do stuff here
return x, y, t
where each t would be a different frame.
I mean, I've tried stuff like using plt.clf(), plt.close() etc. but nothing seems to work.
It is sure possible to animate without FuncAnimation. The purpose of "the enivisioned function", however, is not really clear. In an animation, the time is the independent variable, i.e. for each time step you produce some new data to plot or similar. Therefore the function would take t as an input and give some data back.
import matplotlib.pyplot as plt
import numpy as np
def f(t):
x=np.random.rand(1)
y=np.random.rand(1)
return x,y
fig, ax = plt.subplots()
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for t in range(100):
x,y = f(t)
# optionally clear axes and reset limits
#plt.gca().cla()
#ax.set_xlim(0,1)
#ax.set_ylim(0,1)
ax.plot(x, y, marker="s")
ax.set_title(str(t))
fig.canvas.draw()
plt.pause(0.1)
plt.show()
Also, it is not clear why you would want to avoid FuncAnimation. The same animation as above can be produced with FuncAnimation as follows:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
def f(t):
x=np.random.rand(1)
y=np.random.rand(1)
return x,y
fig, ax = plt.subplots()
ax.set_xlim(0,1)
ax.set_ylim(0,1)
def update(t):
x,y = f(t)
# optionally clear axes and reset limits
#plt.gca().cla()
#ax.set_xlim(0,1)
#ax.set_ylim(0,1)
ax.plot(x, y, marker="s")
ax.set_title(str(t))
ani = matplotlib.animation.FuncAnimation(fig, update, frames=100)
plt.show()
There is not much changed, you have the same number of lines, nothing really awkward to see here.
Plus you have all the benefits from FuncAnimation when the animation gets more complex, when you want to repeat the animation, when you want to use blitting, or when you want to export it to a file.
it is not clear why you would want to avoid FuncAnimation.
For very simple tests, where you want to check a situation deep inside a loop, it is not easy to set up an animation function.
For instance, I wanted to visualize what happens with this strange sort algorithm: https://arxiv.org/pdf/2110.01111.pdf. To my opinion, the simplest way to do it is:
import numpy as np
import matplotlib.pyplot as plt
def sort(table):
n = len(table)
for i in range (n):
for j in range (n):
if table[i] < table[j]:
tmp = table[i]
table[i] = table[j]
table[j] = tmp
plt.plot(table, 'ro')
plt.title(f"i {i} j {j}")
plt.pause(0.001)
plt.clf() # clear figure
return table
n = 50
table = np.random.randint(1,101,n)
sort(table)
```python
I agree that FuncAnimation is awkward to use (not pythonic at all). Actually I believe this function doesn't make too much sense. What is the advantage to have it?
Yes, it introduces an implicit loop that you do not have to write yourself. But the reader cannot fully control this loop and -unless he knows the syntax of the function in advance- he cannot even understand it. Personally I avoid FuncAnimation for reasons of clarity and versatility. Here's a minimal pseudocode example to do that:
fig=plt.figure("animation")
M=zeros((sizeX,sizeY)) # initialize the data (your image)
im=plt.imshow(M) # make an initial plot
########### RUN THE "ANIMATION" ###########################
while {some condition}:
M=yourfunction() # updates your image
im.set_array(M) # prepare the new image
fig.canvas.draw() # draw the image
plt.pause(0.1) # slow down the "animation"
Very simple and you can see what is happening in your code.

Matplotlib pyplot in real time

I have a while function that generates two lists of numbers and at the end I plot them using matplotlib.pyplot.
I'm doing
while True:
#....
plt.plot(list1)
plt.plot(list2)
plt.show()
But in order to see the progression I have to close the plot window.
Is there a way to refresh it with the new data every x seconds?
The most robust way to do what you want is to use matplotlib.animation. Here's an example of animating two lines, one representing sine and one representing cosine.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
sin_l, = ax.plot(np.sin(0))
cos_l, = ax.plot(np.cos(0))
ax.set_ylim(-1, 1)
ax.set_xlim(0, 5)
dx = 0.1
def update(i):
# i is a counter for each frame.
# We'll increment x by dx each frame.
x = np.arange(0, i) * dx
sin_l.set_data(x, np.sin(x))
cos_l.set_data(x, np.cos(x))
return sin_l, cos_l
ani = animation.FuncAnimation(fig, update, frames=51, interval=50)
plt.show()
For your particular example, you would get rid of the while True and put the logic inside that while loop in the update function. Then, you just have to make sure to do set_data instead of making a whole new plt.plot call.
More details can be found in this nice blog post, the animation API, or the animation examples.
I think what you're looking for is the "animation" feature.
Here is an example
This example is a second one.

Categories