Save a plot background

Hi
I haven't managed to save a plot background into buffer to be able to
restore it later.
I use matplotlib to draw weather maps (see www.belgingur.is), and though the
weather constantly changes the outlines of the countries are the same for
every picture.
Currently I plot the coastlines anew for every picture, which is kinda *not
smart*.

I got some promising results when using an interactive background but have
had no luck with the Agg background which I'd like to use.

Best regards, Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20519596.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Have you tried the "copy from bbox" / "restore_region" methods. Draw
just what you want to save, and use "copy_from_bbox". Later when you
want to plot something new, do a "restore_region" onto a clean canvas
and then plot the new stuff on top:

* background = canvas.copy_from_bbox(ax.bbox) - copy the region in
ax.bbox into a pixel buffer and return it in an object type of your
choosing. bbox is a matplotlib BBox instance from the transforms
module. background is not used by the matplotlib frontend, but it
stores it and passes it back to the backend in the restore_region
method. You will probably want to store not only the pixel buffer but
the rectangular region of the canvas from whence it came in the
background object.

* canvas.restore_region(background) - restore the region copied above
to the canvas.

* canvas.blit(bbox) - transfer the pixel buffer in region bounded by
bbox to the canvas.

Search the examples for "copy_from_bbox" on the web site
http://matplotlib.sourceforge.net/search.html

If you are still having troubles, let me know and I can give more help.

JDH

···

On Sat, Nov 15, 2008 at 3:23 PM, Hrafnkell Pálsson <hrp1@...2386...> wrote:

Hi
I haven't managed to save a plot background into buffer to be able to
restore it later.
I use matplotlib to draw weather maps (see www.belgingur.is), and though the
weather constantly changes the outlines of the countries are the same for
every picture.
Currently I plot the coastlines anew for every picture, which is kinda *not
smart*.

Hi

I tried you suggestions but it didn't work out for me.
In the following code I try to save the axes and the grid from figure1 into
buffer and then restore it on figure2 but figure2.png turns out to be of an
empty canvas.

#!/usr/bin/env /opt/python/bin/python
# encoding: utf-8
import matplotlib
if not matplotlib.get_backend()=='agg':
    matplotlib.use('Agg')
import pylab

figure1 = pylab.figure(1)
axes1 = pylab.gca()
axes1.grid()
canvas1 = axes1.figure.canvas
background = canvas1.copy_from_bbox(axes1.bbox)
figure1.savefig('figure1.png')

figure2 = pylab.figure(2)
canvas2 = figure2.canvas
canvas2.restore_region(background)
figure2.savefig('figure2.png')

Thanks for your efforts,
Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20539991.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

The only problem I could see with your code is that you need to force
a draw before copying the background. But when I added the draw, and
then further simplified to reuse the same canvas, I still am not
seeing the restored region. I will need to do further digging to see
what is going wrong, but the problem appears both on the maintenance
branch and the trunk. Here is my modified test script:

    import matplotlib
    matplotlib.use('Agg')

    import matplotlib.pyplot as plt

    fig1 = plt.figure(1)
    ax1 = fig1.add_subplot(111)
    ax1.grid()
    fig1.canvas.draw()
    fig1.savefig('figure1.png')
    background = fig1.canvas.copy_from_bbox(ax1.bbox)

    fig1.clf()

    #figure2 = pylab.figure(2)
    #canvas2 = figure2.canvas
    fig1.canvas.restore_region(background)
    fig1.savefig('figure2.png')

JDH

···

On Mon, Nov 17, 2008 at 8:05 AM, Hrafnkell Pálsson <hrp1@...2386...> wrote:

Hi

I tried you suggestions but it didn't work out for me.
In the following code I try to save the axes and the grid from figure1 into
buffer and then restore it on figure2 but figure2.png turns out to be of an
empty canvas.

#!/usr/bin/env /opt/python/bin/python
# encoding: utf-8
import matplotlib
if not matplotlib.get_backend()=='agg':
   matplotlib.use('Agg')
import pylab

figure1 = pylab.figure(1)
axes1 = pylab.gca()
axes1.grid()
canvas1 = axes1.figure.canvas
background = canvas1.copy_from_bbox(axes1.bbox)
figure1.savefig('figure1.png')

I think the savefig() command calls draw() internally, doesn't it?
So, I guess the restore_region() command comes before the draw() call,
i.e., it has no effect for the saved figure.
One way I can think of is to save the agg buffer without redrawing it.
It seems work.

···

-----------------------

import matplotlib
matplotlib.use('Agg')

import matplotlib.pyplot as plt

fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
ax1.grid()
fig1.canvas.draw()
background = fig1.canvas.copy_from_bbox(ax1.bbox)
fig1.savefig('figure1.png')

from matplotlib import _png
from matplotlib.cbook import is_string_like

def print_png(fig, filename_or_obj):
    canvas = fig.canvas
    renderer = canvas.get_renderer()
    if is_string_like(filename_or_obj):
        filename_or_obj = file(filename_or_obj, 'wb')
    _png.write_png(renderer._renderer.buffer_rgba(0, 0),
                   renderer.width, renderer.height,
                   filename_or_obj, canvas.figure.dpi)

fig1.clf()
fig1.canvas.restore_region(background)
print_png(fig1, 'figure2.png')

---------------------------

Somehow, the copy_from_bbox() needs to be called before the savefig().

This is just a quick hacky solution.
But I think it would be nice if we have this kind of functionality in
the mpl, ideally with zorder support. For example,

ax1.add_background(background, zroder=2)

And, restore_region(background) is called within the draw() method
(with correct zorder).
Just a thought.

IHTH,

-JJ

On Mon, Nov 17, 2008 at 12:37 PM, John Hunter <jdh2358@...287...> wrote:

On Mon, Nov 17, 2008 at 8:05 AM, Hrafnkell Pálsson <hrp1@...2386...> wrote:

Hi

I tried you suggestions but it didn't work out for me.
In the following code I try to save the axes and the grid from figure1 into
buffer and then restore it on figure2 but figure2.png turns out to be of an
empty canvas.

#!/usr/bin/env /opt/python/bin/python
# encoding: utf-8
import matplotlib
if not matplotlib.get_backend()=='agg':
   matplotlib.use('Agg')
import pylab

figure1 = pylab.figure(1)
axes1 = pylab.gca()
axes1.grid()
canvas1 = axes1.figure.canvas
background = canvas1.copy_from_bbox(axes1.bbox)
figure1.savefig('figure1.png')

The only problem I could see with your code is that you need to force
a draw before copying the background. But when I added the draw, and
then further simplified to reuse the same canvas, I still am not
seeing the restored region. I will need to do further digging to see
what is going wrong, but the problem appears both on the maintenance
branch and the trunk. Here is my modified test script:

   import matplotlib
   matplotlib.use('Agg')

   import matplotlib.pyplot as plt

   fig1 = plt.figure(1)
   ax1 = fig1.add_subplot(111)
   ax1.grid()
   fig1.canvas.draw()
   fig1.savefig('figure1.png')
   background = fig1.canvas.copy_from_bbox(ax1.bbox)

   fig1.clf()

   #figure2 = pylab.figure(2)
   #canvas2 = figure2.canvas
   fig1.canvas.restore_region(background)
   fig1.savefig('figure2.png')

JDH

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

Ahh yes. The copy_from_bbox / restore_region was conceived as an aid
to animation and I don't think I've used it in the context of savefig
where none of the artists are involved in the restored region. Ie, in
a typical animation use case, you draw the axes, background and ticks,
store the region, and then animate a line on top of it. When you go
to savefig, the axes background is redrawn because the associated
artists already reside in the figure, along with the line in its final
state.

It should be fairly easy to expose the agg BufferRegion object in the
artist API, so you could grab a buffer region from one canvas, bless
it as an artist, and add it to the figure or another figure, with the
x and y location attrs exposed so it could be easily moved. In this
case you would get the zorder for free. This would help some, and
would certainly address the case at hand, but the really big win would
probably be something more like the chaco model, where each zorder is
rendered onto a different rendering buffer, with a fairly
straight-forward way to just re-render certain planes. This would
certainly be harder.

