1

I'm new to basemap and python but I'm trying to build a plotter for weather model for daily cron job. We plot about 1000 images a day.

I've wrote some script to achieve what i want. But it took very long time, because it re-draw the basemap for every time-step. It took 30 seconds to draw the basemap, and only took 4 seconds to draw the contourf().

I have some idea to speed up the process by pre-draw the basemap and update the contourf() in each iteration. But i don't understand how matplotlib objects work.

I have researched for the same question about this but haven't found any. But I found something similar from here from user3982706.

from matplotlib import animation
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap


fig, ax = plt.subplots()

# set up map projection
m = Basemap(projection='nsper',lon_0=-0,lat_0=90)
m.drawcoastlines()
m.drawparallels(np.arange(0.,180.,30.))
m.drawmeridians(np.arange(0.,360.,60.))

# some 2D geo arrays to plot (time,lat,lon)
data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are animating three artists, the contour and 2 
# annotatons (title), in each frame
ims = []
for i in range(len(data[:,0,0])):
    im = m.contourf(lons,lats,data[i,:,:],latlon=True)
    add_arts = im.collections
    text = 'title={0!r}'.format(i)
    te = ax.text(90, 90, text)
    an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
    ims.append(add_arts + [te,an])

ani = animation.ArtistAnimation(fig, ims)
## If you have ffmpeg you can save the animation by uncommenting 
## the following 2 lines
# FFwriter = animation.FFMpegWriter()
# ani.save('basic_animation.mp4', writer = FFwriter)
plt.show()

That script save the contour data as a list of artist. I don't need animation. so I need to edit this script to save the figure inside the loop. So this script produce figure1.png, figure2.png, figure3.png, and so on.

Any suggestion?

  • If you are new and are not relying on existing work in Basemap yet, then i can strongly recommend using cartopy instead of basemap. It can save you a lot of headaches. – Chiel Sep 19 '17 at 14:31

1 Answers1

0

(a) Using animation to save images

Because you already have the animation present in the code, you may directly save each image of the animation via the ani.save command.

ani.save("figure.png", writer="imagemagick")

This will create 20 figures called figure-0.png to figure-19.png. It requires imagemagick to be installed and available in your environment.

(b) Save individual figures

If the above fails for whatever reason, you may save the individual figures via plt.savefig(). At the end of each loop step, you would then remove the artists from the axes and delete them.

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap

fig, ax = plt.subplots()

m = Basemap(projection='nsper',lon_0=-0,lat_0=90)
m.drawcoastlines()
m.drawparallels(np.arange(0.,180.,30.))
m.drawmeridians(np.arange(0.,360.,60.))

data = np.random.random_sample((20,90,360))
lat = np.arange(len(data[0,:,0]))
lon = np.arange(len(data[0,0,:]))
lons,lats = np.meshgrid(lon,lat)

for i in range(len(data[:,0,0])):
    im = m.contourf(lons,lats,data[i,:,:],latlon=True)
    text = 'title={0!r}'.format(i)
    te = ax.text(90, 90, text)
    an = ax.annotate(text, xy=(0.45, 1.05), xycoords='axes fraction')
    # save the figure
    plt.savefig("figure_{}.png".format(i))
    # remove stuff from axes
    for c in im.collections:
        c.remove()
    te.remove()
    an.remove()
    del im; del te; del an

plt.show()
Community
  • 1
  • 1
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you for your help. But when i tried this, the code run with no error. And only produced one "figure.png" with a large file size. And it can not be opened. I'm using windows, matplotlib 2.0.2, and imagemagick installer from https://www.imagemagick.org/script/binary-releases.php – luthfi.imanal Sep 20 '17 at 15:18
  • I don't know what's wrong then, it works fine for me. So I added an alternative which does not require an animation. – ImportanceOfBeingErnest Sep 20 '17 at 15:29
  • Thanks for the alternative! It works! Silly me. I didn't know that contourf object can be removed by removing it from collections. I thought it can not be deleted because it doesn't have remove method. Can i ask you one more question sir? Can i save the basemap state so i can use it later without redrawing it? Thanks!!! – luthfi.imanal Sep 21 '17 at 16:34
  • A figure always has to be drawn. So you cannot skip the step of redrawing, but you may be able to save some time when not regenerating it. Matplotlib figures [can be pickled](https://stackoverflow.com/questions/35649603/pickle-figures-from-matplotlib). I'm not sure if it works if basemap is involved, but it is certainly worth a try. – ImportanceOfBeingErnest Sep 21 '17 at 16:50