Skip to content

Wrap makecpt #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ Plotting data and laying out the map:
Figure.image
Figure.shift_origin

Color palette table generation:

.. autosummary::
:toctree: generated

makecpt

Saving and displaying the figure:

.. autosummary::
Expand Down
1 change: 1 addition & 0 deletions pygmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .session_management import begin as _begin, end as _end
from .figure import Figure
from .gridding import surface
from .mathops import makecpt
from .modules import info, grdinfo, which
from . import datasets

Expand Down
65 changes: 65 additions & 0 deletions pygmt/mathops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
GMT modules for Mathematical operations on tables or grids
"""
from .clib import Session
from .exceptions import GMTInvalidInput
from .helpers import build_arg_string, fmt_docstring, kwargs_to_strings, use_alias


@fmt_docstring
@use_alias(C="cmap", T="series", G="truncate", H="output", I="reverse", Z="continuous")
@kwargs_to_strings(T="sequence", G="sequence")
def makecpt(**kwargs):
"""
Creates a static color palette table (CPT).

Full option list at :gmt-docs:`makecpt.html`

Parameters
----------
cmap (C) : str
Selects the master color palette table (CPT) to use in the interpolation.
Full list of built-in color palette tables can be found at
:gmt-docs:`cookbook/cpts.html#built-in-color-palette-tables-cpt`.

series (T) : list or str
``[min/max/inc[+b|l|n]|file|list]``.
Defines the range of the new CPT by giving the lowest and highest z-value (and
optionally an interval). If this is not given, the existing range in the master
CPT will be used intact.

truncate (G) : list or str
``zlo/zhi``.
Truncate the incoming CPT so that the lowest and highest z-levels are to zlo and
zhi. If one of these equal NaN then we leave that end of the CPT alone. The
truncation takes place before any resampling. See also
:gmt-docs:`cookbook/features.html#manipulating-cpts`.

output (H) : str
Optional. The file name with extension .cpt to store the generated CPT file.
If not given or False (default), saves the CPT as the session current CPT.

reverse (I) : str
Set this to True or c [Default] to reverse the sense of color progression in the
master CPT. Set this to z to reverse the sign of z-values in the color table.
Note that this change of z-direction happens before -G and -T values are used so
the latter must be compatible with the changed z-range. See also
:gmt-docs:`cookbook/features.html#manipulating-cpts`.

continuous (Z) : bool
Creates a continuous CPT [Default is discontinuous, i.e., constant colors for
each interval]. This option has no effect when no -T is used, or when using
-Tz_min/z_max; in the first case the input CPT remains untouched, in the second
case it is only scaled to match the range z_min/z_max.

{aliases}
"""
with Session() as lib:
if "H" not in kwargs.keys(): # if no output is set
arg_str = build_arg_string(kwargs)
elif "H" in kwargs.keys(): # if output is set
outfile = kwargs.pop("H")
if not outfile or not isinstance(outfile, str):
raise GMTInvalidInput("'output' should be a proper file name.")
arg_str = " ".join([build_arg_string(kwargs), f"-H > {outfile}"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other functions in PyGMT which output to a file use -> instead of >. I don't know why but maybe you can try if -> also works.

Suggested change
arg_str = " ".join([build_arg_string(kwargs), f"-H > {outfile}"])
arg_str = " ".join([build_arg_string(kwargs), f"-H -> {outfile}"])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure it'll work, and I did notice it in other parts of the code but never quite understood why -> is used. Is it meant to be a cross-platform thing?

Copy link
Member Author

@weiji14 weiji14 Oct 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I was wrong, -> doesn't work. The test_makecpt_output_to_cpt_file function fails with this error message:

    def test_makecpt_output_to_cpt_file():
        """
        Save the generated static color palette table to a .cpt file
        """
        with GMTTempFile(suffix=".cpt") as cptfile:
>           makecpt(output=cptfile.name)

../pygmt/tests/test_makecpt.py:82: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../pygmt/helpers/decorators.py:187: in new_module
    return module_func(*args, **kwargs)
../pygmt/helpers/decorators.py:282: in new_module
    return module_func(*args, **kwargs)
../pygmt/mathops.py:65: in makecpt
    lib.call_module(module="makecpt", args=arg_str)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pygmt.clib.session.Session object at 0x7fe9ae6ad470>, module = 'makecpt', args = ' -H -> /tmp/pygmt-dz2kwkqs.cpt'

    def call_module(self, module, args):
        """
        Call a GMT module with the given arguments.
    
        Makes a call to ``GMT_Call_Module`` from the C API using mode
        ``GMT_MODULE_CMD`` (arguments passed as a single string).
    
        Most interactions with the C API are done through this function.
    
        Parameters
        ----------
        module : str
            Module name (``'pscoast'``, ``'psbasemap'``, etc).
        args : str
            String with the command line arguments that will be passed to the
            module (for example, ``'-R0/5/0/10 -JM'``).
    
        Raises
        ------
        GMTCLibError
            If the returned status code of the function is non-zero.
    
        """
        c_call_module = self.get_libgmt_func(
            "GMT_Call_Module",
            argtypes=[ctp.c_void_p, ctp.c_char_p, ctp.c_int, ctp.c_void_p],
            restype=ctp.c_int,
        )
    
        mode = self["GMT_MODULE_CMD"]
        status = c_call_module(
            self.session_pointer, module.encode(), mode, args.encode()
        )
        if status != 0:
            raise GMTCLibError(
                "Module '{}' failed with status code {}:\n{}".format(
>                   module, status, self._error_message
                )
            )
E           pygmt.exceptions.GMTCLibError: Module 'makecpt' failed with status code 71:
E           makecpt [ERROR]: Syntax error: No input files expected unless -E or -S are used

../pygmt/clib/session.py:490: GMTCLibError
--------------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------------
makecpt [ERROR]: Syntax error: No input files expected unless -E or -S are used

lib.call_module(module="makecpt", args=arg_str)
Binary file added pygmt/tests/baseline/test_makecpt_continuous.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 177 additions & 0 deletions pygmt/tests/test_makecpt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# pylint: disable=redefined-outer-name
"""
Tests for makecpt
"""
import os

import numpy as np
import pytest

from .. import Figure, makecpt
from ..datasets import load_earth_relief
from ..exceptions import GMTInvalidInput
from ..helpers import GMTTempFile

TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
POINTS_DATA = os.path.join(TEST_DATA_DIR, "points.txt")


@pytest.fixture(scope="module")
def points():
"Load the points data from the test file"
return np.loadtxt(POINTS_DATA)


@pytest.fixture(scope="module")
def region():
"The data region"
return [10, 70, -5, 10]


@pytest.fixture(scope="module")
def grid():
"Load the grid data from the sample earth_relief file"
return load_earth_relief()


@pytest.mark.mpl_image_compare
def test_makecpt_to_plot_points(points, region):
"""
Use static color palette table to change color of points
"""
fig = Figure()
makecpt(cmap="rainbow")
fig.plot(
x=points[:, 0],
y=points[:, 1],
color=points[:, 2],
region=region,
style="c1c",
cmap=True,
)
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_to_plot_grid(grid):
"""
Use static color palette table to change color of grid
"""
fig = Figure()
makecpt(cmap="relief")
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_to_plot_grid_scaled_with_series(grid):
"""
Use static color palette table scaled to a min/max series to change color of grid
"""
fig = Figure()
makecpt(cmap="oleron", series="-4500/4500")
fig.grdimage(grid, projection="W0/6i")
return fig


def test_makecpt_output_to_cpt_file():
"""
Save the generated static color palette table to a .cpt file
"""
with GMTTempFile(suffix=".cpt") as cptfile:
makecpt(output=cptfile.name)
assert os.path.exists(cptfile.name)


def test_makecpt_blank_output():
"""
Use incorrect setting by passing in blank file name to output parameter
"""
with pytest.raises(GMTInvalidInput):
makecpt(output="")


def test_makecpt_invalid_output():
"""
Use incorrect setting by passing in invalid type to output parameter
"""
with pytest.raises(GMTInvalidInput):
makecpt(output=["some.cpt"])


@pytest.mark.mpl_image_compare
def test_makecpt_truncated_to_zlow_zhigh(grid):
"""
Use static color palette table that is truncated to z-low and z-high
"""
fig = Figure()
makecpt(cmap="rainbow", truncate=[0.15, 0.85], series=[-4500, 4500])
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_truncated_at_zlow_only(grid):
"""
Use static color palette table that is truncated at z-low only
"""
fig = Figure()
makecpt(cmap="rainbow", truncate=[0.5, None], series=[-4500, 4500])
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_truncated_at_zhigh_only(grid):
"""
Use static color palette table that is truncated at z-high only
"""
fig = Figure()
makecpt(cmap="rainbow", truncate=[None, 0.5], series=[-4500, 4500])
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_reverse_color_only(grid):
"""
Use static color palette table with its colors reversed
"""
fig = Figure()
makecpt(cmap="earth", reverse=True)
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_reverse_zsign_only(grid):
"""
Use static color palette table with its z-value sign reversed
"""
fig = Figure()
makecpt(cmap="earth", reverse="z")
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_reverse_color_and_zsign(grid):
"""
Use static color palette table with both its colors and z-value sign reversed
"""
fig = Figure()
makecpt(cmap="earth", reverse="cz")
fig.grdimage(grid, projection="W0/6i")
return fig


@pytest.mark.mpl_image_compare
def test_makecpt_continuous(grid):
"""
Use static color palette table that is continuous from blue to white and scaled from
-4500 to 4500m.
"""
fig = Figure()
makecpt(cmap="blue,white", continuous=True, series="-4500,4500")
fig.grdimage(grid, projection="W0/6i")
return fig