|
| 1 | +.. _plotting-guide-annotation: |
| 2 | + |
| 3 | +**************** |
| 4 | +Annotating Axes |
| 5 | +**************** |
| 6 | + |
| 7 | +Do not proceed unless you already have read |
| 8 | +:func:`~matplotlib.pyplot.text` and :func:`~matplotlib.pyplot.annotate`! |
| 9 | + |
| 10 | + |
| 11 | +Annotating with Text with Box |
| 12 | +============================= |
| 13 | + |
| 14 | +Let's start with a simple example. |
| 15 | + |
| 16 | +.. plot:: users/plotting/examples/annotate_text_arrow.py |
| 17 | + |
| 18 | + |
| 19 | +The :func:`~matplotlib.pyplot.text` function in the pyplot module (or |
| 20 | +text method of the Axes class) takes bbox keyword argument, and when |
| 21 | +given, a box around the text is drawn. :: |
| 22 | + |
| 23 | + bbox_props = dict(boxstyle="rarrow,pad=0.3", fc="cyan", ec="b", lw=2) |
| 24 | + t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45, |
| 25 | + size=15, |
| 26 | + bbox=bbox_props) |
| 27 | + |
| 28 | + |
| 29 | +The patch object associated with the text can be accessed by:: |
| 30 | + |
| 31 | + bb = t.get_bbox_patch() |
| 32 | + |
| 33 | +The return value is an instance of FancyBboxPatch and the patch |
| 34 | +properties like facecolor, edgewidth, etc. can be accessed and |
| 35 | +modified as usual. To change the shape of the box, use *set_boxstyle* |
| 36 | +method. :: |
| 37 | + |
| 38 | + bb.set_boxstyle("rarrow", pad=0.6) |
| 39 | + |
| 40 | +The arguments are the name of the box style with its attributes as |
| 41 | +keyword arguments. Currently, followign box styles are implemented. |
| 42 | + |
| 43 | + ========== ============== ========================== |
| 44 | + Class Name Attrs |
| 45 | + ========== ============== ========================== |
| 46 | + LArrow ``larrow`` pad=0.3 |
| 47 | + RArrow ``rarrow`` pad=0.3 |
| 48 | + Round ``round`` pad=0.3,rounding_size=None |
| 49 | + Round4 ``round4`` pad=0.3,rounding_size=None |
| 50 | + Roundtooth ``roundtooth`` pad=0.3,tooth_size=None |
| 51 | + Sawtooth ``sawtooth`` pad=0.3,tooth_size=None |
| 52 | + Square ``square`` pad=0.3 |
| 53 | + ========== ============== ========================== |
| 54 | + |
| 55 | +.. plot:: mpl_examples/pylab_examples/fancybox_demo2.py |
| 56 | + |
| 57 | + |
| 58 | +Note that the attrubutes arguments can be specified within the style |
| 59 | +name with separating comma (this form can be used as "boxstyle" value |
| 60 | +of bbox argument when initializing the text instance) :: |
| 61 | + |
| 62 | + bb.set_boxstyle("rarrow,pad=0.6") |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | +Annotating with Arrow |
| 68 | +===================== |
| 69 | + |
| 70 | +The :func:`~matplotlib.pyplot.annotate` function in the pyplot module |
| 71 | +(or annotate method of the Axes class) is used to draw an arrow |
| 72 | +connecting two points on the plot. :: |
| 73 | + |
| 74 | + ax.annotate("Annotation", |
| 75 | + xy=(x1, y1), xycoords='data', |
| 76 | + xytext=(x2, y2), textcoords='offset points', |
| 77 | + ) |
| 78 | + |
| 79 | +This annotates a point at ``xy`` in the given coordinate (``xycoords``) |
| 80 | +with the text at ``xytext`` given in ``textcoords``. Often, the |
| 81 | +annotated point is specified in the *data* coordinate and the annotating |
| 82 | +text in *offset points*. |
| 83 | +See :func:`~matplotlib.pyplot.annotate` for available coordinate systems. |
| 84 | + |
| 85 | +An arrow connecting two point (xy & xytext) can be optionally drawn by |
| 86 | +specifying the ``arrowprops`` argument. To draw only an arrow, use |
| 87 | +empty string as the first argument. :: |
| 88 | + |
| 89 | + ax.annotate("", |
| 90 | + xy=(0.2, 0.2), xycoords='data', |
| 91 | + xytext=(0.8, 0.8), textcoords='data', |
| 92 | + arrowprops=dict(arrowstyle="->", |
| 93 | + connectionstyle="arc3"), |
| 94 | + ) |
| 95 | + |
| 96 | +.. plot:: users/plotting/examples/annotate_simple01.py |
| 97 | + |
| 98 | +The arrow drawing takes a few steps. |
| 99 | + |
| 100 | +1. a connecting path between two points are created. This is |
| 101 | + controlled by ``connectionstyle`` key value. |
| 102 | + |
| 103 | +2. If patch object is given (*patchA* & *patchB*), the path is clipped to |
| 104 | + avoid the patch. |
| 105 | + |
| 106 | +3. The path is further shrinked by given amount of pixels (*shirnkA* |
| 107 | + & *shrinkB*) |
| 108 | + |
| 109 | +4. The path is transmuted to arrow patch, which is controlled by the |
| 110 | + ``arrowstyle`` key value. |
| 111 | + |
| 112 | + |
| 113 | +.. plot:: users/plotting/examples/annotate_explain.py |
| 114 | + |
| 115 | + |
| 116 | +The creation of the connecting path between two points is controlled by |
| 117 | +``connectionstyle`` key and follwing styles are available. |
| 118 | + |
| 119 | + ========== ============================================= |
| 120 | + Name Attrs |
| 121 | + ========== ============================================= |
| 122 | + ``angle`` angleA=90,angleB=0,rad=0.0 |
| 123 | + ``angle3`` angleA=90,angleB=0 |
| 124 | + ``arc`` angleA=0,angleB=0,armA=None,armB=None,rad=0.0 |
| 125 | + ``arc3`` rad=0.0 |
| 126 | + ``bar`` armA=0.0,armB=0.0,fraction=0.3,angle=None |
| 127 | + ========== ============================================= |
| 128 | + |
| 129 | +Note that "3" in ``angle3`` and ``arc3`` is meant to indicate that the |
| 130 | +resulting path is a quadratic spline segment (three control |
| 131 | +points). As will be discussed below, some arrow style option only can |
| 132 | +be used when the connecting path is a quadratic spline. |
| 133 | + |
| 134 | +The behavior of each connection style is (limitedly) demonstrated in the |
| 135 | +example below. (Warning : The behavior of the ``bar`` style is currently not |
| 136 | +well defined, it may be changed in the future). |
| 137 | + |
| 138 | +.. plot:: users/plotting/examples/connectionstyle_demo.py |
| 139 | + |
| 140 | + |
| 141 | +The connecting path (after clipping and shrinking) is then mutated to |
| 142 | +an arrow patch, according to the given ``arrowstyle``. |
| 143 | + |
| 144 | + ========== ============================================= |
| 145 | + Name Attrs |
| 146 | + ========== ============================================= |
| 147 | + ``-`` None |
| 148 | + ``->`` head_length=0.4,head_width=0.2 |
| 149 | + ``-[`` widthB=1.0,lengthB=0.2,angleB=None |
| 150 | + ``-|>`` head_length=0.4,head_width=0.2 |
| 151 | + ``<-`` head_length=0.4,head_width=0.2 |
| 152 | + ``<->`` head_length=0.4,head_width=0.2 |
| 153 | + ``<|-`` head_length=0.4,head_width=0.2 |
| 154 | + ``<|-|>`` head_length=0.4,head_width=0.2 |
| 155 | + ``fancy`` head_length=0.4,head_width=0.4,tail_width=0.4 |
| 156 | + ``simple`` head_length=0.5,head_width=0.5,tail_width=0.2 |
| 157 | + ``wedge`` tail_width=0.3,shrink_factor=0.5 |
| 158 | + ========== ============================================= |
| 159 | + |
| 160 | +.. plot:: mpl_examples/pylab_examples/fancyarrow_demo.py |
| 161 | + |
| 162 | +Some arrowstyles only work with connection style that generates a |
| 163 | +quadratic-spline segment. They are ``fancy``, ``simple``, and ``wedge``. |
| 164 | +For these arrow styles, you must use "angle3" or "arc3" connection |
| 165 | +style. |
| 166 | + |
| 167 | +If the annotation string is given, the patchA is set to the bbox patch |
| 168 | +of the text by default. |
| 169 | + |
| 170 | +.. plot:: users/plotting/examples/annotate_simple02.py |
| 171 | + |
| 172 | +As in the text command, a box around the text can be drawn using |
| 173 | +the ``bbox`` argument. |
| 174 | + |
| 175 | +.. plot:: users/plotting/examples/annotate_simple03.py |
| 176 | + |
| 177 | +By default, the starting point is set to the center of the text |
| 178 | +extent. This can be adjusted with ``relpos`` key value. The values |
| 179 | +are normalized to the extent of the text. For example, (0,0) means |
| 180 | +lower-left corner and (1,1) means top-right. |
| 181 | + |
| 182 | +.. plot:: users/plotting/examples/annotate_simple04.py |
| 183 | + |
| 184 | + |
| 185 | +Using ConnectorPatch |
| 186 | +==================== |
| 187 | + |
| 188 | +The ConnectorPatch is like an annotation without a text. While the |
| 189 | +annotate function is recommended in most of situation, the |
| 190 | +ConnectorPatch is useful when you want to connect points in different |
| 191 | +axes. :: |
| 192 | + |
| 193 | + from matplotlib.patches import ConnectionPatch |
| 194 | + xy = (0.2, 0.2) |
| 195 | + con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data", |
| 196 | + axesA=ax1, axesB=ax2) |
| 197 | + ax2.add_artist(con) |
| 198 | + |
| 199 | +The above code connects point xy in data coordinate of ``ax1`` to |
| 200 | +point xy int data coordiante of ``ax2``. Here is a simple example. |
| 201 | + |
| 202 | +.. plot:: users/plotting/examples/connect_simple01.py |
| 203 | + |
| 204 | + |
| 205 | +While the ConnectorPatch instance can be added to any axes, but you |
| 206 | +may want it to be added to the axes in the latter (?) of the axes |
| 207 | +drawing order to prevent overlap (?) by other axes. |
| 208 | + |
| 209 | + |
| 210 | +Placing Artist at the anchored location of the Axes |
| 211 | +=================================================== |
| 212 | + |
| 213 | +There are class of artist that can be placed at the anchored location |
| 214 | +of the Axes. A common example is the legend. This type of artists can |
| 215 | +be created by using the OffsetBox class. A few predefined classes are |
| 216 | +available in ``mpl_toolkits.axes_grid.anchored_artists``. :: |
| 217 | + |
| 218 | + from mpl_toolkits.axes_grid.anchored_artists import AnchoredText |
| 219 | + at = AnchoredText("Figure 1a", |
| 220 | + prop=dict(size=8), frameon=True, |
| 221 | + loc=2, |
| 222 | + ) |
| 223 | + at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") |
| 224 | + ax.add_artist(at) |
| 225 | + |
| 226 | + |
| 227 | +.. plot:: users/plotting/examples/anchored_box01.py |
| 228 | + |
| 229 | + |
| 230 | +The *loc* keyword has same meaning as in the legend command. |
| 231 | + |
| 232 | +A simple application is when the size of the artist (or collection of |
| 233 | +artists) is knwon in pixel size during the time of creation. For |
| 234 | +example, If you want to draw a circle with fixed size of 20 pixel x 20 |
| 235 | +pixel (radius = 10 pixel), you can utilize |
| 236 | +``AnchoredDrawingArea``. The instance is created with a size of the |
| 237 | +drawing area (in pixel). And user can add arbitrary artist to the |
| 238 | +drawing area. Note that the extents of the artists that are added to |
| 239 | +the drawing area has nothing to do with the placement of the drawing |
| 240 | +area itself. The initial size only matters. :: |
| 241 | + |
| 242 | + from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea |
| 243 | + |
| 244 | + ada = AnchoredDrawingArea(20, 20, 0, 0, |
| 245 | + loc=1, pad=0., frameon=False) |
| 246 | + p1 = Circle((10, 10), 10) |
| 247 | + ada.drawing_area.add_artist(p1) |
| 248 | + p2 = Circle((30, 10), 5, fc="r") |
| 249 | + ada.drawing_area.add_artist(p2) |
| 250 | + |
| 251 | +The artists that are added to the drawing area should not have |
| 252 | +transform set (they will be overridden) and the dimension of those |
| 253 | +artists are interpreted as a pixel coordinate, i.e., the radius of the |
| 254 | +circles in above example are 10 pixel and 5 pixel, respectively. |
| 255 | + |
| 256 | +.. plot:: users/plotting/examples/anchored_box02.py |
| 257 | + |
| 258 | +Sometimes, you want to your artists scale with data coordinate (or |
| 259 | +other coordinate than canvas pixel). You can use |
| 260 | +``AnchoredAuxTransformBox`` class. This is similar to |
| 261 | +``AnchoredDrawingArea`` except that the extent of the artist is |
| 262 | +determined during the drawing time respecting the specified transform. :: |
| 263 | + |
| 264 | + from mpl_toolkits.axes_grid.anchored_artists import AnchoredAuxTransformBox |
| 265 | + |
| 266 | + box = AnchoredAuxTransformBox(ax.transData, loc=2) |
| 267 | + el = Ellipse((0,0), width=0.1, height=0.4, angle=30) # in data coordinates! |
| 268 | + box.drawing_area.add_artist(el) |
| 269 | + |
| 270 | +The ellipse in the above example will have width and height |
| 271 | +corresponds to 0.1 and 0.4 in data coordinate and will be |
| 272 | +automatically scaled when the view limits of the axes change. |
| 273 | + |
| 274 | +.. plot:: users/plotting/examples/anchored_box03.py |
| 275 | + |
| 276 | +As in the legend, the bbox_to_anchor argument can be set. Using the |
| 277 | +HPacker and VPacker, you can have an arrangement(?) of artist as in the |
| 278 | +legend (as a matter of fact, this is how the legend is created). |
| 279 | + |
| 280 | +.. plot:: users/plotting/examples/anchored_box04.py |
| 281 | + |
| 282 | +Note that unlike the legend, the ``bbox_transform`` is set |
| 283 | +to IdentityTransform by default. |
| 284 | + |
| 285 | +Advanced Topics |
| 286 | +*************** |
| 287 | + |
| 288 | +Zoom effect between Axes |
| 289 | +======================== |
| 290 | + |
| 291 | +mpl_toolkits.axes_grid.inset_locator defines some patch classs useful |
| 292 | +for interconnect two axes. Understanding the code requires some |
| 293 | +knowledge of how mpl's transform works. But, utilizing it will be |
| 294 | +straight forward. |
| 295 | + |
| 296 | + |
| 297 | +.. plot:: mpl_examples/pylab_examples/axes_zoom_effect.py |
| 298 | + |
| 299 | + |
| 300 | +Define Custom BoxStyle |
| 301 | +====================== |
| 302 | + |
| 303 | +You can use a custom box style. The value for the ``boxstyle`` can be a |
| 304 | +callable object in following forms.:: |
| 305 | + |
| 306 | + def __call__(self, x0, y0, width, height, mutation_size, |
| 307 | + aspect_ratio=1.): |
| 308 | + """ |
| 309 | + Given the location and size of the box, return the path of |
| 310 | + the box around it. |
| 311 | + |
| 312 | + - *x0*, *y0*, *width*, *height* : location and size of the box |
| 313 | + - *mutation_size* : a reference scale for the mutation. |
| 314 | + - *aspect_ratio* : aspect-ration for the mutation. |
| 315 | + """ |
| 316 | + path = ... |
| 317 | + return path |
| 318 | + |
| 319 | +Here is a complete example. |
| 320 | + |
| 321 | +.. plot:: users/plotting/examples/custom_boxstyle01.py |
| 322 | + |
| 323 | +However, it is recommended that you derive from the |
| 324 | +matplotlib.patches.BoxStyle._Base as demonstrated below. |
| 325 | + |
| 326 | +.. plot:: users/plotting/examples/custom_boxstyle02.py |
| 327 | + :include-source: |
| 328 | + |
| 329 | + |
| 330 | +Similarly, you can define custom ConnectionStyle and Custome ArrowStyle. |
| 331 | +See the source code of ``lib/matplotlib/patches.py`` and check |
| 332 | +how each style class is defined. |
0 commit comments