This lesson is being piloted (Beta version)

# Visualize and publish with Python

## Overview

Teaching: 0 min
Exercises: 0 min
Questions
• How to generate interactive plots?

• How to generate animations?

• How to create publication ready jupyter notebooks?

• How to publish jupyter notebooks?

Objectives

# Map plotting

## Hexbin map example

``````import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from mpl_toolkits.basemap import Basemap
%matplotlib inline
import matplotlib.colors as colors
from numpy import array
from numpy import max
import numpy as np

plt.figure()
filename = "data/Justin/icepmag_20190118.csv"

# replace -9999 by NaN (missing values)
frame.replace(-9999.0,np.NaN, inplace=True)
# remove rows where lon or lat are missing
frame.dropna(subset=['lon', 'lat'], inplace=True)
# drop duplicates
latlons = frame[['lat', 'lon']].drop_duplicates()

map = Basemap(llcrnrlon=335,llcrnrlat=63.0,urcrnrlon=348.,urcrnrlat=67.,
resolution='i', projection='tmerc', lat_0 = 64.5, lon_0 = 340)

x,y = map(latlons['lon'].values,latlons['lat'].values)
map.hexbin(x,y, gridsize=20, mincnt=1, cmap='summer', norm=colors.LogNorm())

map.drawcoastlines()
plt.show()
``````

# Interactive plot in the Jupyter notebooks

## Add interactive button to select image

You can allow users to select themselves which plots to show:

``````import matplotlib
import matplotlib.pyplot as plt
from matplotlib import colors as c
%matplotlib inline
from mpl_toolkits.basemap import Basemap, shiftgrid
import numpy as np
import netCDF4
from ipywidgets import interact

mpl.rcParams['figure.figsize'] = [20., 10.]
def drawmap(data, title):
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))
data.z.plot.pcolormesh(
transform=ccrs.PlateCarree())
ax.set_title(data.title)
ax.coastlines()
ax.gridlines()

def myanimate(i, filenames):
dset = xr.open_dataset(filenames[i])
new_contour = drawmap(dset, 'data %03d'%(i) )
dset.close()

@interact(depth=(0,20))
def finteract(depth):
ca = myanimate(depth, filenames)
``````

We used `@interact` function to automatically creates user interface (UI) for selecting the data frame. When you pass this function as the first argument to interact along with a keyword argument (here depth=(0,20)), a slider is generated and bound to the function parameter (depth).

The name of the function `finteract` is chosen by you and can be anything.

## Create a mp4 movie

We can use a similar approach to generate `mp4` movies.

``````import matplotlib.animation as animation

def drawmap(ax, data, title):
transform=ccrs.PlateCarree())
ax.set_title(data.title)
ax.coastlines()
ax.gridlines()

def myanimate(i, ax, filenames):
ax.clear()
dset = xr.open_dataset(filenames[i])
new_contour = drawmap(ax, dset, 'data %03d'%(i) )
dset.close()

FFMpegWriter = animation.writers['ffmpeg']
metadata = dict(title='Grace data', artist='CEED workshop',
comment='Movie for sequence of images')

# Create the figure

fig = plt.figure(figsize=[20,10])  # a new figure window
ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))

ani = animation.FuncAnimation(fig, myanimate, frames=np.arange(21),
fargs=(ax, filenames), interval=100)
ani.save("grace_movie.mp4"))
``````

### Embedded animations within your jupyter notebook

The main goal here is to create animations embedded within your jupyter notebook. This is fairly simple to plot your animation within your jupyter notebook.

Let’s continue our previous example, and add the following:

``````from IPython.display import HTML
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
mpl.rcParams["animation.embed_limit"]=100

import matplotlib.animation as animation

import glob
def getint(name):
_, num = name.split('P_')
num, _ = num.split('.')
return int(num)

filenames = glob.glob("data/Grace/grd_files_to_interp/GypsumP_*.grd")

filenames = sorted(filenames, key=getint)

def drawmap(ax, data, title):
transform=ccrs.PlateCarree())
ax.set_title(data.title)
ax.coastlines()
ax.gridlines()

def myanimate(i, ax, filenames):
ax.clear()
dset = xr.open_dataset(filenames[i])
new_contour = drawmap(ax, dset, 'data %03d'%(i) )
dset.close()

FFMpegWriter = animation.writers['ffmpeg']
metadata = dict(title='Grace data', artist='CEED workshop',
comment='Movie for sequence of images')

# Create the figure
fig = plt.figure(figsize=[20,10])  # a new figure window
ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))

ani = animation.FuncAnimation(fig, myanimate, frames=np.arange(21),
fargs=(ax, filenames), interval=100)

HTML(ani.to_html5_video())
``````

or generate HTML representation of the animation:

``````HTML(ani.to_jshtml())
``````

# Create an interactive map with folium

• Documentation at [https://python-visualization.github.io/folium/quickstart.html](https://python-visualization.github.io/folium/quickstart.html
• you need to give the coordinates (latitude, longitude) in degrees of the centre of your map
• you can choose the initial zoom level for the map by setting `zoom_start` (between 0 to 18)
• The default `tiles` is OpenStreetMap but you can customize it using `tiles`argument. The following tilesets are built-in to Folium. Pass any of the following to the “tiles” keyword:

• “OpenStreetMap”
• “Stamen” (Terrain, Toner, and Watercolor)
• “Cloudmade” (Must pass API key)
• “Mapbox” (Must pass API key)
• “CartoDB” (positron and dark_matter)
``````import folium
``````
``````map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
map
``````

# Custom tileset

``````map = folium.Map(location=[65., 335.0], zoom_start=5 ,
tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png',
attr = "IGN")
map
``````

# Read csv file with pandas

``````import pandas as pd
import numpy as np
``````
``````filename = "/opt/uio/deep_python/data/Justin/icepmag_20190118.csv"

``````
UID ref_id external_database_id region_id area_id location_id site_name lat lon height ... aniso_t aniso_l aniso_f aniso_ll aniso_ff aniso_vg aniso_fl aniso_test aniso_ftest12 aniso_ftest23
0 1 1 1 1 1 1 WB-1 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
1 2 1 1 1 1 1 WB-2 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
2 3 1 1 1 1 1 WB-3 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
3 4 1 1 1 1 1 WB-4 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
4 5 1 1 1 1 1 WB-5 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999

5 rows × 104 columns

# Prepare dataset

• set -9999 to NaN
• Select lat and lon column
• Remove location where lat or lon are missing
``````# replace -9999 by NaN (missing values)
frame.replace(-9999.0,np.NaN, inplace=True)
# remove rows where lon or lat are missing
frame.dropna(subset=['lon', 'lat'], inplace=True)
# drop duplicates
latlons = frame[['lat', 'lon']].drop_duplicates()
``````
``````latlons.head()
``````
lat lon
0 64.35500 338.7277
18 64.25003 338.6219
43 64.25138 338.5982
57 64.25783 338.6408
63 64.26000 338.6318
``````map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
# loop over each point and add a marker to our map
for lat, lon in zip(latlons['lat'],latlons['lon']):
``````# Same as before but add a popup showing lat, lon values when clicking on a marker