Wednesday, April 1, 2009

Inset axes

A little script to have your inset axes to adjust its position relative to the parent axes. The inset position is calculated in the drawing time, thus it works even if the parent axes change its position (e.g., aspect=1).


import matplotlib.transforms

class InsetPosition(object):
def __init__(self, parent, lbwh):
self.parent = parent
self.lbwh = lbwh # position of the inset axes in the
normalized coordinate of the parent axes

def __call__(self, ax, renderer):
bbox_parent = self.parent.get_position(original=False)
trans = matplotlib.transforms.BboxTransformTo(bbox_parent)
bbox_inset = matplotlib.transforms.Bbox.from_bounds(*self.lbwh)
bb = matplotlib.transforms.TransformedBbox(bbox_inset, trans)
return bb

ax = gca()
ax.set_aspect(1.)
axins = axes([0, 0, 1, 1])
ip = InsetPosition(ax, [0.5, 0.1, 0.4, 0.2])
axins.set_axes_locator(ip)


TODO:
  • anchor to the parent axes
  • adjust the inset size according to its data limits

Tuesday, February 17, 2009

MPL w/ svg filter, again.

The svg output from the mpl svn version now can be easily used to apply the svg filter effects.

Two examples are included in the example directory.



images side-by-side in mpl

The matplotlib specifies its axes position in the normalized figure coordinate, and this may not be best option for showing images. In the mpl SVN example directory, there are two helper classes I wrote (axes_divider.py, axes_grid.py) which may help in some situations.

ex 1. Images with same size.

import matplotlib.pyplot as plt
from axes_grid import AxesGrid, get_demo_image


F = plt.figure(1, (6, 6))
grid = AxesGrid(F, 111, # similar to subplot(111)
nrows_ncols = (2, 2), # 2x2 grid of images
axes_pad = 0.1, # pad in inches
add_all=True, # add axes to the figure
share_all=True, # x & yaxis of all axes are shared
label_mode = "L",
)

Z, extent = get_demo_image() # demo image

for i in range(4):
ax = grid[i]
im = ax.imshow(Z, extent=extent,
origin="lower", interpolation="nearest")

plt.draw()



ex 2. Images with same height but different width.

The above code can be similarly used in this case.

import matplotlib.pyplot as plt
from axes_grid import AxesGrid, get_demo_image


F = plt.figure(1, (9, 4.5))
grid = AxesGrid(F, 111, # similar to subplot(111)
nrows_ncols = (1, 3),
axes_pad = 0.1,
add_all=True,
label_mode = "L",
)

Z, extent = get_demo_image() # demo image

im_widths = [7, 5, 3] # image widths

for i, w in enumerate(im_widths):
ax = grid[i]
myextent = (extent[0], extent[0]+w, extent[2], extent[3])
im = ax.imshow(Z, extent=myextent, origin="lower", interpolation="nearest")

plt.draw()

Wednesday, October 29, 2008

Applying Filter Effects on SVG files created using Matplotlib

SVG Filter Effects

Using SVG Filter Effects, some fancy effects can be applied relatively easily.
(from http://www.w3.org/TR/SVG/filters.html)

While this can be done using applications like Inkscape, it is also possible to apply a few simple filter effects using python script. For example, using python, you can read in the original svg file as a DOM tree, modify the tree as necessary (adding the definition of filters, setting the filter attributes to the objects in your interest), and save it as a new svg file.


Original matplotlib figure http://matplotlib.sourceforge.net/screenshots.html#pie_demo

After applying SVG Filters,




The Python script used

  • Note that the original svg file is generated from the slightly modified version of matplotlib SVG backend, which sets the id attributes of patches with its label names. In this way, you can pick the objects you want by their id after you read in the svg file.


import xml.etree.cElementTree as ET


# filter definition for a dropshadow using a gaussian blur

# and etc
filter = """
  <defs  xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
    <filter id='dropshadow' height='1.2' width='1.2'>
      <feGaussianBlur result='blur' stdDeviation='2'/>
    </filter>
   
    <filter id='MyFilter' filterUnits='objectBoundingBox' x='0' y='0' width='1' height='1'>
      <feGaussianBlur in='SourceAlpha' stdDeviation='4%' result='blur'/>
      <feOffset in='blur' dx='4%' dy='4%' result='offsetBlur'/>
      <feSpecularLighting in='blur' surfaceScale='5' specularConstant='.75'
           specularExponent='20' lighting-color='#bbbbbb' result='specOut'>
        <fePointLight x='-5000%' y='-10000%' z='20000%'/>
      </feSpecularLighting>
      <feComposite in='specOut' in2='SourceAlpha' operator='in' result='specOut'/>
      <feComposite in='SourceGraphic' in2='specOut' operator='arithmetic'
    k1='0' k2='1' k3='1' k4='0'/>
    </filter>
  </defs>
"""


tree, xmlid = ET.XMLID(open("pie_demo.svg").read())

# insert the filter definition in the svg dom tree.
filters = ET.XML(filter)
tree.insert(0, filters)

for i, pie_name in enumerate(['Frogs', 'Hogs', 'Dogs', 'Logs']):

    pie = xmlid[pie_name]

    pie.set("filter", 'url(#MyFilter)')

    shadow = xmlid[pie_name + "_shadow"]
    shadow.set("filter",'url(#dropshadow)')
   

ET.ElementTree(tree).write("pie_demo3.svg")


Other than the filter definition string, the code to modify the SVG DOM tree is quite simple.

Followers