matplotlib / basemap

Plot on map projections (with coastlines and political boundaries) using matplotlib
MIT License
775 stars 392 forks source link

Example request: Basemap instance reuse #354

Closed acowlikeobject closed 7 years ago

acowlikeobject commented 7 years ago

I render maps in my Jupyter notebook very often. Creating Basemap instances is slow. I see in several threads talk of reusing Basemap instances (e.g., here and here), but can't find a working example.

(Per #353, save_background.py isn't working, but in any case, it uses plt.savefig() which I'm not sure is the same solution for in-notebook rendering.)

Here's my attempt. This produces just the contourf() plot. The quiver() plot is not shown. I've tried combinations of plt.subplots() and adding axes, but the closest I can get is the contourf plot on a basemap and the quiver plot on its own (not superimposed on the basemap).

Could please someone point me to a minimal example of reuse or correct the snippet below to work?

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

# Create reusable Basemap instance
fig, ax = plt.subplots()
m = Basemap(resolution='c', projection='stere', width=12000000,
            height=8000000, lat_ts=50, lat_0=50, lon_0=260., ax=ax)
m.drawcoastlines()
m.drawmapboundary()
m.fillcontinents(zorder=-1)

min_lat, max_lat = 20, 70
min_lon, max_lon = 210, 310
lats, lons = np.arange(min_lat, max_lat), np.arange(min_lon, max_lon)
shape = len(lats), len(lons)
x, y = m(*np.meshgrid(lons, lats))

# Use basemap for contourf
data = np.random.rand(*shape)
c = m.contourf(x, y, data, zorder=0)
plt.show()
for member in c.collections:  # The next plot will have just quivers, not contours
    member.remove()

# Reuse basemap for quiver
ugrd, vgrd = np.random.rand(*shape), np.random.rand(*shape)
q = m.quiver(x, y, ugrd, vgrd, zorder=0)
plt.show()  # This does nothing?
q.remove()  # To prepare for the next reuse of basemap
acowlikeobject commented 7 years ago

So, pickling the Basemap instance and then unpickling before each reuse seems to do what I need. But this seems goofy - I'm guessing there's a nicer way.

(I had to switch from %matplotlib inline to %matplotlib notebook because of this IPython issue.)

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

fig, ax = plt.subplots()
m = Basemap(resolution='c', projection='stere', width=12000000,
            height=8000000, lat_ts=50, lat_0=50, lon_0=260., ax=ax)
m.drawcoastlines()
m.drawmapboundary()
m.fillcontinents(zorder=-1)
m_pkl = pickle.dumps(m)  # Save object state before plotting anything on it

min_lat, max_lat = 20, 70
min_lon, max_lon = 210, 310
lats, lons = np.arange(min_lat, max_lat), np.arange(min_lon, max_lon)
shape = len(lats), len(lons)
x, y = m(*np.meshgrid(lons, lats))

# Use basemap for contourf
data = np.random.rand(*shape)
c = m.contourf(x, y, data, zorder=0)
plt.show()

# Reuse basemap for quiver
ugrd, vgrd = np.random.rand(*shape), np.random.rand(*shape)
m = pickle.loads(m_pkl)  # Unpickle pre-contour state
q = m.quiver(x, y, ugrd, vgrd, zorder=1)
plt.show()
WeatherGod commented 7 years ago

Pickling/unpickling is pretty much the method that is discussed by others in the links you provided. Basemap is a very old codebase and it has not aged well. Unfortunately, due to its design, it really can't be fixed. Which is why we suggest looking into using the Cartopy package. It is actively developed and it has a much better architecture. It isn't feature-parity with basemap, yet, but it can also do other things a lot better than basemap can.

I am mostly maintaining basemap for legacy code and for those who still need features that aren't in Cartopy yet. I would suggest seeing if Cartopy can perform better than basemap for you.

On Thu, May 18, 2017 at 2:50 AM, acowlikeobject notifications@github.com wrote:

So, pickling the Basemap instance and then unpickling before each reuse seems to do what I need. But this seems goofy - I'm guessing there's a nicer way.

(I had to switch from %matplotlib inline to %matplotlib notebook because of this IPython issue https://github.com/ipython/ipykernel/issues/231.)

%matplotlib notebookimport matplotlib.pyplot as pltfrom mpl_toolkits.basemap import Basemapimport numpy as npimport pickle

fig, ax = plt.subplots() m = Basemap(resolution='c', projection='stere', width=12000000, height=8000000, lat_ts=50, lat_0=50, lon_0=260., ax=ax) m.drawcoastlines() m.drawmapboundary() m.fillcontinents(zorder=-1) m_pkl = pickle.dumps(m) # Save object state before plotting anything on it

min_lat, max_lat = 20, 70 min_lon, max_lon = 210, 310 lats, lons = np.arange(min_lat, max_lat), np.arange(min_lon, max_lon) shape = len(lats), len(lons) x, y = m(*np.meshgrid(lons, lats))

Use basemap for contourf

data = np.random.rand(*shape) c = m.contourf(x, y, data, zorder=0) plt.show()

Reuse basemap for quiver

ugrd, vgrd = np.random.rand(shape), np.random.rand(shape) m = pickle.loads(m_pkl) # Unpickle pre-contour state q = m.quiver(x, y, ugrd, vgrd, zorder=1) plt.show()

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/matplotlib/basemap/issues/354#issuecomment-302316837, or mute the thread https://github.com/notifications/unsubscribe-auth/AARy-OklreorcGqDnXWAQNp9kiFGAL5qks5r6-pAgaJpZM4Nevnq .

acowlikeobject commented 7 years ago

Thanks @WeatherGod . I thought pickling was the answer across runs. Didn't realize it was the go-to within a function, too.

Cartopy looks good! I'm playing with it now. For a start, the saving background method seems to work. The more explicit API (vis-a-vis figures and axes) is also less confusing to me.