A notebook to answer a StackOverflow question relating to animating the rotation of a projecton using cartopy
%matplotlib notebook
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.animation as animation
import matplotlib.image as mimage
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import numpy as np
import shapely.geometry as sgeom
plt.figure(figsize=(6, 6))
line = sgeom.LineString([[0, 15], [-140, -40], [120, -20],
[0, -20], [-140, 15], [90, 45],
[0, 15]])
class HighResPC(ccrs.PlateCarree):
@property
def threshold(self):
return super(HighResPC, self).threshold / 100
projected_line = HighResPC().project_geometry(line, ccrs.Geodetic())
verts = np.concatenate([np.array(l.coords) for l in projected_line])
def setup_axes(ax, x, y):
ax.set_global()
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
# Add the projected line to the map.
ax.add_geometries(
[projected_line], HighResPC(),
edgecolor='blue', facecolor='none')
# Image from http://madmen.wikia.com/wiki/File:Superman.gif.
superman = plt.imread('superman.png')
# Scale the actual image down a little.
img_size = np.array(superman.shape) / 2
x, y = ax.projection.transform_point(x, y, ccrs.PlateCarree())
# Convert the projected coordinates into pixels.
x_pix, y_pix = ax.transData.transform((x, y))
# Make the extent handle the appropriate image size.
extent = [x_pix - 0.5 * img_size[1], y_pix - 0.5 * img_size[0],
x_pix + 0.5 * img_size[1], y_pix + 0.5 * img_size[0]]
bbox = mtransforms.Bbox.from_extents(extent)
img = mimage.BboxImage(bbox, zorder=10)
img.set_data(superman)
ax.add_artist(img)
return img
def animate_superman(i):
i = i % verts.shape[0]
ax = plt.gca()
ax.remove()
ax = plt.axes([0, 0, 1, 1], projection=ccrs.Orthographic(
central_latitude=verts[i, 1], central_longitude=verts[i, 0]))
ax.coastlines()
img = setup_axes(ax, verts[i, 0], verts[i, 1])
ani = animation.FuncAnimation(
plt.gcf(), animate_superman,
frames=verts.shape[0],
interval=125, repeat=False)
ani.save('superman.gif', writer='imagemagick', dpi=plt.gcf().dpi)
plt.show()