The code below generates a animated basemap, but not exactly the one I want: I want the scatterplot from the previous frame to disappear, but it persists through the remainder of the animation.
I suspect it has something to do with my not understanding what the basemap really is. I understand calling it on lat/lons to project them to x/y, but I don't entirely get what's going on when I call event_map.scatter().
import random
import os
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import animation
import pandas as pd
from IPython.display import HTML
# Enables animation display directly in IPython
#(http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/)
from tempfile import NamedTemporaryFile
VIDEO_TAG = """<video controls>
<source src="data:video/x-m4v;base64,{0}" type="video/mp4">
Your browser does not support the video tag.
</video>"""
def anim_to_html(anim):
if not hasattr(anim, '_encoded_video'):
with NamedTemporaryFile(suffix='.mp4') as f:
anim.save(f.name, fps=20, extra_args=['-vcodec', 'libx264'])
video = open(f.name, "rb").read()
anim._encoded_video = video.encode("base64")
return VIDEO_TAG.format(anim._encoded_video)
def display_animation(anim):
plt.close(anim._fig)
return HTML(anim_to_html(anim))
animation.Animation._repr_html_ = anim_to_html
FRAMES = 20
POINTS_PER_FRAME = 30
LAT_MIN = 40.5
LAT_MAX = 40.95
LON_MIN = -74.15
LON_MAX = -73.85
FIGSIZE = (10,10)
MAP_BACKGROUND = '.95'
MARKERSIZE = 20
#Make Sample Data
data_frames = {}
for i in range(FRAMES):
lats = [random.uniform(LAT_MIN, LAT_MAX) for x in range(POINTS_PER_FRAME)]
lons = [random.uniform(LON_MIN, LON_MAX) for x in range(POINTS_PER_FRAME)]
data_frames[i] = pd.DataFrame({'lat':lats, 'lon':lons})
class AnimatedMap(object):
""" An animated scatter plot over a basemap"""
def __init__(self, data_frames):
self.dfs = data_frames
self.fig = plt.figure(figsize=FIGSIZE)
self.event_map = Basemap(projection='merc',
resolution='i', area_thresh=1.0, # Medium resolution
lat_0 = (LAT_MIN + LAT_MAX)/2, lon_0=(LON_MIN + LON_MAX)/2, # Map center
llcrnrlon=LON_MIN, llcrnrlat=LAT_MIN, # Lower left corner
urcrnrlon=LON_MAX, urcrnrlat=LAT_MAX) # Upper right corner
self.ani = animation.FuncAnimation(self.fig, self.update, frames=FRAMES, interval=1000,
init_func=self.setup_plot, blit=True,
repeat=False)
def setup_plot(self):
self.event_map.drawcoastlines()
self.event_map.drawcounties()
self.event_map.fillcontinents(color=MAP_BACKGROUND) # Light gray
self.event_map.drawmapboundary()
self.scat = self.event_map.scatter(x = [], y=[], s=MARKERSIZE,marker='o', zorder=10)
return self.scat
def project_lat_lons(self, i):
df = data_frames[i]
x, y = self.event_map(df.lon.values, df.lat.values)
x_y = pd.DataFrame({'x': x, 'y': y}, index=df.index)
df = df.join(x_y)
return df
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
self.scat = self.event_map.scatter(x = df.x.values, y=df.y.values, marker='o', zorder=10)
return self.scat,
s = AnimatedMap(data_frames)
s.ani
It looks like you're simply adding a new scatter plot at each update. What you should do instead is change the data in the existing path collection at each update. Try something along the lines of
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
new_offsets = np.vstack([df.x.values, df.y.values]).T
self.scat.set_offsets(new_offsets)
return self.scat,
Note that I haven't tested this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With