Is there a bug in svgutils.compose() module regarding the arrangement of figures using tile() method?

748 Views Asked by At

I have 12 figures in svg format and I would like to arrange them into 3 columns and 4 rows in the form of 12 panels that would fit into an A4 printed version for publication. What is desired is to maximize the area covering fraction such that the empty space is minimized in order to see all the features in the 12 images.

I am aware that I can use subplots under a big figure in matplotlib. However, in my case, each one of these figures are produced independently from a different function of the same simulation. I cannot embed a large simulation repeatedly into each call as I need to access some of the figures in different places for different purposes. In other words, the less I manipulate the original simulation the less headache I will have to extract different plots from it. I would like to minimize any changes in the original simulation and instead add my additional code to my sole purpose of gathering some of the plots into same place.

Here is what I have come up with in my python code to accomplish this:

import matplotlib.pyplot as plt
import svgutils.transform as sg
from svgutils.compose import *
import os

plt.figure(1, tight_layout=True)
...
plt.savefig('/some_path/first_xy.svg')

plt.figure(2, tight_layout=True)
...
plt.savefig('/some_path/first_xz.svg')

plt.figure(3, tight_layout=True)
...
plt.savefig('/some_path/first_yz.svg')

plt.figure(4, tight_layout=True)
...
plt.savefig('/some_path/second_xy.svg')

plt.figure(5, tight_layout=True)
...
plt.savefig('/some_path/second_xz.svg')

plt.figure(6, tight_layout=True)
...
plt.savefig('/some_path/second_yz.svg')

plt.figure(7, tight_layout=True)
...
plt.savefig('/some_path/third_xy.svg')

plt.figure(8, tight_layout=True)
...
plt.savefig('/some_path/third_xz.svg')

plt.figure(9, tight_layout=True)
...
plt.savefig('/some_path/third_yz.svg')

plt.figure(10, tight_layout=True)
...
plt.savefig('/some_path/fourth_xy.svg')

plt.figure(11, tight_layout=True)
...
plt.savefig('/some_path/fourth_xz.svg')

plt.figure(12, tight_layout=True)
...
plt.savefig('/some_path/fourth_yz.svg')




myfigure = Figure("21cm", "29.7cm", 
                  SVG("/some_path/first_xy.svg"), 
                  SVG("/some_path/first_xz.svg"), 
                  SVG("/some_path/first_yz.svg"), 
                  SVG("/some_path/second_xy.svg"), 
                  SVG("/some_path/second_xz.svg"), 
                  SVG("/some_path/second_yz.svg"), 
                  SVG("/some_path/third_xy.svg"), 
                  SVG("/some_path/third_xz.svg"), 
                  SVG("/some_path/third_yz.svg"), 
                  SVG("/some_path/fourth_xy.svg"), 
                  SVG("/some_path/fourth_xz.svg"), 
                  SVG("/some_path/fourth_yz.svg")
                  ).tile(3, 4)

myfigure.save('/some_path/complete_figure.svg')
os.system('inkscape --export-png=/some_path/complete_figure.png /some_path/complete_figure.svg --export-background=white --export-area-drawing')

However, running this code produces a weird error message related to the function being used, tile(), to group 12 figures into a single A4 page as below:

Traceback (most recent call last): File "script.py", line 70, in ).tile(3, 4)
File "/usr/local/anaconda3/lib/python3.5/site-packages/svgutils/compose.py", line 287, in tile dx = (self.width/ncols).to('px').value TypeError: unsupported operand type(s) for /: 'Unit' and 'int'

I am running my code on a remote desktop but I can access the content of compose.py. However I don't know if there is a bug in that routine. What is the fix?

1

There are 1 best solutions below

0
On BEST ANSWER

Seems like a bug to me. You should report it at https://github.com/btel/svg_utils/issues

The offending lines are in the tile function:

dx = (self.width/ncols).to('px').value
dy = (self.height/nrows).to('px').value

self.width and self.height are type Unit, which Python doesn't know how to divide by an int.

In the meantime, I quickly fixed it with a monkey-patch of the tile function:

def new_tile(self, ncols, nrows):
    """Automatically tile the panels of the figure.
    This will re-arranged all elements of the figure (first in the
    hierarchy) so that they will uniformly cover the figure area.
    Parameters
    ----------
    ncols, nrows : type
        The number of columns and rows to arange the elements into.
    Notes
    -----
    ncols * nrows must be larger or equal to number of
    elements, otherwise some elements will go outside the figure borders.
    """
    dx = self.width.to('px').value/ncols
    dy = self.height.to('px').value/nrows
    ix, iy = 0, 0
    for el in self:
        el.move(dx*ix, dy*iy)
        ix += 1
        if ix >= ncols:
            ix = 0
            iy += 1
        if iy > nrows:
            break
    return self

apply by executing:

svgutils.compose.Figure.tile = new_tile

EDIT: this is true as of svgutils-0.2.0 freshly installed from pip