{ "metadata": { "name": "", "signature": "sha256:ea241d07d3297b72a7ca2017e9b57aa933a027dd218f033b891aa3ceb46747a3" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Patrick BROCKMANN - LSCE (Climate and Environment Sciences Laboratory)
\n", "

\n", "
" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "A dodecadron pseudoglobe goodies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A dodecadron is made of 12 pentagons with a Gnomonic projection on each face.
\n", "\n", "\n", "Carlos A. Furiti proposes very nice ready-to-print dodecadron pseudoglobes from http://www.progonos.com/furuti/MapProj/Normal/ProjPoly/Foldout/Dodecahedron/dodecahedron.html
\n", "\n", "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.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Create the 12 faces of the dodecadron** " ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.basemap import Basemap\n", "import sys\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "\n", "plt.ioff() #Turn interactive plotting off\n", "\n", "pos=[(0,90),\n", " (-36*4,25),(-36*3,-25),(-36*2,25),(-36,-25),(0,25),\n", " (36,-25),(36*2,25),(36*3,-25),(36*4,25),(36*5,-25),\n", " (36,-90)]\n", "\n", "for n in range(0,len(pos)):\n", " sys.stdout.write(\"%d \"%(n+1))\n", " plt.figure(figsize=(4,4))\n", " map = Basemap(width=9.8E6, height=9.8E6, \\\n", " projection='gnom', lon_0=pos[n][0], lat_0=pos[n][1])\n", " map.drawmapboundary(fill_color='aqua')\n", " map.drawcoastlines()\n", " map.fillcontinents(color='orange',lake_color='aqua')\n", " map.drawparallels(np.arange(-90,90,10))\n", " map.drawmeridians(np.arange(-180,180,10))\n", " plt.axis('off')\n", " plt.savefig('map_' + \"%02d\"%(n+1) + '.png', dpi=100, bbox_inches='tight', pad_inches=0)\n", " plt.clf() # clear memmory\n", " plt.close()\n", " " ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 2 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "3 4 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "5 6 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "7 8 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "9 10 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "11 12 " ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Display our multiple images in the same cell inline**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.display import Image, HTML, display\n", "from glob import glob\n", "imagesList=''.join( [\"\" % str(s) \n", " for s in sorted(glob('map_*.png')) ])\n", "display(HTML(imagesList))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Create pentagons**\n", "\n", "Now let's apply a pentagon mask over images.
\n", "Geometry formulaes are taken from http://mathworld.wolfram.com/Pentagon.html**\n", "\n", "" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Image size is 310x310 when figsize is set to 4,4 and axis not drawn\n", "s0=155\n", "c0=155\n", "\n", "c1 = np.cos(2*np.pi/5) * s0\n", "c2 = np.cos(np.pi/5) * s0 + 4 # +4 to get small ovelays\n", "s1 = np.sin(2*np.pi/5) * s0\n", "s2 = np.sin(4*np.pi/5) * s0" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Define a function to apply pentagon masking**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import PIL\n", "import PIL.ImageDraw\n", "\n", "def mask_pentagon(filein, fileout, up=True):\n", "\n", " # read image as RGB and add alpha (transparency)\n", " im = PIL.Image.open(filein).convert(\"RGBA\")\n", "\n", " # convert to numpy (for convenience)\n", " imArray = np.asarray(im)\n", "\n", " # create mask from polygon ABCDE A(0,1), B(s1,c1), C(s2,-c2), D(-s2,-c2), E(-s1,c1)\n", " # image origin (0,0) is located at upper left corner with positive coordinates downward\n", " if up:\n", " A=(s0, 0)\n", " B=(s0+s1, c0-c1)\n", " C=(s0+s2, c0+c2)\n", " D=(s0-s2, c0+c2)\n", " E=(s0-s1, c0-c1)\n", " else:\n", " A=(s0-s2, c0-c2)\n", " B=(s0+s2, c0-c2)\n", " C=(s0+s1, c0+c1)\n", " D=(s0, c0+c0)\n", " E=(s0-s1, c0+c1) \n", " polygon = [A,B,C,D,E] \n", " maskIm = PIL.Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)\n", " PIL.ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=1)\n", " mask = np.array(maskIm)\n", "\n", " # assemble new image (uint8: 0-255)\n", " newImArray = np.empty(imArray.shape,dtype='uint8')\n", "\n", " # colors (three first columns, RGB)\n", " newImArray[:,:,:3] = imArray[:,:,:3]\n", "\n", " # transparency (4th column)\n", " newImArray[:,:,3] = mask*255\n", "\n", " # back to Image from numpy\n", " newIm = PIL.Image.fromarray(newImArray, \"RGBA\")\n", " newIm.save(fileout)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Process the mask over the 12 different images**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for i in range(1,13) :\n", " mask_pentagon(\"map_%02d\"%i + \".png\", \"pentagon_map_%02d\"%i + \".png\", up=i%2)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Display pentagon images inline**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "imagesList=''.join( [\"\" % str(s) \n", " for s in sorted(glob('pentagon_map_*.png')) ])\n", "display(HTML(imagesList))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Place pentagons over a template**\n", "\n", "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.
\n", "Width and height parameters from matplotlib/Basemap API have been choosen empirically to fit correclty over pentagons.\n", "\n", "Open dodecahedron_template.svg with inkscape for checking and edit with a simple editor the svg (xml file) to make changes.\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Now use our data**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import netCDF4\n", "\n", "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\")\n", "print f.variables\n", "lats = f.variables['nav_lat']\n", "lons = f.variables['nav_lon']\n", "times = f.variables['time_counter']\n", "var = f.variables['PH2']\n", "\n", "print var.shape\n", "var.missing_value=0 # correct missing value" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "OrderedDict([(u'nav_lon', ), (u'nav_lat', ), (u'deptht', ), (u'time_counter', ), (u'PH2', )])\n", "(636, 1, 1021, 1442)\n" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Check time**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print var.long_name\n", "\n", "times = f.variables['time_counter']\n", "print times.shape, times.units, times.calendar\n", "\n", "from netCDF4 import num2date, date2num, date2index\n", "dates = num2date(times[:], times.units, calendar=times.calendar)\n", "\n", "datesStr = [date.strftime('%Y/%m/%d') for date in dates]\n", "print times[-1], datesStr[-1]" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "PH2\n", "(636,) seconds since 1900-01-01 00:00:00 365_day\n", "3499156800.0 2010/12/16\n" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Produce a check plot with variable**\n", "\n", "This image will be used to get a legend and a title" ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10,10))\n", "\n", "map = Basemap(projection='ortho', lat_0=-20, lon_0=-120, resolution='c')\n", "map.drawcoastlines(linewidth=0.25)\n", "map.drawcountries(linewidth=0.25)\n", "map.drawmeridians(np.arange(0, 360, 30))\n", "map.drawparallels(np.arange(-90, 90, 30))\n", "\n", "x, y = map(lons[:], lats[:])\n", "\n", "# colormap: http://matplotlib.org/users/colormaps.html\n", "# extend: \"neither\", \"both\", \"min\", \"max\"\n", "timeIndex = -1\n", "map.contourf(x, y, 1E9*var[timeIndex][0], levels=np.arange(7.5,10.5,.2), extend='both', cmap=plt.cm.CMRmap_r) \n", "plt.colorbar()\n", "plt.title(\"Surface hydrogen ion concentration [H+] (nmol/kg) - \" + datesStr[timeIndex])\n", "\n", "plt.savefig('ORCA0.25_pH.png', dpi=100, bbox_inches='tight', pad_inches=0)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Produce images for all 12 faces**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.ioff() #Turn interactive plotting off\n", "\n", "pos=[(0,90),(-36*4,25),(-36*3,-25),(-36*2,25),(-36,-25),(0,25),\n", " (36,-25),(36*2,25),(36*3,-25),(36*4,25),(36*5,-25),(36,-90)]\n", "\n", "for n in range(0,len(pos)):\n", " sys.stdout.write(\"%d \"%(n+1))\n", " plt.figure(figsize=(4,4))\n", " map = Basemap(width=9.8E6, height=9.8E6, \\\n", " projection='gnom', lon_0=pos[n][0], lat_0=pos[n][1])\n", " map.drawmapboundary(fill_color='aqua')\n", " map.drawcoastlines()\n", " map.drawparallels(np.arange(-90,90,10))\n", " map.drawmeridians(np.arange(-180,180,10))\n", " x, y = map(lons[:], lats[:])\n", " map.contourf(x, y, 1E9*var[timeIndex][0], levels=np.arange(7.5,10.5,.2), extend='both', cmap=plt.cm.CMRmap_r)\n", " plt.axis('off')\n", " plt.savefig('mapvar_' + \"%02d\"%(n+1) + '.png', dpi=100, bbox_inches='tight', pad_inches=0)\n", " plt.clf() # clear memory\n", " plt.close()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 2 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "3 4 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "5 6 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "7 8 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "9 10 " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "11 12 " ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "for i in range(1,13) :\n", " mask_pentagon(\"mapvar_%02d\"%i + \".png\", \"pentagon_mapvar_%02d\"%i + \".png\", up=i%2)\n", " \n", "imagesList=''.join( [\"\" % str(s) \n", " for s in sorted(glob('pentagon_mapvar_*.png')) ])\n", "display(HTML(imagesList))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Handwork**\n", "\n", " * Open the dodecahedron.svg file with inkscape. This file has been already prepared to embed mapvar_*.png files, legend and title taken from ORCA0.25_pH.png.\n", " * Print it.\n", " * Now, making the dodecahedron pseudoglobe is just a matter of cutting, folding and sticking.\n", "\n", "Enjoy.
\n", "Patrick\n", "\n", "\n" ] } ], "metadata": {} } ] }