|
15 | 15 | "cell_type": "markdown",
|
16 | 16 | "metadata": {},
|
17 | 17 | "source": [
|
18 |
| - "\n# Transformations Tutorial\n\n\nTransforming visuals in Matplotlib.\n\nLike any graphics packages, matplotlib is built on top of a\ntransformation framework to easily move between coordinate systems,\nthe userland `data` coordinate system, the `axes` coordinate system,\nthe `figure` coordinate system, and the `display` coordinate system.\nIn 95% of your plotting, you won't need to think about this, as it\nhappens under the hood, but as you push the limits of custom figure\ngeneration, it helps to have an understanding of these objects so you\ncan reuse the existing transformations matplotlib makes available to\nyou, or create your own (see :mod:`matplotlib.transforms`). The table\nbelow summarizes the existing coordinate systems, the transformation\nobject you should use to work in that coordinate system, and the\ndescription of that system. In the `Transformation Object` column,\n``ax`` is a :class:`~matplotlib.axes.Axes` instance, and ``fig`` is a\n:class:`~matplotlib.figure.Figure` instance.\n\n========== ===================== ====================================================================================\nCoordinate Transformation Object Description\n========== ===================== ====================================================================================\n`data` ``ax.transData`` The userland data coordinate system, controlled by the xlim and ylim\n`axes` ``ax.transAxes`` The coordinate system of the :class:`~matplotlib.axes.Axes`; (0, 0) is\n bottom left of the axes, and (1, 1) is top right of the axes.\n`figure` ``fig.transFigure`` The coordinate system of the :class:`~matplotlib.figure.Figure`; (0, 0)\n is bottom left of the figure, and (1, 1) is top right of the figure.\n`display` `None` This is the pixel coordinate system of the display; (0, 0) is the bottom\n left of the display, and (width, height) is the top right of the display in pixels.\n Alternatively, the identity transform\n (:class:`matplotlib.transforms.IdentityTransform()`) may be used instead of None.\n========== ===================== ====================================================================================\n\n\nAll of the transformation objects in the table above take inputs in\ntheir coordinate system, and transform the input to the `display`\ncoordinate system. That is why the `display` coordinate system has\n`None` for the `Transformation Object` column -- it already is in\ndisplay coordinates. The transformations also know how to invert\nthemselves, to go from `display` back to the native coordinate system.\nThis is particularly useful when processing events from the user\ninterface, which typically occur in display space, and you want to\nknow where the mouse click or key-press occurred in your data\ncoordinate system.\n\n\nData coordinates\n================\n\nLet's start with the most commonly used coordinate, the `data`\ncoordinate system. Whenever you add data to the axes, matplotlib\nupdates the datalimits, most commonly updated with the\n:meth:`~matplotlib.axes.Axes.set_xlim` and\n:meth:`~matplotlib.axes.Axes.set_ylim` methods. For example, in the\nfigure below, the data limits stretch from 0 to 10 on the x-axis, and\n-1 to 1 on the y-axis.\n\n" |
| 18 | + "\n# Transformations Tutorial\n\n\nLike any graphics packages, Matplotlib is built on top of a\ntransformation framework to easily move between coordinate systems,\nthe userland `data` coordinate system, the `axes` coordinate system,\nthe `figure` coordinate system, and the `display` coordinate system.\nIn 95% of your plotting, you won't need to think about this, as it\nhappens under the hood, but as you push the limits of custom figure\ngeneration, it helps to have an understanding of these objects so you\ncan reuse the existing transformations Matplotlib makes available to\nyou, or create your own (see :mod:`matplotlib.transforms`). The table\nbelow summarizes the some useful coordinate systems, the transformation\nobject you should use to work in that coordinate system, and the\ndescription of that system. In the `Transformation Object` column,\n``ax`` is a :class:`~matplotlib.axes.Axes` instance, and ``fig`` is a\n:class:`~matplotlib.figure.Figure` instance.\n\n=========== ============================= =====================================\nCoordinates Transformation object Description\n=========== ============================= =====================================\n\"data\" ``ax.transData```` The coordinate system for the data,\n controlled by xlim and ylim.\n\"axes\" ``ax.transAxes`` The coordinate system of the\n `~.Axes`; (0, 0) is bottom left of\n the axes, and (1, 1) is top right of\n the axes.\n\"figure\" ``fig.transFigure`` The coordinate system of the\n `~.Figure`; (0, 0) is bottom left of\n the figure, and (1, 1) is top right\n of the figure.\n\"display\" ``None``, or The pixel coordinate system of the\n ``IdentityTransform()`` display; (0, 0) is bottom left of the\n display, and (width, height) is top\n right of the display in pixels.\n\"xaxis\", ``ax.get_xaxis_transform()``, Blended coordinate systems; use data\n\"yaxis\" ``ax.get_yaxis_transform()`` coordinates on one of the axis and\n axes coordinates on the other.\n=========== ============================= =====================================\n\nAll of the transformation objects in the table above take inputs in\ntheir coordinate system, and transform the input to the `display`\ncoordinate system. That is why the `display` coordinate system has\n`None` for the `Transformation Object` column -- it already is in\ndisplay coordinates. The transformations also know how to invert\nthemselves, to go from `display` back to the native coordinate system.\nThis is particularly useful when processing events from the user\ninterface, which typically occur in display space, and you want to\nknow where the mouse click or key-press occurred in your data\ncoordinate system.\n\n\nData coordinates\n================\n\nLet's start with the most commonly used coordinate, the `data`\ncoordinate system. Whenever you add data to the axes, Matplotlib\nupdates the datalimits, most commonly updated with the\n:meth:`~matplotlib.axes.Axes.set_xlim` and\n:meth:`~matplotlib.axes.Axes.set_ylim` methods. For example, in the\nfigure below, the data limits stretch from 0 to 10 on the x-axis, and\n-1 to 1 on the y-axis.\n\n" |
19 | 19 | ]
|
20 | 20 | },
|
21 | 21 | {
|
|
123 | 123 | "cell_type": "markdown",
|
124 | 124 | "metadata": {},
|
125 | 125 | "source": [
|
126 |
| - ".. transformation-pipeline:\n\nThe transformation pipeline\n===========================\n\nThe ``ax.transData`` transform we have been working with in this\ntutorial is a composite of three different transformations that\ncomprise the transformation pipeline from `data` -> `display`\ncoordinates. Michael Droettboom implemented the transformations\nframework, taking care to provide a clean API that segregated the\nnonlinear projections and scales that happen in polar and logarithmic\nplots, from the linear affine transformations that happen when you pan\nand zoom. There is an efficiency here, because you can pan and zoom\nin your axes which affects the affine transformation, but you may not\nneed to compute the potentially expensive nonlinear scales or\nprojections on simple navigation events. It is also possible to\nmultiply affine transformation matrices together, and then apply them\nto coordinates in one step. This is not true of all possible\ntransformations.\n\n\nHere is how the ``ax.transData`` instance is defined in the basic\nseparable axis :class:`~matplotlib.axes.Axes` class::\n\n self.transData = self.transScale + (self.transLimits + self.transAxes)\n\nWe've been introduced to the ``transAxes`` instance above in\n`axes-coords`, which maps the (0, 0), (1, 1) corners of the\naxes or subplot bounding box to `display` space, so let's look at\nthese other two pieces.\n\n``self.transLimits`` is the transformation that takes you from\n``data`` to ``axes`` coordinates; i.e., it maps your view xlim and ylim\nto the unit space of the axes (and ``transAxes`` then takes that unit\nspace to display space). We can see this in action here\n\n.. sourcecode:: ipython\n\n In [80]: ax = subplot(111)\n\n In [81]: ax.set_xlim(0, 10)\n Out[81]: (0, 10)\n\n In [82]: ax.set_ylim(-1, 1)\n Out[82]: (-1, 1)\n\n In [84]: ax.transLimits.transform((0, -1))\n Out[84]: array([ 0., 0.])\n\n In [85]: ax.transLimits.transform((10, -1))\n Out[85]: array([ 1., 0.])\n\n In [86]: ax.transLimits.transform((10, 1))\n Out[86]: array([ 1., 1.])\n\n In [87]: ax.transLimits.transform((5, 0))\n Out[87]: array([ 0.5, 0.5])\n\nand we can use this same inverted transformation to go from the unit\n`axes` coordinates back to `data` coordinates.\n\n.. sourcecode:: ipython\n\n In [90]: inv.transform((0.25, 0.25))\n Out[90]: array([ 2.5, -0.5])\n\nThe final piece is the ``self.transScale`` attribute, which is\nresponsible for the optional non-linear scaling of the data, e.g., for\nlogarithmic axes. When an Axes is initially setup, this is just set to\nthe identity transform, since the basic matplotlib axes has linear\nscale, but when you call a logarithmic scaling function like\n:meth:`~matplotlib.axes.Axes.semilogx` or explicitly set the scale to\nlogarithmic with :meth:`~matplotlib.axes.Axes.set_xscale`, then the\n``ax.transScale`` attribute is set to handle the nonlinear projection.\nThe scales transforms are properties of the respective ``xaxis`` and\n``yaxis`` :class:`~matplotlib.axis.Axis` instances. For example, when\nyou call ``ax.set_xscale('log')``, the xaxis updates its scale to a\n:class:`matplotlib.scale.LogScale` instance.\n\nFor non-separable axes the PolarAxes, there is one more piece to\nconsider, the projection transformation. The ``transData``\n:class:`matplotlib.projections.polar.PolarAxes` is similar to that for\nthe typical separable matplotlib Axes, with one additional piece\n``transProjection``::\n\n self.transData = self.transScale + self.transProjection + \\\n (self.transProjectionAffine + self.transAxes)\n\n``transProjection`` handles the projection from the space,\ne.g., latitude and longitude for map data, or radius and theta for polar\ndata, to a separable Cartesian coordinate system. There are several\nprojection examples in the ``matplotlib.projections`` package, and the\nbest way to learn more is to open the source for those packages and\nsee how to make your own, since matplotlib supports extensible axes\nand projections. Michael Droettboom has provided a nice tutorial\nexample of creating a hammer projection axes; see\n`sphx_glr_gallery_api_custom_projection_example.py`.\n\n" |
| 126 | + ".. transformation-pipeline:\n\nThe transformation pipeline\n===========================\n\nThe ``ax.transData`` transform we have been working with in this\ntutorial is a composite of three different transformations that\ncomprise the transformation pipeline from `data` -> `display`\ncoordinates. Michael Droettboom implemented the transformations\nframework, taking care to provide a clean API that segregated the\nnonlinear projections and scales that happen in polar and logarithmic\nplots, from the linear affine transformations that happen when you pan\nand zoom. There is an efficiency here, because you can pan and zoom\nin your axes which affects the affine transformation, but you may not\nneed to compute the potentially expensive nonlinear scales or\nprojections on simple navigation events. It is also possible to\nmultiply affine transformation matrices together, and then apply them\nto coordinates in one step. This is not true of all possible\ntransformations.\n\n\nHere is how the ``ax.transData`` instance is defined in the basic\nseparable axis :class:`~matplotlib.axes.Axes` class::\n\n self.transData = self.transScale + (self.transLimits + self.transAxes)\n\nWe've been introduced to the ``transAxes`` instance above in\n`axes-coords`, which maps the (0, 0), (1, 1) corners of the\naxes or subplot bounding box to `display` space, so let's look at\nthese other two pieces.\n\n``self.transLimits`` is the transformation that takes you from\n``data`` to ``axes`` coordinates; i.e., it maps your view xlim and ylim\nto the unit space of the axes (and ``transAxes`` then takes that unit\nspace to display space). We can see this in action here\n\n.. sourcecode:: ipython\n\n In [80]: ax = subplot(111)\n\n In [81]: ax.set_xlim(0, 10)\n Out[81]: (0, 10)\n\n In [82]: ax.set_ylim(-1, 1)\n Out[82]: (-1, 1)\n\n In [84]: ax.transLimits.transform((0, -1))\n Out[84]: array([ 0., 0.])\n\n In [85]: ax.transLimits.transform((10, -1))\n Out[85]: array([ 1., 0.])\n\n In [86]: ax.transLimits.transform((10, 1))\n Out[86]: array([ 1., 1.])\n\n In [87]: ax.transLimits.transform((5, 0))\n Out[87]: array([ 0.5, 0.5])\n\nand we can use this same inverted transformation to go from the unit\n`axes` coordinates back to `data` coordinates.\n\n.. sourcecode:: ipython\n\n In [90]: inv.transform((0.25, 0.25))\n Out[90]: array([ 2.5, -0.5])\n\nThe final piece is the ``self.transScale`` attribute, which is\nresponsible for the optional non-linear scaling of the data, e.g., for\nlogarithmic axes. When an Axes is initially setup, this is just set to\nthe identity transform, since the basic Matplotlib axes has linear\nscale, but when you call a logarithmic scaling function like\n:meth:`~matplotlib.axes.Axes.semilogx` or explicitly set the scale to\nlogarithmic with :meth:`~matplotlib.axes.Axes.set_xscale`, then the\n``ax.transScale`` attribute is set to handle the nonlinear projection.\nThe scales transforms are properties of the respective ``xaxis`` and\n``yaxis`` :class:`~matplotlib.axis.Axis` instances. For example, when\nyou call ``ax.set_xscale('log')``, the xaxis updates its scale to a\n:class:`matplotlib.scale.LogScale` instance.\n\nFor non-separable axes the PolarAxes, there is one more piece to\nconsider, the projection transformation. The ``transData``\n:class:`matplotlib.projections.polar.PolarAxes` is similar to that for\nthe typical separable matplotlib Axes, with one additional piece\n``transProjection``::\n\n self.transData = self.transScale + self.transProjection + \\\n (self.transProjectionAffine + self.transAxes)\n\n``transProjection`` handles the projection from the space,\ne.g., latitude and longitude for map data, or radius and theta for polar\ndata, to a separable Cartesian coordinate system. There are several\nprojection examples in the ``matplotlib.projections`` package, and the\nbest way to learn more is to open the source for those packages and\nsee how to make your own, since Matplotlib supports extensible axes\nand projections. Michael Droettboom has provided a nice tutorial\nexample of creating a Hammer projection axes; see\n`sphx_glr_gallery_api_custom_projection_example.py`.\n\n" |
127 | 127 | ]
|
128 | 128 | }
|
129 | 129 | ],
|
|
0 commit comments