Skip to content Skip to sidebar Skip to footer

Incorrect Rectangle Location In Matplotlib

I'm making a plot with bars and I'm trying to find their absolute location (in pixels) on the plot for further processing later. It seems to me that this should be able to be figu

Solution 1:

As usual, the revelation happens when posting the question ... The problem here is that the units are not pixels, they're dots. When the figure is initialized, it is set up with a default dots per inch (fig.set_dpi() and fig.get_dpi() allow you to change/inquire the current figure's resolution). Of course, things can't be that easy -- When you save the figure, the dpi changes based on your rc settings for the particular backend. For me with the default backend and png output, this goes to 100 dpi. One thing which is invariant here, however is the figure size fig.get_size_inches(). So, if I scale my results, things seem to turn our a little more consistently ...

def get_useful_info(fig):
    dpi = fig.get_dpi()
    pngdpi = 100
    scale_x = pngdpi / dpi
    scale_y = pngdpi / dpi
    for ax in fig.get_axes():
        for rect in ax.monkey_rectangles:
            corners = rect.get_bbox().corners()[::3]
            pos = ax.transData.transform(corners)
            left = pos[0,0] * scale_x
            width = (pos[1,0] - pos[0,0]) * scale_x
            bottom = pos[0,1] * scale_y
            height = (pos[1,1] - pos[0,1]) * scale_y
            yield left, width, bottom, height

I have a sinking feeling that this isn't the full solution though -- After all, we're still working in dots. I'm not completely sure how a dot translates to a pixel for png images ... My hope is that the display engine (in my particular case, a web browser) renders each dot in a single pixel and doesn't care about the image's reported size. I suppose that only some experimentation will sort that one out ...

Solution 2:

As you are referring to web-browsers, I assume you are building image maps?

import matplotlib.pyplot as plt

x = range(10)
y = range(1, 11)

fig = plt.figure()
ax = fig.add_subplot(111)
bars = ax.bar(x, y, width=.5, label="foo")
ax.monkey_rectangles = bars
ax.legend()

def fig_fraction_info(fig):
    for ax in fig.get_axes():
        inv = fig.transFigure.inverted()
        # transformations are applied left to right
        my_trans = ax.transData + inv 
        for rect in ax.monkey_rectangles:
            corners = rect.get_bbox().corners()[::3]
            pos = my_trans.transform(corners)
            left = pos[0,0]
            width = pos[1,0] - pos[0,0]
            bottom = pos[0,1]
            height = pos[1,1] - pos[0,1]
            yield left, width, bottom, height

I think something like this is what you want. If you feed this data and the png into the next level of code you can sort out exactly where everything is.

Issues like this are (I think) a consequence of way that matplotlib abstracts the backend from the front end. Thinking about the size in pixels doesn't make a lot of sense for vector outputs.

Alternately:

dpi = 300
...
fig.set_dpi(dpi)
...
fig.savefig(..., dpi=dpi)

Post a Comment for "Incorrect Rectangle Location In Matplotlib"