Skip to content

pdf backend displaces patches #3364

Closed
Closed

Description

@mvllngr

I try to draw a circle in display coordinates around a point to avoid stretching when the axes are not equal. Using the pdf backend, the following code will give me a circle that is not centered on the point anymore:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import ipdb
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# plot two points
ax.plot((0.2,0.7),(0.5,0.5), "o")
ax.set_xlim((0,1))
# get the display coordinates
x, y = ax.transData.transform((0.2,0.5))
# plot a circle around the point
circle = mpatches.Circle((x,y),50,fill=None)
# add to figure
fig.artists.append(circle)

# save to pdf - circle will not be centered on (0.2,0.5)
fig.savefig("displaced.pdf")
# save to pdf - circle will not be centered on (0.2,0.5)
fig.savefig("displaced.pdf", dpi=fig.dpi)
# save to png - circle will not be centered on (0.2,0.5)
fig.savefig("displaced.png")
# save to png - circle will be centered on (0.2,0.5)
fig.savefig("displaced.png", dpi=fig.dpi)
# show plot - circle will be centered
plt.show(block=True)

The reason for this can be found inFigureCanvasPdf.print_pdf()

def print_pdf(self, filename, **kwargs):
    image_dpi = kwargs.get('dpi', 72)  # dpi to use for images
    self.figure.set_dpi(72)            # there are 72 pdf points to an inch

such that the input dpi will be ignored and the scaling will be off at some point. It seems like RendererPDF can handle arbitrary dpi, as it uses a scaling factor to 72dpi, and a change to

     self.figure.set_dpi(image_dpi)

solved my problem. I'm not sure though if this is just a typo or a quick fix to some other problem that is not documented here. If someone can ok this, I will open a pull request.
\ Edit
I guess I missunderstood the idea behind this, since I just reverted any scaling altogether. Then I don't really know, why this is happening.

Activity

  1. tacaswell commented on Aug 12, 2014

    @tacaswell
    Member

    @jkseppan Can you comment on this? I think 72 is a magic number in pdfs.

    I think this is related to #2831.

  2. added this to the milestone on Aug 12, 2014
  3. jkseppan commented on Aug 18, 2014

    @jkseppan
    Member

    There's some earlier discussion here: http://thread.gmane.org/gmane.comp.python.matplotlib.devel/4306/

    Yeah, 72 is just the number of PostScript points in an inch.

  4. jkseppan commented on Aug 18, 2014

    @jkseppan
    Member

    The same seems to happen when saving to svg. I agree that it looks like #2831 is related.

  5. jklymak commented on May 10, 2018

    @jklymak
    Member

    Thanks to @ImportanceOfBeingErnest pointing this out on gitter:

    import matplotlib.pyplot as plt
    import matplotlib.patches as mpatches
    
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    # plot two points
    ax.plot((0.2,0.7),(0.5,0.5), "o")
    ax.set_xlim((0,1))
    # get the coordinates in inches from lower left corner:
    x, y = fig.dpi_scale_trans.inverted().transform(
            ax.transData.transform((0.2,0.5)))
    # plot a circle around the point
    print(x,y)
    circle = mpatches.Circle((x, y), 50/72, fill=None, transform=fig.dpi_scale_trans)
    # add to figure
    fig.artists.append(circle)
    
    # save to pdf - circle will not be centered on (0.2,0.5)
    fig.savefig("displaced.pdf")
    # save to png - circle will not be centered on (0.2,0.5)
    fig.savefig("displaced.png")
    # save to png - circle will be centered on (0.2,0.5)
    fig.savefig("displaced.png", dpi=fig.dpi)
    # show plot - circle will be centered
    plt.show(block=True)
  6. jklymak commented on May 10, 2018

    @jklymak
    Member

    The problem with the original code is that you cannot set the position in pixel co-ordinates and expect anything nice to happen when you print to vector backends because as you noted the dpi changes. Using the fig.dpi_scale_transform does everything in inches, and the drawing changes as the dpi of the figure changes at draw and print time.

  7. jklymak commented on May 10, 2018

    @jklymak
    Member

    Better as now the ellipse follows the data point under pan and zoom...

    import matplotlib.patches as mpatches
    import matplotlib.transforms as mtransforms
    import matplotlib.path as mpath
    
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    xdata, ydata = (0.2, 0.7), (0.5, 0.5)
    
    mytrans =  mtransforms.ScaledTranslation(xdata[0], ydata[0], ax.transData) + fig.dpi_scale_trans
    ell = mpatches.Ellipse((0, 0), 150/72., 130/72., angle=40, transform=mytrans)
    ax.add_patch(ell)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions