Visualize and publish with Python
Overview
Teaching: 0 min
Exercises: 0 minQuestions
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"
frame = pd.read_csv(filename)
frame.head()
# 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.
More information and examples here.
Create a mp4 movie
We can use a similar approach to generate mp4
movies.
import matplotlib.animation as animation
def drawmap(ax, data, title):
data.z.plot.pcolormesh(add_colorbar=False,
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')
writer = FFMpegWriter(fps=20, metadata=metadata)
# 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):
data.z.plot.pcolormesh(add_colorbar=False,
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')
writer = FFMpegWriter(fps=20, metadata=metadata)
# 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 usingtiles
argument. The following tilesets are built-in to Folium. Pass any of the following to the “tiles” keyword:- “OpenStreetMap”
- “Mapbox Bright” (Limited levels of zoom for free tiles)
- “Mapbox Control Room” (Limited levels of zoom for free tiles)
- “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
- You need to give the tileset url (
tiles
) and attributes (attr
). - See https://leaflet-extras.github.io/leaflet-providers/preview/
- See https://mapbox.github.io/mapbox-gl-native/macos/0.5.0/tile-url-templates.html for information on Tile url templates.
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"
frame = pd.read_csv(filename)
frame.head()
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']):
folium.Marker([lat, lon]).add_to(map)
map
# Same as before but add a popup showing lat, lon values when clicking on a marker
# For popup, you can use HTML syntax
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
for lat, lon in zip(latlons['lat'],latlons['lon']):
folium.Marker([lat, lon], popup='<b>'+str(lat)+','+str(lon)+'</b>').add_to(map)
map
Instead of Marker, you can use Circle
# Same as before but add a popup showing lat, lon values when clicking on a marker
# For popup, you can use HTML syntax
# CircleMarker can be customized too with colors and radius
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
for lat, lon, age in zip(latlons['lat'],latlons['lon'], latlons['age']):
if np.isnan(age):
age=0.5
folium.CircleMarker([lat, lon], popup='<b>'+str(lat)+','+str(lon)+'</b>', radius=age , color='black',
fill_color='red', fill_opacity=0.5, opacity=0.4).add_to(map)
map
Color Circle Marker with variable values
- Create a new colormap
import branca.colormap as cm
linear = cm.LinearColormap(
['green', 'yellow', 'red'],
vmin=0, vmax=16
)
- Use colormap to color CircleMarker as a function of age
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
for lat, lon, age in zip(latlons['lat'],latlons['lon'], latlons['age']):
if np.isnan(age):
age=0.5
folium.CircleMarker([lat, lon], popup='<b>'+str(lat)+','+str(lon)+'</b>', radius=age , color='black',
fill_color=linear(age), fill_opacity=0.5, opacity=0.4).add_to(map)
map
Save your map as HTML
map.save('map_iceland.html')
Same as before but using ipyleaflet
- ipyleaflet is a more recent python package; it is still under active development
- Documentation available at https://ipyleaflet.readthedocs.io/en/latest/
import ipyleaflet
map = ipyleaflet.Map(center=(65., 335.0), zoom=5, basemap=ipyleaflet.basemaps.Hydda.Full)
map
# Add Markers. There is no pop-up!
map = ipyleaflet.Map(center=(65., 335.0), zoom=5, basemap=ipyleaflet.basemaps.Hydda.Full)
for lat, lon in zip(latlons['lat'],latlons['lon']):
marker=ipyleaflet.Marker(location=(lat, lon))
map.add_layer(marker)
map
Sentinel Hub
- Register at https://services.sentinel-hub.com
- Get INSTANCE_ID (to set below)
- Get more information here
INSTANCE_ID=""
map = folium.Map(location=[60, 10.7], zoom_start=7, tiles=None)
folium.raster_layers.WmsTileLayer(
url='https://services.sentinel-hub.com/ogc/wms/' + INSTANCE_ID + "?showlogo=0&time=2018-07-18/2018-07-20",
layers='TRUE_COLOR',
fmt='image/png',
overlay=False,
control=True,
).add_to(map)
map
Non-Earth Planets
opm_attr='<a href="https://github.com/openplanetary/opm/wiki/OPM-Basemaps" target="blank">OpenPlanetaryMap</a>'
map = folium.Map(attr=f'NASA/MOLA |{opm_attr}', max_zoom=10, no_wrap=True,
tiles='https://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/mola-color/{z}/{x}/{-y}.png')
map
Moon
map = folium.Map([0,0],attr='Lunar_LOLA_Basemap', max_zoom=8, no_wrap=True,
tiles='https://webgis3.wr.usgs.gov/arcgis/rest/services/Lunar_LOLA_Basemap/MapServer/tile/{z}/{y}/{x}.jp2')
map
map = folium.Map([0,0],attr='LOLA/USGS', max_zoom=5,
tiles='https://s3.amazonaws.com/opmbuilder/301_moon/tiles/w/hillshaded-albedo/{z}/{x}/{y}.png'
)
map
Overlay TileLayer
import folium
map = folium.Map(location=[41, -70], zoom_start=5, tiles=None)
folium.raster_layers.TileLayer(
tiles='http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
attr='google',
name='google maps',
max_zoom=20,
subdomains=['mt0', 'mt1', 'mt2', 'mt3'],
overlay=False,
control=True,
).add_to(map)
folium.raster_layers.TileLayer(
tiles='http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
attr='google',
name='google street view',
max_zoom=20,
subdomains=['mt0', 'mt1', 'mt2', 'mt3'],
overlay=False,
control=True,
).add_to(map)
folium.raster_layers.WmsTileLayer(
url='https://demo.boundlessgeo.com/geoserver/ows?',
layers='nasa:bluemarble',
name='bluemarble',
fmt='image/png',
overlay=False,
control=True,
).add_to(map)
folium.raster_layers.WmsTileLayer(
url='http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi',
name='test',
fmt='image/png',
layers='nexrad-n0r-900913',
attr=u'Weather data © 2012 IEM Nexrad',
transparent=True,
overlay=True,
control=True,
).add_to(map)
folium.LayerControl().add_to(map)
map
3D visualization
Get all the filenames
import glob
def getint(name):
_, num = name.split('P_')
num, _ = num.split('.')
return int(num)
filenames = glob.glob("/opt/uio/deep_python/data/Grace/grd_files_to_interp/GypsumP_*.grd")
filenames = sorted(filenames, key=getint)
Step-1
- Read all the netCDF files and create new xarray dataset with all depths
import numpy as np
import xarray as xr
for ix, filename in enumerate(filenames):
print(ix, filename)
dset = xr.open_dataset(filename)
dset=dset.expand_dims('depth')
dset['depth'] = ('depth', [float(getint(filename))])
if ix > 0:
tmp = xr.concat((newarray, dset), dim='depth')
newarray = tmp
else:
newarray = dset
dset.close()
- Save to netCDF with all depths
newarray.to_netcdf("/opt/uio/deep_python/data/Grace/Gypsump_all.grd")
Step-2
- Interpolate by the depth, lat and lon
depths=np.linspace(100.,2650.,500)
print(depths)
lats=np.linspace(-90.,90,newarray.lat.shape[0]*2)
lons=np.linspace(-180.,180,newarray.lon.shape[0]*2)
newarray=newarray.interp(depth=depths, lat=lats, lon=lons)
newarray.to_netcdf("/opt/uio/deep_python/data/Grace/Gypsump_all_interp.grd")
Open and load dataset
newarray = xr.open_dataset("/opt/uio/deep_python/data/Grace/Gypsump_all_interp.grd")
Extrude array to image
img = np.zeros((newarray.depth.shape[0],newarray.lat.shape[0], newarray.lon.shape[0]))
img[:,:,:] = newarray.where((newarray.z<=-0.5) | (newarray.z >= 0.5))['z'][:,:]
Step-3
- 3D plots of anomalies (z)
bounds = np.array(
[-180, 180, -90, 90, 1, 110]
).astype("int")
import k3d
volume = k3d.volume(img[0:200,::,::] ,
compression_level=9,height=1200, bounds = bounds,
color_range=[-4,4], color_map=np.array(k3d.basic_color_maps.Jet , dtype=np.float64))
plot = k3d.plot(height=1200)
plot += volume
plot.display()
Interactive plots with Plotly
2D plots
import pandas as pd
import numpy as np
Read input data
filename = "data/Isabel/Edmundson_data_python_DEEP.xlsx"
frame = pd.read_excel(filename)
frame.head()
Area | Overburden | HC_column | Trap_height | Trap_fill_% | Trap_fill_normalised_% | |
---|---|---|---|---|---|---|
0 | 1 | 1281 | 89 | 153 | 58 | 58 |
1 | 1 | 2292 | 151 | 160 | 94 | 94 |
2 | 1 | 2806 | 77 | 320 | 24 | 24 |
3 | 1 | 1278 | 18 | 18 | 100 | 100 |
4 | 1 | 1281 | 40 | 40 | 100 | 100 |
Use plotly offline to be able to save and use your plots offline
import plotly.offline as py
import plotly.graph_objs as go
To initialize plotly for notebook usage
py.init_notebook_mode()
Simple scatter plot with plotly
data_to_plot = [go.Scatter(x=frame.index, y=frame["HC_column"])]
py.iplot(data_to_plot)
Add title and x-axis and y-axis labels
# To add a title, etc.
layout = go.Layout(
title='Test ',
xaxis=dict(
title='index',
titlefont=dict(
size=18,
color='#7f7f7f'
)
),
yaxis=dict(
title='HC_column',
titlefont=dict(
size=18,
color='#7f7f7f'
)
)
)
fig = go.Figure(data=data_to_plot, layout=layout)
py.iplot(fig)
Save your interactive plot in HTML file
py.plot(fig, filename = 'plotly_example.html', auto_open=False)
3D plots: 3D interactive map on a sphere using plotly
References:
- from a notebook by the Plotly user @Dreamshot, https://plot.ly/~Dreamshot/9152.
- from notebook by Plotly user @empet, Heatmap plot on a spherical map
Import python packages
import os
os.environ['PROJ_LIB']= "/opt/tljh/user/share/proj"
import plotly.offline as py
import cartopy as ccrs
import numpy as np
import xarray as xr
import numpy as np
import matplotlib as mpl
mpl.use('Agg')
from mpl_toolkits.basemap import Basemap
# For plotly
py.init_notebook_mode()
Define utility functions
# convert degrees to radians
def degree2radians(degree):
#convert degrees to radians
return degree*np.pi/180.
# get depth from filename
def getint(name):
_, num = name.split('P_')
num, _ = num.split('.')
return int(num)
# maps the points of coords (lon, lat) to points onto the sphere of radius radius
def mapping_map_to_sphere(lon, lat, radius=1):
lon=np.array(lon, dtype=np.float64)
lat=np.array(lat, dtype=np.float64)
lon=degree2radians(lon)
lat=degree2radians(lat)
xs=radius*np.cos(lon)*np.cos(lat)
ys=radius*np.sin(lon)*np.cos(lat)
zs=radius*np.sin(lat)
return xs, ys, zs
# Functions converting coastline/country polygons to lon/lat traces
def polygons_to_traces(poly_paths, N_poly, m):
'''
pos arg 1. (poly_paths): paths to polygons
pos arg 2. (N_poly): number of polygon to convert
'''
# init. plotting list
lons=[]
lats=[]
for i_poly in range(N_poly):
poly_path = poly_paths[i_poly]
# get the Basemap coordinates of each segment
coords_cc = np.array(
[(vertex[0],vertex[1])
for (vertex,code) in poly_path.iter_segments(simplify=False)]
)
# convert coordinates to lon/lat by 'inverting' the Basemap projection
lon_cc, lat_cc = m(coords_cc[:,0],coords_cc[:,1], inverse=True)
lats.extend(lat_cc.tolist()+[None])
lons.extend(lon_cc.tolist()+[None])
return lons, lats
# Function generating coastline lon/lat
def get_coastline_traces(m):
poly_paths = m.drawcoastlines().get_paths() # coastline polygon paths
N_poly = 91 # use only the 91st biggest coastlines (i.e. no rivers)
cc_lons, cc_lats= polygons_to_traces(poly_paths, N_poly, m)
return cc_lons, cc_lats
# Function generating country lon/lat
def get_country_traces(m):
poly_paths = m.drawcountries().get_paths() # country polygon paths
N_poly = len(poly_paths) # use all countries
country_lons, country_lats= polygons_to_traces(poly_paths, N_poly, m)
return country_lons, country_lats
Get depth from filename
filename = "/opt/uio/deep_python/data/Grace/grd_files_to_interp/GypsumP_100.grd"
depth = getint(filename)
Dataset
- open dataset
- get lat, lon and z values as numpy arrays
df = xr.open_dataset(filename)
df
<xarray.Dataset>
Dimensions: (lat: 165, lon: 328)
Coordinates:
* lon (lon) float64 -180.0 -178.9 -177.8 -176.7 ... 177.8 178.9 180.0
* lat (lat) float64 -90.0 -88.9 -87.8 -86.71 ... 86.71 87.8 88.9 90.0
Data variables:
z (lat, lon) float32 ...
Attributes:
Conventions: COARDS/CF-1.0
title: GypsumP_100.grd
history: xyz2grd -V -Rd -I1.1 GypsumP_175.txt -GGypsumP_100.grd
GMT_version: 4.5.5 [64-bit]
lon=df.lon.values
lat=df.lat.values
z=df.z.values
# min and max values (used for plotting)
vmin=z.min()
vmax=z.max()
Generate 3D map on a sphere with Plotly
# Make shortcut to Basemap object,
# not specifying projection type for this example
map = Basemap()
Get list of of coastline, country, and state lon/lat
cc_lons, cc_lats=get_coastline_traces(map)
country_lons, country_lats=get_country_traces(map)
#concatenate the lon/lat for coastlines and country boundaries:
lons=cc_lons+[None]+country_lons
lats=cc_lats+[None]+country_lats
xs, ys, zs=mapping_map_to_sphere(lons, lats, radius=1.01)# here the radius is slightly greater than 1
#to ensure lines visibility; otherwise (with radius=1)
# some lines are hidden by contours colors
boundaries=dict(type='scatter3d',
x=xs,
y=ys,
z=zs,
mode='lines',
line=dict(color='black', width=1)
)
# Take lat, lon from field
clons=np.array(lon.tolist()+[180], dtype=np.float64)
clats=np.array(lat, dtype=np.float64)
# Create a meshgrid from lats and lons
clons, clats=np.meshgrid(clons, clats)
# Project our (lat,lon,z) to our sphere
XS, YS, ZS=mapping_map_to_sphere(clons, clats)
# generate values for the field Z
# Wrap over longitudes
nrows, ncolumns=clons.shape
VALUES=np.zeros(clons.shape, dtype=np.float64)
VALUES[:, :ncolumns-1]=np.copy(np.array(z, dtype=np.float64))
VALUES[:, ncolumns-1]=np.copy(z[:, 0])
# Create a sphere: colors are taken from our field Z that was mapped to our sphere(VALUES)
sphere=dict(type='surface',
x=XS,
y=YS,
z=ZS,
colorscale='Jet',
surfacecolor=VALUES,
cmin=vmin,
cmax=vmax,
colorbar=dict(thickness=20, len=0.75, ticklen=4, title= 'Z'),
hoverinfo='text')
Make Pyplot figure
# Turn off all axis
noaxis=dict(showbackground=False,
showgrid=False,
showline=False,
showticklabels=False,
ticks='',
title='',
zeroline=False)
# Define layout i.e. title, figure size, axis (x, y, z), camera, etc.
layout3d=dict(title='Z<br>Depth '+ str(getint(filename)) + 'm',
font=dict(family='Balto', size=14),
width=700,
height=700,
scene=dict(xaxis=noaxis,
yaxis=noaxis,
zaxis=noaxis,
aspectratio=dict(x=1,
y=1,
z=1),
camera=dict(eye=dict(x=1.15,
y=1.15,
z=1.15)
)
),
paper_bgcolor='rgba(255,255,255, 1.0)'
)
fig=dict(data=[sphere, boundaries], layout=layout3d)
py.iplot(fig, filename='z-map2sphere')
Globe with ipyvolume
import ipyvolume as ipv
Z = df.z.values.astype('float64')
from matplotlib import cm
colormap = cm.jet
znorm = VALUES - VALUES.min()
znorm /= znorm.ptp()
znorm.min(), znorm.max()
color = colormap(znorm)
ipv.figure()
mesh = ipv.plot_surface(XS, YS, ZS, color=color[...,:3])
countries = ipv.pylab.plot(xs,ys,zs, color='black')
ipv.pylab.style.box_off()
ipv.pylab.style.axes_off()
ipv.show()
ipv.pylab.save("ipyvolume_sphere3D.html")
Publish your notebook (mybinder)
Key Points