26

My question is quite simple : in matplotlib, how can I easily convert coordinates in Axis system to/from Data system (Ideally I'm looking for a simple function output_coords = magic_func(input_coords) )

Actually my exact problem is : I'd like to plot an matplotlib.patches.Ellipse with his center in Axis system but his size (width & length) in Data system. But the transforms.blended_transform_factory method doesn't work in this case.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Covich
  • 2,544
  • 4
  • 26
  • 37
  • 4
    You probably should have a look at [this](http://matplotlib.org/users/transforms_tutorial.html) – Arthur Jul 23 '15 at 09:31

3 Answers3

32

To get transformations from the Axes instance ax, you can use

axis_to_data = ax.transAxes + ax.transData.inverted()
points_data = axis_to_data.transform(points_axis)
data_to_axis = axis_to_data.inverted()
numpy.testing.assert_allclose(points_axis, data_to_axis.transform(points_data))
wilywampa
  • 1,280
  • 1
  • 15
  • 15
  • 1
    Hi there, this amazingly solved my case. Is there any doc page where is combining trans*() methods and classes explained? – n1_ Nov 13 '17 at 10:54
  • 3
    @grafa See this [tutorial](https://matplotlib.org/users/transforms_tutorial.html). The idea here is that every 'transform' instance provides tools to transform coordinates from some special system to universal 'display' coordinates in pixels/dots. An inverted transform goes the other way. When you add multiple transform instances, it creates a new instance of a new class that performs those transformations in sequence. So, the above `transform` call goes from axes to universal, then from universal to data. – Luke Davis Nov 10 '18 at 04:48
  • Note `data_to_axis` here is equivalent to `ax.TransData + ax.transAxes.inverted()`. Even more concise is to use the built-in (but not very well-documented) `ax.transLimits` & `ax.transLimits.inverted()` transforms to go to/from axes coordinates, respectively. – corvus Sep 21 '22 at 15:00
12

Following the transforms tutorial, the simplest way is to use ax.transLimits.

output_coords = ax.transLimits.transform(input_coords)
emem
  • 5,588
  • 1
  • 24
  • 30
  • 3
    Just as a note for others, this answer is for axes -> data. For data -> axes it's `output_coords = ax.transLimits.inverted().transform(input_coords)` – golmschenk Dec 22 '20 at 20:00
  • 2
    @golmschenk I think your comment is helpful, but you have it backwards (at least for the latest version). `transLimits` takes *data* coordinates -> *axes* coordinates. – corvus Sep 21 '22 at 14:59
  • @corvus, good typo catch! – golmschenk Sep 22 '22 at 00:53
3

No immediate solution found, need to write it by myself :

def axis_data_coords_sys_transform(axis_obj_in,xin,yin,inverse=False):
    """ inverse = False : Axis => Data
                = True  : Data => Axis
    """
    xlim = axis_obj_in.get_xlim()
    ylim = axis_obj_in.get_ylim()

    xdelta = xlim[1] - xlim[0]
    ydelta = ylim[1] - ylim[0]
    if not inverse:
        xout =  xlim[0] + xin * xdelta
        yout =  ylim[0] + yin * ydelta
    else:
        xdelta2 = xin - xlim[0]
        ydelta2 = yin - ylim[0]
        xout = xdelta2 / xdelta
        yout = ydelta2 / ydelta
    return xout,yout
Covich
  • 2,544
  • 4
  • 26
  • 37
  • This is nice, thanks. It could easily be modified to work for log scaled axes too, using `axis_obj_in.get_[x or y]scale()`. – askewchan Aug 17 '15 at 14:51