Could you elaborate a bit on your use case, which will help me in
thinking about how this should be done?

JDH

···

On Mon, Nov 17, 2008 at 1:50 PM, Jae-Joon Lee <lee.j.joon@...287...> wrote:

I think the savefig() command calls draw() internally, doesn't it?

Matplotlibbers,

While I though this would be fairly
easy, I’ve yet to find a decent solution. I’m interested in using the
RangeSelector or standard zoom feature along with an autoscaling
function. In the example below there are two gaussian profiles created
in a matrix. I would like to have the behavior in which when I zoom in
that the Z scale is reordered to account for the new range of data. Is
my only solution to take the limits returned by a RangeSelector event
and extract a sub-matix from the original data and replot each time I
zoom in or out or is there a more elegant solution? Is there a way to also extend such functionality to contour, pcolor, etc.?

Cheers,

Brian

######paste in ipython###########################################

from numpy import *

from matplotlib.widgets import RectangleSelector

import pylab as P

def gaussian(height, center_x, center_y, width_x, width_y):

"""Returns a gaussian function with the given parameters"""

width_x = float(width_x)

width_y = float(width_y)

return lambda x,y: height*exp(-(((center_x-x)/width_x)**2+((center_y-y)/width_y)**2)/2)

def line_select_callback(event1, event2):

'event1 and event2 are the press and release events'

x1, y1 = event1.xdata, event1.ydata

x2, y2 = event2.xdata, event2.ydata

print "(%3.2f, %3.2f) --> (%3.2f, %3.2f)"%(x1,y1,x2,y2)

print " The button you used were: ",event1.button, event2.button

cla()

Create the gaussian data

Xin, Yin = mgrid[0:201, 0:201]

Xin2, Yin2 = mgrid[0:201, 0:201]

data = gaussian(20, 100, 100, 20, 40)(Xin, Yin) + random.random(Xin.shape)

data2 = gaussian(10, 10, 10, 2, 4)(Xin2, Yin2) + random.random(Xin.shape)

P.imshow(data+data2, cmap=cm.jet)

current_ax = gca()

LS = RectangleSelector(current_ax, line_select_callback, drawtype=‘box’,useblit=True,\

                   minspanx=5,minspany=5,spancoords='pixels')

P.show()

OK, ever since Jae-Joon clued me into to the importance of the savefig
call, I realized there is a fairly easy solution for your problem.
The problem you are having is that savefig is redrawing the "figure
frame" which is essentially just filling the rectangular background of
the figure canvas. This is obscuring the background you are trying to
restore. SO just turn the frame off, and your background will be
revealed in its place. Here is the example code:

import matplotlib
matplotlib.use('Agg')

import matplotlib.pyplot as plt

plt.close('all')

# make sure we have all the same properties on all figs
figprops = dict(figsize=(8,6), dpi=100, facecolor='white')

fig1 = plt.figure(1, **figprops)
ax1 = fig1.add_subplot(111)
ax1.grid()
fig1.canvas.draw()
background = fig1.canvas.copy_from_bbox(fig1.bbox)
fig1.savefig('figure1.png', dpi=100)

# turn the frame off or it will overwrite the background
fig2 = plt.figure(2, frameon=False, **figprops)
fig2.canvas.restore_region(background)
fig2.savefig('figure2.png', dpi=100)

···

On Mon, Nov 17, 2008 at 8:05 AM, Hrafnkell Pálsson <hrp1@...2386...> wrote:

Hi

I tried you suggestions but it didn't work out for me.
In the following code I try to save the axes and the grid from figure1 into
buffer and then restore it on figure2 but figure2.png turns out to be of an
empty canvas.

Ok, I tried your last suggestion and sure enough it worked.
But it turns out to solve only half of my problem. I'd like to be able to
restore the background (using the Agg backend) and then use it further, i.e.
plot on it, without it disappearing.

