How to do Cartopy simple animations - python-3.x

I am trying to create a simple animation using Cartopy. Basically just drawing a few lines in the map. So far I am trying the following:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.animation as animation
import numpy as np
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.coastlines()
lons = 10 * np.arange(1, 10)
lats = 10 * np.arange(1, 10)
def animate(i):
plt.plot([lons[i-1], lons[i]], [lats[i-1], lats[i]], color='blue', transform=ccrs.PlateCarree())
return plt
anim = animation.FuncAnimation(plt.gcf(), animate, frames=np.arange(1, 8), init_func=None, interval=2000, blit=True)
plt.show()
Does anyone know why this is not working?

This is unrelated to cartopy, I would guess. The problem is that you cannot return pyplot from the animation function. (It's like instead of buying a book, you'd buy the whole book store and then wonder why you can't read a book store.)
The easiest solution is to turn blitting off:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots()
lons = 10 * np.arange(1, 10)
lats = 10 * np.arange(1, 10)
def animate(i):
plt.plot([lons[i-1], lons[i]], [lats[i-1], lats[i]], color='blue')
anim = animation.FuncAnimation(plt.gcf(), animate, frames=np.arange(1, 8),
init_func=None, interval=200, blit=False)
plt.show()
If for some reason you need blitting (which would the case if the animation is too slow or consumes too much CPU), you need to return a list of the Line2D objects you want to draw.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots()
lons = 10 * np.arange(1, 10)
lats = 10 * np.arange(1, 10)
lines = []
def animate(i):
line, = plt.plot([lons[i-1], lons[i]], [lats[i-1], lats[i]])
lines.append(line)
return lines
anim = animation.FuncAnimation(plt.gcf(), animate, frames=np.arange(1, 8),
interval=200, blit=True, repeat=False)
plt.xlim(0,100) #<- remove when using cartopy
plt.ylim(0,100)
plt.show()

The Warining is
The animation function must return a sequence of Artist objects
So just change the blit keyword from True to false and then save the animation into a gif file can solve this.
Otherwise, you can plot a sequence of Artist objects can then set the
blit=True as follows:
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.coastlines()
lons = 10 * np.arange(1, 10)
lats = 10 * np.arange(1, 10)
def animate(i):
line = ax.plot([lons[i-1], lons[i]], [lats[i-1], lats[i]], color='blue', transform=ccrs.PlateCarree())
return line
anim = animation.FuncAnimation(plt.gcf(), animate, frames=np.arange(1, 8), init_func=None, interval=200, blit=True)
anim.save('test.gif')
plt.show()
May these help you.

Related

Matplotlib: how to get color bars that are one on top of each other as opposed to side by side?

I have the following code:
import matplotlib.pyplot as plt
import numpy as np
img1 = np.zeros([512,512])
img2 = np.zeros([512,512])
plt.figure(figsize=(10,10))
plt.imshow(img1, cmap='inferno')
plt.axis('off')
cba = plt.colorbar(shrink=0.25)
cba.ax.set_ylabel('Events / counts', fontsize=14)
cba.ax.tick_params(labelsize=12)
plt.imshow(img2, cmap='turbo', alpha=0.5)
plt.axis('off')
cba = plt.colorbar(shrink=0.25)
cba.ax.set_ylabel('Lifetime / ns)', fontsize=14)
cba.ax.tick_params(labelsize=12)
plt.tight_layout()
plt.show()
which produces the following output:
My question is, how can I get color bars that are on top of one another as opposed to next to each other? Ideally, I would like to get something like this:
You can grab the position of the ax and use it to create new axes for the colorbars. Here is an example:
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
data = ndimage.gaussian_filter(np.random.randn(512, 512), sigma=15, mode='nearest') * 20
fig, ax = plt.subplots()
im1 = ax.imshow(data, vmin=-1, vmax=0, cmap='viridis')
data[data < 0] = np.nan
im2 = ax.imshow(data, vmin=0.001, vmax=1, cmap='Reds_r')
ax.axis('off')
pos = ax.get_position()
bar_h = (pos.y1 - pos.y0) * 0.5 # 0.5 joins the two bars, e.g. 0.48 separates them a bit
ax_cbar1 = fig.add_axes([pos.x1 + 0.02, pos.y0, 0.03, bar_h])
cbar1 = fig.colorbar(im1, cax=ax_cbar1, orientation='vertical')
ax_cbar2 = fig.add_axes([pos.x1 + 0.02, pos.y1 - bar_h, 0.03, bar_h])
cbar2 = fig.colorbar(im2, cax=ax_cbar2, orientation='vertical')
plt.show()

Matplotlib- Add a color bar below a multi-colored line subplot as shown in the image

I am having a multicolored line plot and I want to add a color bar under it in the same figure like as shown in the image below, Is it possible?
I have attached a color bar image as a reference which I took from another code.
My intention here is to use the color bar like a legend for each segment of the line in the plot.
Edit-1: I want to have the color bar using a mappable object such as an image, So don't want to create a new subplot for the sole purpose of the color bar.
Any suggestion is welcome. Thanks in Advance.
This is the code for multicolored line plot
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
plt.show()
This is a workaround I'am using:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import matplotlib.colorbar as mcolorbar
import matplotlib.colors as mcolors
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots(2, 1, gridspec_kw={'height_ratios' : [5,1]})
ax[0].add_collection(lc)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax[0].set_xlim([0, 15])
ax[0].set_ylim([0, 10])
cb2 = mcolorbar.ColorbarBase(ax = ax[1], cmap = cmap, orientation = 'horizontal', extendfrac='auto')
cb2.set_ticks(bounds)
cb2.set_ticklabels(labels)
plt.tight_layout()
plt.show()
If you specifically want to avoid subplots, you can use a scalar mappable:
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
cmap = mcolors.ListedColormap(['r','g','b','y'])
sm = plt.cm.ScalarMappable(cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar = fig.colorbar(sm, ax=ax, orientation='horizontal',aspect=90)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax.set_xlim([0, 15])
ax.set_ylim([0, 10])
cbar.set_ticks(bounds)
cbar.set_ticklabels(labels)
plt.tight_layout()
plt.show()
This helped me to get what I asked.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
c=[1,2,3,4,5]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
cmap = mcolors.ListedColormap(['r','g','b','y'])
norm = mcolors.BoundaryNorm([1,2,3,4,5],4)
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar=fig.colorbar(sm, ticks=c, orientation='horizontal')
cbar.set_ticklabels(['Action1', 'Action2', 'Action3', 'Action4'])
plt.show()

How to animate png with Matplotlib?

I am trying to animate an png by moving it with changing its x position but the the loop doesn't overwrite the previous step but instead it just leave it and show a new version beside it:
first step
Final step
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.image as mgimg
fig, ax = plt.subplots()
plt.axis([0, 400, 0, 300])
def animate(i):
imobj = ax.imshow(np.zeros((0,0)),extent=[0+i, 67+i, 136, 100], aspect='auto',zorder=1)
fname='catapult0.png'
img = mgimg.imread(fname)[-1::-1]
imobj.set_data(img)
return imobj,
anim = animation.FuncAnimation(fig, animate,
frames=100,
interval=5,
repeat=False)
ax.set_aspect('equal')
plt.show()
You should create the image once, outside the animating function. Then for the animation you only need to change the image's position, i.e.
image.set_extent(...)
Complete code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.image as mgimg
fig, ax = plt.subplots( figsize=(4,4))
plt.axis([0, 200, 0, 300])
fname='https://i.stack.imgur.com/UuTST.png'
img = mgimg.imread(fname)[230:230+55,51:51+86]
imobj = ax.imshow(img,extent=[0, img.shape[1], 136, 136+img.shape[0]], zorder=1)
def animate(i):
imobj.set_extent([0+i, img.shape[1]+i, 136, 136+img.shape[0]])
return imobj,
anim = animation.FuncAnimation(fig, animate, frames=100, interval=40, repeat=False)
ax.set_aspect('equal')
plt.show()

How to draw a graph using matplotlib?

draw a graph of equation in the form of y=mx+b in python3.x
example y = 5x + 9
This is a very general question. Try to be more specific. It depends how you want to draw it.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0., 5., 0.2)
y = 5 * x + 9
plt.plot(x, y)
plt.show()
or
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-1., 5., 0.2)
y = 5 * x + 9
fig, ax = plt.subplots()
ax.plot(x,y)
ax.grid(True, which='both')
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
These are very basic drawing. You can create more sophisticated graphs, but you will have to be more specific in your question.
You can define your y(x) function and then plot it as follows:
import matplotlib.pyplot as plt
def y(x):
return [5*i+9 for i in x]
x = range(0,10)
plt.plot(x,y(x))
plt.show()
This produces follwing graph:
With turtle
You can as well get a graph with turtle with following code for example:
from turtle import Turtle, Screen
def y(x):
return 5*x+9
def plotter(turtle, x_range):
turtle.penup()
for x in x_range:
turtle.goto(x, y(x))
turtle.pendown()
screen = Screen()
screen.setworldcoordinates(0, 0, 9, 60)
turtle = Turtle(visible=False)
x = range(0,10)
plotter(turtle, x)
screen.exitonclick()
Which produces:

Matplotlib FuncAnimation not animating line plot

I have two random vectors which are used to create a line plot. Using the same vectors, I would like to animate the line but the animation is static - it just plot the original graph. Any suggestions on how to animate such a line plot?
import numpy as np
import matplotlib.pyplot as py
from matplotlib import animation
# random line plot example
x = np.random.rand(10)
y = np.random.rand(10)
py.figure(3)
py.plot(x, y, lw=2)
py.show()
# animation line plot example
fig = py.figure(4)
ax = py.axes(xlim=(0, 1), ylim=(0, 1))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=False)
The final frame of the animation should look something like the plot below. Keep in mind that this is a random plot so the actual figure will change with each run.
Okay, I think what you want is to only plot up to the i-th index for frame i of the animation. In that case, you can just use the frame number to limit the data displayed:
import numpy as np
import matplotlib.pyplot as py
from matplotlib import animation
x = np.random.rand(10)
y = np.random.rand(10)
# animation line plot example
fig = py.figure(4)
ax = py.axes(xlim=(0, 1), ylim=(0, 1))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(x[:i], y[:i])
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=len(x)+1,
interval=200, blit=False)
Notice I changed the number of frames to len(x)+1 and increased the interval so it's slow enough to see.

Resources