Patrick Brockmann - LSCE
The motivation is to render variables read and computed from pyferret as slippy maps. This can be done directly from the memory without having to save them in netCDF files and expose them through a Thredds server to get a Web Map Service.
The tiles are generated by "workers" from a gunicorn server, a python WSGI HTTP server, and they use pyferret for the rendering. You can then use the classic ferret syntax to display your variable and apply if needed classic ferret dimension transformation (@min, @max, @var, ...) or any combinaison or operation on variables you may need in a fully pan-and-zoom environment.
Slippy maps avoid command-line typing and display loops and will help anyone on model analysis. Moreover, considering that nowdays models are becoming incredibily refined, sometimes with resolutions of 1/12°, the pan/zoom navigation is even more useful.
Usage: pyferretWMS.py [--width=400] [--height=400] [--size=value] [--center=[0,0]] [--zoom=1]
[--env=pyferretWMS.jnl] [--server] [--port=8000]
'cmd/qualifiers variable; cmd/qualifiers variable'
'cmd/qualifiers variable' is a classic ferret call (no space allowed except to
separate the variable from the command and its qualifiers). The semi-colon character ';'
is the separator between commands and will determine the number of maps to be drawn.
The qualifiers can include the title qualifier considering that the space character
is not allowed since used to distinguish the cmd/qualifiers and the variable(s).
For this, you can use the HTML code ' ' for the non-breaking space (without the ending semi-colon).
For example: 'shade/lev=20/title=Simulation A varA; shade/lev=20/title=Simulation B varB'
Options:
--version show program's version number and exit
-h, --help show this help message and exit
--width=WIDTH 200 < map width <= 600
--height=HEIGHT 200 < map height <= 600
--size=SIZE 200 < map height and width <= 600
--env=ENVSCRIPT ferret script to set the environment
(default=pyferretWMS.jnl). It contains datasets to open,
variables definition.
--center=CENTER Initial center of maps as [lat, lon] (default=[0,-40])
--zoom=ZOOM Initial zoom of maps (default=1)
--server Server only (default=False)
--port=PORT Server port number (default=8000)
- Using the levitus climatology dataset:
./pyferretWMS.py 'shade/x=-180:180/y=-90:90/lev=20v/pal=mpl_PSU_inferno temp[k=@max]; shade/x=-180:180/y=-90:90/lev=(-inf)(0,140,5)(inf)/pal=mpl_Seq1_RdPu temp[k=@var]; shade/x=-180:180/y=-90:90/lev=(-inf)(30,40,0.5)(inf)/pal=mpl_PSU_viridis salt[k=1]'
-
Same as above with titles
./pyferretWMS.py 'shade/x=-180:180/y=-90:90/lev=20v/pal=mpl_PSU_inferno/title=Maximum temp[k=@max]; shade/x=-180:180/y=-90:90/lev=(-inf)(0,140,5)(inf)/pal=mpl_Seq1_RdPu/title=Temperature variance temp[k=@var]; shade/x=-180:180/y=-90:90/lev=(-inf)(30,40,0.5)(inf)/pal=mpl_PSU_viridis/title=Surface salinity salt[k=1]'
-
Using a NEMO configuration (curvilinear grid) focussed on the Mediterranean sea:
./pyferretWMS.py --zoom 3 --center [40,15] --width 500 --env MED8.jnl 'shade/lev=20v/pal=mpl_PSU_inferno/title=O2 O2, nav_lon, nav_lat; shade/lev=20v/pal=mpl_PSU_viridis/title=NO3 NO3, nav_lon, nav_lat'
Palettes used are available from: http://www.pmel.noaa.gov/maillists/tmap/ferret_users/fu_2015/msg00475.html or from https://github.com/PBrockmann/fast
- pyferret which can be installed in the usual way from http://ferret.pmel.noaa.gov/Ferret/downloads/pyferret/ or by the conda-forge channel from https://anaconda.org/conda-forge/pyferret
- gunicorn (http://gunicorn.org) to be installed with conda:
conda install gunicorn
- nwjs (http://nwjs.io/downloads/), choose the stable release.
- nw should be accessible from your $PATH environment variable
- on Mac OS X: nwjs should be renamed nw and accessible with the $PATH environment variable (or changed in pyferretWMS.py)
- [OpenGIS® Web Map Service Interface Standard (WMS)] (http://www.opengeospatial.org/standards/wms)
- [pyferret] (http://ferret.pmel.noaa.gov/Ferret/documentation/pyferret)
- [gunicorn: a Python WSGI HTTP Server] (http://gunicorn.org)
- [WMS in Leaflet] (http://leafletjs.com/examples/wms/wms.html)
- [Node-Webkit] (http://nwjs.io)
- [Synchronous maps] (https://github.com/turban/Leaflet.Sync)
2022/02/02
-
Modifications for python3
-
Tested with pyferret 7.63, gunicorn 20.1.0, nwjs 0.60.0
-
Add a 3rd example with dynamic creation of maps
2017/01/23
- Add a 3rd example with dynamic creation of maps
- Add resizable properties to maps (page_03.html)
2016/11/25
- Add port number option (tag 0.9.6)
- Add daemon file to set a service with pyferretWMS
2016/10/19
- Add access to command when click on title map (tag 0.9.5)
2016/10/18
- Add size option
- Merge server mode as a new option (tag 0.9.4)
- Colorbars are now created from workers that can handle either a GetColorBar or a GetMap request (tag 0.9.3)
2016/09/21
- Colorbars are created by master process and added to the template html page
- Add map center and zoom option
- Change call to allow curvilinear grid plot (shade command with 3 arguments)
- Resize zoom control buttons
- Remove attribution display (for better visibility)
2016/18/10
Colorbars are now created from workers that can handle either a GetColorBar or a GetMap request.
2016/09/20
An environment script is loaded from the master process. All loaded datasets and defined variables are then available to the different workers. Depending on the number of commands separated by ; passed as argument, you can now get up to 4 synchronous maps with colorbars (keys) made from specified qualifiers.
2016/09/16
Slippy maps are now made from multiple workers. Problem: the dataset and variables if defined should be passed somehow to the workers. I haven't found yet how to inherit from the calling environment.
Also, how this should be called? From an external function? As a new command?
Speed for creating tiles is also an issue, especially when you work with a curvilinear grid that is quite large (1440x1021), even with several workers.
2016/09/09
You can now get slippy maps by a simple import pyferretWMS
and a call to pyferretWMS.slippyMap()
.
It is made possible because the gunicorn is now launched directly from python and not anymore from the command line.
All temporary files (png tiles and the html + package.json for the nw application)
are cleaned properly when exiting (either by closing the client application or by typing "CTRL+C" when launched from a python script).
Test it quicky from:
python pyferretWMS_test.py 'shade/lev=(-inf)(-10,30,1)(inf)/pal=mpl_PSU_plasma temp[k=@max]'
2016/09/06
First script with a gunicorn started from a shell. Only one worker.
Next steps:
- Read variables and start the gunicorn server directly from pyferret (Custom Application).
- Propose synchronous maps when 2 (or more) variables are requested to allow direct spatial intercomparison.
Examples of calls:
./slippy_map.bash 'shade/lev=(-inf)(-10,30,1)(inf)/pal=mpl_PSU_viridis temp[k=@max]'
./slippy_map.bash 'shade/lev=(-inf)(30,40,1)(inf)/pal=mpl_PSU_inferno salt[k=1]'