Source code for pygmt.src.grdhisteq

"""
grdhisteq - Perform histogram equalization for a grid.
"""

from typing import Literal

import numpy as np
import pandas as pd
import xarray as xr
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import (
    build_arg_list,
    fmt_docstring,
    kwargs_to_strings,
    use_alias,
    validate_output_table_type,
)

__doctest_skip__ = ["grdhisteq.*"]


[docs] class grdhisteq: # noqa: N801 r""" Perform histogram equalization for a grid. Two common use cases of :class:`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 (: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 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 :func:`pygmt.makecpt` and an image with :meth:`pygmt.Figure.grdimage` that has all levels of gray occurring 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. """
[docs] @staticmethod @fmt_docstring @use_alias( C="divisions", R="region", N="gaussian", Q="quadratic", V="verbose", h="header", ) @kwargs_to_strings(R="sequence") def equalize_grid( grid, outgrid: str | None = None, **kwargs ) -> xr.DataArray | None: 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} {outgrid} 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} {verbose} Returns ------- ret Return type depends on the ``outgrid`` parameter: - :class:`xarray.DataArray` if ``outgrid`` is ``None`` - ``None`` if ``outgrid`` is a str (grid output is stored in ``outgrid``) Example ------- >>> import pygmt >>> # Load a grid of @earth_relief_30m data, with a longitude range >>> # of 10°E to 30°E, and a latitude range of 15°N to 25°N >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) >>> # Create a new grid with a Gaussian data distribution >>> grid = pygmt.grdhisteq.equalize_grid(grid=grid, gaussian=True) See Also -------- :func:`pygmt.grd2cpt` Note ---- This method does a weighted histogram equalization for geographic grids to account for node area varying with latitude. """ with Session() as lib: with ( lib.virtualfile_in(check_kind="raster", data=grid) as vingrd, lib.virtualfile_out(kind="grid", fname=outgrid) as voutgrd, ): kwargs["G"] = voutgrd lib.call_module( module="grdhisteq", args=build_arg_list(kwargs, infile=vingrd) ) return lib.virtualfile_to_raster(vfname=voutgrd, outgrid=outgrid)
[docs] @staticmethod @fmt_docstring @use_alias( C="divisions", R="region", N="gaussian", Q="quadratic", V="verbose", h="header", ) @kwargs_to_strings(R="sequence") def compute_bins( grid, output_type: Literal["pandas", "numpy", "file"] = "pandas", outfile: str | None = None, **kwargs, ) -> pd.DataFrame | np.ndarray | None: 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 :func:`pygmt.makecpt` and an image with :meth:`pygmt.Figure.grdimage` that has all levels of gray occurring equally. Full option list at :gmt-docs:`grdhisteq.html` {aliases} Parameters ---------- {grid} {output_type} {outfile} divisions : int Set the number of divisions of the data range. quadratic : bool Perform quadratic equalization [Default is linear]. {region} {verbose} {header} Returns ------- ret Return type depends on ``outfile`` and ``output_type``: - ``None`` if ``outfile`` is set (output will be stored in file set by ``outfile``) - :class:`pandas.DataFrame` or :class:`numpy.ndarray` if ``outfile`` is not set (depends on ``output_type``) Example ------- >>> import pygmt >>> # Load a grid of @earth_relief_30m data, with a longitude range of >>> # 10° E to 30° E, and a latitude range of 15° N to 25° N >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[10, 30, 15, 25] ... ) >>> # Find elevation intervals that split 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) >>> print(bins) start stop bin_id 0 183.5 395.0 1 395.0 472.0 2 472.0 575.0 3 575.0 709.5 4 709.5 1807.0 See Also -------- :func:`pygmt.grd2cpt` Note ---- This method does a weighted histogram equalization for geographic grids to account for node area varying with latitude. """ output_type = validate_output_table_type(output_type, outfile=outfile) if kwargs.get("h") is not None and output_type != "file": msg = "'header' is only allowed with output_type='file'." raise GMTInvalidInput(msg) with Session() as lib: with ( lib.virtualfile_in(check_kind="raster", data=grid) as vingrd, lib.virtualfile_out(kind="dataset", fname=outfile) as vouttbl, ): kwargs["D"] = vouttbl # -D for output file name lib.call_module( module="grdhisteq", args=build_arg_list(kwargs, infile=vingrd) ) return lib.virtualfile_to_dataset( vfname=vouttbl, output_type=output_type, column_names=["start", "stop", "bin_id"], dtype={ "start": np.float32, "stop": np.float32, "bin_id": np.uint32, }, index_col="bin_id" if output_type == "pandas" else None, )