Mercurial > repos > climate > psy_maps
changeset 2:e6d1e9d6b399 draft
planemo upload for repository https://github.com/NordicESMhub/galaxy-tools/tree/master/tools/psy-maps commit d330adb8ea0000ffb2e1dcc2346fd34409f6618e
author | climate |
---|---|
date | Thu, 26 Sep 2024 10:32:08 +0000 |
parents | 706666d912d5 |
children | 988212a7466a |
files | psy-maps.xml psymap_simple.py test-data/ESACCI-OC-L3S-CHLOR_A-20220601-RESIZED-fv6.0.nc test-data/ESACCI-OC-L3S-CHLOR_A-20220601-RESIZED-fv6.0.png test-data/TS.f2000.T31T31.control.cam.h0.0014-12.png test-data/TS.f2000.T31T31.control.cam.h0.0014-12_ortho.png |
diffstat | 6 files changed, 117 insertions(+), 80 deletions(-) [+] |
line wrap: on
line diff
--- a/psy-maps.xml Sat Oct 05 17:11:18 2019 -0400 +++ b/psy-maps.xml Thu Sep 26 10:32:08 2024 +0000 @@ -1,16 +1,23 @@ -<tool id="psy_maps" name="map plot" version="1.2.1"> +<tool id="psy_maps" name="map plot" version="1.3.0" profile="23.0"> <description>gridded (lat/lon) netCDF data</description> + <edam_topics> + <edam_topic>topic_3855</edam_topic> + <edam_topic>topic_3318</edam_topic> + </edam_topics> + <edam_operations> + <edam_operation>operation_0573</edam_operation> + </edam_operations> <requirements> <requirement type="package" version="3">python</requirement> - <requirement type="package" version="1.2.1">psyplot</requirement> - <requirement type="package" version="1.2.0">psy-maps</requirement> - <requirement type="package" version="1.2.0">psy-reg</requirement> - <requirement type="package" version="1.4.2">netcdf4</requirement> + <requirement type="package" version="1.4.3">psyplot</requirement> + <requirement type="package" version="1.4.2">psy-maps</requirement> + <requirement type="package" version="1.4.0">psy-reg</requirement> + <requirement type="package" version="1.6.5">netcdf4</requirement> </requirements> <command detect_errors="exit_code"><![CDATA[ - HOME=`pwd` && python3 '$__tool_directory__/psymap_simple.py' '$ifilename' '$variable' + --logscale '$adv.logscale' --proj '$adv.projection' --cmap '$adv.colormap' --output image.png @@ -44,6 +51,10 @@ <param name="title" type="text" value="" label="plot title" /> </when> </conditional> + <param name="logscale" type="select" label="log scale the data"> + <option value="yes">yes</option> + <option value="no" selected="true">no</option> + </param> <param name="projection" type="select"> <option value="" selected="true">PlateCarree</option> <option value="robin">Robinson</option> @@ -155,6 +166,14 @@ <param name="colormap" value="RdBu_r" /> <output name="ofilename" ftype="png" file="TS.f2000.T31T31.control.cam.h0.0014-12_ortho.png" compare="sim_size" delta="500"/> </test> + <test> + <param name="ifilename" value="ESACCI-OC-L3S-CHLOR_A-20220601-RESIZED-fv6.0.nc" /> + <param name="variable" value="chlor_a" /> + <param name="projection" value="" /> + <param name="colormap" value="jet" /> + <param name="logscale" value="yes" /> + <output name="ofilename" ftype="png" file="ESACCI-OC-L3S-CHLOR_A-20220601-RESIZED-fv6.0.png" compare="sim_size" delta="1500"/> + </test> </tests> <help><![CDATA[ @@ -167,15 +186,17 @@ .. class:: infomark The wrappers aims at providing the same functionality as ``psyplot.plot.mapplot`` but it has some limitations. - The input file must be in netCDF format and must contain 2D arrays with geographical coordinatates - (latitudes, longitudes) given in degrees. - TODO: Fill in help. + The input file must be in netCDF format and must contain 2D arrays with geographical coordinatates + (latitudes, longitudes) given in degrees. + The code may fail when the data is significantly different from what has been tested. **What it does** ---------------- This tools creates an image (png format) corresponding to the visualization on a geographical map of a variable extracted from a -netCDF file (input file). By default, the projection is ``PlateCarree`` and colormap is ``jet``. These settings can be changed in *Advanced settings*. +netCDF file (input file). +By default, the projection is ``PlateCarree`` and colormap is ``jet``. +These settings can be changed in *Advanced settings* alongside an option to log scale the plot. **Usage** @@ -183,21 +204,22 @@ :: usage: psymap_simple.py [-h] [--proj PROJ] [--cmap CMAP] [--output OUTPUT] [--time TIME] - [--format FORMAT] [--title TITLE] [--ncol NCOL] [--nrow NROW] [-v] input varname + [--format FORMAT] [--title TITLE] [--ncol NCOL] [--nrow NROW] [-l] [-v] input varname Positional arguments: ~~~~~~~~~~~~~~~~~~~~~ - **input**: input filename with geographical coordinates (netCDF format) -- **varname**: Specify which variable to plot (case sensitive) +- **varname**: specify which variable to plot (case sensitive) Optional arguments: ~~~~~~~~~~~~~~~~~~~~~ -h, --help show this help message and exit - --proj PROJ Specify the projection on which we draw - --cmap CMAP Specify which colormap to use for plotting + -l, --logscale log scale the data + --proj PROJ specify the projection on which we draw + --cmap CMAP specify which colormap to use for plotting --output OUTPUT output filename to store resulting image (png format) -v, --verbose switch on verbose mode --time TIME list of times to plot for multiple plots @@ -211,5 +233,6 @@ ]]></help> <citations> + <citation type="doi">10.21105/joss.00363</citation> </citations> </tool>
--- a/psymap_simple.py Sat Oct 05 17:11:18 2019 -0400 +++ b/psymap_simple.py Thu Sep 26 10:32:08 2024 +0000 @@ -4,6 +4,7 @@ # usage: psymap_simple.py [-h] [--proj PROJ] # [--cmap CMAP] # [--output OUTPUT] +# [-l] # [-v] # input varname # @@ -14,6 +15,7 @@ # # optional arguments: # -h, --help show this help message and exit +# -l, --logscale log scale the data # --proj PROJ Specify the projection on which we draw # --cmap CMAP Specify which colormap to use for plotting # --output OUTPUT output filename to store resulting image (png format) @@ -26,47 +28,63 @@ # import argparse +import math import warnings from pathlib import Path import matplotlib as mpl +import psyplot.project as psy # noqa: I202,E402 +import xarray + mpl.use('Agg') from matplotlib import pyplot # noqa: I202,E402 - -import psyplot.project as psy # noqa: I202,E402 from psyplot import rcParams # noqa: I202,E402 class PsyPlot (): - def __init__(self, input, proj, varname, cmap, output, verbose=False, - time=[], nrow=1, ncol=1, format="%B %e, %Y", - title=""): + def __init__(self, input, varname, output=None, logscale=False, cmap=None, + proj=None, verbose=False, time=None, nrow=None, ncol=None, + format=None, title=None): self.input = input - self.proj = proj self.varname = varname - self.cmap = cmap - self.time = time + if proj is None or proj == "": + self.proj = "cyl" + else: + self.proj = proj + self.cmap = cmap if cmap is not None else "jet" + self.time = time if time is not None else [] + self.ncol = int(ncol) if ncol is not None else int(1) + self.nrow = int(nrow) if nrow is not None else int(1) + + # Open dataset + ds = xarray.open_dataset(input)[varname] + minv = math.log2(ds.data.min()) + maxv = math.log2(ds.data.max()) + if title is not None: + self.title = title + else: + self.title = ds.long_name + if len(self.title) > 60: + self.title = ds.standard_name + del ds + + if logscale: + # Check that data range is sufficient for log scaling + if maxv < (minv * (10.0 ** 2.0)): + print("Not possible to log scale, switching to linear scale") + self.bounds = None + else: + self.bounds = ['log', 2] + else: + self.bounds = None if format is None: self.format = "" else: - self.format = format.replace('X', '%') - if title is None: - self.title = "" - else: - self.title = title - if ncol is None: - self.ncol = 1 - else: - self.ncol = int(ncol) - if nrow is None: - self.nrow = 1 - else: - self.nrow = int(nrow) + self.format = "%B %e, %Y" if output is None: self.output = Path(input).stem + '.png' else: self.output = output - self.verbose = verbose if verbose: print("input: ", self.input) print("proj: ", self.proj) @@ -77,37 +95,32 @@ print("nrow: ", self.nrow) print("title: ", self.title) print("date format: ", self.format) + print("logscale: ", self.bounds) print("output: ", self.output) def plot(self): + clabel = '{desc}' if self.title and self.format: title = self.title + "\n" + self.format elif not self.title and self.format: title = self.format elif self.title and not self.format: title = self.title - else: - title = '%(long_name)s' + clabel = self.title - if self.cmap is None and self.proj is None: - psy.plot.mapplot(self.input, name=self.varname, - title=title, - clabel='{desc}') - elif self.proj is None or not self.proj: - psy.plot.mapplot(self.input, name=self.varname, - title=title, - cmap=self.cmap, clabel='{desc}') - elif self.cmap is None or not self.cmap: - psy.plot.mapplot(self.input, name=self.varname, - projection=self.proj, - title=title, - clabel='{desc}') - else: + # Plot with chosen options + if self.bounds is None: psy.plot.mapplot(self.input, name=self.varname, cmap=self.cmap, projection=self.proj, title=title, - clabel='{desc}') + clabel=clabel) + else: + psy.plot.mapplot(self.input, name=self.varname, + cmap=self.cmap, bounds=self.bounds, + projection=self.proj, + title=title, + clabel=clabel) pyplot.savefig(self.output) @@ -119,32 +132,13 @@ title = self.format else: title = self.title + "\n" + self.format + mpl.rcParams['figure.figsize'] = [20, 8] mpl.rcParams.update({'font.size': 8}) rcParams.update({'plotter.maps.grid_labelsize': 8.0}) - if self.cmap is None and self.proj is None: - m = psy.plot.mapplot(self.input, name=self.varname, - title=title, - ax=(self.nrow, self.ncol), - time=self.time, sort=['time'], - clabel='{desc}') - m.share(keys='bounds') - elif self.proj is None or not self.proj: - m = psy.plot.mapplot(self.input, name=self.varname, - title=title, - ax=(self.nrow, self.ncol), - time=self.time, sort=['time'], - cmap=self.cmap, clabel='{desc}') - m.share(keys='bounds') - elif self.cmap is None or not self.cmap: - m = psy.plot.mapplot(self.input, name=self.varname, - projection=self.proj, - ax=(self.nrow, self.ncol), - time=self.time, sort=['time'], - title=title, - clabel='{desc}') - m.share(keys='bounds') - else: + + # Plot using options + if self.bounds is None: m = psy.plot.mapplot(self.input, name=self.varname, cmap=self.cmap, projection=self.proj, @@ -152,17 +146,27 @@ time=self.time, sort=['time'], title=title, clabel='{desc}') - m.share(keys='bounds') + else: + m = psy.plot.mapplot(self.input, name=self.varname, + cmap=self.cmap, bounds=self.bounds, + projection=self.proj, + ax=(self.nrow, self.ncol), + time=self.time, sort=['time'], + title=title, + clabel='{desc}') + + m.share(keys='bounds') pyplot.savefig(self.output) -def psymap_plot(input, proj, varname, cmap, output, verbose, time, +def psymap_plot(input, proj, varname, logscale, cmap, output, verbose, time, nrow, ncol, format, title): """Generate plot from input filename""" - p = PsyPlot(input, proj, varname, cmap, output, verbose, time, + p = PsyPlot(input, varname, output, logscale, cmap, proj, verbose, time, nrow, ncol, format, title) + if len(time) == 0: p.plot() else: @@ -186,6 +190,10 @@ help='Specify which variable to plot (case sensitive)' ) parser.add_argument( + "--logscale", + help='Plot the log scaled data' + ) + parser.add_argument( '--cmap', help='Specify which colormap to use for plotting' ) @@ -223,6 +231,12 @@ time = [] else: time = list(map(int, args.time.split(","))) - psymap_plot(args.input, args.proj, args.varname, args.cmap, + + if args.logscale == 'no': + logscale = False + else: + logscale = True + + psymap_plot(args.input, args.proj, args.varname, logscale, args.cmap, args.output, args.verbose, time, args.nrow, args.ncol, args.format, args.title)