Elaborating on my real use case, what I want to do is to make a Basemap
instance, plot the coastlines and other things that are constant in time
(put marker on the maps for cities, watermark the figure, draw meridians and
parallels and so on) and save that as a background.
I would then restore this background and plot "the weather" (open and filled
contours, quivers and barbs) onto it. I typically have data for some dozens
of timesteps, so I'd be restoring the background many times. Each time I'd
plot a particular weather (corresponding to a particular timestep) onto the
background I'd save that figure, so in the end I have some dozens of
figures.
Sliding the slider beneath the pictures at http://www.belgingur.is
http://www.belgingur.is should give a good idea of what I mean (notice that
the website is also in English).

A somewhat simplified example of what I'd like to do (but it catches the
essence of it) follows.
The problem that arises is that when I try to plot onto the background it
disappears and only what I tried to plot onto it remains. So figure2.png
contains the coastline of Iceland while figure3.png contains only the
quivers, the coastline has disappeared.

import matplotlib
matplotlib.use('Agg')

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

plt.close('all')

basemap = Basemap(llcrnrlat=62.8,
                  llcrnrlon=-24.8,
                  urcrnrlat=66.7,
                  urcrnrlon=-12.4,
                  lat_0=65.0,
                  lon_0=-19.5)

# make sure we have all the same properties on all figs
figprops = dict(figsize=(8,6), dpi=100, facecolor='white')
fig1 = plt.figure(1, **figprops)

basemap.drawcoastlines()

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

# turn the frame off or it will overwrite the background
fig2 = plt.figure(2, frameon=False, **figprops)
fig2.canvas.restore_region(background)
fig2.savefig('figure2.png', dpi=100)

# turn the frame off or it will overwrite the background
fig3 = plt.figure(3, frameon=False, **figprops)
fig3.canvas.restore_region(background)
# create a lon-lat grid for plotting quivers
n, m = 10, 5
latitudes = numpy.resize(numpy.linspace(63.5, 65.0, n), (n, m))
longitudes = numpy.transpose(numpy.resize(numpy.linspace(-22, -14, n),(n,
m)))
x, y = basemap(longitudes, latitudes)
# create the u and v components of the quivers
u = numpy.resize([5], (n, m))
v = numpy.resize([5], (n, m))
# since keyword ax is not supplied the current axes instance is used, i.e.
the axes of fig3
basemap.quiver(x, y, u, v)
fig3.savefig('figure3.png', dpi=100)

Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20562028.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

This should work fine as long as you make the figure frame and axes
frame invisible. Basically, you need to create the figure background,
and the axes background with all the grid lines, tick labels,
coastlines, etc, save it, and then for the new figure make sure all
the stuff you have saved is turned off. I am not a basemap user, but
I know all this stuff is ultimately exposed. Eg for a plain vanilla
axes, you would do

  fig1 = figure(frameon=False)
  ax1 = fig1.add_subplot(111)

  fig = figure(frameon=False)
  ax = fig.add_subplot(111, frameon=false, xticks=, yticks=)
  fig.canvas.restore_region(background)

This will turn off the rendering of both the background rectangle as
well as all the ticks and their associated labels, ticklines and
gridlines. Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

···

On Tue, Nov 18, 2008 at 9:41 AM, Hrafnkell Pálsson <hrp1@...2386...> wrote:

Ok, I tried your last suggestion and sure enough it worked.
But it turns out to solve only half of my problem. I'd like to be able to
restore the background (using the Agg backend) and then use it further, i.e.
plot on it, without it disappearing.

Elaborating on my real use case, what I want to do is to make a Basemap
instance, plot the coastlines and other things that are constant in time
(put marker on the maps for cities, watermark the figure, draw meridians and
parallels and so on) and save that as a background.
I would then restore this background and plot "the weather" (open and filled
contours, quivers and barbs) onto it. I typically have data for some dozens
of timesteps, so I'd be restoring the background many times. Each time I'd

John Hunter wrote:

···

On Tue, Nov 18, 2008 at 9:41 AM, Hrafnkell Pálsson <hrp1@...2386...> wrote:
  

