{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Boxfill Tutorial \n", "\n", "This is an in-depth introduction to the boxfill graphics method in VCS. It breaks down each of the important attributes of the graphics method, how to use them, and what effects they have on your plots.\n", "\n", "The CDAT software was developed by LLNL. This tutorial was written by Charles Doutriaux. This work was performed under the auspices of the U.S. Department of Energy by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344.\n", "\n", "[Download the Jupyter Notebook](boxfill.ipynb)" ] }, { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup\n", "\n", "[Back to Top](#top)\n", "\n", "This is just the normal setup for a plot in VCS; we get the data, retrieve a variable, initialize our canvas, and plot a basic boxfill." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import vcs, cdms2\n", "import os\n", "if not os.path.exists(vcs.sample_data):\n", " vcs.download_sample_data_files()\n", "f = cdms2.open(os.path.join(vcs.sample_data, \"clt.nc\"))\n", "s = f('clt')\n", "x = vcs.init(bg=True)\n", "box = vcs.createboxfill()\n", "x.plot(s, box)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> #### Tip 1:\n", "> You can use the list() function on most VCS objects to see what their attributes are" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.list()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Attributes\n", "\n", "[Back to Top](#top)\n", "\n", "So, let's begin working our way through the attributes in the `list()` output.\n", "\n", "\n", "### box.projection\n", "\n", "[Back to Top](#top)\n", "\n", "Projections change the mappings of spherical coordinates (longitude and latitude) to cartesian coordinates (X/Y). The `\"linear\"` projection maps each degree of lon/lat to an X/Y point directly, so each degree is equidistant, but that does cause some distortion of landmasses/data in parts of the globe. To get a good look at the north/south pole, a better projection to use is the `\"polar\"` projection. Each projection can be customized to get the exact mapping you want, but that's a subject for a different tutorial." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.projection = \"polar\"\n", "x.clear()\n", "x.plot(s, box)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.(x|y)ticlabels(1|2)\n", "\n", "[Back to Top](#top)\n", "\n", "These attributes control where tick marks appear on the axes of the plot. You pass in a dictionary that maps axis values (lon/lat) to string labels.\n", "\n", "> *Note:* You can also set ticlabels to the name of a vcs \"List\", which are a series of predefined mappings that you can look at using `vcs.listelements(\"list\")`\n", "\n", "\n", "VCS will determine the correct position (in X/Y) for your axis label and tick mark to appear, and place them there. In the below example, we'll set the positions for a few nice landmarks in lon/lat, and be able to see their locations on the map. `xticlabels1` controls tick marks on the bottom X axis; `xticlabels2` controls the marks on the top of the axis. `yticlabels1` controls the left Y axis, and `yticlabels2` controls the right Y axis.\n", "\n", "> #### Tip 2:\n", "> `xticlabels2` and `yticlabels2` don't display labels when using the default template in VCS. To make them appear, you have to create a new template (`vcs.createtemplate()`) and set the `xlabel2` and/or `ylabel2` priority to >0 (`xlabel2.priority = 1`). Priority of 0 on a template attribute prevents that attribute from being displayed; a handy way to hide any element of the plot that you don't want displayed!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.projection = \"linear\"\n", "box.xticlabels1 = {0: \"Prime Meridian\", -121.7680: \"Livermore\", 37.6173: \"Moscow\"}\n", "box.yticlabels1 = {0: \"Eq.\", 37.6819: \"L\", 55.7558: \"M\"}\n", "x.clear()\n", "x.plot(s, box)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.(x|y)mtics(1|2)\n", "\n", "[Back to Top](#top)\n", "\n", "The **m**tics are \"mini\" ticks; they are meant to be smaller, intermediate tickmarks without labels that show up between the larger ticks. The default template does not display them; you have to use a custom template and set their priority to 1. Once again, `xmtics1` points to the bottom, `xmtics2` the top, `ymtics1` the left, and `ymtics2` the right. Below, we use the names of some of the predefined VCS lists that were mentioned in the previous section." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.xmtics1 = \"lon30\" # Every 30deg\n", "box.ymtics1 = \"lat20\" # Every 20deg\n", "template = vcs.createtemplate()\n", "template.xmintic1.priority = 1\n", "template.ymintic1.priority = 1\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.datawc_(x|y)(1|2)\n", "\n", "[Back to Top](#top)\n", "\n", "The `datawc_*` attributes control the \"world coordinates\" of the plot. That is, they describe the section of the data to display in the plot. You specify values in the coordinate space of the axes of your data; for most of us, that means lon/lat values. This then allows you to reduce the displayed portion of your data to a specific area. Here, we'll zoom in on Livermore, and use the mini ticks to show the exact point that Livermore is located at. X1 is the start of the x axis, X2 is the end of the x axis; the same applies to Y1 and Y2." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.datawc_x1 = -125\n", "box.datawc_x2 = -117\n", "box.datawc_y1 = 34\n", "box.datawc_y2 = 38\n", "box.xmtics1 = box.xticlabels1\n", "box.ymtics1 = box.yticlabels1\n", "box.xticlabels1 = \"*\"\n", "box.yticlabels1 = \"*\"\n", "save_xmin_y1 = template.xmintic1.y1\n", "save_xmin_y2 = template.xmintic1.y2\n", "save_ymin_x1 = template.ymintic1.x1\n", "save_ymin_x2 = template.ymintic1.x2\n", "template.xmintic1.y1 = template.data.y1\n", "template.xmintic1.y2 = template.data.y2\n", "template.ymintic1.x1 = template.data.x1\n", "template.ymintic1.x2 = template.data.x2\n", "x.clear()\n", "x.plot(s, box, template, ratio=\"autot\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> #### Tip 3:\n", "> 1e20 is the \"automatic\" value for datawc_* attributes, and uses the entire dataset as provided to the plot call.\n", "\n", "### box.level_(1|2)\n", "\n", "[Back to Top](#top)\n", "\n", "`level_1` and `level_2` control what the minimum and maximum values of data displayed in the plot will be. They allow you to specify the exact range of values to use in the legend of the plot. Values outside of those ranges will be left blank on the plot. To reset them, set them to 1e20." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.datawc_x1 = 1e20\n", "box.datawc_x2 = 1e20\n", "box.datawc_y1 = 1e20\n", "box.datawc_y2 = 1e20\n", "box.xmtics1 = None\n", "box.ymtics1 = None\n", "box.level_1 = 20\n", "box.level_2 = 40\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.color_(1|2)\n", "\n", "[Back to Top](#top)\n", "\n", "`color_1` and `color_2` control which parts of the colormap to use for the plot. It defaults to the full range of the colormap (0-255), but if you use fewer colors, it will break up your data into precisely that many discrete colors. You assign indices from the colormap." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.color_1 = 60\n", "box.color_2 = 80\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.boxfill_type\n", "\n", "[Back to Top](#top)\n", "\n", "`boxfill_type` allows you to fully customize your boxfill. If you assign a `boxfill_type` of \"custom\", `level_1`, `level_2`, `color_1`, and `color_2` are ignored, and instead the attributes `levels`, `fillareacolors`, `fillareaindices`, `fillareaopacity`, and `fillareastyle` are used. We'll go into those more below. Since we're now using `levels` instead of the `level_*` and `color_*` attributes, VCS will attempt to break your data values into a nice range with 10-12 colors by default, which you'll learn to override shortly." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Restore template min tics\n", "template.xmintic1.y1 = save_xmin_y1\n", "template.xmintic1.y2 = save_xmin_y2\n", "template.ymintic1.x1 = save_ymin_x1\n", "template.ymintic1.x2 = save_ymin_x2\n", "\n", "box.level_1 = 1e20\n", "box.level_2 = 1e20\n", "box.color_1 = 0\n", "box.color_2 = 255\n", "box.boxfill_type = \"custom\"\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.levels and box.fillareacolors\n", "\n", "[Back to Top](#top)\n", "\n", "`levels` describes the bounds for each \"bin\" that your data will be slotted into. To recreate the above levels, you'd do this:\n", "\n", "```\n", "box.levels = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n", "```\n", "\n", "`fillareacolors` sets the color for each bin. Usually, the easiest way to choose colors is to let VCS do it for you:\n", "\n", "```\n", "box.fillareacolors = vcs.utils.getcolors(box.levels)\n", "```\n", "\n", "but you can do it manually as well:\n", "\n", "```\n", "box.fillareacolors = [0, 20, 40, 60, 80, 100, 120, 140, 160, 180]\n", "```\n", "\n", "You use any of VCS' color syntaxes for setting the colors here:\n", "\n", "\n", "```\n", "# In order: colormap index, color string, RGB tuple (0-100 for each value), RGBA tuple (0-100 for each value)\n", "box.fillareacolors = [0, \"red\", (85, 12, 3), (100, 100, 100, 50)]\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.levels = [10, 15, 20, 25, 30, 35]\n", "box.fillareacolors = [0, 20, 40, 60, 80]\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.fillareastyle and box.fillareaindices\n", "\n", "[Back to Top](#top)\n", "\n", "If you want even more control of the appearance of your plot, you can use the pattern/hatch system. `fillareastyle` has three possible values: `solid`, `hatch`, and `pattern`. `pattern` will have the same behavior as `hatch`, but be in black/white. `fillareaindices` let you choose what pattern to use for the fill; new patterns are added from time to time, so you will want to experiment and find ones you like." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.levels = [0, 25, 50, 75, 100]\n", "box.fillareacolors = [0, 60, 120, 180]\n", "box.fillareastyle = \"hatch\" # or pattern\n", "box.fillareaindices = [1, 2, 3, 4]\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.fillareaopacity\n", "\n", "[Back to Top](#top)\n", "\n", "`fillareaopacity` lets you control the opacity of each of your bins. While you can manually set opacity using the `fillareacolors` ability to use RGBA values (or using a colormap with transparent colors), this will allow you to set the opacity of your bins from a single place. It takes a list of values the length of the number of bins you want, where the values are between 0 and 100 (100 being fully opaque)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.fillareaopacity = [50, 100, 100, 25] # 0-100\n", "box.fillareastyle = \"pattern\"\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.legend\n", "\n", "[Back to Top](#top)\n", "\n", "`legend` controls which levels are labeled in the legend. You set it to a dictionary that has keys from your range of values of your data, and values of the label you want displayed. VCS will place the labels at the appropriate point above/to the side of the legend.\n", "\n", "> #### Tip 4:\n", "> If you set the geometry of the legend (`template.legend.x1`, `template.legend.x2`, `template.legend.y1`, `template.legend.y2`) in the template to be taller than it is wide, the legend will draw itself vertically instead of horizontally (labels will be to the side, and the ticks will be across it in the appropriate direction)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.legend = {15: \"This is a point\", 30: \"This is another point\", 60: \"One more\"}\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.ext_1 and box.ext_2\n", "\n", "[Back to Top](#top)\n", "\n", "`ext_1` and `ext_2` mark the edge levels as open or closed; if set to true, any values below the lowest level or above the highest will be contained in the lowest or highest level." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box.ext_1 = True\n", "box.ext_2 = True\n", "box.levels = [25, 50, 75]\n", "box.fillareacolors = [100, 200]\n", "box.fillareaindices = [1, 2]\n", "box.fillareaopacity = [50, 100]\n", "x.clear()\n", "x.plot(s, box, template)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### box.missing\n", "\n", "[Back to Top](#top)\n", "\n", "`missing` is the color used for masked out data. A mask can be applied to any variable using the `variable.mask` attribute, or using the `masked` comparison functions in `cdms2.MV2`; a number of other ways (such as using the `fill_value` attribute of a variable in a NetCDF file) will also mark a value as \"missing\"." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "masked = cdms2.MV2.masked_less_equal(s, 30)\n", "box.missing = \"darkgrey\"\n", "box.fillareastyle = \"solid\"\n", "box.levels = [a for a in range(0, 101, 10)]\n", "box.fillareacolors = vcs.getcolors(box.levels)\n", "box.fillareaindices = [1]\n", "box.fillareaopacity = [100]\n", "box.ext_1 = False\n", "box.ext_2 = False\n", "x.clear()\n", "x.plot(masked, box, template)" ] } ], "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.7.3" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }