Skip to content

Commit 2efcbba

Browse files
willschlitzerweiji14seisman
authored
Wrap grd2cpt (#803)
*Wrap grd2cpt function *Add tests for grd2cpt function *Modify docstring in makecpt function Co-authored-by: Wei Ji <[email protected]> Co-authored-by: Dongdong Tian <[email protected]>
1 parent c869531 commit 2efcbba

File tree

6 files changed

+273
-1
lines changed

6 files changed

+273
-1
lines changed

doc/api/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Color palette table generation:
4444
.. autosummary::
4545
:toctree: generated
4646

47+
grd2cpt
4748
makecpt
4849

4950
Saving and displaying the figure:

pygmt/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from pygmt.session_management import end as _end
2020
from pygmt.src import (
2121
blockmedian,
22+
grd2cpt,
2223
grdcut,
2324
grdfilter,
2425
grdinfo,

pygmt/src/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pygmt.src.coast import coast
88
from pygmt.src.colorbar import colorbar
99
from pygmt.src.contour import contour
10+
from pygmt.src.grd2cpt import grd2cpt
1011
from pygmt.src.grdcontour import grdcontour
1112
from pygmt.src.grdcut import grdcut
1213
from pygmt.src.grdfilter import grdfilter

pygmt/src/grd2cpt.py

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
"""
2+
grd2cpt - Create a CPT from a grid file.
3+
"""
4+
5+
from pygmt.clib import Session
6+
from pygmt.exceptions import GMTInvalidInput
7+
from pygmt.helpers import (
8+
build_arg_string,
9+
data_kind,
10+
dummy_context,
11+
fmt_docstring,
12+
kwargs_to_strings,
13+
use_alias,
14+
)
15+
16+
17+
@fmt_docstring
18+
@use_alias(
19+
A="transparency",
20+
C="cmap",
21+
D="background",
22+
F="color_model",
23+
E="nlevels",
24+
G="truncate",
25+
H="output",
26+
I="reverse",
27+
L="limit",
28+
M="overrule_bg",
29+
N="no_bg",
30+
Q="log",
31+
R="region",
32+
T="series",
33+
V="verbose",
34+
W="categorical",
35+
Ww="cyclic",
36+
Z="continuous",
37+
)
38+
@kwargs_to_strings(G="sequence", L="sequence", R="sequence", T="sequence")
39+
def grd2cpt(grid, **kwargs):
40+
r"""
41+
Make GMT color palette tables from a grid file.
42+
43+
This is a module that will help you make static color palette tables
44+
(CPTs). By default, the CPT will simply be saved to the current session,
45+
but you can use ``output`` to save it to a file. The CPT is based on an
46+
existing dynamic master CPT of your choice, and the mapping from data value
47+
to colors is through the data's cumulative distribution function (CDF), so
48+
that the colors are histogram equalized. Thus if the grid(s) and the
49+
resulting CPT are used in :meth:`pygmt.Figure.grdimage` with a linear
50+
projection, the colors will be uniformly distributed in area on the plot.
51+
Let z be the data values in the grid. Define CDF(Z) = (# of z < Z) / (# of
52+
z in grid). (NaNs are ignored). These z-values are then normalized to the
53+
master CPT and colors are sampled at the desired intervals.
54+
55+
The CPT includes three additional colors beyond the range of z-values.
56+
These are the background color (B) assigned to values lower than the lowest
57+
*z*-value, the foreground color (F) assigned to values higher than the
58+
highest *z*-value, and the NaN color (N) painted wherever values are
59+
undefined. For color tables beyond the standard GMT offerings, visit
60+
`cpt-city <http://soliton.vm.bytemark.co.uk/pub/cpt-city/>`_ and
61+
`Scientific Colour-Maps <http://www.fabiocrameri.ch/colourmaps.php>`_.
62+
63+
If the master CPT includes B, F, and N entries, these will be copied into
64+
the new master file. If not, the parameters :gmt-term:`COLOR_BACKGROUND`,
65+
:gmt-term:`COLOR_FOREGROUND`, and :gmt-term:`COLOR_NAN` from the
66+
:gmt-docs:`gmt.conf <gmt.conf>` file or the command line will be used. This
67+
default behavior can be overruled using the options ``background``,
68+
``overrule_bg`` or ``no_bg``.
69+
70+
The color model (RGB, HSV or CMYK) of the palette created by
71+
:meth:`pygmt.grd2cpt` will be the same as specified in the header of the
72+
master CPT. When there is no :gmt-term:`COLOR_MODEL` entry in the master
73+
CPT, the :gmt-term:`COLOR_MODEL` specified in the
74+
:gmt-docs:`gmt.conf <gmt.conf>` file or the ``color_model`` option will be
75+
used.
76+
77+
Full option list at :gmt-docs:`grd2cpt.html`
78+
79+
{aliases}
80+
81+
Parameters
82+
----------
83+
grid : str or xarray.DataArray
84+
The file name of the input grid or the grid loaded as a DataArray.
85+
transparency : int or float or str
86+
Sets a constant level of transparency (0-100) for all color slices.
87+
Append **+a** to also affect the fore-, back-, and nan-colors
88+
[Default is no transparency, i.e., 0 (opaque)].
89+
cmap : str
90+
Selects the master color palette table (CPT) to use in the
91+
interpolation. Full list of built-in color palette tables can be found
92+
at :gmt-docs:`cookbook/cpts.html#built-in-color-palette-tables-cpt`.
93+
background : bool or str
94+
Select the back- and foreground colors to match the colors for lowest
95+
and highest *z*-values in the output CPT [Default (``background=True``
96+
or ``background='o'``) uses the colors specified in the master file, or
97+
those defined by the parameters :gmt-term:`COLOR_BACKGROUND`,
98+
:gmt-term:`COLOR_FOREGROUND`, and :gmt-term:`COLOR_NAN`]. Use
99+
``background='i'`` to match the colors for the lowest and highest
100+
values in the input (instead of the output) CPT.
101+
color_model :
102+
[**R**\|\ **r**\|\ **h**\|\ **c**][**+c**\ [*label*]].
103+
Force output CPT to be written with r/g/b codes, gray-scale values or
104+
color name (**R**, default) or r/g/b codes only (**r**), or h-s-v codes
105+
(**h**), or c/m/y/k codes (**c**). Optionally or alternatively, append
106+
**+c** to write discrete palettes in categorical format. If *label* is
107+
appended then we create labels for each category to be used when the
108+
CPT is plotted. The *label* may be a comma-separated list of category
109+
names (you can skip a category by not giving a name), or give
110+
*start*\[-], where we automatically build monotonically increasing
111+
labels from *start* (a single letter or an integer). Append ``-`` to
112+
build ranges *start*-*start+1* instead.
113+
nlevels : bool or int or str
114+
Set to ``True`` to create a linear color table by using the grid
115+
z-range as the new limits in the CPT. Alternatively, set *nlevels*
116+
to resample the color table into *nlevels* equidistant slices.
117+
series : list or str
118+
[*min/max/inc*\ [**+b**\|\ **l**\|\ **n**\]|\ *file*\|\ *list*\].
119+
Defines the range of the new CPT by giving the lowest and highest
120+
z-value (and optionally an interval). If this is not given, the
121+
existing range in the master CPT will be used intact. The values
122+
produced defines the color slice boundaries. If **+n** is used it
123+
refers to the number of such boundaries and not the number of slices.
124+
For details on array creation, see
125+
:gmt-docs:`makecpt.html#generate-1d-array`.
126+
truncate : list or str
127+
*zlo/zhi*.
128+
Truncate the incoming CPT so that the lowest and highest z-levels are
129+
to *zlo* and *zhi*. If one of these equal NaN then we leave that end of
130+
the CPT alone. The truncation takes place before any resampling. See
131+
also :gmt-docs:`cookbook/features.html#manipulating-cpts`.
132+
output : str
133+
Optional argument to set the file name with extension .cpt to store
134+
the generated CPT file. If not given or False (default), saves the CPT
135+
as the session current CPT.
136+
reverse : str
137+
Set this to True or c [Default] to reverse the sense of color
138+
progression in the master CPT. Set this to z to reverse the sign of
139+
z-values in the color table. Note that this change of z-direction
140+
happens before *truncate* and *series* values are used so the latter
141+
must be compatible with the changed *z*-range. See also
142+
:gmt-docs:`cookbook/features.html#manipulating-cpts`.
143+
overrule_bg : str
144+
Overrule background, foreground, and NaN colors specified in the master
145+
CPT with the values of the parameters :gmt-term:`COLOR_BACKGROUND`,
146+
:gmt-term:`COLOR_FOREGROUND`, and :gmt-term:`COLOR_NAN` specified in
147+
the :gmt-docs:`gmt.conf <gmt.conf>` file or on the command line. When
148+
combined with ``background``, only :gmt-term:`COLOR_NAN` is considered.
149+
no_bg : bool
150+
Do not write out the background, foreground, and NaN-color fields
151+
[Default will write them, i.e. ``no_bg=False``].
152+
log : bool
153+
For logarithmic interpolation scheme with input given as logarithms.
154+
Expects input z-values provided via ``series`` to be log10(*z*),
155+
assigns colors, and writes out *z*.
156+
continuous : bool
157+
Force a continuous CPT when building from a list of colors and a list
158+
of z-values [Default is None, i.e. discrete values].
159+
categorical : bool
160+
Do not interpolate the input color table but pick the output colors
161+
starting at the beginning of the color table, until colors for all
162+
intervals are assigned. This is particularly useful in combination with
163+
a categorical color table, like ``cmap='categorical'``.
164+
cyclic : bool
165+
Produce a wrapped (cyclic) color table that endlessly repeats its
166+
range. Note that ``cyclic=True`` cannot be set together with
167+
``categorical=True``.
168+
{V}
169+
"""
170+
if "W" in kwargs and "Ww" in kwargs:
171+
raise GMTInvalidInput("Set only categorical or cyclic to True, not both.")
172+
kind = data_kind(grid)
173+
with Session() as lib:
174+
if kind == "file":
175+
file_context = dummy_context(grid)
176+
elif kind == "grid":
177+
file_context = lib.virtualfile_from_grid(grid)
178+
else:
179+
raise GMTInvalidInput(f"Unrecognized data type: {type(grid)}")
180+
with file_context as infile:
181+
if "H" not in kwargs.keys(): # if no output is set
182+
arg_str = " ".join([infile, build_arg_string(kwargs)])
183+
elif "H" in kwargs.keys(): # if output is set
184+
outfile = kwargs.pop("H")
185+
if not outfile or not isinstance(outfile, str):
186+
raise GMTInvalidInput("'output' should be a proper file name.")
187+
arg_str = " ".join(
188+
[infile, build_arg_string(kwargs), f"-H > {outfile}"]
189+
)
190+
lib.call_module("grd2cpt", arg_str)

pygmt/src/makecpt.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def makecpt(**kwargs):
118118
happens before *truncate* and *series* values are used so the latter
119119
must be compatible with the changed *z*-range. See also
120120
:gmt-docs:`cookbook/features.html#manipulating-cpts`.
121-
overrule_bg :
121+
overrule_bg : str
122122
Overrule background, foreground, and NaN colors specified in the master
123123
CPT with the values of the parameters :gmt-term:`COLOR_BACKGROUND`,
124124
:gmt-term:`COLOR_FOREGROUND`, and :gmt-term:`COLOR_NAN` specified in

pygmt/tests/test_grd2cpt.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Tests for grd2cpt.
3+
"""
4+
import os
5+
6+
import pytest
7+
from pygmt import Figure
8+
from pygmt.datasets import load_earth_relief
9+
from pygmt.exceptions import GMTInvalidInput
10+
from pygmt.helpers import GMTTempFile
11+
from pygmt.helpers.testing import check_figures_equal
12+
from pygmt.src.grd2cpt import grd2cpt
13+
14+
15+
@pytest.fixture(scope="module", name="grid")
16+
def fixture_grid():
17+
"""
18+
Load the grid data from the sample earth_relief file.
19+
"""
20+
return load_earth_relief()
21+
22+
23+
@check_figures_equal()
24+
def test_grd2cpt(grid):
25+
"""
26+
Test creating a CPT with grd2cpt to create a CPT based off a grid input and
27+
plot it with a color bar.
28+
"""
29+
fig_ref, fig_test = Figure(), Figure()
30+
# Use single-character arguments for the reference image
31+
fig_ref.basemap(B="a", J="W0/15c", R="d")
32+
grd2cpt(grid="@earth_relief_01d")
33+
fig_ref.colorbar(B="a2000")
34+
fig_test.basemap(frame="a", projection="W0/15c", region="d")
35+
grd2cpt(grid=grid)
36+
fig_test.colorbar(frame="a2000")
37+
return fig_ref, fig_test
38+
39+
40+
def test_grd2cpt_blank_output(grid):
41+
"""
42+
Use incorrect setting by passing in blank file name to output parameter.
43+
"""
44+
with pytest.raises(GMTInvalidInput):
45+
grd2cpt(grid=grid, output="")
46+
47+
48+
def test_grd2cpt_invalid_output(grid):
49+
"""
50+
Use incorrect setting by passing in invalid type to output parameter.
51+
"""
52+
with pytest.raises(GMTInvalidInput):
53+
grd2cpt(grid=grid, output=["some.cpt"])
54+
55+
56+
def test_grd2cpt_output_to_cpt_file(grid):
57+
"""
58+
Save the generated static color palette table to a .cpt file.
59+
"""
60+
with GMTTempFile(suffix=".cpt") as cptfile:
61+
grd2cpt(grid=grid, output=cptfile.name)
62+
assert os.path.getsize(cptfile.name) > 0
63+
64+
65+
def test_grd2cpt_unrecognized_data_type():
66+
"""
67+
Test that an error will be raised if an invalid data type is passed to
68+
grid.
69+
"""
70+
with pytest.raises(GMTInvalidInput):
71+
grd2cpt(grid=0)
72+
73+
74+
def test_grd2cpt_categorical_and_cyclic(grid):
75+
"""
76+
Use incorrect setting by setting both categorical and cyclic to True.
77+
"""
78+
with pytest.raises(GMTInvalidInput):
79+
grd2cpt(grid=grid, cmap="batlow", categorical=True, cyclic=True)

0 commit comments

Comments
 (0)