diff --git a/pygmt/src/histogram.py b/pygmt/src/histogram.py index 539527fa071..cfc6f4aa649 100644 --- a/pygmt/src/histogram.py +++ b/pygmt/src/histogram.py @@ -13,6 +13,7 @@ build_arg_list, deprecate_parameter, fmt_docstring, + is_given, kwargs_to_strings, use_alias, ) @@ -43,7 +44,7 @@ def histogram( bar_offset: float | str | None = None, cmap: str | bool = False, pen: str | None = None, - fill: str | None = None, + fill: str | Literal["position", "value"] | None = None, horizontal: bool = False, out_range: Literal["first", "last", "both"] | None = None, stairs: bool = False, @@ -67,7 +68,7 @@ def histogram( $aliases - A = horizontal - B = frame - - C = cmap + - C = cmap, **+b**: fill - E = bar_width, **+o**: bar_offset - G = fill - J = projection @@ -87,12 +88,22 @@ def histogram( data Pass in either a file name to an ASCII data table, a Python list, a 2-D $table_classes. - $cmap pen Draw bar outline (or stair-case curve) using the specified pen thickness [Default is no outline]. fill - Set color or pattern for filling bars [Default is no fill]. + Set color or pattern for filling bars [Default is no fill]. Set it to one of the + two special values to fill bars by looking up the color from a CPT instead of a + constant color: + + - ``"position"``: look up the color using the mid-coordinate of the bin. This is + the default when ``cmap`` is set. + - ``"value"``: look up the color using the bin value, i.e., the bar count or + frequency. + + The special values require a CPT (either the current CPT or explicitly set by + ``cmap``), and can't be used with ``fill``. + $cmap annotate : bool or str [**+b**][**+f**\ *font*][**+o**\ *off*][**+r**]. Annotate each bar with the count it represents. Append any of the @@ -172,14 +183,31 @@ def histogram( required="bar_width", reason="Required when 'bar_offset' is set." ) + # "position" and "value" are special values that fill bars by values and cmap. + match fill: + case "position": + _fill_color, _fill_lookup = None, "" + case "value": + _fill_color, _fill_lookup = None, "+b" + case _: + _fill_color, _fill_lookup = fill, None # type: ignore[assignment] + if _fill_color is not None and is_given(cmap): + raise GMTParameterError( + at_most_one=["cmap", "fill"], + reason="Cannot use 'cmap' when 'fill' is a constant color or pattern.", + ) + aliasdict = AliasSystem( A=Alias(horizontal, name="horizontal"), - C=Alias(cmap, name="cmap"), + C=[ + Alias(cmap, name="cmap"), + Alias(_fill_lookup, name="fill"), + ], E=[ Alias(bar_width, name="bar_width"), Alias(bar_offset, name="bar_offset", prefix="+o"), ], - G=Alias(fill, name="fill"), + G=Alias(_fill_color, name="fill"), L=Alias( out_range, name="out_range", diff --git a/pygmt/tests/baseline/test_histogram_fill.png b/pygmt/tests/baseline/test_histogram_fill.png new file mode 100644 index 00000000000..33bdffce4e1 --- /dev/null +++ b/pygmt/tests/baseline/test_histogram_fill.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a82f632aba7a78db5bd880ab34fdeec8c7472b5e1257e79b61efd5ccd72aa813 +size 12900 diff --git a/pygmt/tests/test_histogram.py b/pygmt/tests/test_histogram.py index c839105f995..bd6ef95983d 100644 --- a/pygmt/tests/test_histogram.py +++ b/pygmt/tests/test_histogram.py @@ -4,7 +4,7 @@ import pandas as pd import pytest -from pygmt import Figure +from pygmt import Figure, makecpt from pygmt.exceptions import GMTParameterError from pygmt.params import Axis @@ -51,3 +51,75 @@ def test_histogram_baroffset(data): fill="green", bar_offset=0.25, ) + + +@pytest.mark.mpl_image_compare(filename="test_histogram_fill.png") +def test_histogram_fill(data): + """ + Test filling bars with constant color and lookup table values. + """ + kwargs = { + "data": data, + "projection": "X5c/5c", + "region": [0, 10, 0, 8], + "series": 1, + "frame": Axis(annot=True), + } + fig = Figure() + # Constant fill + fig.histogram(fill="green", **kwargs) + fig.shift_origin(xshift=6) + # Fill bars by bin position + makecpt(cmap="viridis", series=[0, 9, 1]) + fig.histogram(fill="position", **kwargs) + fig.colorbar(frame=True) + # Fill bars by bin value + fig.shift_origin(xshift=6) + makecpt(cmap="viridis", series=[0, 6, 1]) + fig.histogram(fill="value", **kwargs) + fig.colorbar(frame=True) + return fig + + +@pytest.mark.mpl_image_compare(filename="test_histogram_fill.png") +def test_histogram_fill_old_cmap_syntax(data): + """ + Test filling bars with constant color and lookup table values. + """ + kwargs = { + "data": data, + "projection": "X5c/5c", + "region": [0, 10, 0, 8], + "series": 1, + "frame": Axis(annot=True), + } + fig = Figure() + # Constant fill + fig.histogram(fill="green", **kwargs) + fig.shift_origin(xshift=6) + # Fill bars by bin position + makecpt(cmap="viridis", series=[0, 9, 1]) + fig.histogram(cmap=True, **kwargs) + fig.colorbar(frame=True) + # Fill bars by bin value + fig.shift_origin(xshift=6) + makecpt(cmap="viridis", series=[0, 6, 1]) + fig.histogram(cmap="+b", **kwargs) + fig.colorbar(frame=True) + return fig + + +def test_histogram_fill_color_with_cmap(data): + """ + Test that a constant fill color cannot be combined with cmap. + """ + fig = Figure() + with pytest.raises(GMTParameterError): + fig.histogram( + data=data, + projection="X10c/10c", + region=[0, 9, 0, 8], + series=1, + cmap=True, + fill="green", + )