diff --git a/pygmt/src/grdcontour.py b/pygmt/src/grdcontour.py index 53e6b32ced8..8a6db2715e7 100644 --- a/pygmt/src/grdcontour.py +++ b/pygmt/src/grdcontour.py @@ -2,8 +2,16 @@ grdcontour - Plot a contour figure. """ +import warnings + from pygmt.clib import Session -from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias +from pygmt.helpers import ( + build_arg_list, + fmt_docstring, + is_nonstr_iter, + kwargs_to_strings, + use_alias, +) __doctest_skip__ = ["grdcontour"] @@ -27,9 +35,7 @@ p="perspective", t="transparency", ) -@kwargs_to_strings( - R="sequence", L="sequence", A="sequence_plus", c="sequence_comma", p="sequence" -) +@kwargs_to_strings(R="sequence", L="sequence", c="sequence_comma", p="sequence") def grdcontour(self, grid, **kwargs): r""" Convert grids or images to contours and plot them on maps. @@ -43,26 +49,26 @@ def grdcontour(self, grid, **kwargs): Parameters ---------- {grid} - interval : str or int + interval : float, list, or str Specify the contour lines to generate. - - The file name of a CPT file where the color boundaries will - be used as contour levels. - - The file name of a 2 (or 3) column file containing the contour - levels (col 1), (**C**)ontour or (**A**)nnotate (col 2), and optional - angle (col 3). - - A fixed contour interval *cont_int* or a single contour with - +\ *cont_int*. - annotation : str, int, or list + - The file name of a CPT file where the color boundaries will be used as + contour levels. + - The file name of a 2 (or 3) column file containing the contour levels (col 0), + (**C**)ontour or (**A**)nnotate (col 1), and optional angle (col 2). + - A fixed contour interval. + - A list of contour levels. + annotation : float, list, or str Specify or disable annotated contour levels, modifies annotated contours specified in ``interval``. - - Specify a fixed annotation interval *annot_int* or a - single annotation level +\ *annot_int*. - - Disable all annotation with **-**. - - Optional label modifiers can be specified as a single string - ``"[annot_int]+e"`` or with a list of arguments - ``([annot_int], "e", "f10p", "gred")``. + - Specify a fixed annotation interval. + - Specify a list of annotation levels. + - Disable all annotations by setting ``annotation="n"``. + - Adjust the appearance by appending different modifiers, e.g., + ``"annot_int+f10p+gred"`` gives annotations with a font size of 10 points + and a red filled box. For all available modifiers see + :gmt-docs:`grdcontour.html#a`. limit : str or list of 2 ints *low*/*high*. Do no draw contours below `low` or above `high`, specify as string @@ -96,32 +102,58 @@ def grdcontour(self, grid, **kwargs): Example ------- >>> import pygmt - >>> # load the 15 arc-minutes grid with "gridline" registration - >>> # in a specified region + >>> # Load the 15 arc-minutes grid with "gridline" registration in the + >>> # specified region >>> grid = pygmt.datasets.load_earth_relief( ... resolution="15m", ... region=[-92.5, -82.5, -3, 7], ... registration="gridline", ... ) - >>> # create a new plot with pygmt.Figure() + >>> # Create a new plot with pygmt.Figure() >>> fig = pygmt.Figure() - >>> # create the contour plot + >>> # Create the contour plot >>> fig.grdcontour( - ... # pass in the grid downloaded above + ... # Pass in the grid downloaded above ... grid=grid, - ... # set the interval for contour lines at 250 meters + ... # Set the interval for contour lines at 250 meters ... interval=250, - ... # set the interval for annotated contour lines at 1,000 meters + ... # Set the interval for annotated contour lines at 1,000 meters ... annotation=1000, - ... # add a frame for the plot + ... # Add a frame for the plot ... frame="a", - ... # set the projection to Mercator for the 10 cm figure + ... # Set the projection to Mercator for the 10 cm figure ... projection="M10c", ... ) - >>> # show the plot + >>> # Show the plot >>> fig.show() """ kwargs = self._preprocess(**kwargs) + + # Backward compatibility with the old syntax for the annotation parameter, e.g., + # [100, "e", "f10p", "gred"]. + if is_nonstr_iter(kwargs.get("A")) and any( + i[0] in "acdefgijlLnoprtuvwx=" for i in kwargs["A"] if isinstance(i, str) + ): + msg = ( + "Argument of the parameter 'annotation'/'A' is using the old, deprecated " + "syntax. Please refer to the PyGMT documentation for the new syntax. " + "The warning will be removed in v0.14.0 and the old syntax will no longer " + "be supported. " + ) + warnings.warn(msg, category=FutureWarning, stacklevel=2) + kwargs["A"] = "+".join(f"{item}" for item in kwargs["A"]) + + # Specify levels for the annotation and interval parameters. + # One level is converted to a string with a trailing comma to separate it from + # specifying an interval. + # Multiple levels are concatenated to a comma-separated string. + for arg in ["A", "C"]: + if is_nonstr_iter(kwargs.get(arg)): + if len(kwargs[arg]) == 1: # One level + kwargs[arg] = str(kwargs[arg][0]) + "," + else: # Multiple levels + kwargs[arg] = ",".join(f"{item}" for item in kwargs[arg]) + with Session() as lib: with lib.virtualfile_in(check_kind="raster", data=grid) as vingrd: lib.call_module( diff --git a/pygmt/tests/baseline/test_grdcontour_multiple_levels.png.dvc b/pygmt/tests/baseline/test_grdcontour_multiple_levels.png.dvc new file mode 100644 index 00000000000..e0558fff47d --- /dev/null +++ b/pygmt/tests/baseline/test_grdcontour_multiple_levels.png.dvc @@ -0,0 +1,6 @@ +outs: +- md5: 4d20cdb71af2e6568f64f0246ec860ea + size: 64008 + isexec: true + hash: md5 + path: test_grdcontour_multiple_levels.png diff --git a/pygmt/tests/baseline/test_grdcontour_one_level.png.dvc b/pygmt/tests/baseline/test_grdcontour_one_level.png.dvc new file mode 100644 index 00000000000..4072632ab00 --- /dev/null +++ b/pygmt/tests/baseline/test_grdcontour_one_level.png.dvc @@ -0,0 +1,6 @@ +outs: +- md5: fc624766f0b8eac8206735a05c7c9662 + size: 45023 + isexec: true + hash: md5 + path: test_grdcontour_one_level.png diff --git a/pygmt/tests/test_grdcontour.py b/pygmt/tests/test_grdcontour.py index 2cedd80dc14..33fd04bdd0c 100644 --- a/pygmt/tests/test_grdcontour.py +++ b/pygmt/tests/test_grdcontour.py @@ -24,7 +24,8 @@ def fixture_grid(): @pytest.mark.mpl_image_compare def test_grdcontour(grid): """ - Plot a contour image using an xarray grid with fixed contour interval. + Plot a contour image using an xarray grid with fixed (different) contour and + annotation intervals. """ fig = Figure() fig.grdcontour( @@ -33,6 +34,53 @@ def test_grdcontour(grid): return fig +@pytest.mark.mpl_image_compare +def test_grdcontour_one_level(grid): + """ + Plot a contour image using an xarray grid with one contour level and one + (different) annotation level. + """ + fig = Figure() + fig.grdcontour( + grid=grid, interval=[400], annotation=[570], projection="M10c", frame=True + ) + return fig + + +@pytest.mark.mpl_image_compare(filename="test_grdcontour_one_level.png") +def test_grdcontour_old_annotations(grid): + """ + Test the old syntax for the annotation parameter using "sequence_plus". + Modified from the "test_grdcontour_one_level()" test. Can be removed in v0.14.0. + """ + fig = Figure() + fig.grdcontour( + grid=grid, + interval=[400], + annotation=["570,", "gwhite"], + projection="M10c", + frame=True, + ) + return fig + + +@pytest.mark.mpl_image_compare +def test_grdcontour_multiple_levels(grid): + """ + Plot a contour image using an xarray grid with multiple (different) contour + and annotation levels. + """ + fig = Figure() + fig.grdcontour( + grid=grid, + interval=[400, 450, 500], + annotation=[400, 570], + projection="M10c", + frame=True, + ) + return fig + + @pytest.mark.benchmark @pytest.mark.mpl_image_compare def test_grdcontour_labels(grid):