{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Making highly customizable plot of Signal2D using matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows how to make a publication quality figure of a HyperSpy Signal2D object, where we use the matplotlib figure and subplot object to customize the plot." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Requires" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HyperSpy 1.3 or later\n", "\n", "Matplotlib 2.0 or later" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Credits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 2017/09/10 Magnus Nord. Initial version" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making a Signal2D" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "import numpy as np\n", "import hyperspy.api as hs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Firstly, we make a 2D signal by generating a numpy array" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "image_data = np.arange(10000).reshape(100, 100) + np.random.random(size=(100, 100))*5000" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HyperSpy signals can be made by passing a numpy array to a HyperSpy signal class." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "s = hs.signals.Signal2D(image_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Making the matplotlib figure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to the pyplot module, we also need to import the size bar, font manager and patheffects. As the three latter modules are not present in the main pyplot module." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar\n", "import matplotlib.font_manager as fm\n", "import matplotlib.patheffects as patheffects" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The figsize argument is used to make sure the figure has the same length in both the x- and y-directions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(6, 6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Image data is normally not plotted with ticks on the axis, so we disable these using `set_axis_off`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "ax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plotting the image data itself is done by directly accessing the numpy array in the signal: `s.data`.\n", "\n", "The calibrated size of the signal is contained in s.axes_manager.signal_extent, which is needed if we want the scalebar to have the correct size." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "cax = ax.imshow(s.data, extent=s.axes_manager.signal_extent, cmap='viridis')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "cax can be used to set color limits for the plotted image" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "cax.set_clim(200, 11000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scalebar" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In matplotlib, what is normally referred to as a scalebar, is called an AnchoredSizeBar.\n", "\n", "This AnchoredSizeBar takes a large amount of arguments, which can be seen in the docstring by running AnchoredSizeBar?\n", "\n", "To set the size of the text in the scalebar, the fontproperties object is used." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fontprops = fm.FontProperties(size=30)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "scalebar = AnchoredSizeBar(\n", " transform=ax.transData, size=30, label='30 nm',\n", " loc=4, frameon=False,\n", " color='black', size_vertical=3,\n", " label_top=False,\n", " fontproperties=fontprops)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To increase the contrast of the scalebar text, use the patheffects feature. This adds a border around the text. The capstyle argument needs to be used to get a continuous border." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "scalebar.txt_label._text.set_path_effects([patheffects.withStroke(linewidth=2, foreground='white', capstyle=\"round\")])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.add_artist(scalebar)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next line is needed due to a bug in the current version of matplotlib (2.0.2), which leads to the scalebar being not solid. This has been fixed in the current development version." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "scalebar.size_bar.get_children()[0].fill = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrow annotation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using ax.annotate, we add an arrow point to some xy coordinate. The xytext specifies either the tail or the text of the arrow. If no text is wanted, use s=''." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "annotation = ax.annotate(s=\"An arrow\", xy=(20, 70), xytext=(40, 50), \n", " arrowprops={'arrowstyle':'->'},\n", " path_effects=[patheffects.withStroke(linewidth=2, foreground='white', capstyle=\"round\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To increase the contrast of arrow, add a border in a different color." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "scrolled": true }, "outputs": [], "source": [ "annotation.arrow_patch.set_path_effects([patheffects.withStroke(linewidth=2, foreground='white', capstyle=\"round\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Text" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.text(0.05, 0.95, \"Some random data\", size=20, transform=ax.transAxes,\n", " verticalalignment='top', horizontalalignment='left', color='white',\n", " path_effects=[patheffects.withStroke(linewidth=3, foreground='black', capstyle=\"round\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Saving the figure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For figures of image data, it is possible to make the figure borderless by using the subplot_adjust.\n", "\n", "Note, this only works well if the axis ticks are deactivated." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "fig.subplots_adjust(left=0., bottom=0., right=1., top=1.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig.savefig(\"image_data_figure.png\", dpi=300)" ] } ], "metadata": { "kernelspec": { "display_name": "hyperspy_dev", "language": "python", "name": "hyperspy_dev" }, "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 }