Ok, I tried your last suggestion and sure enough it worked.
But it turns out to solve only half of my problem. I'd like to be able to
restore the background (using the Agg backend) and then use it further, i.e.
plot on it, without it disappearing.

Elaborating on my real use case, what I want to do is to make a Basemap
instance, plot the coastlines and other things that are constant in time
(put marker on the maps for cities, watermark the figure, draw meridians and
parallels and so on) and save that as a background.
I would then restore this background and plot "the weather" (open and filled
contours, quivers and barbs) onto it. I typically have data for some dozens
of timesteps, so I'd be restoring the background many times. Each time I'd
    
This should work fine as long as you make the figure frame and axes
frame invisible. Basically, you need to create the figure background,
and the axes background with all the grid lines, tick labels,
coastlines, etc, save it, and then for the new figure make sure all
the stuff you have saved is turned off. I am not a basemap user, but
I know all this stuff is ultimately exposed. Eg for a plain vanilla
axes, you would do

  fig1 = figure(frameon=False)
  ax1 = fig1.add_subplot(111)

  fig = figure(frameon=False)
  ax = fig.add_subplot(111, frameon=false, xticks=, yticks=)
  fig.canvas.restore_region(background)

This will turn off the rendering of both the background rectangle as
well as all the ticks and their associated labels, ticklines and
gridlines. Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Don't see why this wouldn't work fine with basemap, as long as you don't call the drawmapboundary method (which will set the axes frame on).

-Jeff

--
Jeffrey S. Whitaker Phone : (303)497-6313
Meteorologist FAX : (303)497-6449
NOAA/OAR/PSD R/PSD1 Email : Jeffrey.S.Whitaker@...259...
325 Broadway Office : Skaggs Research Cntr 1D-113
Boulder, CO, USA 80303-3328 Web : Jeffrey S. Whitaker: NOAA Physical Sciences Laboratory

This will turn off the rendering of both the background rectangle as
well as all the ticks and their associated labels, ticklines and
gridlines. Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Ok, I tested your example and see what you mean.
But if I understood you correctly this won't allow me to retain the
watermark and the dots I've used for marking cities.
It would be nice if I could retain the whole background, if it wouldn't
matter whether I retrieved the background or actually plotted it.
But even just avoiding plotting the coastline every time would save a lot of
time.

Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Could you, Jeff, give me a nudge in the right direction? I've read through
the basemap documentation but didn't notice anything that might help.

Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20582574.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Hrafnkell Pálsson wrote:

This will turn off the rendering of both the background rectangle as
well as all the ticks and their associated labels, ticklines and
gridlines. Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Ok, I tested your example and see what you mean.
But if I understood you correctly this won't allow me to retain the
watermark and the dots I've used for marking cities.
It would be nice if I could retain the whole background, if it wouldn't
matter whether I retrieved the background or actually plotted it.
But even just avoiding plotting the coastline every time would save a lot of
time.

Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Could you, Jeff, give me a nudge in the right direction? I've read through
the basemap documentation but didn't notice anything that might help.

Hrafnkell
  
Hrafnkell: You can go by the example John gave, using figure and axes instance methods. Basemap just draws stuff on the current figure and axes instances.

Are you recreating the Basemap instance every time you generate a plot? If the projection isn't changing, you can save a lot of overhead by re-using a single Basemap instance. You can even pickle it to a file for use in another script.

-Jeff

···

--
Jeffrey S. Whitaker Phone : (303)497-6313
Meteorologist FAX : (303)497-6449
NOAA/OAR/PSD R/PSD1 Email : Jeffrey.S.Whitaker@...259...
325 Broadway Office : Skaggs Research Cntr 1D-113
Boulder, CO, USA 80303-3328 Web : Jeffrey S. Whitaker: NOAA Physical Sciences Laboratory

Hrafnkell P�lsson wrote:

