capitalistcuttle
capitalistcuttle

Reputation: 1823

How can I create a Basemap instance and reuse it with different "overlays"?

I'd like to create a Basemap instance once (since it's expensive), and then reuse it with, say, different contourf() overlays.

To clarify, I need (basemap + contours1) and (basemap + contours2), not (basemap + contours1 + contours2) which is what this question is about.

There are examples here and here (with responses from the same author, five years apart!), but I cannot get a simple reusable snippet working.

Per that second example, my code is structured as follows:

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


def get_basemap():
    fig = plt.figure('Background')
    fig.add_subplot(111)

    map_ = Basemap(resolution='c', projection='stere', width=12000000,
                   height=8000000, lat_ts=50, lat_0=50, lon_0=260.)
    map_.drawcoastlines()
    map_.drawmapboundary()
    map_.fillcontinents(zorder=-1)

    fig.canvas.draw()
    background = fig.canvas.copy_from_bbox(fig.bbox)

    return map_, background


def plot_thing1(map_, background, lats, lons, data):
    fig = plt.figure('Contourf', frameon=False)
    fig.add_subplot(111, frameon=False)

    fig.canvas.restore_region(background)    

    x, y = map_(*np.meshgrid(lons, lats))    
    map_.contourf(x, y, data, zorder=0)
    fig.show()


def plot_thing2(map_, background, lats, lons, ugrd, vgrd):
    fig = plt.figure('Quiver', frameon=False)
    fig.add_subplot(111, frameon=False)

    fig.canvas.restore_region(background)    

    x, y = map_(*np.meshgrid(lons, lats))    
    map_.quiver(x, y, ugrd, vgrd, zorder=0)
    fig.show()


min_lat, max_lat = 20, 70
min_lon, max_lon = 210, 310
lats = np.arange(min_lat, max_lat)
lons = np.arange(min_lon, max_lon)
shape = len(lats), len(lons)

# Run this once to get a reusable basemap
map_, background = get_basemap()

# Random data for thing1
data = np.random.rand(*shape)
plot_thing1(map_, background, lats, lons, data)

# Random data for thing2
ugrd, vgrd = np.random.rand(*shape), np.random.rand(*shape)
plot_thing2(map_, background, lats, lons, ugrd, vgrd)

But plot_thing1() produces two "plots", one with the basemap background followed by the other with the contours by itself (instead of the contours superimposed on the basemap in one plot). And plot_thing2() produces just the quivers.

How can I get them to appear on the same plot? (And reuse the basemap in plot_thing1(), plot_thing2(), etc.) This probably has to do with things getting attached to the wrong active figure/subplot/Axes, but I can't get my head around how Basemap() fits into the matplotlib hierarchy.

Upvotes: 2

Views: 765

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339340

The following saves three images, the background alone, the background with the contour plot and the background with the quiver:

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

def get_basemap():
    fig = plt.figure('Background')
    fig.add_subplot(111)
    map_ = Basemap(resolution='c', projection='stere', width=12000000,
                   height=8000000, lat_ts=50, lat_0=50, lon_0=260.)
    map_.drawcoastlines()
    map_.drawmapboundary()
    map_.fillcontinents(zorder=-1)
    return map_

def plot_thing1(map_, lats, lons, data):
    x, y = map_(*np.meshgrid(lons, lats))    
    c = map_.contourf(x, y, data, zorder=0)
    return c

def plot_thing2(map_, lats, lons, ugrd, vgrd):
    x, y = map_(*np.meshgrid(lons, lats))    
    q = map_.quiver(x, y, ugrd, vgrd, zorder=0)
    return q

min_lat, max_lat = 20, 70
min_lon, max_lon = 210, 310
lats = np.arange(min_lat, max_lat)
lons = np.arange(min_lon, max_lon)
shape = len(lats), len(lons)

# Run this once to get a reusable basemap
m  = get_basemap()
plt.savefig("background.png")
#Plot stuff on the axes
data = np.random.rand(*shape)
c = plot_thing1(m, lats, lons, data)
plt.savefig("thing1.png")
#remove the contourplot from the axes:
for member in c.collections:
    member.remove()
#Plot new stuff on the axes
ugrd, vgrd = np.random.rand(*shape), np.random.rand(*shape)
q = plot_thing2(m, lats, lons, ugrd, vgrd)
plt.savefig("thing2.png")
# to remove the quiver use
# q.remove()
plt.show()

enter image description here

As a workaround, to display the saved images directly in a jupyter notebook, you can do

from IPython.display import Image, display

listOfImageNames = ['background.png','thing1.png', 'thing2.png']

for fn in listOfImageNames:
    display(Image(filename=fn))

Upvotes: 2

Related Questions