Patrick BROCKMANN - LSCE (Climate and Environment Sciences Laboratory)
<img align="left" width="40%" src="http://www.lsce.ipsl.fr/Css/img/banniere_LSCE_75.png" >
A dodecadron is made of 12 pentagons with a Gnomonic projection on each face.
<img align="left" width="200px" src="dodecahedron.jpg" >
Carlos A. Furiti proposes very nice ready-to-print dodecadron pseudoglobes from http://www.progonos.com/furuti/MapProj/Normal/ProjPoly/Foldout/Dodecahedron/dodecahedron.html
Let's try to do the same using the matplotlib basemap API (http://matplotlib.org/basemap/api/basemap_api.html) with a Marine BioGeochemical (MBG) model output produced by IPSL/LSCE.
Create the 12 faces of the dodecadron
from mpl_toolkits.basemap import Basemap
import sys
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.ioff() #Turn interactive plotting off
pos=[(0,90),
(-36*4,25),(-36*3,-25),(-36*2,25),(-36,-25),(0,25),
(36,-25),(36*2,25),(36*3,-25),(36*4,25),(36*5,-25),
(36,-90)]
for n in range(0,len(pos)):
sys.stdout.write("%d "%(n+1))
plt.figure(figsize=(4,4))
map = Basemap(width=9.8E6, height=9.8E6, \
projection='gnom', lon_0=pos[n][0], lat_0=pos[n][1])
map.drawmapboundary(fill_color='aqua')
map.drawcoastlines()
map.fillcontinents(color='orange',lake_color='aqua')
map.drawparallels(np.arange(-90,90,10))
map.drawmeridians(np.arange(-180,180,10))
plt.axis('off')
plt.savefig('map_' + "%02d"%(n+1) + '.png', dpi=100, bbox_inches='tight', pad_inches=0)
plt.clf() # clear memmory
plt.close()
1 2 3 4 5 6 7 8 9 10 11 12
Display our multiple images in the same cell inline
from IPython.display import Image, HTML, display
from glob import glob
imagesList=''.join( ["<img style='width: 120px; margin: 0px; float: left; border: 1px solid black;' src='%s' />" % str(s)
for s in sorted(glob('map_*.png')) ])
display(HTML(imagesList))
Create pentagons
Now let's apply a pentagon mask over images.
Geometry formulaes are taken from http://mathworld.wolfram.com/Pentagon.html**
<img align="left" src="PentagonVertices_750.gif" >
# Image size is 310x310 when figsize is set to 4,4 and axis not drawn
s0=155
c0=155
c1 = np.cos(2*np.pi/5) * s0
c2 = np.cos(np.pi/5) * s0 + 4 # +4 to get small ovelays
s1 = np.sin(2*np.pi/5) * s0
s2 = np.sin(4*np.pi/5) * s0
Define a function to apply pentagon masking
import PIL
import PIL.ImageDraw
def mask_pentagon(filein, fileout, up=True):
# read image as RGB and add alpha (transparency)
im = PIL.Image.open(filein).convert("RGBA")
# convert to numpy (for convenience)
imArray = np.asarray(im)
# create mask from polygon ABCDE A(0,1), B(s1,c1), C(s2,-c2), D(-s2,-c2), E(-s1,c1)
# image origin (0,0) is located at upper left corner with positive coordinates downward
if up:
A=(s0, 0)
B=(s0+s1, c0-c1)
C=(s0+s2, c0+c2)
D=(s0-s2, c0+c2)
E=(s0-s1, c0-c1)
else:
A=(s0-s2, c0-c2)
B=(s0+s2, c0-c2)
C=(s0+s1, c0+c1)
D=(s0, c0+c0)
E=(s0-s1, c0+c1)
polygon = [A,B,C,D,E]
maskIm = PIL.Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
PIL.ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=1)
mask = np.array(maskIm)
# assemble new image (uint8: 0-255)
newImArray = np.empty(imArray.shape,dtype='uint8')
# colors (three first columns, RGB)
newImArray[:,:,:3] = imArray[:,:,:3]
# transparency (4th column)
newImArray[:,:,3] = mask*255
# back to Image from numpy
newIm = PIL.Image.fromarray(newImArray, "RGBA")
newIm.save(fileout)
Process the mask over the 12 different images
for i in range(1,13) :
mask_pentagon("map_%02d"%i + ".png", "pentagon_map_%02d"%i + ".png", up=i%2)
Display pentagon images inline
imagesList=''.join( ["<img style='width: 120px; margin: 0px; float: left; border: 1px solid black; background: #CCC' src='%s' />" % str(s)
for s in sorted(glob('pentagon_map_*.png')) ])
display(HTML(imagesList))
Place pentagons over a template
I have used as template one of the ready-to-print dodecahedron pdf file (http://www.progonos.com/furuti/MapProj/Normal/ProjPoly/Foldout/Dodecahedron/Files/dodGn_pof-flt.pdf) from Carlos A. Furuti with the inkscape software (https://inkscape.org/fr/) and have placed each image with a correct position and an adpated size.
Width and height parameters from matplotlib/Basemap API have been choosen empirically to fit correclty over pentagons.
Open dodecahedron_template.svg with inkscape for checking and edit with a simple editor the svg (xml file) to make changes.
<img align="left" src="dodecahedron_template.png" >
Now use our data
import netCDF4
f=netCDF4.Dataset("https://prodn.idris.fr/thredds/dodsC/STORE/rfry938/ORCA025-PIS2DIC/MBG/Analyse/TS_MO/surf/ORCA025_1958_2010_1M_PH2_surf.nc")
print f.variables
lats = f.variables['nav_lat']
lons = f.variables['nav_lon']
times = f.variables['time_counter']
var = f.variables['PH2']
print var.shape
var.missing_value=0 # correct missing value
OrderedDict([(u'nav_lon', <netCDF4.Variable object at 0xaf5c2974>), (u'nav_lat', <netCDF4.Variable object at 0xaf5c24f4>), (u'deptht', <netCDF4.Variable object at 0xaf5c238c>), (u'time_counter', <netCDF4.Variable object at 0xaf5c29bc>), (u'PH2', <netCDF4.Variable object at 0xaf5c2a04>)]) (636, 1, 1021, 1442)
Check time
print var.long_name
times = f.variables['time_counter']
print times.shape, times.units, times.calendar
from netCDF4 import num2date, date2num, date2index
dates = num2date(times[:], times.units, calendar=times.calendar)
datesStr = [date.strftime('%Y/%m/%d') for date in dates]
print times[-1], datesStr[-1]
PH2 (636,) seconds since 1900-01-01 00:00:00 365_day 3499156800.0 2010/12/16
Produce a check plot with variable
This image will be used to get a legend and a title
plt.figure(figsize=(10,10))
map = Basemap(projection='ortho', lat_0=-20, lon_0=-120, resolution='c')
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
x, y = map(lons[:], lats[:])
# colormap: http://matplotlib.org/users/colormaps.html
# extend: "neither", "both", "min", "max"
timeIndex = -1
map.contourf(x, y, 1E9*var[timeIndex][0], levels=np.arange(7.5,10.5,.2), extend='both', cmap=plt.cm.CMRmap_r)
plt.colorbar()
plt.title("Surface hydrogen ion concentration [H+] (nmol/kg) - " + datesStr[timeIndex])
plt.savefig('ORCA0.25_pH.png', dpi=100, bbox_inches='tight', pad_inches=0)
Produce images for all 12 faces
plt.ioff() #Turn interactive plotting off
pos=[(0,90),(-36*4,25),(-36*3,-25),(-36*2,25),(-36,-25),(0,25),
(36,-25),(36*2,25),(36*3,-25),(36*4,25),(36*5,-25),(36,-90)]
for n in range(0,len(pos)):
sys.stdout.write("%d "%(n+1))
plt.figure(figsize=(4,4))
map = Basemap(width=9.8E6, height=9.8E6, \
projection='gnom', lon_0=pos[n][0], lat_0=pos[n][1])
map.drawmapboundary(fill_color='aqua')
map.drawcoastlines()
map.drawparallels(np.arange(-90,90,10))
map.drawmeridians(np.arange(-180,180,10))
x, y = map(lons[:], lats[:])
map.contourf(x, y, 1E9*var[timeIndex][0], levels=np.arange(7.5,10.5,.2), extend='both', cmap=plt.cm.CMRmap_r)
plt.axis('off')
plt.savefig('mapvar_' + "%02d"%(n+1) + '.png', dpi=100, bbox_inches='tight', pad_inches=0)
plt.clf() # clear memory
plt.close()
1 2 3 4 5 6 7 8 9 10 11 12
for i in range(1,13) :
mask_pentagon("mapvar_%02d"%i + ".png", "pentagon_mapvar_%02d"%i + ".png", up=i%2)
imagesList=''.join( ["<img style='width: 120px; margin: 0px; float: left; border: 1px solid black; background: #CCC' src='%s' />" % str(s)
for s in sorted(glob('pentagon_mapvar_*.png')) ])
display(HTML(imagesList))
Handwork
Enjoy.
Patrick
<img align="left" src="dodecahedron.png" >