This will turn off the rendering of both the background rectangle as
well as all the ticks and their associated labels, ticklines and
gridlines. Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Ok, I tested your example and see what you mean.
But if I understood you correctly this won't allow me to retain the
watermark and the dots I've used for marking cities.
It would be nice if I could retain the whole background, if it wouldn't
matter whether I retrieved the background or actually plotted it.
But even just avoiding plotting the coastline every time would save a lot of
time.

Perhaps Jeff can advise you vis-a-vis the basemap api what
the equivalent is

Could you, Jeff, give me a nudge in the right direction? I've read through
the basemap documentation but didn't notice anything that might help.

Hrafnkell
  
Hrafnkell:

Had some time this morning, so I used John's method to create a working Basemap example:

import matplotlib
matplotlib.use('Agg')
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt

# this example shows how to save a map background and
# reuse it in another figure.

# make sure we have all the same properties on all figs
figprops = dict(figsize=(8,6), dpi=100, facecolor='white')

# generate the first figure.
fig1 = plt.figure(1,**figprops)
ax1 = fig1.add_subplot(111)
# create basemap instance, plot coastlines.
map = Basemap(projection='moll',lon_0=0)
map.drawcoastlines()
map.drawmapboundary(fill_color='aqua')
map.fillcontinents(color='coral',lake_color='aqua')
fig1.canvas.draw()
background = fig1.canvas.copy_from_bbox(fig1.bbox)
fig1.savefig('figure1.png', dpi=100)

# generate the second figure, re-using the background
# from figure 1.
fig2 = plt.figure(2,frameon=False,**figprops)
ax2 = fig2.add_subplot(111, frameon=False, xticks=, yticks=)
# restore previous background.
fig2.canvas.restore_region(background)
# draw parallels and meridians on existing background.
map.drawparallels(range(-90,90,30))
map.drawmeridians(range(-180,180,60))
fig2.savefig('figure2.png', dpi=100)

I've added this to the basemap examples directory as save_background.py

HTH,

-Jeff

···

--
Jeffrey S. Whitaker Phone : (303)497-6313
NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449
325 Broadway Boulder, CO, USA 80305-3328

Jeff Whitaker wrote:

Hrafnkell:

Had some time this morning, so I used John's method to create a working
Basemap example:

import matplotlib
matplotlib.use('Agg')
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt

# this example shows how to save a map background and
# reuse it in another figure.

# make sure we have all the same properties on all figs
figprops = dict(figsize=(8,6), dpi=100, facecolor='white')

# generate the first figure.
fig1 = plt.figure(1,**figprops)
ax1 = fig1.add_subplot(111)
# create basemap instance, plot coastlines.
map = Basemap(projection='moll',lon_0=0)
map.drawcoastlines()
map.drawmapboundary(fill_color='aqua')
map.fillcontinents(color='coral',lake_color='aqua')
fig1.canvas.draw()
background = fig1.canvas.copy_from_bbox(fig1.bbox)
fig1.savefig('figure1.png', dpi=100)

# generate the second figure, re-using the background
# from figure 1.
fig2 = plt.figure(2,frameon=False,**figprops)
ax2 = fig2.add_subplot(111, frameon=False, xticks=, yticks=)
# restore previous background.
fig2.canvas.restore_region(background)
# draw parallels and meridians on existing background.
map.drawparallels(range(-90,90,30))
map.drawmeridians(range(-180,180,60))
fig2.savefig('figure2.png', dpi=100)

I've added this to the basemap examples directory as save_background.py

HTH,

-Jeff

There's still one snag.
Using your example I could restore the background.
But it turns out everything is plotted over the background; so if my
background consist only of the coastline and then I do a filled contour plot
after restoring it the coastline disappears under the filled contours (by
using the alpha keyword to contourf I could see the coastline beneath).

Had I plotted the coastline into the figure instead of restoring it this
would not have happened.

Is there any way of avoiding this?

Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20606641.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Hrafnkell Pálsson wrote:

Jeff Whitaker wrote:
  

Hrafnkell:

Had some time this morning, so I used John's method to create a working
Basemap example:

import matplotlib
matplotlib.use('Agg')
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt

# this example shows how to save a map background and
# reuse it in another figure.

# make sure we have all the same properties on all figs
figprops = dict(figsize=(8,6), dpi=100, facecolor='white')

# generate the first figure.
fig1 = plt.figure(1,**figprops)
ax1 = fig1.add_subplot(111)
# create basemap instance, plot coastlines.
map = Basemap(projection='moll',lon_0=0)
map.drawcoastlines()
map.drawmapboundary(fill_color='aqua')
map.fillcontinents(color='coral',lake_color='aqua')
fig1.canvas.draw()
background = fig1.canvas.copy_from_bbox(fig1.bbox)
fig1.savefig('figure1.png', dpi=100)

# generate the second figure, re-using the background
# from figure 1.
fig2 = plt.figure(2,frameon=False,**figprops)
ax2 = fig2.add_subplot(111, frameon=False, xticks=, yticks=)
# restore previous background.
fig2.canvas.restore_region(background)
# draw parallels and meridians on existing background.
map.drawparallels(range(-90,90,30))
map.drawmeridians(range(-180,180,60))
fig2.savefig('figure2.png', dpi=100)

I've added this to the basemap examples directory as save_background.py

HTH,

-Jeff

There's still one snag.
Using your example I could restore the background.
But it turns out everything is plotted over the background; so if my
background consist only of the coastline and then I do a filled contour plot
after restoring it the coastline disappears under the filled contours (by
using the alpha keyword to contourf I could see the coastline beneath).

Had I plotted the coastline into the figure instead of restoring it this
would not have happened.

Is there any way of avoiding this?

Hrafnkell

Hrafnkell:

I see your point, but I don't see any obvious solution. Perhaps someone with more knowledge of matplotlib internals (John?) can answer.

-Jeff

···

--
Jeffrey S. Whitaker Phone : (303)497-6313
Meteorologist FAX : (303)497-6449
NOAA/OAR/PSD R/PSD1 Email : Jeffrey.S.Whitaker@...259...
325 Broadway Office : Skaggs Research Cntr 1D-113
Boulder, CO, USA 80303-3328 Web : Jeffrey S. Whitaker: NOAA Physical Sciences Laboratory

Any chance of further help?
John?

Hrafnkell

···

--
View this message in context: http://www.nabble.com/Save-a-plot-background-tp20519596p20771515.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Hrafnkell Pálsson wrote:

Any chance of further help?
John?

Hrafnkell
  

Hrafnkell: I'm pretty sure this is a fundamental limitation of canvas.restore_region - everything gets draw on top of it.

If I recall correctly, you'd like to save the map with coastlines drawn, and just redraw contours on it. If you're reusing a Basemap instance, I'd be surprised if the time spent redrawing the coastlines is all that significant. Are you? Creating the coastline polygons in map projection coordinates is the most expensive operation, but that is done when the Basemap instance is created.

Another option would be to just remove the contours from the figure, then redraw the new ones and re-save the figure, i.e.

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
# create new figure
fig=plt.figure()
# setup of of mollweide Basemap.
m = Basemap(resolution='c',projection='moll',lon_0=0)
# draw some contours.
x, y = m(lons, lats) # get map projection coords of lat/lon grid
CS = m.contour(x,y,data,15,linewidths=0.5,colors='k')
# draw coastlines and projection limb.
m.drawcoastlines()
m.drawmapboundary()
# save figure with contours.
plt.savefig('fig1.png')

# remove contours, save figure again.
for coll in CS.collections:
    coll.remove()
plt.savefig('fig2.png')

# draw contours again, this time red, save figure a third time.
CS = m.contour(x,y,data,15,linewidths=0.5,colors='r')
plt.savefig('fig3.png')

HTH,

-Jeff

···

--
Jeffrey S. Whitaker Phone : (303)497-6313
Meteorologist FAX : (303)497-6449
NOAA/OAR/PSD R/PSD1 Email : Jeffrey.S.Whitaker@...259...
325 Broadway Office : Skaggs Research Cntr 1D-113
Boulder, CO, USA 80303-3328 Web : Jeffrey S. Whitaker: NOAA Physical Sciences Laboratory