{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Artists as annotations\n\n`.AnnotationBbox` facilitates using arbitrary artists as annotations, i.e. data at\nposition *xy* is annotated by a box containing an artist at position *xybox*. The\ncoordinate systems for these points are set via the *xycoords* and *boxcoords*\nparameters, respectively; see the *xycoords* and *textcoords* parameters of\n`.Axes.annotate` for a full listing of supported coordinate systems.\nThe box containing the artist is a subclass of `.OffsetBox`, which is a container\nartist for positioning an artist relative to a parent artist.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from pathlib import Path\n\nimport PIL\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom matplotlib import get_data_path\nfrom matplotlib.offsetbox import AnnotationBbox, DrawingArea, OffsetImage, TextArea\nfrom matplotlib.patches import Annulus, Circle, ConnectionPatch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Text\n\n`.AnnotationBbox` supports positioning annotations relative to data, Artists, and\ncallables, as described in `annotations`. The `.TextArea` is used to create a\ntextbox that is not explicitly attached to an axes, which allows it to be used for\nannotating figure objects. When annotating an axes element (such as a plot) with text,\nuse `.Axes.annotate` because it will create the text artist for you.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, axd = plt.subplot_mosaic([['t1', '.', 't2']], layout='compressed')\n\n# Define a 1st position to annotate (display it with a marker)\nxy1 = (.25, .75)\nxy2 = (.75, .25)\naxd['t1'].plot(*xy1, \".r\")\naxd['t2'].plot(*xy2, \".r\")\naxd['t1'].set(xlim=(0, 1), ylim=(0, 1), aspect='equal')\naxd['t2'].set(xlim=(0, 1), ylim=(0, 1), aspect='equal')\n\n# Draw a connection patch arrow between the points\nc = ConnectionPatch(xyA=xy1, xyB=xy2,\n coordsA=axd['t1'].transData, coordsB=axd['t2'].transData,\n arrowstyle='->')\nfig.add_artist(c)\n\n# Annotate the ConnectionPatch position ('Test 1')\noffsetbox = TextArea(\"Test 1\")\n\n# place the annotation above the midpoint of c\nab1 = AnnotationBbox(offsetbox,\n xy=(.5, .5),\n xybox=(0, 30),\n xycoords=c,\n boxcoords=\"offset points\",\n arrowprops=dict(arrowstyle=\"->\"),\n bboxprops=dict(boxstyle=\"sawtooth\"))\nfig.add_artist(ab1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Images\nThe `.OffsetImage` container facilitates using images as annotations\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n# Define a position to annotate\nxy = (0.3, 0.55)\nax.scatter(*xy, s=200, marker='X')\n\n# Annotate a position with an image generated from an array of pixels\narr = np.arange(100).reshape((10, 10))\nim = OffsetImage(arr, zoom=2, cmap='viridis')\nim.image.axes = ax\n\n# place the image NW of xy\nab = AnnotationBbox(im, xy=xy,\n xybox=(-50., 50.),\n xycoords='data',\n boxcoords=\"offset points\",\n pad=0.3,\n arrowprops=dict(arrowstyle=\"->\"))\nax.add_artist(ab)\n\n# Annotate the position with an image from file (a Grace Hopper portrait)\nimg_fp = Path(get_data_path(), \"sample_data\", \"grace_hopper.jpg\")\nwith PIL.Image.open(img_fp) as arr_img:\n imagebox = OffsetImage(arr_img, zoom=0.2)\n\nimagebox.image.axes = ax\n\n# place the image SE of xy\nab = AnnotationBbox(imagebox, xy=xy,\n xybox=(120., -80.),\n xycoords='data',\n boxcoords=\"offset points\",\n pad=0.5,\n arrowprops=dict(\n arrowstyle=\"->\",\n connectionstyle=\"angle,angleA=0,angleB=90,rad=3\")\n )\n\nax.add_artist(ab)\n\n# Fix the display limits to see everything\nax.set(xlim=(0, 1), ylim=(0, 1))\n\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arbitrary Artists\n\nMultiple and arbitrary artists can be placed inside a `.DrawingArea`.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# make this the thumbnail image\nfig, ax = plt.subplots()\n\n# Define a position to annotate\nxy = (0.05, 0.5)\nax.scatter(*xy, s=500, marker='X')\n\n# Annotate the position with a circle and annulus\nda = DrawingArea(120, 120)\np = Circle((30, 30), 25, color='C0')\nda.add_artist(p)\nq = Annulus((65, 65), 50, 5, color='C1')\nda.add_artist(q)\n\n\n# Use the drawing area as an annotation\nab = AnnotationBbox(da, xy=xy,\n xybox=(.55, xy[1]),\n xycoords='data',\n boxcoords=(\"axes fraction\", \"data\"),\n box_alignment=(0, 0.5),\n arrowprops=dict(arrowstyle=\"->\"),\n bboxprops=dict(alpha=0.5))\n\nax.add_artist(ab)\n\n# Fix the display limits to see everything\nax.set(xlim=(0, 1), ylim=(0, 1))\n\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. admonition:: References\n\n The use of the following functions, methods, classes and modules is shown\n in this example:\n\n - `matplotlib.offsetbox.TextArea`\n - `matplotlib.offsetbox.DrawingArea`\n - `matplotlib.offsetbox.OffsetImage`\n - `matplotlib.offsetbox.AnnotationBbox`\n\n.. tags::\n component: annotation, styling: position\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.15" } }, "nbformat": 4, "nbformat_minor": 0 }