Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e24619d

Browse files
committed
DOC: add infor to transforms tutorial about fig.dpi_scale_trans
1 parent 4d1ca36 commit e24619d

File tree

2 files changed

+138
-51
lines changed

2 files changed

+138
-51
lines changed

lib/matplotlib/axes/_base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,10 @@ def add_artist(self, a):
18111811
to manually update the dataLim if the artist is to be included in
18121812
autoscaling.
18131813
1814+
If no ``transform`` has been specified when creating the artist (e.g.
1815+
``artist.get_transform()==None``) then the transform is set to
1816+
``ax.transData``.
1817+
18141818
Returns the artist.
18151819
"""
18161820
a.axes = self

tutorials/advanced/transforms_tutorial.py

Lines changed: 134 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,59 @@
1818
``ax`` is a :class:`~matplotlib.axes.Axes` instance, and ``fig`` is a
1919
:class:`~matplotlib.figure.Figure` instance.
2020
21-
+-----------+-----------------------------+-----------------------------------+
22-
|Coordinates|Transformation object |Description |
23-
+-----------+-----------------------------+-----------------------------------+
24-
|"data" |``ax.transData`` |The coordinate system for the data,|
25-
| | |controlled by xlim and ylim. |
26-
+-----------+-----------------------------+-----------------------------------+
27-
|"axes" |``ax.transAxes`` |The coordinate system of the |
28-
| | |`~matplotlib.axes.Axes`; (0, 0) |
29-
| | |is bottom left of the axes, and |
30-
| | |(1, 1) is top right of the axes. |
31-
+-----------+-----------------------------+-----------------------------------+
32-
|"figure" |``fig.transFigure`` |The coordinate system of the |
33-
| | |`.Figure`; (0, 0) is bottom left |
34-
| | |of the figure, and (1, 1) is top |
35-
| | |right of the figure. |
36-
+-----------+-----------------------------+-----------------------------------+
37-
|"display" |``None``, or |The pixel coordinate system of the |
38-
| |``IdentityTransform()`` |display; (0, 0) is bottom left of |
39-
| | |the display, and (width, height) is|
40-
| | |top right of the display in pixels.|
41-
+-----------+-----------------------------+-----------------------------------+
42-
|"xaxis", |``ax.get_xaxis_transform()``,|Blended coordinate systems; use |
43-
|"yaxis" |``ax.get_yaxis_transform()`` |data coordinates on one of the axis|
44-
| | |and axes coordinates on the other. |
45-
+-----------+-----------------------------+-----------------------------------+
21+
+----------------+-----------------------------+-----------------------------------+
22+
|Coordinates |Transformation object |Description |
23+
+----------------+-----------------------------+-----------------------------------+
24+
|"data" |``ax.transData`` |The coordinate system for the data,|
25+
| | |controlled by xlim and ylim. |
26+
+----------------+-----------------------------+-----------------------------------+
27+
|"axes" |``ax.transAxes`` |The coordinate system of the |
28+
| | |`~matplotlib.axes.Axes`; (0, 0) |
29+
| | |is bottom left of the axes, and |
30+
| | |(1, 1) is top right of the axes. |
31+
+----------------+-----------------------------+-----------------------------------+
32+
|"figure" |``fig.transFigure`` |The coordinate system of the |
33+
| | |`.Figure`; (0, 0) is bottom left |
34+
| | |of the figure, and (1, 1) is top |
35+
| | |right of the figure. |
36+
+----------------+-----------------------------+-----------------------------------+
37+
|"figure-inches" |``fig.dpi_scale_trans`` |The coordinate system of the |
38+
| | |`.Figure` in inches; (0, 0) is |
39+
| | |bottom left of the figure, and |
40+
| | |(width, height) is the top right |
41+
| | |of the figure ininches. |
42+
+----------------+-----------------------------+-----------------------------------+
43+
|"display" |``None``, or |The pixel coordinate system of the |
44+
| |``IdentityTransform()`` |display window; (0, 0) is bottom |
45+
| | |left of the window, and (width, |
46+
| | |height) is top right of the |
47+
| | |display window in pixels. |
48+
+----------------+-----------------------------+-----------------------------------+
49+
|"xaxis", |``ax.get_xaxis_transform()``,|Blended coordinate systems; use |
50+
|"yaxis" |``ax.get_yaxis_transform()`` |data coordinates on one of the axis|
51+
| | |and axes coordinates on the other. |
52+
+----------------+-----------------------------+-----------------------------------+
4653
4754
All of the transformation objects in the table above take inputs in
48-
their coordinate system, and transform the input to the `display`
49-
coordinate system. That is why the `display` coordinate system has
50-
`None` for the `Transformation Object` column -- it already is in
55+
their coordinate system, and transform the input to the ``display``
56+
coordinate system. That is why the ``display`` coordinate system has
57+
``None`` for the ``Transformation Object`` column -- it already is in
5158
display coordinates. The transformations also know how to invert
52-
themselves, to go from `display` back to the native coordinate system.
59+
themselves, to go from ``display`` back to the native coordinate system.
5360
This is particularly useful when processing events from the user
5461
interface, which typically occur in display space, and you want to
5562
know where the mouse click or key-press occurred in your data
5663
coordinate system.
5764
65+
Note that specifying objects in ``display`` coordinates will change their
66+
location if the ``dpi`` of the figure changes. This can cause confusion when
67+
printing or changing screen resolution, because the object can change location
68+
and size. Therefore it is most common
69+
for artists placed in an axes or figure to have their transform set to
70+
something *other* than the ``IdentityTransform()``; the default when an artist
71+
is placed on an axes using ``~Axes.axes.add_artist`` is for the transform to be
72+
`ax.transData`.
73+
5874
.. _data-coords:
5975
6076
Data coordinates
@@ -71,6 +87,7 @@
7187

7288
import numpy as np
7389
import matplotlib.pyplot as plt
90+
import matplotlib.patches as mpatches
7491

7592
x = np.arange(0, 10, 0.005)
7693
y = np.exp(-x/2.) * np.sin(2*np.pi*x)
@@ -143,14 +160,12 @@
143160
(xdata, ydata), xytext=(-2*offset, offset), textcoords='offset points',
144161
bbox=bbox, arrowprops=arrowprops)
145162

146-
147163
disp = ax.annotate('display = (%.1f, %.1f)' % (xdisplay, ydisplay),
148164
(xdisplay, ydisplay), xytext=(0.5*offset, -offset),
149165
xycoords='figure pixels',
150166
textcoords='offset points',
151167
bbox=bbox, arrowprops=arrowprops)
152168

153-
154169
plt.show()
155170

156171
###############################################################################
@@ -229,15 +244,13 @@
229244
# move, but the circle will remain fixed because it is not in `data`
230245
# coordinates and will always remain at the center of the axes.
231246

232-
import matplotlib.patches as patches
233-
234247
fig = plt.figure()
235248
ax = fig.add_subplot(111)
236249
x, y = 10*np.random.rand(2, 1000)
237-
ax.plot(x, y, 'go') # plot some data in data coordinates
250+
ax.plot(x, y, 'go', alpha=0.2) # plot some data in data coordinates
238251

239-
circ = patches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes,
240-
facecolor='yellow', alpha=0.5)
252+
circ = mpatches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes,
253+
facecolor='blue', alpha=0.75)
241254
ax.add_patch(circ)
242255
plt.show()
243256

@@ -281,7 +294,7 @@
281294
# highlight the 1..2 stddev region with a span.
282295
# We want x to be in data coordinates and y to
283296
# span from 0..1 in axes coords
284-
rect = patches.Rectangle((1, 0), width=1, height=1,
297+
rect = mpatches.Rectangle((1, 0), width=1, height=1,
285298
transform=trans, color='yellow',
286299
alpha=0.5)
287300

@@ -303,12 +316,90 @@
303316
#
304317
# trans = ax.get_xaxis_transform()
305318
#
319+
# .. _transforms-fig-scale-dpi:
320+
#
321+
# Plotting in physical units
322+
# ==========================
323+
#
324+
# Sometimes we want an object to be a certain physical size on the plot.
325+
# Here we draw the same circle as above, but in physical units. If done
326+
# interactively, you can see that changing the size of the figure does
327+
# not change the offset of the circle from the lower-left corner,
328+
# does not change its size, and the circle remains a circle regardless of
329+
# the aspect ratio of the axes.
330+
331+
fig, ax = plt.subplots(figsize=(5, 4))
332+
x, y = 10*np.random.rand(2, 1000)
333+
ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates
334+
# add a circle in fixed-units
335+
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
336+
facecolor='blue', alpha=0.75)
337+
ax.add_patch(circ)
338+
plt.show()
339+
340+
###############################################################################
341+
# If we change the figure size...
342+
343+
fig, ax = plt.subplots(figsize=(7, 2))
344+
x, y = 10*np.random.rand(2, 1000)
345+
ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates
346+
# add a circle in fixed-units
347+
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
348+
facecolor='blue', alpha=0.75)
349+
ax.add_patch(circ)
350+
plt.show()
351+
352+
###############################################################################
353+
# Another use is putting a patch with a set physical dimmension around a
354+
# data point on the axes. Here we add together two transforms. The
355+
# first sets the scaling of how large the ellipse should be and the second
356+
# sets its position. The ellipse is then placed at the origin, and then
357+
# we use the helper transform :class:`~matplotlib.transforms.ScaledTranslation`
358+
# to move it
359+
# to the right place in the ``ax.transData`` coordinate system.
360+
# This helper is instantiated with::
361+
#
362+
# trans = ScaledTranslation(xt, yt, scale_trans)
363+
#
364+
# where `xt` and `yt` are the translation offsets, and `scale_trans` is
365+
# a transformation which scales `xt` and `yt` at transformation time
366+
# before applying the offsets.
367+
#
368+
# Note that in interactive use, the ellipse stays the same size even if the
369+
# axes limits are changed via zoom, but the ellipse's size doesn't change.
370+
#
371+
# Also note that the order of transformation matters. Here the ellipse
372+
# is given the right dimensions in display space *first* and then moved
373+
# in data space to the correct spot.
374+
# If we had done the ``ScaledTranslation`` first, then
375+
# ``xdata[0]`` and ``ydata[0]`` would
376+
# first be transfromed to ``display`` coordinates (``[ 358.4 475.2]`` on
377+
# a 200-dpi monitor) and then those coordinates
378+
# would be scaled by ``fig.dpi_scale_trans`` pushing the center of
379+
# the ellipse well off the screen (i.e. ``[ 71680. 95040.]``).
380+
381+
fig, ax = plt.subplots()
382+
xdata, ydata = (0.2, 0.7), (0.5, 0.5)
383+
ax.plot(xdata, ydata, "o")
384+
ax.set_xlim((0, 1))
385+
386+
trans = fig.dpi_scale_trans + \
387+
mtransforms.ScaledTranslation(xdata[0], ydata[0], ax.transData)
388+
389+
# plot an ellipse around the point that is 150 x 130 points in diameter...
390+
circle = mpatches.Ellipse((0, 0), 150/72, 130/72, angle=40, fill=None,
391+
transform=trans)
392+
ax.add_patch(circle)
393+
plt.show()
394+
395+
###############################################################################
306396
# .. _offset-transforms-shadow:
307397
#
308398
# Using offset transforms to create a shadow effect
309399
# =================================================
310400
#
311-
# One use of transformations is to create a new transformation that is
401+
# Another use of :class:`~matplotlib.transforms.ScaledTranslation` is to create
402+
# a new transformation that is
312403
# offset from another transformation, e.g., to place one object shifted a
313404
# bit relative to another object. Typically you want the shift to be in
314405
# some physical dimension, like points or inches rather than in data
@@ -318,18 +409,11 @@
318409
# One use for an offset is to create a shadow effect, where you draw one
319410
# object identical to the first just to the right of it, and just below
320411
# it, adjusting the zorder to make sure the shadow is drawn first and
321-
# then the object it is shadowing above it. The transforms module has a
322-
# helper transformation
323-
# :class:`~matplotlib.transforms.ScaledTranslation`. It is
324-
# instantiated with::
325-
#
326-
# trans = ScaledTranslation(xt, yt, scale_trans)
412+
# then the object it is shadowing above it.
327413
#
328-
# where `xt` and `yt` are the translation offsets, and `scale_trans` is
329-
# a transformation which scales `xt` and `yt` at transformation time
330-
# before applying the offsets. A typical use case is to use the figure
331-
# ``fig.dpi_scale_trans`` transformation for the `scale_trans` argument,
332-
# to first scale `xt` and `yt` specified in points to `display` space
414+
# As above, in :class:`~matplotlib.transforms.ScaledTranslation` we use
415+
# ``fig.dpi_scale_trans`` transformation for the ``scale_trans`` argument,
416+
# to first scale ``xt`` and ``yt`` specified in points to ``display`` space
333417
# before doing the final offset. The dpi and inches offset is a
334418
# common-enough use case that we have a special helper function to
335419
# create it in :func:`matplotlib.transforms.offset_copy`, which returns
@@ -348,8 +432,7 @@
348432
# 1/72 inches, and by specifying your offsets in points, your figure
349433
# will look the same regardless of the dpi resolution it is saved in.
350434

351-
fig = plt.figure()
352-
ax = fig.add_subplot(111)
435+
fig, ax = plt.subplots()
353436

354437
# make a simple sine wave
355438
x = np.arange(0., 2., 0.01)

0 commit comments

Comments
 (0)