{"cells":[{"cell_type":"markdown","id":"f548e7e8","metadata":{"id":"f548e7e8"},"source":["# How to embed an image into a Jupyter notebook markdown cell\n","\n"," * Creator(s): Chris Slocum\n"," * Affiliation: NOAA Center for Satellite Applications and Research\n"," * History:\n"," * 4 April 2023 — initial version\n","\n","---\n","\n","\n","## Overview\n","\n","In creating an effective notebook, content from external sources is useful to assist the reader in understanding the material. Often, this content is from graphical material (e.g., an image or schematic). However, including graphics into a notebook is easier said than done. This notebook provide recommendations on how to embed graphics in notebook via Base64 encoding.\n","\n","### Prerequisite\n","\n","To successfully complete this notebook, a basic understanding of the following is helpful but not necessary.\n","* Markdown language syntax\n","* Python programming standard library\n","\n","### Learning Outcome\n","\n","* Embed images into Jupyter notebook\n","\n","---"]},{"cell_type":"markdown","id":"d41d615a","metadata":{"id":"d41d615a"},"source":["## Adding graphics to notebooks\n","\n","There are four ways to add graphics to a Jupyter notebook:\n","1. Markdown (e.g., ``![alternative text](URL-to-image/Path-to-image)``)\n","2. HTML (e.g., ``\"Alternative``)\n","3. IPython (e.g., ``from IPython import display; display.Image(\"URL-to-image/Path-to-image\"``)\n","4. Base64 encoding\n","\n","The first three require that the image be accessible on a remote server or that the image be saved locally. The former has a permanency issue, and the latter depends on the place hosting the notebook.\n","\n","To avoid these limitations, we will use the Base64 encoding option. This option converts the graphic binary from the native image format (e.g., png, gif, jpeg) and converts it into ASCII characters. To do this, we will walk through an example.\n","\n","### Using Base64 encoding\n","For our example, we will use the following remote image file hosted on an HTTPS web server (in this case, tropical cyclone centric longwave infrared satellite from the Regional and Mesoscale Meteorology Branch of the NOAA Center for Satellite Applications and Research).\n","\n","``https://rammb-data.cira.colostate.edu/tc_realtime/products/storms/2023sh17/4kmirimg/2023sh17_4kmirimg_202303311800.gif``\n","\n","Using the HTML rendering in the markdown cell, this image renders as shown below."]},{"cell_type":"markdown","id":"473d9370","metadata":{"id":"473d9370"},"source":["
\n"," \"Himawari-9\n","
Himawari-9 infrared imagery of Cyclone Herman on 1800 UTC 31 March 2023 from the remote server (Credit: NOAA/STAR Tropical Cyclone Characteristics Team).
\n","
"]},{"cell_type":"markdown","id":"17cee094","metadata":{"id":"17cee094"},"source":["### Reading the file\n","\n","To read this file into Python, we will use the `urlopen` function from the [urllib.request](https://docs.python.org/3/library/urllib.request.html) module from the Python standard library."]},{"cell_type":"code","execution_count":null,"id":"b374686c","metadata":{"id":"b374686c"},"outputs":[],"source":["# get the urlopen function\n","from urllib.request import urlopen\n","# define the url for out image\n","url = \"https://rammb-data.cira.colostate.edu/tc_realtime/products/storms/2023sh17/4kmirimg/2023sh17_4kmirimg_202303311800.gif\"\n","# use the `with` statement to make the request and read the file contents\n","with urlopen(url) as response:\n"," contents = response.read()"]},{"cell_type":"markdown","id":"dcc94148","metadata":{"id":"dcc94148"},"source":["Here, we opened the remote file from a web server using HTTPS protocol. But, the same can be done for a local file. The above block of code would simplify to:\n","\n","```python\n","filename = \"2023sh17_4kmirimg_202303311800.gif\"\n","with open(filename, mode='rb') as fid:\n"," contents = fid.read()\n","```\n","\n","In both cases, we'd get Python [bytes literals](https://docs.python.org/3/library/stdtypes.html#bytes) if we printed `contents`. While we need byte literals output, this format of the output is not useful for our application. Instead, we need to encode the data into a format that is understandable to the HTML `` tag.\n"]},{"cell_type":"markdown","id":"09340029","metadata":{"id":"09340029"},"source":["### Encoding the file contents\n","To encode the file, we use the `b64encode` function from the [base64](https://docs.python.org/3/library/base64.html) module in the Python standard library."]},{"cell_type":"code","execution_count":null,"id":"17a6b206","metadata":{"id":"17a6b206"},"outputs":[],"source":["# Get base64\n","import base64\n","# Encode the `contents` bytes literals using b64encode\n","encoded_contents = base64.b64encode(contents)"]},{"cell_type":"markdown","id":"66c74ac5","metadata":{"id":"66c74ac5"},"source":["`encoded_contents` contains bytes literals using the base 64 encoding. To see the output, uncomment the print statement below."]},{"cell_type":"code","execution_count":null,"id":"2c069ff5","metadata":{"id":"2c069ff5"},"outputs":[],"source":["# Warning: prints a lot of text\n","#print(encoded_contents)"]},{"cell_type":"markdown","source":["Note that you will want to do this step elsewhere and then copy the output from `encoded_contents` into your notebook. You do not want to include the snippet of code above in your actual notebook."],"metadata":{"id":"ktkv9hrltKGY"},"id":"ktkv9hrltKGY"},{"cell_type":"markdown","id":"22b24f17","metadata":{"id":"22b24f17"},"source":["### Displaying the encoded contents in a Markdown cell\n","\n","Recall, you will only use the output from the print statement of `encode_contents` from the section above. From that print statement, you want to copy the `encode_contents` bytes literals (i.e., the content between the ``b' '``) to include in your notebook.\n","\n","In your notebook, you will use the following HTML syntax for embedding an image in a text cell:\n","\n","```html\n","
\n"," \"Alternative\n","
Figure caption text (Credit: Add credit text).
\n","\n"," \"Himawari-9\n","
Himawari-9 infrared imagery of Cyclone Herman on 1800 UTC 31 March 2023 embedded in the Jupyter notebook (Credit: NOAA/STAR Tropical Cyclone Characteristics Team).
\n","
"]},{"cell_type":"markdown","id":"5b4d0e34","metadata":{"id":"5b4d0e34"},"source":["## References\n","* Python, 2023: The Python Standard Library. Accessed 4 April 2023, [https://docs.python.org/3/library/index.html](https://docs.python.org/3/library/index.html).\n","\n","## Metadata\n","* Language / package(s): Python / Markdown\n","* Domain: general, training\n","* Application keywords: notebooks\n","* Geophysical keywords: N/A\n","* AI keywords: N/A\n","\n","## Disclaimer\n","This Jupyter notebook is a scientific product and is not official communication of the National Oceanic and Atmospheric Administration, or the United States Department of Commerce. All NOAA Jupyter notebooks are provided on an 'as is' basis and the user assumes responsibility for its use. Any claims against the Department of Commerce or Department of Commerce bureaus stemming from the use of this Jupyter notebook will be governed by all applicable Federal law. Any reference to specific commercial products, processes, or services by service mark, trademark, manufacturer, or otherwise does not constitute or imply their endorsement, recommendation or favoring by the Department of Commerce. The Department of Commerce seal and logo, or the seal and logo of a DOC bureau, shall not be used in any manner to imply endorsement of any commercial product or activity by DOC or the United States Government."]},{"cell_type":"code","execution_count":null,"id":"52a7e334","metadata":{"id":"52a7e334"},"outputs":[],"source":[]}],"metadata":{"kernelspec":{"display_name":"Python 3 (ipykernel)","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.9.5"},"colab":{"provenance":[]}},"nbformat":4,"nbformat_minor":5}