From b48cad923b7105ce3ad979ff1d3c620145182aba Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 29 Jul 2021 18:22:15 -0400 Subject: [PATCH 01/47] Add grdhisteq processing function --- doc/api/index.rst | 1 + pygmt/__init__.py | 1 + pygmt/src/__init__.py | 1 + pygmt/src/grdhisteq.py | 74 +++++++++++++++++++++++++++++++++++ pygmt/tests/test_grdhisteq.py | 42 ++++++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 pygmt/src/grdhisteq.py create mode 100644 pygmt/tests/test_grdhisteq.py diff --git a/doc/api/index.rst b/doc/api/index.rst index 40ab49ac430..5fcb6a03dff 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -92,6 +92,7 @@ Operations on grids: grdcut grdfill grdfilter + grdhisteq grdlandmask grdgradient grdtrack diff --git a/pygmt/__init__.py b/pygmt/__init__.py index 972f6474286..d21dd328a02 100644 --- a/pygmt/__init__.py +++ b/pygmt/__init__.py @@ -38,6 +38,7 @@ grdfill, grdfilter, grdgradient, + grdhisteq, grdinfo, grdlandmask, grdtrack, diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index f8bae667f02..47b4d0b73ec 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -16,6 +16,7 @@ from pygmt.src.grdfill import grdfill from pygmt.src.grdfilter import grdfilter from pygmt.src.grdgradient import grdgradient +from pygmt.src.grdhisteq import grdhisteq from pygmt.src.grdimage import grdimage from pygmt.src.grdinfo import grdinfo from pygmt.src.grdlandmask import grdlandmask diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py new file mode 100644 index 00000000000..5193c1f22a1 --- /dev/null +++ b/pygmt/src/grdhisteq.py @@ -0,0 +1,74 @@ +""" +grdhisteq - Compute directional gradients from a grid. +""" + +import xarray as xr +from pygmt.clib import Session +from pygmt.helpers import ( + GMTTempFile, + build_arg_string, + fmt_docstring, + kwargs_to_strings, + use_alias, +) + + +@fmt_docstring +@use_alias( + C="divisions", + G="outgrid", + R="region", + N="gaussian", + Q="quadratic", + V="verbose", + n="interpolation", +) +@kwargs_to_strings(R="sequence") +def grdhisteq(grid, **kwargs): + r""" + Perform histogram equalization for a grid. + + Full option list at :gmt-docs:`grdhisteq.html` + + {aliases} + + Parameters + ---------- + grid : str or xarray.DataArray + The file name of the input grid or the grid loaded as a DataArray. + outgrid : str or None + The name of the output netCDF file with extension .nc to store the grid + in. + divisions : int or str + The number of divisions of data range to make [Default is 16]. + + {R} + {V} + + Returns + ------- + ret: xarray.DataArray or None + Return type depends on whether the ``outgrid`` parameter is set: + + - :class:`xarray.DataArray` if ``outgrid`` is not set + - None if ``outgrid`` is set (grid output will be stored in file set by + ``outgrid``) + """ + with GMTTempFile(suffix=".nc") as tmpfile: + with Session() as lib: + file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) + with file_context as infile: + if "G" not in kwargs.keys(): # if outgrid is unset, output to tempfile + kwargs.update({"G": tmpfile.name}) + outgrid = kwargs["G"] + arg_str = " ".join([infile, build_arg_string(kwargs)]) + lib.call_module("grdhisteq", arg_str) + + if outgrid == tmpfile.name: # if user did not set outgrid, return DataArray + with xr.open_dataarray(outgrid) as dataarray: + result = dataarray.load() + _ = result.gmt # load GMTDataArray accessor information + else: + result = None # if user sets an outgrid, return None + + return result diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py new file mode 100644 index 00000000000..b4038e5aa17 --- /dev/null +++ b/pygmt/tests/test_grdhisteq.py @@ -0,0 +1,42 @@ +""" +Tests for grdhisteq. +""" +import os + +import numpy.testing as npt +import pytest +from pygmt import grdhisteq, grdinfo +from pygmt.datasets import load_earth_relief +from pygmt.helpers import GMTTempFile + + +@pytest.fixture(scope="module", name="grid") +def fixture_grid(): + """ + Load the grid data from the sample earth_relief file. + """ + return load_earth_relief(resolution="01d", region=[-5, 5, -5, 5]) + + +def test_grdhisteq_outgrid(grid): + """ + Test the azimuth and direction parameters for grdhisteq with a set outgrid. + """ + with GMTTempFile(suffix=".nc") as tmpfile: + result = grdhisteq(grid=grid, outgrid=tmpfile.name) + assert result is None # return value is None + assert os.path.exists(path=tmpfile.name) # check that outgrid exists + result = ( + grdinfo(grid=tmpfile.name, force_scan="a", per_column="n").strip().split() + ) + + +def test_grdhisteq_no_outgrid(grid): + """ + Test the azimuth and direction parameters for grdhisteq with no set + outgrid. + """ + temp_grid = grdhisteq(grid=grid) + assert temp_grid.dims == ("lat", "lon") + assert temp_grid.gmt.gtype == 1 # Geographic grid + assert temp_grid.gmt.registration == 1 # Pixel registration From e248dcda8c4e4f313b83b4fb53b63ce587f8ed9f Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sat, 18 Sep 2021 20:38:45 -0400 Subject: [PATCH 02/47] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/src/grdhisteq.py | 42 +++++++++++++++++++++++++++++++---- pygmt/tests/test_grdhisteq.py | 23 ++++++++++--------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 5193c1f22a1..004f68226d9 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -1,8 +1,7 @@ """ -grdhisteq - Compute directional gradients from a grid. +grdhisteq - Perform histogram equalization for a grid. """ -import xarray as xr from pygmt.clib import Session from pygmt.helpers import ( GMTTempFile, @@ -11,6 +10,7 @@ kwargs_to_strings, use_alias, ) +from pygmt.io import load_dataarray @fmt_docstring @@ -21,13 +21,46 @@ N="gaussian", Q="quadratic", V="verbose", - n="interpolation", + h="header", ) @kwargs_to_strings(R="sequence") def grdhisteq(grid, **kwargs): r""" Perform histogram equalization for a grid. + Allows the user to find the data values which divide a given grid file into + patches of equal area. One common use of **grdhisteq** is in a kind of + histogram equalization of an image. In this application, the user might + have a grid of flat topography with a mountain in the middle. Ordinary gray + shading of this file (using :meth:`pygmt.Figure.grdimage` or + :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to + graytone will result in most of the image being very dark gray, with the + mountain being almost white. One could use **grdhisteq** to write to a + :class:`pandas.DataFrame` or ASCII file with a list of those data values + which divide the range of the data into *n_cells* segments, each of which + has an equal area in the image. Using **awk** or :meth:`pygmt.makecpt` one + can take this output and build a CPT; using the CPT with + :meth:`pygmt.Figure.grdimage` will result in an image with all levels of + gray occurring equally. Alternatively, see :meth:`pygmt.grd2cpt`. + + The second common use of **grdhisteq** is in writing a grid with statistics + based on some kind of cumulative distribution function. In this + application, the output has relative highs and lows in the same (x,y) + locations as the input file, but the values are changed to reflect their + place in some cumulative distribution. One example would be to find the + lowest 10% of the data: Take a grid, run **grdhisteq** and make a grid + using *n_cells* = 10, and then contour the result to trace the 1 contour. + This will enclose the lowest 10% of the data, regardless of their original + values. Another example is in equalizing the output of + :meth:`pygmt.grdgradient`. For shading purposes it is desired that the data + have a smooth distribution, such as a Gaussian. If you run **grdhisteq** on + output from :meth:`pygmt.grdgradient` and make a grid file output with the + Gaussian option, you will have a grid whose values are distributed + according to a Gaussian distribution with zero mean and unit variance. The + locations of these values will correspond to the locations of the input; + that is, the most negative output value will be in the (x,y) location of + the most negative input value, and so on. + Full option list at :gmt-docs:`grdhisteq.html` {aliases} @@ -39,11 +72,12 @@ def grdhisteq(grid, **kwargs): outgrid : str or None The name of the output netCDF file with extension .nc to store the grid in. - divisions : int or str + divisions : int or bool The number of divisions of data range to make [Default is 16]. {R} {V} + {h} Returns ------- diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index b4038e5aa17..32cbaccfd87 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -3,9 +3,9 @@ """ import os -import numpy.testing as npt import pytest -from pygmt import grdhisteq, grdinfo +import xarray.testing as xrt +from pygmt import grdhisteq from pygmt.datasets import load_earth_relief from pygmt.helpers import GMTTempFile @@ -20,23 +20,24 @@ def fixture_grid(): def test_grdhisteq_outgrid(grid): """ - Test the azimuth and direction parameters for grdhisteq with a set outgrid. + Test the gaussian parameter of grdhisteq with a set outgrid. """ with GMTTempFile(suffix=".nc") as tmpfile: - result = grdhisteq(grid=grid, outgrid=tmpfile.name) + result = grdhisteq(grid=grid, gaussian=True, outgrid=tmpfile.name) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) # check that outgrid exists - result = ( - grdinfo(grid=tmpfile.name, force_scan="a", per_column="n").strip().split() - ) def test_grdhisteq_no_outgrid(grid): """ - Test the azimuth and direction parameters for grdhisteq with no set - outgrid. + Test the quadratic and region parameters for grdhisteq with no set outgrid. """ - temp_grid = grdhisteq(grid=grid) - assert temp_grid.dims == ("lat", "lon") + temp_grid = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5]) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration + expected_grid = xr.DataArray( + data=[[4.0, 0.0, 8.0, 11.0], [13.0, 4.0, 8.0, 13.0], [15.0, 15.0, 15.0, 15.0]], + coords=dict(lon=[-2.5, -1.5, -0.5, 0.5], lat=[2.5, 3.5, 4.5]), + dims=["lat", "lon"], + ) + xrt.assert_allclose(a=temp_grid, b=expected_grid) From e36c93ad76c8facf28b0b7a529117ec16b19e65f Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sat, 18 Sep 2021 20:51:52 -0400 Subject: [PATCH 03/47] Support table or grid output --- pygmt/src/grdhisteq.py | 22 ++++++++++------------ pygmt/tests/test_grdhisteq.py | 1 + 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 004f68226d9..041631ece25 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -2,6 +2,7 @@ grdhisteq - Perform histogram equalization for a grid. """ +import pandas as pd from pygmt.clib import Session from pygmt.helpers import ( GMTTempFile, @@ -81,12 +82,11 @@ def grdhisteq(grid, **kwargs): Returns ------- - ret: xarray.DataArray or None + ret: pandas.DataFrame or xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - - - :class:`xarray.DataArray` if ``outgrid`` is not set - - None if ``outgrid`` is set (grid output will be stored in file set by - ``outgrid``) + - pandas.DataFrame if ``outgrid`` is None (default) + - xarray.DataArray if ``outgrid`` is True + - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) """ with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: @@ -98,11 +98,9 @@ def grdhisteq(grid, **kwargs): arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module("grdhisteq", arg_str) - if outgrid == tmpfile.name: # if user did not set outgrid, return DataArray - with xr.open_dataarray(outgrid) as dataarray: - result = dataarray.load() - _ = result.gmt # load GMTDataArray accessor information - else: - result = None # if user sets an outgrid, return None + try: + result = load_dataarray(outgrid) if outgrid == tmpfile.name else None + except UnboundLocalError: # if outgrid unset, return pd.DataFrame + result = pd.read_csv(tmpfile.name, sep="\t", header=None) - return result + return result diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 32cbaccfd87..c01f6f3dbe6 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -4,6 +4,7 @@ import os import pytest +import xarray as xr import xarray.testing as xrt from pygmt import grdhisteq from pygmt.datasets import load_earth_relief From 67f4e41c969c2b2187d5a399c7134e5e35914d56 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sat, 18 Sep 2021 20:58:52 -0400 Subject: [PATCH 04/47] Only accept int for divisions --- pygmt/src/grdhisteq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 041631ece25..a59f946553c 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -73,7 +73,7 @@ def grdhisteq(grid, **kwargs): outgrid : str or None The name of the output netCDF file with extension .nc to store the grid in. - divisions : int or bool + divisions : int The number of divisions of data range to make [Default is 16]. {R} From 556dc47c6a617a7627725e60ce9c7336dc503857 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sat, 18 Sep 2021 21:03:36 -0400 Subject: [PATCH 05/47] Add more code supporting table or grid output --- pygmt/src/grdhisteq.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index a59f946553c..63bd39f0aa4 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -92,9 +92,14 @@ def grdhisteq(grid, **kwargs): with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with file_context as infile: - if "G" not in kwargs.keys(): # if outgrid is unset, output to tempfile - kwargs.update({"G": tmpfile.name}) - outgrid = kwargs["G"] + if "G" not in kwargs: # table output if outgrid is unset + kwargs.update({">": tmpfile.name}) + else: # NetCDF or xarray.DataArray output if outgrid is set + if ( + kwargs["G"] is True + ): # xarray.DataArray output if outgrid is True + kwargs.update({"G": tmpfile.name}) + outgrid = kwargs["G"] arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module("grdhisteq", arg_str) From 40fbbbe4a2d73774a2aa352bf698f5adf9f1c0a0 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sat, 18 Sep 2021 21:09:34 -0400 Subject: [PATCH 06/47] Update test --- pygmt/tests/test_grdhisteq.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index c01f6f3dbe6..28503873b1c 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -31,9 +31,10 @@ def test_grdhisteq_outgrid(grid): def test_grdhisteq_no_outgrid(grid): """ - Test the quadratic and region parameters for grdhisteq with no set outgrid. + Test the quadratic and region parameters for grdhisteq with + ``outgrid=True``. """ - temp_grid = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5]) + temp_grid = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=True) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration expected_grid = xr.DataArray( From e04ef8a5c25901c7db95951580bddc5f92c3f040 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sun, 19 Sep 2021 16:34:54 -0400 Subject: [PATCH 07/47] Update docstring --- pygmt/src/grdhisteq.py | 61 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 63bd39f0aa4..c78f5d17751 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -29,38 +29,28 @@ def grdhisteq(grid, **kwargs): r""" Perform histogram equalization for a grid. - Allows the user to find the data values which divide a given grid file into - patches of equal area. One common use of **grdhisteq** is in a kind of - histogram equalization of an image. In this application, the user might - have a grid of flat topography with a mountain in the middle. Ordinary gray - shading of this file (using :meth:`pygmt.Figure.grdimage` or + Two common use cases of :meth:`pygmt.grdhisteq` are to find data values that + divide a grid into patches of equal area or to write a grid with statistics + based on some kind of cumulative distribution function. + + Histogram equalization provides a way to highlight data that has most + values clustered in a small portion of the dynamic range, such as a grid + of flat topography with a mountain in the middle. Ordinary gray shading of + this grid (using :meth:`pygmt.Figure.grdimage` or :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to graytone will result in most of the image being very dark gray, with the - mountain being almost white. One could use **grdhisteq** to write to a - :class:`pandas.DataFrame` or ASCII file with a list of those data values - which divide the range of the data into *n_cells* segments, each of which - has an equal area in the image. Using **awk** or :meth:`pygmt.makecpt` one - can take this output and build a CPT; using the CPT with - :meth:`pygmt.Figure.grdimage` will result in an image with all levels of - gray occurring equally. Alternatively, see :meth:`pygmt.grd2cpt`. + mountain being almost white. :meth:`pygmt.grdhisteq` can provide a list of + data values that divide the data range into divisions which have an equal + area in the image [Default is 16 if ``divisions`` is not set]. The + :class:`pandas.DataFrame` or ASCII file output can be used to make a + colormap with :meth:`pygmt.makecpt` and an image with + :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring equally. - The second common use of **grdhisteq** is in writing a grid with statistics - based on some kind of cumulative distribution function. In this - application, the output has relative highs and lows in the same (x,y) - locations as the input file, but the values are changed to reflect their - place in some cumulative distribution. One example would be to find the - lowest 10% of the data: Take a grid, run **grdhisteq** and make a grid - using *n_cells* = 10, and then contour the result to trace the 1 contour. - This will enclose the lowest 10% of the data, regardless of their original - values. Another example is in equalizing the output of - :meth:`pygmt.grdgradient`. For shading purposes it is desired that the data - have a smooth distribution, such as a Gaussian. If you run **grdhisteq** on - output from :meth:`pygmt.grdgradient` and make a grid file output with the - Gaussian option, you will have a grid whose values are distributed - according to a Gaussian distribution with zero mean and unit variance. The - locations of these values will correspond to the locations of the input; - that is, the most negative output value will be in the (x,y) location of - the most negative input value, and so on. + :meth:`pygmt.grdhisteq` also provides a way to write a grid with statistics based + on a cumulative distribution function. In this application, the ``outgrid`` + has relative highs and lows in the same (x,y) locations as the ``grid``, + but the values are changed to reflect their place in the cumulative + distribution. Full option list at :gmt-docs:`grdhisteq.html` @@ -72,9 +62,12 @@ def grdhisteq(grid, **kwargs): The file name of the input grid or the grid loaded as a DataArray. outgrid : str or None The name of the output netCDF file with extension .nc to store the grid - in. + in. Requires ``Gaussian`` to be set. + outfile : str or None + The name of the output ASCII file to store the results of the + histogram equalization in. Not allowed if ``outgrid`` is used. divisions : int - The number of divisions of data range to make [Default is 16]. + Set the number of divisions of the data range. {R} {V} @@ -84,9 +77,15 @@ def grdhisteq(grid, **kwargs): ------- ret: pandas.DataFrame or xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: + - pandas.DataFrame if ``outgrid`` is None (default) - xarray.DataArray if ``outgrid`` is True - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) + - None if ``outfile`` is a str (file output is stored in ``outfile``) + + See Also + ------- + :meth:`pygmt.grd2cpt` """ with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: From 6602098338104883af3ea94ba392bebc5b0699cf Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 18:30:11 -0400 Subject: [PATCH 08/47] Improve handling of tabular output --- pygmt/src/grdhisteq.py | 50 ++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index c78f5d17751..9e90d4ff79f 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -4,6 +4,7 @@ import pandas as pd from pygmt.clib import Session +from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( GMTTempFile, build_arg_string, @@ -17,6 +18,7 @@ @fmt_docstring @use_alias( C="divisions", + D="outfile", G="outgrid", R="region", N="gaussian", @@ -29,9 +31,9 @@ def grdhisteq(grid, **kwargs): r""" Perform histogram equalization for a grid. - Two common use cases of :meth:`pygmt.grdhisteq` are to find data values that - divide a grid into patches of equal area or to write a grid with statistics - based on some kind of cumulative distribution function. + Two common use cases of :meth:`pygmt.grdhisteq` are to find data values + that divide a grid into patches of equal area or to write a grid with + statistics based on some kind of cumulative distribution function. Histogram equalization provides a way to highlight data that has most values clustered in a small portion of the dynamic range, such as a grid @@ -46,11 +48,11 @@ def grdhisteq(grid, **kwargs): colormap with :meth:`pygmt.makecpt` and an image with :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring equally. - :meth:`pygmt.grdhisteq` also provides a way to write a grid with statistics based - on a cumulative distribution function. In this application, the ``outgrid`` - has relative highs and lows in the same (x,y) locations as the ``grid``, - but the values are changed to reflect their place in the cumulative - distribution. + :meth:`pygmt.grdhisteq` also provides a way to write a grid with statistics + based on a cumulative distribution function. In this application, the + ``outgrid`` has relative highs and lows in the same (x,y) locations as the + ``grid``, but the values are changed to reflect their place in the + cumulative distribution. Full option list at :gmt-docs:`grdhisteq.html` @@ -60,9 +62,9 @@ def grdhisteq(grid, **kwargs): ---------- grid : str or xarray.DataArray The file name of the input grid or the grid loaded as a DataArray. - outgrid : str or None + outgrid : str or bool or None The name of the output netCDF file with extension .nc to store the grid - in. Requires ``Gaussian`` to be set. + in. outfile : str or None The name of the output ASCII file to store the results of the histogram equalization in. Not allowed if ``outgrid`` is used. @@ -78,7 +80,7 @@ def grdhisteq(grid, **kwargs): ret: pandas.DataFrame or xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - - pandas.DataFrame if ``outgrid`` is None (default) + - pandas.DataFrame if ``outgrid`` and ``outfile`` are None (default) - xarray.DataArray if ``outgrid`` is True - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) - None if ``outfile`` is a str (file output is stored in ``outfile``) @@ -87,12 +89,23 @@ def grdhisteq(grid, **kwargs): ------- :meth:`pygmt.grd2cpt` """ + if "D" in kwargs and "G" in kwargs: + raise GMTInvalidInput("Cannot use both ``outfile`` and ``outgrid``.") with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with file_context as infile: - if "G" not in kwargs: # table output if outgrid is unset - kwargs.update({">": tmpfile.name}) + if "G" not in kwargs: # Return table data if outgrid is not set + if ( + "D" not in kwargs or kwargs["D"] is True + ): # Return pd.Dataframe if filename not provided + outfile = tmpfile.name + else: # Return set output to file Name + outfile = kwargs["D"] + kwargs.update( + {"D": True} + ) # Temporary workaround to GMT bug, see GitHub issue 5785 + kwargs.update({">": outfile}) else: # NetCDF or xarray.DataArray output if outgrid is set if ( kwargs["G"] is True @@ -102,9 +115,12 @@ def grdhisteq(grid, **kwargs): arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module("grdhisteq", arg_str) - try: + try: # Try returning a xr.DataArray result = load_dataarray(outgrid) if outgrid == tmpfile.name else None - except UnboundLocalError: # if outgrid unset, return pd.DataFrame - result = pd.read_csv(tmpfile.name, sep="\t", header=None) - + except UnboundLocalError: # if outgrid unset, return pd.DataFrame or text file + result = ( + pd.read_csv(outfile, sep="\t", header=None) + if outfile == tmpfile.name + else None + ) return result From 94fd5883b9fc4522ff6218e5a16752cc572f106e Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 18:32:04 -0400 Subject: [PATCH 09/47] Add more tests --- pygmt/tests/test_grdhisteq.py | 95 +++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 28503873b1c..ff6b2fef8fb 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -3,11 +3,13 @@ """ import os +import numpy as np +import pandas as pd import pytest import xarray as xr -import xarray.testing as xrt -from pygmt import grdhisteq +from pygmt import grdhisteq, load_dataarray from pygmt.datasets import load_earth_relief +from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import GMTTempFile @@ -19,17 +21,62 @@ def fixture_grid(): return load_earth_relief(resolution="01d", region=[-5, 5, -5, 5]) -def test_grdhisteq_outgrid(grid): +@pytest.fixture(scope="module", name="expected_grid") +def expected_grid(): + """ + Load the expected grdhisteq grid result. + """ + return xr.DataArray( + data=[[4.0, 0.0, 8.0, 11.0], [13.0, 4.0, 8.0, 13.0], [15.0, 15.0, 15.0, 15.0]], + coords=dict(lon=[-2.5, -1.5, -0.5, 0.5], lat=[2.5, 3.5, 4.5]), + dims=["lat", "lon"], + ) + + +@pytest.fixture(scope="module", name="expected_df") +def expected_df(): + """ + Load the expected grdhisteq table result. + """ + return pd.DataFrame( + data=np.array( + [ + [-5.1050e03, -5.1050e03, 0.0000e00], + [-5.1050e03, -5.1050e03, 1.0000e00], + [-5.1050e03, -5.0695e03, 2.0000e00], + [-5.0695e03, -5.0695e03, 3.0000e00], + [-5.0695e03, -4.9960e03, 4.0000e00], + [-4.9960e03, -4.9960e03, 5.0000e00], + [-4.9960e03, -4.9960e03, 6.0000e00], + [-4.9960e03, -4.9370e03, 7.0000e00], + [-4.9370e03, -4.7620e03, 8.0000e00], + [-4.7620e03, -4.7620e03, 9.0000e00], + [-4.7620e03, -4.7080e03, 1.0000e01], + [-4.7080e03, -4.7080e03, 1.1000e01], + [-4.7080e03, -4.5990e03, 1.2000e01], + [-4.5990e03, -4.1155e03, 1.3000e01], + [-4.1155e03, -3.8975e03, 1.4000e01], + [-3.8975e03, -1.6000e02, 1.5000e01], + ] + ) + ).astype({0: np.float64, 1: np.float64, 2: np.int64}) + + +def test_grdhisteq_outgrid_file(grid, expected_grid): """ Test the gaussian parameter of grdhisteq with a set outgrid. """ with GMTTempFile(suffix=".nc") as tmpfile: - result = grdhisteq(grid=grid, gaussian=True, outgrid=tmpfile.name) + result = grdhisteq( + grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=tmpfile.name + ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) # check that outgrid exists + temp_grid = load_dataarray(tmpfile.name) + xr.testing.assert_allclose(a=temp_grid, b=expected_grid) -def test_grdhisteq_no_outgrid(grid): +def test_grdhisteq_outgrid(grid, expected_grid): """ Test the quadratic and region parameters for grdhisteq with ``outgrid=True``. @@ -37,9 +84,35 @@ def test_grdhisteq_no_outgrid(grid): temp_grid = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=True) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration - expected_grid = xr.DataArray( - data=[[4.0, 0.0, 8.0, 11.0], [13.0, 4.0, 8.0, 13.0], [15.0, 15.0, 15.0, 15.0]], - coords=dict(lon=[-2.5, -1.5, -0.5, 0.5], lat=[2.5, 3.5, 4.5]), - dims=["lat", "lon"], - ) - xrt.assert_allclose(a=temp_grid, b=expected_grid) + xr.testing.assert_allclose(a=temp_grid, b=expected_grid) + + +def test_grdhisteq_no_outgrid(grid, expected_df): + """ + Test the quadratic and region parameters for grdhisteq with no ``outgrid``. + """ + temp_df = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5]) + assert isinstance(temp_df, pd.DataFrame) + pd.testing.assert_frame_equal(left=temp_df, right=expected_df) + + +def test_grdhisteq_outfile(grid, expected_df): + """ + Test the quadratic and region parameters for grdhisteq with no ``outgrid``. + """ + with GMTTempFile(suffix=".txt") as tmpfile: + result = grdhisteq( + grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=tmpfile.name + ) + assert result is None # return value is None + assert os.path.exists(path=tmpfile.name) + temp_df = pd.read_csv(tmpfile.name, sep="\t", header=None) + pd.testing.assert_frame_equal(left=temp_df, right=expected_df) + + +def test_grdhisteq_outfile_outgrid(grid): + """ + Test that an error is raised when ``outgrid`` and ``outfile`` are provided. + """ + with pytest.raises(GMTInvalidInput): + grdhisteq(grid=grid, outfile=True, outgrid=True) From cc6fde18d0bf73b12c86cb4bdb44c2bc6daf7748 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 18:33:44 -0400 Subject: [PATCH 10/47] Fix function names --- pygmt/tests/test_grdhisteq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index ff6b2fef8fb..f087a49c334 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -22,7 +22,7 @@ def fixture_grid(): @pytest.fixture(scope="module", name="expected_grid") -def expected_grid(): +def fixture_grid_result(): """ Load the expected grdhisteq grid result. """ @@ -34,7 +34,7 @@ def expected_grid(): @pytest.fixture(scope="module", name="expected_df") -def expected_df(): +def fixture_df_result(): """ Load the expected grdhisteq table result. """ From 9cd2cbc68ba29762affc0042d8a4a13e6f0288d7 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 18:37:09 -0400 Subject: [PATCH 11/47] Remove -h common option --- pygmt/src/grdhisteq.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 9e90d4ff79f..e61ee6f766c 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -24,7 +24,6 @@ N="gaussian", Q="quadratic", V="verbose", - h="header", ) @kwargs_to_strings(R="sequence") def grdhisteq(grid, **kwargs): @@ -73,7 +72,6 @@ def grdhisteq(grid, **kwargs): {R} {V} - {h} Returns ------- From 68e23dd57c4e88fc45ece2035da72b20e1e51c64 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 18:49:17 -0400 Subject: [PATCH 12/47] Support outgrid=None --- pygmt/src/grdhisteq.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index e61ee6f766c..f0005515788 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -93,21 +93,18 @@ def grdhisteq(grid, **kwargs): with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with file_context as infile: - if "G" not in kwargs: # Return table data if outgrid is not set - if ( - "D" not in kwargs or kwargs["D"] is True - ): # Return pd.Dataframe if filename not provided + # Return table data if outgrid is not set + if "G" not in kwargs or kwargs["G"] is None: + # Return pd.Dataframe if filename not provided + if "D" not in kwargs or kwargs["D"] is True: outfile = tmpfile.name else: # Return set output to file Name outfile = kwargs["D"] - kwargs.update( - {"D": True} - ) # Temporary workaround to GMT bug, see GitHub issue 5785 + # Temporary workaround to GMT bug, see GitHub issue 5785 + kwargs.update({"D": True}) kwargs.update({">": outfile}) else: # NetCDF or xarray.DataArray output if outgrid is set - if ( - kwargs["G"] is True - ): # xarray.DataArray output if outgrid is True + if kwargs["G"] is True: kwargs.update({"G": tmpfile.name}) outgrid = kwargs["G"] arg_str = " ".join([infile, build_arg_string(kwargs)]) From 7512225d0d655c852332752d0bea5192a8a7ed8f Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 20 Sep 2021 19:18:38 -0400 Subject: [PATCH 13/47] Update implementation to require outfile or outgrid --- pygmt/src/grdhisteq.py | 9 +++++++-- pygmt/tests/test_grdhisteq.py | 8 +++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index f0005515788..cfa1aa58183 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -7,6 +7,7 @@ from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( GMTTempFile, + args_in_kwargs, build_arg_string, fmt_docstring, kwargs_to_strings, @@ -53,6 +54,8 @@ def grdhisteq(grid, **kwargs): ``grid``, but the values are changed to reflect their place in the cumulative distribution. + Must provide ``outfile`` or ``outgrid``. + Full option list at :gmt-docs:`grdhisteq.html` {aliases} @@ -64,7 +67,7 @@ def grdhisteq(grid, **kwargs): outgrid : str or bool or None The name of the output netCDF file with extension .nc to store the grid in. - outfile : str or None + outfile : str or bool or None The name of the output ASCII file to store the results of the histogram equalization in. Not allowed if ``outgrid`` is used. divisions : int @@ -78,7 +81,7 @@ def grdhisteq(grid, **kwargs): ret: pandas.DataFrame or xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - - pandas.DataFrame if ``outgrid`` and ``outfile`` are None (default) + - pandas.DataFrame if ``outfile`` is True - xarray.DataArray if ``outgrid`` is True - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) - None if ``outfile`` is a str (file output is stored in ``outfile``) @@ -89,6 +92,8 @@ def grdhisteq(grid, **kwargs): """ if "D" in kwargs and "G" in kwargs: raise GMTInvalidInput("Cannot use both ``outfile`` and ``outgrid``.") + if not args_in_kwargs(["D", "G"], kwargs): + raise GMTInvalidInput("Must set ``outgrid`` or ``outfile``.") with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index f087a49c334..ec0b87991d9 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -91,7 +91,7 @@ def test_grdhisteq_no_outgrid(grid, expected_df): """ Test the quadratic and region parameters for grdhisteq with no ``outgrid``. """ - temp_df = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5]) + temp_df = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=True) assert isinstance(temp_df, pd.DataFrame) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) @@ -110,9 +110,11 @@ def test_grdhisteq_outfile(grid, expected_df): pd.testing.assert_frame_equal(left=temp_df, right=expected_df) -def test_grdhisteq_outfile_outgrid(grid): +def test_grdhisteq_invalid_output(grid): """ - Test that an error is raised when ``outgrid`` and ``outfile`` are provided. + Test that an error is raised without proper output arguments. """ with pytest.raises(GMTInvalidInput): grdhisteq(grid=grid, outfile=True, outgrid=True) + with pytest.raises(GMTInvalidInput): + grdhisteq(grid=grid) From a021d302dda47aad3058eadb330e6cafaf8806e6 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 1 Oct 2021 16:11:54 -0400 Subject: [PATCH 14/47] Try out class structure for grid versus table output --- doc/api/index.rst | 2 + pygmt/src/grdhisteq.py | 337 ++++++++++++++++++++++++---------- pygmt/tests/test_grdhisteq.py | 23 +-- 3 files changed, 248 insertions(+), 114 deletions(-) diff --git a/doc/api/index.rst b/doc/api/index.rst index 12db4f2ffa9..c1b919209aa 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -100,6 +100,8 @@ Operations on grids: grdfilter grdgradient grdhisteq + grdhisteq.equalize_grid + grdhisteq.compute_bins grdlandmask grdproject grdsample diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index cfa1aa58183..89d323a2f49 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -16,111 +16,250 @@ from pygmt.io import load_dataarray -@fmt_docstring -@use_alias( - C="divisions", - D="outfile", - G="outgrid", - R="region", - N="gaussian", - Q="quadratic", - V="verbose", -) -@kwargs_to_strings(R="sequence") -def grdhisteq(grid, **kwargs): +class grdhisteq: r""" Perform histogram equalization for a grid. Two common use cases of :meth:`pygmt.grdhisteq` are to find data values - that divide a grid into patches of equal area or to write a grid with - statistics based on some kind of cumulative distribution function. + that divide a grid into patches of equal area using + :meth:`pygmt.grdhisteq.equalize_grid` or to write a grid with + statistics based on some kind of cumulative distribution function using + :meth:`pygmt.grdhisteq.compute_bins`. Histogram equalization provides a way to highlight data that has most - values clustered in a small portion of the dynamic range, such as a grid - of flat topography with a mountain in the middle. Ordinary gray shading of - this grid (using :meth:`pygmt.Figure.grdimage` or + values clustered in a small portion of the dynamic range, such as a + grid of flat topography with a mountain in the middle. Ordinary gray + shading of this grid (using :meth:`pygmt.Figure.grdimage` or :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to graytone will result in most of the image being very dark gray, with the - mountain being almost white. :meth:`pygmt.grdhisteq` can provide a list of - data values that divide the data range into divisions which have an equal - area in the image [Default is 16 if ``divisions`` is not set]. The - :class:`pandas.DataFrame` or ASCII file output can be used to make a - colormap with :meth:`pygmt.makecpt` and an image with - :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring equally. - - :meth:`pygmt.grdhisteq` also provides a way to write a grid with statistics - based on a cumulative distribution function. In this application, the - ``outgrid`` has relative highs and lows in the same (x,y) locations as the - ``grid``, but the values are changed to reflect their place in the - cumulative distribution. - - Must provide ``outfile`` or ``outgrid``. - - Full option list at :gmt-docs:`grdhisteq.html` - - {aliases} - - Parameters - ---------- - grid : str or xarray.DataArray - The file name of the input grid or the grid loaded as a DataArray. - outgrid : str or bool or None - The name of the output netCDF file with extension .nc to store the grid - in. - outfile : str or bool or None - The name of the output ASCII file to store the results of the - histogram equalization in. Not allowed if ``outgrid`` is used. - divisions : int - Set the number of divisions of the data range. - - {R} - {V} - - Returns - ------- - ret: pandas.DataFrame or xarray.DataArray or None - Return type depends on whether the ``outgrid`` parameter is set: - - - pandas.DataFrame if ``outfile`` is True - - xarray.DataArray if ``outgrid`` is True - - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) - - None if ``outfile`` is a str (file output is stored in ``outfile``) - - See Also - ------- - :meth:`pygmt.grd2cpt` + mountain being almost white. :meth:`pygmt.grdhisteq.compute_bins` can + provide a list of data values that divide the data range into divisions + which have an equal area in the image [Default is 16 if ``divisions`` is + not set]. The :class:`pandas.DataFrame` or ASCII file output can be used to + make a colormap with :meth:`pygmt.makecpt` and an image with + :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring + equally. + + :meth:`pygmt.grdhisteq.equalize_grid` provides a way to write a grid with + statistics based on a cumulative distribution function. In this + application, the ``outgrid`` has relative highs and lows in the same + (x,y) locations as the ``grid``, but the values are changed to reflect + their place in the cumulative distribution. """ - if "D" in kwargs and "G" in kwargs: - raise GMTInvalidInput("Cannot use both ``outfile`` and ``outgrid``.") - if not args_in_kwargs(["D", "G"], kwargs): - raise GMTInvalidInput("Must set ``outgrid`` or ``outfile``.") - with GMTTempFile(suffix=".nc") as tmpfile: - with Session() as lib: - file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) - with file_context as infile: - # Return table data if outgrid is not set - if "G" not in kwargs or kwargs["G"] is None: - # Return pd.Dataframe if filename not provided - if "D" not in kwargs or kwargs["D"] is True: - outfile = tmpfile.name - else: # Return set output to file Name - outfile = kwargs["D"] - # Temporary workaround to GMT bug, see GitHub issue 5785 - kwargs.update({"D": True}) - kwargs.update({">": outfile}) - else: # NetCDF or xarray.DataArray output if outgrid is set - if kwargs["G"] is True: - kwargs.update({"G": tmpfile.name}) - outgrid = kwargs["G"] - arg_str = " ".join([infile, build_arg_string(kwargs)]) - lib.call_module("grdhisteq", arg_str) - - try: # Try returning a xr.DataArray - result = load_dataarray(outgrid) if outgrid == tmpfile.name else None - except UnboundLocalError: # if outgrid unset, return pd.DataFrame or text file - result = ( - pd.read_csv(outfile, sep="\t", header=None) - if outfile == tmpfile.name - else None - ) - return result + + @staticmethod + def _grdhisteq(grid, **kwargs): + r""" + Perform histogram equalization for a grid. + + Two common use cases of :meth:`pygmt.grdhisteq` are to find data values + that divide a grid into patches of equal area or to write a grid with + statistics based on some kind of cumulative distribution function. + + Histogram equalization provides a way to highlight data that has most + values clustered in a small portion of the dynamic range, such as a + grid of flat topography with a mountain in the middle. Ordinary gray + shading of this grid (using :meth:`pygmt.Figure.grdimage` or + :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to + graytone will result in most of the image being very dark gray, with + the mountain being almost white. :meth:`pygmt.grdhisteq` can provide a + list of data values that divide the data range into divisions which + have an equal area in the image [Default is 16 if ``divisions`` is not + set]. The :class:`pandas.DataFrame` or ASCII file output can be used to + make a colormap with :meth:`pygmt.makecpt` and an image with + :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring + equally. + + :meth:`pygmt.grdhisteq` also provides a way to write a grid with + statistics based on a cumulative distribution function. In this + application, the ``outgrid`` has relative highs and lows in the same + (x,y) locations as the ``grid``, but the values are changed to reflect + their place in the cumulative distribution. + + Must provide ``outfile`` or ``outgrid``. + + Full option list at :gmt-docs:`grdhisteq.html` + + {aliases} + + Parameters + ---------- + grid : str or xarray.DataArray + The file name of the input grid or the grid loaded as a DataArray. + outgrid : str or bool or None + The name of the output netCDF file with extension .nc to store the + grid in. + outfile : str or bool or None + The name of the output ASCII file to store the results of the + histogram equalization in. Not allowed if ``outgrid`` is used. + divisions : int + Set the number of divisions of the data range. + + {R} + {V} + + Returns + ------- + ret: pandas.DataFrame or xarray.DataArray or None + Return type depends on whether the ``outgrid`` parameter is set: + + - pandas.DataFrame if ``outfile`` is True + - xarray.DataArray if ``outgrid`` is True + - None if ``outgrid`` is a str (grid output is stored in + ``outgrid``) + - None if ``outfile`` is a str (file output is stored in + ``outfile``) + + See Also + ------- + :meth:`pygmt.grd2cpt` + """ + if "D" in kwargs and "G" in kwargs: + raise GMTInvalidInput("Cannot use both ``outfile`` and ``outgrid``.") + if not args_in_kwargs(["D", "G"], kwargs): + raise GMTInvalidInput("Must set ``outgrid`` or ``outfile``.") + with GMTTempFile(suffix=".nc") as tmpfile: + with Session() as lib: + file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) + with file_context as infile: + # Return table data if outgrid is not set + if "G" not in kwargs or kwargs["G"] is None: + # Return pd.Dataframe if filename not provided + if "D" not in kwargs or kwargs["D"] is True: + outfile = tmpfile.name + else: # Return set output to file Name + outfile = kwargs["D"] + # Temporary workaround to GMT bug (Issue #5785) + kwargs.update({"D": True}) + kwargs.update({">": outfile}) + else: # NetCDF or xarray.DataArray output if outgrid is set + if kwargs["G"] is True: + kwargs.update({"G": tmpfile.name}) + outgrid = kwargs["G"] + arg_str = " ".join([infile, build_arg_string(kwargs)]) + lib.call_module("grdhisteq", arg_str) + + try: # Try returning a xr.DataArray + result = load_dataarray(outgrid) if outgrid == tmpfile.name else None + except UnboundLocalError: # if outgrid unset, return pd.DataFrame or text file + result = ( + pd.read_csv(outfile, sep="\t", header=None) + if outfile == tmpfile.name + else None + ) + return result + + @staticmethod + @fmt_docstring + @use_alias( + C="divisions", + G="outgrid", + R="region", + N="gaussian", + Q="quadratic", + V="verbose", + ) + @kwargs_to_strings(R="sequence") + def equalize_grid(grid, **kwargs): + r""" + Perform histogram equalization for a grid. + + + :meth:`pygmt.grdhisteq.equalize_grid` provides a way to write a grid + with statistics based on a cumulative distribution function. The + ``outgrid`` has relative highs and lows in the same (x,y) locations as + the ``grid``, but the values are changed to reflect their place in the + cumulative distribution. + + Full option list at :gmt-docs:`grdhisteq.html` + + {aliases} + + Parameters + ---------- + grid : str or xarray.DataArray + The file name of the input grid or the grid loaded as a DataArray. + outgrid : str or bool or None + The name of the output netCDF file with extension .nc to store the + grid in. + divisions : int + Set the number of divisions of the data range. + + {R} + {V} + + Returns + ------- + ret: xarray.DataArray or None + Return type depends on the ``outgrid`` parameter: + + - xarray.DataArray if ``outgrid`` is True or None + - None if ``outgrid`` is a str (grid output is stored in + ``outgrid``) + + See Also + ------- + :meth:`pygmt.grd2cpt` + """ + return grdhisteq._grdhisteq(grid, **kwargs) + + @staticmethod + @fmt_docstring + @use_alias( + C="divisions", + D="outfile", + R="region", + N="gaussian", + Q="quadratic", + V="verbose", + ) + @kwargs_to_strings(R="sequence") + def compute_bins(grid, **kwargs): + r""" + Perform histogram equalization for a grid. + + Histogram equalization provides a way to highlight data that has most + values clustered in a small portion of the dynamic range, such as a + grid of flat topography with a mountain in the middle. Ordinary gray + shading of this grid (using :meth:`pygmt.Figure.grdimage` or + :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to + graytone will result in most of the image being very dark gray, with + the mountain being almost white. :meth:`pygmt.grdhisteq.compute_bins` + can provide a list of data values that divide the data range into + divisions which have an equal area in the image [Default is 16 if + ``divisions`` is not set]. The :class:`pandas.DataFrame` or ASCII file + output can be used to make a colormap with :meth:`pygmt.makecpt` and an + image with :meth:`pygmt.Figure.grdimage` that has all levels of gray + occuring equally. + + Full option list at :gmt-docs:`grdhisteq.html` + + {aliases} + + Parameters + ---------- + grid : str or xarray.DataArray + The file name of the input grid or the grid loaded as a DataArray. + outfile : str or bool or None + The name of the output ASCII file to store the results of the + histogram equalization in. + divisions : int + Set the number of divisions of the data range. + + {R} + {V} + + Returns + ------- + ret: pandas.DataFrame None + Return type depends on the ``outfile`` parameter: + + - pandas.DataFrame if ``outfile`` is True or None + - None if ``outfile`` is a str (file output is stored in + ``outfile``) + + See Also + ------- + :meth:`pygmt.grd2cpt` + """ + return grdhisteq._grdhisteq(grid, **kwargs) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index ec0b87991d9..96fa724f43f 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -9,7 +9,6 @@ import xarray as xr from pygmt import grdhisteq, load_dataarray from pygmt.datasets import load_earth_relief -from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import GMTTempFile @@ -67,7 +66,7 @@ def test_grdhisteq_outgrid_file(grid, expected_grid): Test the gaussian parameter of grdhisteq with a set outgrid. """ with GMTTempFile(suffix=".nc") as tmpfile: - result = grdhisteq( + result = grdhisteq.equalize_grid( grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=tmpfile.name ) assert result is None # return value is None @@ -81,7 +80,9 @@ def test_grdhisteq_outgrid(grid, expected_grid): Test the quadratic and region parameters for grdhisteq with ``outgrid=True``. """ - temp_grid = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=True) + temp_grid = grdhisteq.equalize_grid( + grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=True + ) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration xr.testing.assert_allclose(a=temp_grid, b=expected_grid) @@ -91,7 +92,9 @@ def test_grdhisteq_no_outgrid(grid, expected_df): """ Test the quadratic and region parameters for grdhisteq with no ``outgrid``. """ - temp_df = grdhisteq(grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=True) + temp_df = grdhisteq.compute_bins( + grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=True + ) assert isinstance(temp_df, pd.DataFrame) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) @@ -101,20 +104,10 @@ def test_grdhisteq_outfile(grid, expected_df): Test the quadratic and region parameters for grdhisteq with no ``outgrid``. """ with GMTTempFile(suffix=".txt") as tmpfile: - result = grdhisteq( + result = grdhisteq.compute_bins( grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=tmpfile.name ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) temp_df = pd.read_csv(tmpfile.name, sep="\t", header=None) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) - - -def test_grdhisteq_invalid_output(grid): - """ - Test that an error is raised without proper output arguments. - """ - with pytest.raises(GMTInvalidInput): - grdhisteq(grid=grid, outfile=True, outgrid=True) - with pytest.raises(GMTInvalidInput): - grdhisteq(grid=grid) From 20781204dbcc36311180730422a1e0376e94ae18 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 27 Dec 2021 18:16:19 -0500 Subject: [PATCH 15/47] Remove workaround for GMT 6.2 bug --- pygmt/src/grdhisteq.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 89d323a2f49..7c0b3f7b732 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -123,15 +123,11 @@ def _grdhisteq(grid, **kwargs): file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with file_context as infile: # Return table data if outgrid is not set - if "G" not in kwargs or kwargs["G"] is None: + if "G" not in kwargs: # Return pd.Dataframe if filename not provided if "D" not in kwargs or kwargs["D"] is True: - outfile = tmpfile.name - else: # Return set output to file Name - outfile = kwargs["D"] - # Temporary workaround to GMT bug (Issue #5785) - kwargs.update({"D": True}) - kwargs.update({">": outfile}) + kwargs.update({"D": tmpfile.name}) + outfile = kwargs["D"] else: # NetCDF or xarray.DataArray output if outgrid is set if kwargs["G"] is True: kwargs.update({"G": tmpfile.name}) @@ -201,6 +197,9 @@ def equalize_grid(grid, **kwargs): ------- :meth:`pygmt.grd2cpt` """ + # Return a xarray.DataArray if ``outgrid`` is not set + if "G" not in kwargs: + kwargs.update({"G": True}) return grdhisteq._grdhisteq(grid, **kwargs) @staticmethod @@ -262,4 +261,7 @@ def compute_bins(grid, **kwargs): ------- :meth:`pygmt.grd2cpt` """ + # Return a pandas.DataFrame if ``outfile`` is not set + if "D" not in kwargs: + kwargs.update({"D": True}) return grdhisteq._grdhisteq(grid, **kwargs) From a08c724c1f6d225640b7d15affb7fd0d59defdba Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 4 Feb 2022 16:13:48 -0500 Subject: [PATCH 16/47] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/src/grdhisteq.py | 2 +- pygmt/tests/test_grdhisteq.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 7c0b3f7b732..760bbd9cee8 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -16,7 +16,7 @@ from pygmt.io import load_dataarray -class grdhisteq: +class grdhisteq: # pylint: disable=invalid-name r""" Perform histogram equalization for a grid. diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 96fa724f43f..ca0defe77ee 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -75,13 +75,14 @@ def test_grdhisteq_outgrid_file(grid, expected_grid): xr.testing.assert_allclose(a=temp_grid, b=expected_grid) -def test_grdhisteq_outgrid(grid, expected_grid): +@pytest.mark.parametrize("outgrid", [True, None]) +def test_grdhisteq_outgrid(grid, outgrid, expected_grid): """ Test the quadratic and region parameters for grdhisteq with - ``outgrid=True``. + ``outgrid=True`` and ``outgrid=None``. """ temp_grid = grdhisteq.equalize_grid( - grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=True + grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=outgrid ) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration From e3d0678a2bfc76107bc4bf80ad0cdaef6a7995b4 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 25 Feb 2022 12:02:41 -0500 Subject: [PATCH 17/47] Separate out parsing of D and G options --- pygmt/src/grdhisteq.py | 95 +++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 38 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 760bbd9cee8..e5c94bfdccd 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -1,13 +1,13 @@ """ grdhisteq - Perform histogram equalization for a grid. """ +import warnings import pandas as pd from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( GMTTempFile, - args_in_kwargs, build_arg_string, fmt_docstring, kwargs_to_strings, @@ -48,7 +48,7 @@ class grdhisteq: # pylint: disable=invalid-name """ @staticmethod - def _grdhisteq(grid, **kwargs): + def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): r""" Perform histogram equalization for a grid. @@ -114,35 +114,21 @@ def _grdhisteq(grid, **kwargs): ------- :meth:`pygmt.grd2cpt` """ - if "D" in kwargs and "G" in kwargs: - raise GMTInvalidInput("Cannot use both ``outfile`` and ``outgrid``.") - if not args_in_kwargs(["D", "G"], kwargs): - raise GMTInvalidInput("Must set ``outgrid`` or ``outfile``.") - with GMTTempFile(suffix=".nc") as tmpfile: - with Session() as lib: - file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) - with file_context as infile: - # Return table data if outgrid is not set - if "G" not in kwargs: - # Return pd.Dataframe if filename not provided - if "D" not in kwargs or kwargs["D"] is True: - kwargs.update({"D": tmpfile.name}) - outfile = kwargs["D"] - else: # NetCDF or xarray.DataArray output if outgrid is set - if kwargs["G"] is True: - kwargs.update({"G": tmpfile.name}) - outgrid = kwargs["G"] - arg_str = " ".join([infile, build_arg_string(kwargs)]) - lib.call_module("grdhisteq", arg_str) - - try: # Try returning a xr.DataArray - result = load_dataarray(outgrid) if outgrid == tmpfile.name else None - except UnboundLocalError: # if outgrid unset, return pd.DataFrame or text file - result = ( - pd.read_csv(outfile, sep="\t", header=None) - if outfile == tmpfile.name - else None - ) + + with Session() as lib: + file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) + with file_context as infile: + arg_str = " ".join([infile, build_arg_string(kwargs)]) + lib.call_module("grdhisteq", arg_str) + + if output_type == "file": + return None + if output_type == "xarray": + return load_dataarray(tmpfile.name) + + result = pd.read_csv(kwargs["D"], sep="\t", header=None) + if output_type == "numpy": + result = result.to_numpy() return result @staticmethod @@ -198,9 +184,19 @@ def equalize_grid(grid, **kwargs): :meth:`pygmt.grd2cpt` """ # Return a xarray.DataArray if ``outgrid`` is not set - if "G" not in kwargs: - kwargs.update({"G": True}) - return grdhisteq._grdhisteq(grid, **kwargs) + if "G" in kwargs and isinstance(kwargs["G"], str): + output_type = "file" + else: + output_type = "xarray" + with GMTTempFile(suffix=".nc") as tmpfile: + if output_type != "file": + kwargs.update({"G": tmpfile.name}) + return grdhisteq._grdhisteq( + grid=grid, + output_type=output_type, + tmpfile=tmpfile, + **kwargs, + ) @staticmethod @fmt_docstring @@ -213,7 +209,7 @@ def equalize_grid(grid, **kwargs): V="verbose", ) @kwargs_to_strings(R="sequence") - def compute_bins(grid, **kwargs): + def compute_bins(grid, output_type="pandas", **kwargs): r""" Perform histogram equalization for a grid. @@ -242,6 +238,13 @@ def compute_bins(grid, **kwargs): outfile : str or bool or None The name of the output ASCII file to store the results of the histogram equalization in. + output_type : str + Determine the format the xyz data will be returned in [Default is + ``pandas``]: + + - ``numpy`` - :class:`numpy.ndarray` + - ``pandas``- :class:`pandas.DataFrame` + - ``file`` - ASCII file (requires ``outfile``) divisions : int Set the number of divisions of the data range. @@ -262,6 +265,22 @@ def compute_bins(grid, **kwargs): :meth:`pygmt.grd2cpt` """ # Return a pandas.DataFrame if ``outfile`` is not set - if "D" not in kwargs: - kwargs.update({"D": True}) - return grdhisteq._grdhisteq(grid, **kwargs) + if output_type not in ["numpy", "pandas", "file"]: + raise GMTInvalidInput( + "Must specify 'output_type' either as 'numpy', 'pandas' or 'file'." + ) + + if "D" in kwargs and output_type != "file": + msg = ( + f"Changing 'output_type' of grd2xyz from '{output_type}' to 'file' " + "since 'outfile' parameter is set. Please use output_type='file' " + "to silence this warning." + ) + warnings.warn(message=msg, category=RuntimeWarning, stacklevel=2) + output_type = "file" + with GMTTempFile(suffix=".txt") as tmpfile: + if output_type != "file": + kwargs.update({"D": tmpfile.name}) + return grdhisteq._grdhisteq( + grid, output_type=output_type, tmpfile=tmpfile, **kwargs + ) From 6f3e23fbf5bca864e9fce11bf5cb78ec2941aacb Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 25 Feb 2022 12:23:39 -0500 Subject: [PATCH 18/47] Update tests to use static_earth_relief --- pygmt/tests/test_grdhisteq.py | 61 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index ca0defe77ee..d3814e69be4 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -8,8 +8,8 @@ import pytest import xarray as xr from pygmt import grdhisteq, load_dataarray -from pygmt.datasets import load_earth_relief from pygmt.helpers import GMTTempFile +from pygmt.helpers.testing import load_static_earth_relief @pytest.fixture(scope="module", name="grid") @@ -17,7 +17,15 @@ def fixture_grid(): """ Load the grid data from the sample earth_relief file. """ - return load_earth_relief(resolution="01d", region=[-5, 5, -5, 5]) + return load_static_earth_relief() + + +@pytest.fixture(scope="module", name="region") +def fixture_region(): + """ + Load the grid data from the sample earth_relief file. + """ + return [-52, -48, -22, -18] @pytest.fixture(scope="module", name="expected_grid") @@ -26,8 +34,8 @@ def fixture_grid_result(): Load the expected grdhisteq grid result. """ return xr.DataArray( - data=[[4.0, 0.0, 8.0, 11.0], [13.0, 4.0, 8.0, 13.0], [15.0, 15.0, 15.0, 15.0]], - coords=dict(lon=[-2.5, -1.5, -0.5, 0.5], lat=[2.5, 3.5, 4.5]), + data=[[0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 1, 1], [1, 1, 1, 1]], + coords=dict(lon=[-51.5, -50.5, -49.5, -48.5], lat=[-21.5, -20.5, -19.5, -18.5]), dims=["lat", "lon"], ) @@ -40,34 +48,20 @@ def fixture_df_result(): return pd.DataFrame( data=np.array( [ - [-5.1050e03, -5.1050e03, 0.0000e00], - [-5.1050e03, -5.1050e03, 1.0000e00], - [-5.1050e03, -5.0695e03, 2.0000e00], - [-5.0695e03, -5.0695e03, 3.0000e00], - [-5.0695e03, -4.9960e03, 4.0000e00], - [-4.9960e03, -4.9960e03, 5.0000e00], - [-4.9960e03, -4.9960e03, 6.0000e00], - [-4.9960e03, -4.9370e03, 7.0000e00], - [-4.9370e03, -4.7620e03, 8.0000e00], - [-4.7620e03, -4.7620e03, 9.0000e00], - [-4.7620e03, -4.7080e03, 1.0000e01], - [-4.7080e03, -4.7080e03, 1.1000e01], - [-4.7080e03, -4.5990e03, 1.2000e01], - [-4.5990e03, -4.1155e03, 1.3000e01], - [-4.1155e03, -3.8975e03, 1.4000e01], - [-3.8975e03, -1.6000e02, 1.5000e01], + [345.5, 519.5, 0], + [519.5, 726.5, 1], ] ) ).astype({0: np.float64, 1: np.float64, 2: np.int64}) -def test_grdhisteq_outgrid_file(grid, expected_grid): +def test_grdhisteq_outgrid_file(grid, expected_grid, region): """ - Test the gaussian parameter of grdhisteq with a set outgrid. + Test grdhisteq.equalize_grid with a set outgrid. """ with GMTTempFile(suffix=".nc") as tmpfile: result = grdhisteq.equalize_grid( - grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=tmpfile.name + grid=grid, divisions=2, region=region, outgrid=tmpfile.name ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) # check that outgrid exists @@ -76,37 +70,34 @@ def test_grdhisteq_outgrid_file(grid, expected_grid): @pytest.mark.parametrize("outgrid", [True, None]) -def test_grdhisteq_outgrid(grid, outgrid, expected_grid): +def test_grdhisteq_outgrid(grid, outgrid, expected_grid, region): """ - Test the quadratic and region parameters for grdhisteq with - ``outgrid=True`` and ``outgrid=None``. + Test grdhisteq.equalize_grid with ``outgrid=True`` and ``outgrid=None``. """ temp_grid = grdhisteq.equalize_grid( - grid=grid, quadratic=True, region=[-3, 1, 2, 5], outgrid=outgrid + grid=grid, divisions=2, region=region, outgrid=outgrid ) assert temp_grid.gmt.gtype == 1 # Geographic grid assert temp_grid.gmt.registration == 1 # Pixel registration xr.testing.assert_allclose(a=temp_grid, b=expected_grid) -def test_grdhisteq_no_outgrid(grid, expected_df): +def test_grdhisteq_no_outfile(grid, expected_df, region): """ - Test the quadratic and region parameters for grdhisteq with no ``outgrid``. + Test grdhisteq.compute_bins with no ``outfile``. """ - temp_df = grdhisteq.compute_bins( - grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=True - ) + temp_df = grdhisteq.compute_bins(grid=grid, divisions=2, region=region) assert isinstance(temp_df, pd.DataFrame) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) -def test_grdhisteq_outfile(grid, expected_df): +def test_grdhisteq_outfile(grid, expected_df, region): """ - Test the quadratic and region parameters for grdhisteq with no ``outgrid``. + Test grdhisteq.compute_bins with ``outfile``. """ with GMTTempFile(suffix=".txt") as tmpfile: result = grdhisteq.compute_bins( - grid=grid, quadratic=True, region=[-3, 1, 2, 5], outfile=tmpfile.name + grid=grid, divisions=2, region=region, outfile=tmpfile.name ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) From b29ef794b1660a86f4319b69859d9561207257e5 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 25 Feb 2022 13:13:43 -0500 Subject: [PATCH 19/47] Fix docstring --- pygmt/src/grdhisteq.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index e5c94bfdccd..d00f07717c6 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -21,10 +21,10 @@ class grdhisteq: # pylint: disable=invalid-name Perform histogram equalization for a grid. Two common use cases of :meth:`pygmt.grdhisteq` are to find data values - that divide a grid into patches of equal area using - :meth:`pygmt.grdhisteq.equalize_grid` or to write a grid with + that divide a grid into patches of equal area + (:meth:`pygmt.grdhisteq.compute_bins`) or to write a grid with statistics based on some kind of cumulative distribution function using - :meth:`pygmt.grdhisteq.compute_bins`. + (:meth:`pygmt.grdhisteq.equalize_grid`). Histogram equalization provides a way to highlight data that has most values clustered in a small portion of the dynamic range, such as a From d985b9895c243d4b91467046ad74d45abf34e3af Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sun, 27 Feb 2022 13:19:13 -0500 Subject: [PATCH 20/47] Do not use aliases in public grdhisteq functions --- pygmt/src/grdhisteq.py | 123 +++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 36 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index d00f07717c6..9291679537f 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -48,6 +48,17 @@ class grdhisteq: # pylint: disable=invalid-name """ @staticmethod + @fmt_docstring + @use_alias( + C="divisions", + D="outfile", + G="outgrid", + R="region", + N="gaussian", + Q="quadratic", + V="verbose", + ) + @kwargs_to_strings(R="sequence") def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): r""" Perform histogram equalization for a grid. @@ -93,7 +104,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): The name of the output ASCII file to store the results of the histogram equalization in. Not allowed if ``outgrid`` is used. divisions : int - Set the number of divisions of the data range. + Set the number of divisions of the data range [Default is 16]. {R} {V} @@ -132,17 +143,16 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): return result @staticmethod - @fmt_docstring - @use_alias( - C="divisions", - G="outgrid", - R="region", - N="gaussian", - Q="quadratic", - V="verbose", - ) - @kwargs_to_strings(R="sequence") - def equalize_grid(grid, **kwargs): + def equalize_grid( + grid, + *, + outgrid=True, + divisions=None, + region=None, + gaussian=None, + quadratic=None, + verbose=None, + ): r""" Perform histogram equalization for a grid. @@ -155,8 +165,6 @@ def equalize_grid(grid, **kwargs): Full option list at :gmt-docs:`grdhisteq.html` - {aliases} - Parameters ---------- grid : str or xarray.DataArray @@ -166,9 +174,28 @@ def equalize_grid(grid, **kwargs): grid in. divisions : int Set the number of divisions of the data range. + gaussian : bool or int or float + *norm* + Produce an output grid with standard normal scores using + ``gaussian=True`` or force the scores to fall in the ±\ *norm* + range. + quadratic: bool + Perform quadratic equalization [Default is linear]. + region : str or list + *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. + Specify the :doc:`region ` of interest. + verbose : bool or str + Select verbosity level [Default is **w**], which modulates the messages + written to stderr. Choose among 7 levels of verbosity: + + - **q** - Quiet, not even fatal error messages are produced + - **e** - Error messages only + - **w** - Warnings [Default] + - **t** - Timings (report runtimes for time-intensive algorithms); + - **i** - Informational messages (same as ``verbose=True``) + - **c** - Compatibility warnings + - **d** - Debugging message - {R} - {V} Returns ------- @@ -184,32 +211,36 @@ def equalize_grid(grid, **kwargs): :meth:`pygmt.grd2cpt` """ # Return a xarray.DataArray if ``outgrid`` is not set - if "G" in kwargs and isinstance(kwargs["G"], str): + if isinstance(outgrid, str): output_type = "file" else: output_type = "xarray" with GMTTempFile(suffix=".nc") as tmpfile: if output_type != "file": - kwargs.update({"G": tmpfile.name}) + outgrid = tmpfile.name return grdhisteq._grdhisteq( grid=grid, output_type=output_type, tmpfile=tmpfile, - **kwargs, + outgrid=outgrid, + divisions=divisions, + region=region, + gaussian=gaussian, + quadratic=quadratic, + verbose=verbose, ) @staticmethod - @fmt_docstring - @use_alias( - C="divisions", - D="outfile", - R="region", - N="gaussian", - Q="quadratic", - V="verbose", - ) - @kwargs_to_strings(R="sequence") - def compute_bins(grid, output_type="pandas", **kwargs): + def compute_bins( + grid, + *, + output_type="pandas", + outfile=None, + divisions=None, + quadratic=None, + verbose=None, + region=None, + ): r""" Perform histogram equalization for a grid. @@ -229,7 +260,6 @@ def compute_bins(grid, output_type="pandas", **kwargs): Full option list at :gmt-docs:`grdhisteq.html` - {aliases} Parameters ---------- @@ -247,9 +277,23 @@ def compute_bins(grid, output_type="pandas", **kwargs): - ``file`` - ASCII file (requires ``outfile``) divisions : int Set the number of divisions of the data range. + quadratic: bool + Perform quadratic equalization [Default is linear]. + region : str or list + *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. + Specify the :doc:`region ` of interest. + verbose : bool or str + Select verbosity level [Default is **w**], which modulates the messages + written to stderr. Choose among 7 levels of verbosity: + + - **q** - Quiet, not even fatal error messages are produced + - **e** - Error messages only + - **w** - Warnings [Default] + - **t** - Timings (report runtimes for time-intensive algorithms); + - **i** - Informational messages (same as ``verbose=True``) + - **c** - Compatibility warnings + - **d** - Debugging message - {R} - {V} Returns ------- @@ -270,7 +314,7 @@ def compute_bins(grid, output_type="pandas", **kwargs): "Must specify 'output_type' either as 'numpy', 'pandas' or 'file'." ) - if "D" in kwargs and output_type != "file": + if isinstance(outfile, str) and output_type != "file": msg = ( f"Changing 'output_type' of grd2xyz from '{output_type}' to 'file' " "since 'outfile' parameter is set. Please use output_type='file' " @@ -280,7 +324,14 @@ def compute_bins(grid, output_type="pandas", **kwargs): output_type = "file" with GMTTempFile(suffix=".txt") as tmpfile: if output_type != "file": - kwargs.update({"D": tmpfile.name}) + outfile = tmpfile.name return grdhisteq._grdhisteq( - grid, output_type=output_type, tmpfile=tmpfile, **kwargs + grid, + output_type=output_type, + tmpfile=tmpfile, + outfile=outfile, + divisions=divisions, + quadratic=quadratic, + verbose=verbose, + region=region, ) From a41423ac959c0458cd067931b6b6108b36e792c7 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Sun, 27 Feb 2022 13:21:35 -0500 Subject: [PATCH 21/47] Format --- pygmt/src/grdhisteq.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 9291679537f..637684ba91c 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -185,8 +185,8 @@ def equalize_grid( *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. Specify the :doc:`region ` of interest. verbose : bool or str - Select verbosity level [Default is **w**], which modulates the messages - written to stderr. Choose among 7 levels of verbosity: + Select verbosity level [Default is **w**], which modulates the + messages written to stderr. Choose among 7 levels of verbosity: - **q** - Quiet, not even fatal error messages are produced - **e** - Error messages only @@ -283,8 +283,8 @@ def compute_bins( *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. Specify the :doc:`region ` of interest. verbose : bool or str - Select verbosity level [Default is **w**], which modulates the messages - written to stderr. Choose among 7 levels of verbosity: + Select verbosity level [Default is **w**], which modulates the + messages written to stderr. Choose among 7 levels of verbosity: - **q** - Quiet, not even fatal error messages are produced - **e** - Error messages only From b84c74571e2e801d9a8e9a7062d291c78516ba60 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 28 Feb 2022 16:36:14 -0500 Subject: [PATCH 22/47] Add default column names --- pygmt/src/grdhisteq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 637684ba91c..fcedab4d0d1 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -137,7 +137,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): if output_type == "xarray": return load_dataarray(tmpfile.name) - result = pd.read_csv(kwargs["D"], sep="\t", header=None) + result = pd.read_csv(kwargs["D"], sep="\t", header=None, names=["start", "stop", "bin_id"]) if output_type == "numpy": result = result.to_numpy() return result From 033883726ff76fd7a54a78d81cf1b50d762eacb9 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 28 Feb 2022 16:38:34 -0500 Subject: [PATCH 23/47] Format --- pygmt/src/grdhisteq.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index fcedab4d0d1..fdc5ea345e1 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -137,7 +137,9 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): if output_type == "xarray": return load_dataarray(tmpfile.name) - result = pd.read_csv(kwargs["D"], sep="\t", header=None, names=["start", "stop", "bin_id"]) + result = pd.read_csv( + kwargs["D"], sep="\t", header=None, names=["start", "stop", "bin_id"] + ) if output_type == "numpy": result = result.to_numpy() return result From ed5a9ffb3d9e9d4b04a306f64c9f14d53adddf08 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 28 Feb 2022 17:31:30 -0500 Subject: [PATCH 24/47] Add inline examples --- pygmt/src/grdhisteq.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index fdc5ea345e1..1d54827f901 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -208,6 +208,19 @@ def equalize_grid( - None if ``outgrid`` is a str (grid output is stored in ``outgrid``) + Example + ------- + >>> import pygmt # doctest: +SKIP + >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to 30, + >>> # and a y-range of 15 to 25 + >>> grid = pygmt.datasets.load_earth_relief( + ... resolution="30m", region=[10, 30, 15, 25] + ... ) # doctest: +SKIP + >>> # Create a new grid with a Guassian data distribution + >>> grid = pygmt.grdhisteq.equalize_grid( + ... grid=grid, gaussian=True + ... ) # doctest: +SKIP + See Also ------- :meth:`pygmt.grd2cpt` @@ -306,6 +319,20 @@ def compute_bins( - None if ``outfile`` is a str (file output is stored in ``outfile``) + Example + ------- + >>> import pygmt # doctest: +SKIP + >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to 30, + >>> # and a y-range of 15 to 25 + >>> grid = pygmt.datasets.load_earth_relief( + ... resolution="30m", region=[10, 30, 15, 25] + ... ) # doctest: +SKIP + >>> # Find the elevation intervals that divide the grid into 5 divisions + >>> # of equal area + >>> bins = pygmt.grdhisteq.compute_bins( + ... grid=grid, divisions=5 + ... ) # doctest: +SKIP + See Also ------- :meth:`pygmt.grd2cpt` From 0d1e6fbaa94b426ab77040f71de3bc75b455bfc5 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 28 Feb 2022 17:38:34 -0500 Subject: [PATCH 25/47] Format --- pygmt/src/grdhisteq.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 1d54827f901..7114fbba4b8 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -211,8 +211,8 @@ def equalize_grid( Example ------- >>> import pygmt # doctest: +SKIP - >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to 30, - >>> # and a y-range of 15 to 25 + >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to + >>> # 30, and a y-range of 15 to 25 >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP @@ -322,8 +322,8 @@ def compute_bins( Example ------- >>> import pygmt # doctest: +SKIP - >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to 30, - >>> # and a y-range of 15 to 25 + >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to + >>> # 30, and a y-range of 15 to 25 >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP From c6d53682d6d5cb1db22b335f8e3bad938b976c7e Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Mon, 28 Feb 2022 17:48:56 -0500 Subject: [PATCH 26/47] Format --- pygmt/src/grdhisteq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 7114fbba4b8..06b4218a778 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -327,8 +327,8 @@ def compute_bins( >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP - >>> # Find the elevation intervals that divide the grid into 5 divisions - >>> # of equal area + >>> # Find the elevation intervals that divide the grid into 5 + >>> # divisions of equal area >>> bins = pygmt.grdhisteq.compute_bins( ... grid=grid, divisions=5 ... ) # doctest: +SKIP From 7031d931e8838c8ebce579ad2825175ff2ffacf2 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Wed, 2 Mar 2022 15:31:11 -0500 Subject: [PATCH 27/47] Fix tests --- pygmt/tests/test_grdhisteq.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index d3814e69be4..5e614fc567d 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -51,11 +51,12 @@ def fixture_df_result(): [345.5, 519.5, 0], [519.5, 726.5, 1], ] - ) - ).astype({0: np.float64, 1: np.float64, 2: np.int64}) + ), + columns=["start", "stop", "bin_id"], + ).astype({"start": np.float64, "stop": np.float64, "bin_id": np.int64}) -def test_grdhisteq_outgrid_file(grid, expected_grid, region): +def test_equalize_grid_outgrid_file(grid, expected_grid, region): """ Test grdhisteq.equalize_grid with a set outgrid. """ @@ -70,7 +71,7 @@ def test_grdhisteq_outgrid_file(grid, expected_grid, region): @pytest.mark.parametrize("outgrid", [True, None]) -def test_grdhisteq_outgrid(grid, outgrid, expected_grid, region): +def test_equalize_grid_outgrid(grid, outgrid, expected_grid, region): """ Test grdhisteq.equalize_grid with ``outgrid=True`` and ``outgrid=None``. """ @@ -82,7 +83,7 @@ def test_grdhisteq_outgrid(grid, outgrid, expected_grid, region): xr.testing.assert_allclose(a=temp_grid, b=expected_grid) -def test_grdhisteq_no_outfile(grid, expected_df, region): +def test_compute_bins_no_outfile(grid, expected_df, region): """ Test grdhisteq.compute_bins with no ``outfile``. """ @@ -91,7 +92,7 @@ def test_grdhisteq_no_outfile(grid, expected_df, region): pd.testing.assert_frame_equal(left=temp_df, right=expected_df) -def test_grdhisteq_outfile(grid, expected_df, region): +def test_compute_bins_outfile(grid, expected_df, region): """ Test grdhisteq.compute_bins with ``outfile``. """ @@ -101,5 +102,7 @@ def test_grdhisteq_outfile(grid, expected_df, region): ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) - temp_df = pd.read_csv(tmpfile.name, sep="\t", header=None) + temp_df = pd.read_csv( + tmpfile.name, sep="\t", header=None, names=["start", "stop", "bin_id"] + ) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) From d479a29fbfa94b5eeae6f23191e79be78bed9ae5 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 11:44:03 -0500 Subject: [PATCH 28/47] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/src/grdhisteq.py | 55 ++++++++++------------------------- pygmt/tests/test_grdhisteq.py | 7 +---- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 06b4218a778..ed4978d9425 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -145,6 +145,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): return result @staticmethod + @fmt_docstring def equalize_grid( grid, *, @@ -177,27 +178,14 @@ def equalize_grid( divisions : int Set the number of divisions of the data range. gaussian : bool or int or float - *norm* + *norm*. Produce an output grid with standard normal scores using ``gaussian=True`` or force the scores to fall in the ±\ *norm* range. quadratic: bool Perform quadratic equalization [Default is linear]. - region : str or list - *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. - Specify the :doc:`region ` of interest. - verbose : bool or str - Select verbosity level [Default is **w**], which modulates the - messages written to stderr. Choose among 7 levels of verbosity: - - - **q** - Quiet, not even fatal error messages are produced - - **e** - Error messages only - - **w** - Warnings [Default] - - **t** - Timings (report runtimes for time-intensive algorithms); - - **i** - Informational messages (same as ``verbose=True``) - - **c** - Compatibility warnings - - **d** - Debugging message - + {R} + {V} Returns ------- @@ -216,7 +204,7 @@ def equalize_grid( >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP - >>> # Create a new grid with a Guassian data distribution + >>> # Create a new grid with a Gaussian data distribution >>> grid = pygmt.grdhisteq.equalize_grid( ... grid=grid, gaussian=True ... ) # doctest: +SKIP @@ -225,13 +213,12 @@ def equalize_grid( ------- :meth:`pygmt.grd2cpt` """ - # Return a xarray.DataArray if ``outgrid`` is not set - if isinstance(outgrid, str): - output_type = "file" - else: - output_type = "xarray" + # Return an xarray.DataArray if ``outgrid`` is not set with GMTTempFile(suffix=".nc") as tmpfile: - if output_type != "file": + if isinstance(outgrid, str): + output_type = "file" + else: + output_type = "xarray" outgrid = tmpfile.name return grdhisteq._grdhisteq( grid=grid, @@ -246,6 +233,7 @@ def equalize_grid( ) @staticmethod + @fmt_docstring def compute_bins( grid, *, @@ -292,27 +280,14 @@ def compute_bins( - ``file`` - ASCII file (requires ``outfile``) divisions : int Set the number of divisions of the data range. - quadratic: bool + quadratic : bool Perform quadratic equalization [Default is linear]. - region : str or list - *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. - Specify the :doc:`region ` of interest. - verbose : bool or str - Select verbosity level [Default is **w**], which modulates the - messages written to stderr. Choose among 7 levels of verbosity: - - - **q** - Quiet, not even fatal error messages are produced - - **e** - Error messages only - - **w** - Warnings [Default] - - **t** - Timings (report runtimes for time-intensive algorithms); - - **i** - Informational messages (same as ``verbose=True``) - - **c** - Compatibility warnings - - **d** - Debugging message - + {R} + {V} Returns ------- - ret: pandas.DataFrame None + ret: pandas.DataFrame or None Return type depends on the ``outfile`` parameter: - pandas.DataFrame if ``outfile`` is True or None diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 5e614fc567d..d179d9701b1 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -46,12 +46,7 @@ def fixture_df_result(): Load the expected grdhisteq table result. """ return pd.DataFrame( - data=np.array( - [ - [345.5, 519.5, 0], - [519.5, 726.5, 1], - ] - ), + data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), columns=["start", "stop", "bin_id"], ).astype({"start": np.float64, "stop": np.float64, "bin_id": np.int64}) From 325f72cad9ac1fe6345e2f5886a07044a613deff Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:00:27 -0500 Subject: [PATCH 29/47] Remove two extra blank lines --- pygmt/src/grdhisteq.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index ed4978d9425..695acac0cde 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -159,7 +159,6 @@ def equalize_grid( r""" Perform histogram equalization for a grid. - :meth:`pygmt.grdhisteq.equalize_grid` provides a way to write a grid with statistics based on a cumulative distribution function. The ``outgrid`` has relative highs and lows in the same (x,y) locations as @@ -263,7 +262,6 @@ def compute_bins( Full option list at :gmt-docs:`grdhisteq.html` - Parameters ---------- grid : str or xarray.DataArray From d7c22e05282b31fda14d03b36f3ee86b68a57f0b Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:02:06 -0500 Subject: [PATCH 30/47] Fix wording --- pygmt/src/grdhisteq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 695acac0cde..205b32e193d 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -300,7 +300,7 @@ def compute_bins( >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP - >>> # Find the elevation intervals that divide the grid into 5 + >>> # Find elevation intervals that divide the grid into 5 >>> # divisions of equal area >>> bins = pygmt.grdhisteq.compute_bins( ... grid=grid, divisions=5 From 7cccc333b0dbaee50cb6b10bf7b1adde27bd7254 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:11:44 -0500 Subject: [PATCH 31/47] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/src/grdhisteq.py | 11 ++++++++++- pygmt/tests/test_grdhisteq.py | 8 ++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 205b32e193d..de0343868f0 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -3,6 +3,7 @@ """ import warnings +import numpy as np import pandas as pd from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput @@ -138,7 +139,15 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): return load_dataarray(tmpfile.name) result = pd.read_csv( - kwargs["D"], sep="\t", header=None, names=["start", "stop", "bin_id"] + filepath_or_buffer=kwargs["D"], + sep="\t", + header=None, + names=["start", "stop", "bin_id"], + dtype={ + "start": np.float32, + "stop": np.float32, + "bin_id": np.uint8 if kwargs["C"] < 256 else np.uint32, + }, ) if output_type == "numpy": result = result.to_numpy() diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index d179d9701b1..53439c5cfcb 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -48,7 +48,7 @@ def fixture_df_result(): return pd.DataFrame( data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), columns=["start", "stop", "bin_id"], - ).astype({"start": np.float64, "stop": np.float64, "bin_id": np.int64}) + ).astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint8}) def test_equalize_grid_outgrid_file(grid, expected_grid, region): @@ -98,6 +98,10 @@ def test_compute_bins_outfile(grid, expected_df, region): assert result is None # return value is None assert os.path.exists(path=tmpfile.name) temp_df = pd.read_csv( - tmpfile.name, sep="\t", header=None, names=["start", "stop", "bin_id"] + filepath_or_buffer=tmpfile.name, + sep="\t", + header=None, + names=["start", "stop", "bin_id"], + dtype={"start": np.float32, "stop": np.float32, "bin_id": np.uint8}, ) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) From d6d8a69e9690277e49ddeb75f80970f647bcd99c Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:15:54 -0500 Subject: [PATCH 32/47] Update dtypes --- pygmt/src/grdhisteq.py | 2 +- pygmt/tests/test_grdhisteq.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index de0343868f0..17a073153db 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -146,7 +146,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): dtype={ "start": np.float32, "stop": np.float32, - "bin_id": np.uint8 if kwargs["C"] < 256 else np.uint32, + "bin_id": np.uint32, }, ) if output_type == "numpy": diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 53439c5cfcb..4feb0d81626 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -48,7 +48,7 @@ def fixture_df_result(): return pd.DataFrame( data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), columns=["start", "stop", "bin_id"], - ).astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint8}) + ).astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint32}) def test_equalize_grid_outgrid_file(grid, expected_grid, region): @@ -102,6 +102,6 @@ def test_compute_bins_outfile(grid, expected_df, region): sep="\t", header=None, names=["start", "stop", "bin_id"], - dtype={"start": np.float32, "stop": np.float32, "bin_id": np.uint8}, + dtype={"start": np.float32, "stop": np.float32, "bin_id": np.uint32}, ) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) From 76ba9f0839d9ef85ab136011b50310a365deaa5f Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:33:56 -0500 Subject: [PATCH 33/47] Add note about weighted equalization --- pygmt/src/grdhisteq.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 17a073153db..6c0e4159c82 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -220,6 +220,11 @@ def equalize_grid( See Also ------- :meth:`pygmt.grd2cpt` + + Notes + ----- + This method does a weighted histogram equalization for geographic + grids to account for node area varying with latitude. """ # Return an xarray.DataArray if ``outgrid`` is not set with GMTTempFile(suffix=".nc") as tmpfile: @@ -318,6 +323,11 @@ def compute_bins( See Also ------- :meth:`pygmt.grd2cpt` + + Notes + ----- + This method does a weighted histogram equalization for geographic + grids to account for node area varying with latitude. """ # Return a pandas.DataFrame if ``outfile`` is not set if output_type not in ["numpy", "pandas", "file"]: From 32757d88e916e2a1087d69e956481f29727ac3ac Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 14:37:07 -0500 Subject: [PATCH 34/47] Try to improve wording in example --- pygmt/src/grdhisteq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 6c0e4159c82..2ade4953825 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -314,8 +314,8 @@ def compute_bins( >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP - >>> # Find elevation intervals that divide the grid into 5 - >>> # divisions of equal area + >>> # Find elevation intervals that divide the data range into + >>> # divisions which have an equal area in the original grid. >>> bins = pygmt.grdhisteq.compute_bins( ... grid=grid, divisions=5 ... ) # doctest: +SKIP From 938b729fbe7532f5170496cfaa9d9ee8eef3c1f4 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 15:13:34 -0500 Subject: [PATCH 35/47] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/src/grdhisteq.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 2ade4953825..def15d40b2c 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -314,11 +314,18 @@ def compute_bins( >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) # doctest: +SKIP - >>> # Find elevation intervals that divide the data range into - >>> # divisions which have an equal area in the original grid. + >>> # Find elevation intervals that splits the data range into 5 + >>> # divisions, each of which have an equal area in the original grid. >>> bins = pygmt.grdhisteq.compute_bins( ... grid=grid, divisions=5 ... ) # doctest: +SKIP + >>> print(bins) # doctest: +SKIP + start stop bin_id + 0 -3.023719 -0.846985 0 + 1 -0.846985 -0.257874 1 + 2 -0.257874 0.248180 2 + 3 0.248180 0.838059 3 + 4 0.838059 3.023719 4 See Also ------- From 71ed9bbfae3947e75d8e2bea88e1a39fbd6c0898 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 15:37:36 -0500 Subject: [PATCH 36/47] Set index for DataFrame output --- pygmt/src/grdhisteq.py | 5 +++-- pygmt/tests/test_grdhisteq.py | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index def15d40b2c..53e5031540b 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -150,8 +150,9 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): }, ) if output_type == "numpy": - result = result.to_numpy() - return result + return result.to_numpy() + + return result.set_index("bin_id") @staticmethod @fmt_docstring diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 4feb0d81626..ba4edcc9566 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -45,10 +45,14 @@ def fixture_df_result(): """ Load the expected grdhisteq table result. """ - return pd.DataFrame( - data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), - columns=["start", "stop", "bin_id"], - ).astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint32}) + return ( + pd.DataFrame( + data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), + columns=["start", "stop", "bin_id"], + ) + .astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint32}) + .set_index("bin_id") + ) def test_equalize_grid_outgrid_file(grid, expected_grid, region): @@ -93,7 +97,11 @@ def test_compute_bins_outfile(grid, expected_df, region): """ with GMTTempFile(suffix=".txt") as tmpfile: result = grdhisteq.compute_bins( - grid=grid, divisions=2, region=region, outfile=tmpfile.name + grid=grid, + divisions=2, + region=region, + outfile=tmpfile.name, + output_type="file", ) assert result is None # return value is None assert os.path.exists(path=tmpfile.name) @@ -103,5 +111,6 @@ def test_compute_bins_outfile(grid, expected_df, region): header=None, names=["start", "stop", "bin_id"], dtype={"start": np.float32, "stop": np.float32, "bin_id": np.uint32}, + index_col="bin_id", ) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) From 492980fc0ae5471f1cdd4e04df4f1eb61d195be8 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 15:42:09 -0500 Subject: [PATCH 37/47] Update docstring example --- pygmt/src/grdhisteq.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 53e5031540b..e827bc20ed8 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -321,12 +321,13 @@ def compute_bins( ... grid=grid, divisions=5 ... ) # doctest: +SKIP >>> print(bins) # doctest: +SKIP - start stop bin_id - 0 -3.023719 -0.846985 0 - 1 -0.846985 -0.257874 1 - 2 -0.257874 0.248180 2 - 3 0.248180 0.838059 3 - 4 0.838059 3.023719 4 + start stop + bin_id + 0 179.0 397.5 + 1 397.5 475.5 + 2 475.5 573.5 + 3 573.5 710.5 + 4 710.5 2103.0 See Also ------- From 09f138550aaf68d237ac369ded184e1d78c695fd Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 15:47:56 -0500 Subject: [PATCH 38/47] Add test for invalid input --- pygmt/src/grdhisteq.py | 2 +- pygmt/tests/test_grdhisteq.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index e827bc20ed8..15ca45c38ed 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -346,7 +346,7 @@ def compute_bins( if isinstance(outfile, str) and output_type != "file": msg = ( - f"Changing 'output_type' of grd2xyz from '{output_type}' to 'file' " + f"Changing 'output_type' from '{output_type}' to 'file' " "since 'outfile' parameter is set. Please use output_type='file' " "to silence this warning." ) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index ba4edcc9566..fd4abd3a450 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -8,6 +8,7 @@ import pytest import xarray as xr from pygmt import grdhisteq, load_dataarray +from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import GMTTempFile from pygmt.helpers.testing import load_static_earth_relief @@ -114,3 +115,11 @@ def test_compute_bins_outfile(grid, expected_df, region): index_col="bin_id", ) pd.testing.assert_frame_equal(left=temp_df, right=expected_df) + + +def test_compute_bins_invalid_format(grid): + """ + Test that compute_bins fails with incorrect format. + """ + with pytest.raises(GMTInvalidInput): + grdhisteq.compute_bins(grid=grid, output_type=1) From 77937cf2604952a91b7b489b13259485f0a84335 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Thu, 3 Mar 2022 16:23:28 -0500 Subject: [PATCH 39/47] Add test for numpy output --- pygmt/tests/test_grdhisteq.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index fd4abd3a450..d3f5dc64d4a 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -46,14 +46,10 @@ def fixture_df_result(): """ Load the expected grdhisteq table result. """ - return ( - pd.DataFrame( - data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), - columns=["start", "stop", "bin_id"], - ) - .astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint32}) - .set_index("bin_id") - ) + return pd.DataFrame( + data=np.array([[345.5, 519.5, 0], [519.5, 726.5, 1]]), + columns=["start", "stop", "bin_id"], + ).astype({"start": np.float32, "stop": np.float32, "bin_id": np.uint32}) def test_equalize_grid_outgrid_file(grid, expected_grid, region): @@ -89,7 +85,18 @@ def test_compute_bins_no_outfile(grid, expected_df, region): """ temp_df = grdhisteq.compute_bins(grid=grid, divisions=2, region=region) assert isinstance(temp_df, pd.DataFrame) - pd.testing.assert_frame_equal(left=temp_df, right=expected_df) + pd.testing.assert_frame_equal(left=temp_df, right=expected_df.set_index("bin_id")) + + +def test_compute_bins_ndarray_output(grid, expected_df, region): + """ + Test grdhisteq.compute_bins with "numpy" output type. + """ + temp_array = grdhisteq.compute_bins( + grid=grid, divisions=2, region=region, output_type="numpy" + ) + assert isinstance(temp_array, np.ndarray) + np.testing.assert_allclose(temp_array, expected_df.to_numpy()) def test_compute_bins_outfile(grid, expected_df, region): @@ -114,7 +121,9 @@ def test_compute_bins_outfile(grid, expected_df, region): dtype={"start": np.float32, "stop": np.float32, "bin_id": np.uint32}, index_col="bin_id", ) - pd.testing.assert_frame_equal(left=temp_df, right=expected_df) + pd.testing.assert_frame_equal( + left=temp_df, right=expected_df.set_index("bin_id") + ) def test_compute_bins_invalid_format(grid): From c5afdcc199fc073b25cb2108663576f42e8a1a98 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 4 Mar 2022 10:59:18 -0500 Subject: [PATCH 40/47] Apply suggestions from code review Co-authored-by: Dongdong Tian --- pygmt/src/grdhisteq.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 15ca45c38ed..ef5a80dbd81 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -24,7 +24,7 @@ class grdhisteq: # pylint: disable=invalid-name Two common use cases of :meth:`pygmt.grdhisteq` are to find data values that divide a grid into patches of equal area (:meth:`pygmt.grdhisteq.compute_bins`) or to write a grid with - statistics based on some kind of cumulative distribution function using + statistics based on some kind of cumulative distribution function (:meth:`pygmt.grdhisteq.equalize_grid`). Histogram equalization provides a way to highlight data that has most @@ -58,6 +58,7 @@ class grdhisteq: # pylint: disable=invalid-name N="gaussian", Q="quadratic", V="verbose", + h="header", ) @kwargs_to_strings(R="sequence") def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): @@ -109,6 +110,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): {R} {V} + {h} Returns ------- From 245c560a498ed0d95e74019ffddcc3721797c376 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Fri, 4 Mar 2022 11:26:11 -0500 Subject: [PATCH 41/47] Increase code coverage --- pygmt/tests/test_grdhisteq.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index d3f5dc64d4a..9a061649e3f 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -104,13 +104,14 @@ def test_compute_bins_outfile(grid, expected_df, region): Test grdhisteq.compute_bins with ``outfile``. """ with GMTTempFile(suffix=".txt") as tmpfile: - result = grdhisteq.compute_bins( - grid=grid, - divisions=2, - region=region, - outfile=tmpfile.name, - output_type="file", - ) + with pytest.warns(RuntimeWarning) as record: + result = grdhisteq.compute_bins( + grid=grid, + divisions=2, + region=region, + outfile=tmpfile.name, + ) + assert len(record) == 1 # check that only one warning was raised assert result is None # return value is None assert os.path.exists(path=tmpfile.name) temp_df = pd.read_csv( From c5e430fefea94df7bcae2a9e79bdecbe230467a6 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 16:28:00 -0500 Subject: [PATCH 42/47] Remove redundant docs --- pygmt/src/grdhisteq.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index ef5a80dbd81..35f8d887bcc 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -65,30 +65,6 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): r""" Perform histogram equalization for a grid. - Two common use cases of :meth:`pygmt.grdhisteq` are to find data values - that divide a grid into patches of equal area or to write a grid with - statistics based on some kind of cumulative distribution function. - - Histogram equalization provides a way to highlight data that has most - values clustered in a small portion of the dynamic range, such as a - grid of flat topography with a mountain in the middle. Ordinary gray - shading of this grid (using :meth:`pygmt.Figure.grdimage` or - :meth:`pygmt.Figure.grdview`) with a linear mapping from topography to - graytone will result in most of the image being very dark gray, with - the mountain being almost white. :meth:`pygmt.grdhisteq` can provide a - list of data values that divide the data range into divisions which - have an equal area in the image [Default is 16 if ``divisions`` is not - set]. The :class:`pandas.DataFrame` or ASCII file output can be used to - make a colormap with :meth:`pygmt.makecpt` and an image with - :meth:`pygmt.Figure.grdimage` that has all levels of gray occuring - equally. - - :meth:`pygmt.grdhisteq` also provides a way to write a grid with - statistics based on a cumulative distribution function. In this - application, the ``outgrid`` has relative highs and lows in the same - (x,y) locations as the ``grid``, but the values are changed to reflect - their place in the cumulative distribution. - Must provide ``outfile`` or ``outgrid``. Full option list at :gmt-docs:`grdhisteq.html` From 50174406a81c99fa620033bd5d529d87887344ac Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 16:39:41 -0500 Subject: [PATCH 43/47] Do not pass tmpfile to _grdhisteq --- pygmt/src/grdhisteq.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 35f8d887bcc..319780b9dbd 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -61,7 +61,7 @@ class grdhisteq: # pylint: disable=invalid-name h="header", ) @kwargs_to_strings(R="sequence") - def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): + def _grdhisteq(grid, output_type=None, **kwargs): r""" Perform histogram equalization for a grid. @@ -80,7 +80,10 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): grid in. outfile : str or bool or None The name of the output ASCII file to store the results of the - histogram equalization in. Not allowed if ``outgrid`` is used. + histogram equalization in. + output_type: str + Determines the output type. Use "file", "xarray", "pandas", or + "numpy". divisions : int Set the number of divisions of the data range [Default is 16]. @@ -93,12 +96,11 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): ret: pandas.DataFrame or xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - - pandas.DataFrame if ``outfile`` is True - - xarray.DataArray if ``outgrid`` is True - - None if ``outgrid`` is a str (grid output is stored in - ``outgrid``) - - None if ``outfile`` is a str (file output is stored in - ``outfile``) + - xarray.DataArray if ``output_type`` is "xarray"" + - numpy.ndarray if ``output_type`` is "numpy" + - pandas.DataFrame if ``output_type`` is "pandas" + - None if ``output_type`` is "file" (output is stored in + ``outgrid`` or ``outfile``) See Also ------- @@ -114,7 +116,7 @@ def _grdhisteq(grid, output_type=None, tmpfile=None, **kwargs): if output_type == "file": return None if output_type == "xarray": - return load_dataarray(tmpfile.name) + return load_dataarray(kwargs["G"]) result = pd.read_csv( filepath_or_buffer=kwargs["D"], @@ -215,7 +217,6 @@ def equalize_grid( return grdhisteq._grdhisteq( grid=grid, output_type=output_type, - tmpfile=tmpfile, outgrid=outgrid, divisions=divisions, region=region, @@ -336,7 +337,6 @@ def compute_bins( return grdhisteq._grdhisteq( grid, output_type=output_type, - tmpfile=tmpfile, outfile=outfile, divisions=divisions, quadratic=quadratic, From 8b5fc4988244a679eb3061f7981c2dfddc72f2c1 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 16:40:30 -0500 Subject: [PATCH 44/47] Make output_type a required argument --- pygmt/src/grdhisteq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 319780b9dbd..609854a7ba7 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -61,7 +61,7 @@ class grdhisteq: # pylint: disable=invalid-name h="header", ) @kwargs_to_strings(R="sequence") - def _grdhisteq(grid, output_type=None, **kwargs): + def _grdhisteq(grid, output_type, **kwargs): r""" Perform histogram equalization for a grid. From 75f61e3ce91c5a81ceb783829f27f28200e66b98 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 17:01:12 -0500 Subject: [PATCH 45/47] Use pytest-doctestplus for skipping doctests --- pygmt/src/grdhisteq.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 609854a7ba7..1833610b72b 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -16,6 +16,8 @@ ) from pygmt.io import load_dataarray +__doctest_skip__ = ["grdhisteq.*"] + class grdhisteq: # pylint: disable=invalid-name r""" @@ -187,16 +189,14 @@ def equalize_grid( Example ------- - >>> import pygmt # doctest: +SKIP + >>> import pygmt >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to >>> # 30, and a y-range of 15 to 25 >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] - ... ) # doctest: +SKIP + ... ) >>> # Create a new grid with a Gaussian data distribution - >>> grid = pygmt.grdhisteq.equalize_grid( - ... grid=grid, gaussian=True - ... ) # doctest: +SKIP + >>> grid = pygmt.grdhisteq.equalize_grid(grid=grid, gaussian=True) See Also ------- @@ -288,18 +288,16 @@ def compute_bins( Example ------- - >>> import pygmt # doctest: +SKIP + >>> import pygmt >>> # Load a grid of @earth_relief_30m data, with an x-range of 10 to >>> # 30, and a y-range of 15 to 25 >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] - ... ) # doctest: +SKIP + ... ) >>> # Find elevation intervals that splits the data range into 5 >>> # divisions, each of which have an equal area in the original grid. - >>> bins = pygmt.grdhisteq.compute_bins( - ... grid=grid, divisions=5 - ... ) # doctest: +SKIP - >>> print(bins) # doctest: +SKIP + >>> bins = pygmt.grdhisteq.compute_bins(grid=grid, divisions=5) + >>> print(bins) start stop bin_id 0 179.0 397.5 From ad239238b4aed242df2c9087b84b6b7b012deb1e Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 17:01:29 -0500 Subject: [PATCH 46/47] Apply suggestions from code review Co-authored-by: Dongdong Tian --- pygmt/src/grdhisteq.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 1833610b72b..6dbe5295317 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -236,6 +236,7 @@ def compute_bins( quadratic=None, verbose=None, region=None, + header=None, ): r""" Perform histogram equalization for a grid. @@ -276,6 +277,7 @@ def compute_bins( Perform quadratic equalization [Default is linear]. {R} {V} + {h} Returns ------- @@ -340,4 +342,5 @@ def compute_bins( quadratic=quadratic, verbose=verbose, region=region, + header=header, ) From 25c1a281447881f77b9a29c3aaaf25cf6ecdf0f3 Mon Sep 17 00:00:00 2001 From: Meghan Jones Date: Tue, 8 Mar 2022 17:18:39 -0500 Subject: [PATCH 47/47] Only allow header argument for file output --- pygmt/src/grdhisteq.py | 3 +++ pygmt/tests/test_grdhisteq.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 6dbe5295317..61de7503d4e 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -323,6 +323,9 @@ def compute_bins( "Must specify 'output_type' either as 'numpy', 'pandas' or 'file'." ) + if header is not None and output_type != "file": + raise GMTInvalidInput("'header' is only allowed with output_type='file'.") + if isinstance(outfile, str) and output_type != "file": msg = ( f"Changing 'output_type' from '{output_type}' to 'file' " diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 9a061649e3f..9a4fd004b3d 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -133,3 +133,5 @@ def test_compute_bins_invalid_format(grid): """ with pytest.raises(GMTInvalidInput): grdhisteq.compute_bins(grid=grid, output_type=1) + with pytest.raises(GMTInvalidInput): + grdhisteq.compute_bins(grid=grid, output_type="pandas", header="o+c")