From 9b5506cf086e117d35bcbfaefa5598f9086c137a Mon Sep 17 00:00:00 2001 From: Will Schlitzer Date: Sun, 7 Feb 2021 14:12:07 +0000 Subject: [PATCH] Wrap grdinfo aliases (#799) *Wrap grdinfo R, C, D, F, I, L, T, and M aliases *Move grdinfo function to grdinfo.py Co-authored-by: Dongdong Tian Co-authored-by: actions-bot <58130806+actions-bot@users.noreply.github.com> Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/__init__.py | 3 +- pygmt/modules.py | 51 +------------- pygmt/src/__init__.py | 1 + pygmt/src/grdinfo.py | 127 ++++++++++++++++++++++++++++++++++ pygmt/tests/test_grdcut.py | 4 +- pygmt/tests/test_grdfilter.py | 4 +- pygmt/tests/test_grdinfo.py | 17 ++++- 7 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 pygmt/src/grdinfo.py diff --git a/pygmt/__init__.py b/pygmt/__init__.py index 12c43328d65..f22df6dff7d 100644 --- a/pygmt/__init__.py +++ b/pygmt/__init__.py @@ -14,13 +14,14 @@ # Import modules to make the high-level GMT Python API from pygmt import datasets from pygmt.figure import Figure -from pygmt.modules import GMTDataArrayAccessor, config, grdinfo +from pygmt.modules import GMTDataArrayAccessor, config from pygmt.session_management import begin as _begin from pygmt.session_management import end as _end from pygmt.src import ( blockmedian, grdcut, grdfilter, + grdinfo, grdtrack, info, makecpt, diff --git a/pygmt/modules.py b/pygmt/modules.py index 8a2f40ccd87..2dd6370f57e 100644 --- a/pygmt/modules.py +++ b/pygmt/modules.py @@ -4,54 +4,7 @@ import xarray as xr from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput -from pygmt.helpers import ( - GMTTempFile, - build_arg_string, - data_kind, - dummy_context, - fmt_docstring, - use_alias, -) - - -@fmt_docstring -@use_alias(V="verbose") -def grdinfo(grid, **kwargs): - """ - Get information about a grid. - - Can read the grid from a file or given as an xarray.DataArray grid. - - Full option list at :gmt-docs:`grdinfo.html` - - Parameters - ---------- - grid : str or xarray.DataArray - The file name of the input grid or the grid loaded as a DataArray. - - {V} - - Returns - ------- - info : str - A string with information about the grid. - """ - kind = data_kind(grid, None, None) - with GMTTempFile() as outfile: - with Session() as lib: - if kind == "file": - file_context = dummy_context(grid) - elif kind == "grid": - file_context = lib.virtualfile_from_grid(grid) - else: - raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid))) - with file_context as infile: - arg_str = " ".join( - [infile, build_arg_string(kwargs), "->" + outfile.name] - ) - lib.call_module("grdinfo", arg_str) - result = outfile.read() - return result +from pygmt.src.grdinfo import grdinfo class config: # pylint: disable=invalid-name @@ -149,7 +102,7 @@ def __init__(self, xarray_obj): # From the shortened summary information of `grdinfo`, # get grid registration in column 10, and grid type in column 11 self._registration, self._gtype = map( - int, grdinfo(self._source, C="n", o="10,11").split() + int, grdinfo(self._source, per_column="n", o="10,11").split() ) except KeyError: self._registration = 0 # Default to Gridline registration diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 23a05664f57..132cb80cece 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -11,6 +11,7 @@ from pygmt.src.grdcut import grdcut from pygmt.src.grdfilter import grdfilter from pygmt.src.grdimage import grdimage +from pygmt.src.grdinfo import grdinfo from pygmt.src.grdtrack import grdtrack from pygmt.src.grdview import grdview from pygmt.src.image import image diff --git a/pygmt/src/grdinfo.py b/pygmt/src/grdinfo.py new file mode 100644 index 00000000000..ab9ad6b26fa --- /dev/null +++ b/pygmt/src/grdinfo.py @@ -0,0 +1,127 @@ +""" +grdinfo - Retrieve info about grid file. +""" +from pygmt.clib import Session +from pygmt.exceptions import GMTInvalidInput +from pygmt.helpers import ( + GMTTempFile, + build_arg_string, + data_kind, + dummy_context, + fmt_docstring, + kwargs_to_strings, + use_alias, +) + + +@fmt_docstring +@use_alias( + C="per_column", + D="tiles", + F="geographic", + I="spacing", + L="force_scan", + M="minmax_pos", + R="region", + T="nearest_multiple", + V="verbose", +) +@kwargs_to_strings(D="sequence", I="sequence", R="sequence") +def grdinfo(grid, **kwargs): + r""" + Get information about a grid. + + Can read the grid from a file or given as an xarray.DataArray grid. + + Full option list at :gmt-docs:`grdinfo.html` + + Parameters + ---------- + grid : str or xarray.DataArray + The file name of the input grid or the grid loaded as a DataArray. + This is the only required argument. + {R} + per_column : str or bool + **n**\|\ **t**. + Formats the report using tab-separated fields on a single line. The + output is name *w e s n z0 z1 dx dy nx ny* [ *x0 y0 x1 y1* ] + [ *med scale* ] [ *mean std rms* ] [ *n_nan* ] *registration gtype*. + The data in brackets are outputted depending on the ``force_scan`` + and ``minmax_pos`` arguments. Use **t** to place file name at the end + of the output record or, **n** or ``True`` to only output numerical + columns. The registration is either 0 (gridline) or 1 (pixel), while + gtype is either 0 (Cartesian) or 1 (geographic). The default value is + ``False``. This cannot be called if ``geographic`` is also set. + tiles : str or list + *xoff*\ [/*yoff*][**+i**]. + Divide a single grid's domain (or the ``region`` domain, if no grid + given) into tiles of size dx times dy (set via ``spacing``). You can + specify overlap between tiles by appending *xoff*\ [/*yoff*]. If the + single grid is given you may use the modifier **+i** to ignore tiles + that have no data within each tile subregion. Default output is text + region strings. Use ``per_column`` to instead report four columns with + xmin xmax ymin ymax per tile, or use ``per_column="t"`` to also have + the region string appended as trailing text. + geographic : bool + Report grid domain and x/y-increments in world mapping format + The default value is ``False``. This cannot be called if + ``per_column`` is also set. + spacing : str or list + *dx*\ [/*dy*]\|\ **b**\|\ **i**\|\ **r**. + Report the min/max of the region to the nearest multiple of dx and dy, + and output this in the form w/e/s/n (unless ``per_column`` is set). To + report the actual grid region, append **r**. For a grid produced by + the img supplement (a Cartesian Mercator grid), the exact geographic + region is given with **i** (if not found then we return the actual + grid region instead). If no argument is given then we report the grid + increment in the form *xinc*\ [/*yinc*]. If **b** is given we write + each grid's bounding box polygon instead. Finally, if ``tiles`` is in + effect then *dx* and *dy* are the dimensions of the desired tiles. + force_scan : int or str + **0**\|\ **1**\|\ **2**\|\ **p**\|\ **a**. + + **0**\ : Report range of z after actually scanning the data, not just + reporting what the header says. + **1**\ : Report median and L1 scale of z (L1 scale = 1.4826 * Median + Absolute Deviation (MAD)). + **2**\ : Report mean, standard deviation, and root-mean-square (rms) + of z. + **p**\ : Report mode (LMS) and LMS scale of z. + **a**\ : Include all of the above. + minxmax_pos : bool + Include the x/y values at the location of the minimum and maximum + z-values. + nearest_multiple : str + [*dz*]\ [**+a**\ [*alpha*]]\ [**+s**]. + Determine min and max z-value. If *dz* is provided then we first round + these values off to multiples of *dz*. To exclude the two tails of the + distribution when determining the min and max you can add **+a** to + set the *alpha* value (in percent): We then sort the grid, exclude the + data in the 0.5*\ *alpha* and 100 - 0.5*\ *alpha* tails, and revise + the min and max. To force a symmetrical range about zero, using + minus/plus the max absolute value of the two extremes, append **+s**\ . + We report the result via the text string *zmin/zmax* or *zmin/zmax/dz* + (if *dz* was given) as expected by :meth:`pygmt.makecpt`. + {V} + + Returns + ------- + info : str + A string with information about the grid. + """ + kind = data_kind(grid, None, None) + with GMTTempFile() as outfile: + with Session() as lib: + if kind == "file": + file_context = dummy_context(grid) + elif kind == "grid": + file_context = lib.virtualfile_from_grid(grid) + else: + raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid))) + with file_context as infile: + arg_str = " ".join( + [infile, build_arg_string(kwargs), "->" + outfile.name] + ) + lib.call_module("grdinfo", arg_str) + result = outfile.read() + return result diff --git a/pygmt/tests/test_grdcut.py b/pygmt/tests/test_grdcut.py index d3e5d5fbc00..d1676fca669 100644 --- a/pygmt/tests/test_grdcut.py +++ b/pygmt/tests/test_grdcut.py @@ -28,7 +28,7 @@ def test_grdcut_file_in_file_out(): result = grdcut("@earth_relief_01d", outgrid=tmpfile.name, region="0/180/0/90") assert result is None # return value is None assert os.path.exists(path=tmpfile.name) # check that outgrid exists - result = grdinfo(tmpfile.name, C=True) + result = grdinfo(tmpfile.name, per_column=True) assert result == "0 180 0 90 -8182 5651.5 1 1 180 90 1 1\n" @@ -60,7 +60,7 @@ def test_grdcut_dataarray_in_file_out(grid): with GMTTempFile(suffix=".nc") as tmpfile: result = grdcut(grid, outgrid=tmpfile.name, region="0/180/0/90") assert result is None # grdcut returns None if output to a file - result = grdinfo(tmpfile.name, C=True) + result = grdinfo(tmpfile.name, per_column=True) assert result == "0 180 0 90 -8182 5651.5 1 1 180 90 1 1\n" diff --git a/pygmt/tests/test_grdfilter.py b/pygmt/tests/test_grdfilter.py index a69bc0bbf50..bb064327223 100644 --- a/pygmt/tests/test_grdfilter.py +++ b/pygmt/tests/test_grdfilter.py @@ -45,7 +45,7 @@ def test_grdfilter_dataarray_in_file_out(grid): with GMTTempFile(suffix=".nc") as tmpfile: result = grdfilter(grid, outgrid=tmpfile.name, filter="g600", distance="4") assert result is None # grdfilter returns None if output to a file - result = grdinfo(tmpfile.name, C=True) + result = grdinfo(tmpfile.name, per_column=True) assert ( result == "-180 180 -90 90 -6147.47265625 5164.11572266 1 1 360 180 1 1\n" ) @@ -88,7 +88,7 @@ def test_grdfilter_file_in_file_out(): ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) # check that outgrid exists - result = grdinfo(tmpfile.name, C=True) + result = grdinfo(tmpfile.name, per_column=True) assert result == "0 180 0 90 -6147.49072266 5164.06005859 1 1 180 90 1 1\n" diff --git a/pygmt/tests/test_grdinfo.py b/pygmt/tests/test_grdinfo.py index 889cb7aef92..5526a23811e 100644 --- a/pygmt/tests/test_grdinfo.py +++ b/pygmt/tests/test_grdinfo.py @@ -13,7 +13,7 @@ def test_grdinfo(): Make sure grd info works as expected. """ grid = load_earth_relief(registration="gridline") - result = grdinfo(grid, L=0, C="n") + result = grdinfo(grid=grid, force_scan=0, per_column="n") assert result.strip() == "-180 180 -90 90 -8592.5 5559 1 1 361 181 0 1" @@ -21,7 +21,7 @@ def test_grdinfo_file(): """ Test grdinfo with file input. """ - result = grdinfo("@earth_relief_01d", L=0, C="n") + result = grdinfo(grid="@earth_relief_01d", force_scan=0, per_column="n") assert result.strip() == "-180 180 -90 90 -8182 5651.5 1 1 360 180 1 1" @@ -31,3 +31,16 @@ def test_grdinfo_fails(): """ with pytest.raises(GMTInvalidInput): grdinfo(np.arange(10).reshape((5, 2))) + + +def test_grdinfo_region(): + """ + Check that the region argument works in grdinfo. + """ + result = grdinfo( + grid="@earth_relief_01d", + force_scan=0, + per_column="n", + region=[-170, 170, -80, 80], + ) + assert result.strip() == "-170 170 -80 80 -8182 5651.5 1 1 340 160 1 1"