diff --git a/examples/gallery/embellishments/gmt_logo.py b/examples/gallery/embellishments/gmt_logo.py index b0311881577..28e55bd89ab 100644 --- a/examples/gallery/embellishments/gmt_logo.py +++ b/examples/gallery/embellishments/gmt_logo.py @@ -7,12 +7,12 @@ # %% import pygmt +from pygmt.params import Position fig = pygmt.Figure() fig.basemap(region=[0, 10, 0, 2], projection="X6c", frame=True) # Add the GMT logo in the Top Right (TR) corner of the current plot, scaled up to be 3 # centimeters wide and offset by 0.3 cm in x-direction and 0.6 cm in y-direction. -fig.logo(position="jTR+o0.3c/0.6c+w3c") - +fig.logo(position=Position("TR", offset=(0.3, 0.6)), width="3c") fig.show() diff --git a/pygmt/src/inset.py b/pygmt/src/inset.py index 18f33efe4d0..36a3778369f 100644 --- a/pygmt/src/inset.py +++ b/pygmt/src/inset.py @@ -109,7 +109,7 @@ def inset( Examples -------- >>> import pygmt - >>> from pygmt.params import Box + >>> from pygmt.params import Box, Position >>> >>> # Create the larger figure >>> fig = pygmt.Figure() @@ -126,9 +126,8 @@ def inset( ... dcw="MG+gred", ... ) ... - >>> # Map elements outside the "with" statement are plotted in the main - >>> # figure - >>> fig.logo(position="jBR+o0.2c+w3c") + >>> # Map elements outside the "with" statement are plotted in the main figure + >>> fig.logo(position=Position("BR", offset=0.2), width="3c") >>> fig.show() """ self._activate_figure() diff --git a/pygmt/src/logo.py b/pygmt/src/logo.py index 0087a7dddec..1636f34e6a4 100644 --- a/pygmt/src/logo.py +++ b/pygmt/src/logo.py @@ -7,18 +7,21 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias -from pygmt.params import Box +from pygmt.exceptions import GMTInvalidInput +from pygmt.helpers import build_arg_list, fmt_docstring +from pygmt.params import Box, Position @fmt_docstring -@use_alias(D="position") -def logo( +def logo( # noqa: PLR0913 self, + position: Position | None = None, + width: float | str | None = None, + height: float | str | None = None, + box: Box | bool = False, + style: Literal["standard", "url", "no_label"] = "standard", projection: str | None = None, region: Sequence[float | str] | str | None = None, - style: Literal["standard", "url", "no_label"] = "standard", - box: Box | bool = False, verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, @@ -26,17 +29,25 @@ def logo( perspective: float | Sequence[float] | str | bool = False, **kwargs, ): - r""" + """ Plot the GMT logo. - By default, the GMT logo is 2 inches wide and 1 inch high and - will be positioned relative to the current plot origin. - Use various options to change this and to place a transparent or - opaque rectangular map panel behind the GMT logo. + .. figure:: https://docs.generic-mapping-tools.org/6.6/_images/GMT_coverlogo.png + :alt: GMT logo + :align: center + :width: 300px + + By default, the GMT logo is 2 inches wide and 1 inch high and will be positioned + relative to the current plot origin. Full GMT docs at :gmt-docs:`gmtlogo.html`. - $aliases + **Aliases:** + + .. hlist:: + :columns: 3 + + - D = position, **+w**: width, **+h**: height - F = box - J = projection - R = region @@ -48,12 +59,13 @@ def logo( Parameters ---------- - $projection - $region - position : str - [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\ - **+w**\ *width*\ [**+j**\ *justify*]\ [**+o**\ *dx*\ [/*dy*]]. - Set reference point on the map for the image. + position + Specify the position of the GMT logo. See :class:`pygmt.params.Position` for + details. + width + height + Width or height of the GMT logo. Since the aspect ratio is fixed, only one of + the two can be specified. box Draw a background box behind the logo. If set to ``True``, a simple rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box appearance, @@ -65,6 +77,8 @@ def logo( - ``"standard"``: The text label "The Generic Mapping Tools". - ``"no_label"``: Skip the text label. - ``"url"``: The URL to the GMT website. + $projection + $region $verbose $panel $transparency @@ -72,7 +86,26 @@ def logo( """ self._activate_figure() + # Prior PyGMT v0.17.0, 'position' can accept a raw GMT CLI string. Check for + # conflicts with other parameters. + if isinstance(position, str) and any(v is not None for v in (width, height)): + msg = ( + "Parameter 'position' is given with a raw GMT command string, and conflicts " + "with parameters 'width' and 'height'." + ) + raise GMTInvalidInput(msg) + + # width and height are mutually exclusive. + if width is not None and height is not None: + msg = "Cannot specify both 'width' and 'height'." + raise GMTInvalidInput(msg) + aliasdict = AliasSystem( + D=[ + Alias(position, name="position"), + Alias(height, name="height", prefix="+h"), + Alias(width, name="width", prefix="+w"), + ], F=Alias(box, name="box"), S=Alias( style, name="style", mapping={"standard": "l", "url": "u", "no_label": "n"} diff --git a/pygmt/tests/test_logo.py b/pygmt/tests/test_logo.py index 62bddf4eb24..3eea9033be5 100644 --- a/pygmt/tests/test_logo.py +++ b/pygmt/tests/test_logo.py @@ -4,6 +4,8 @@ import pytest from pygmt import Figure +from pygmt.exceptions import GMTInvalidInput +from pygmt.params import Position @pytest.mark.benchmark @@ -24,5 +26,36 @@ def test_logo_on_a_map(): """ fig = Figure() fig.basemap(region=[-90, -70, 0, 20], projection="M15c", frame=True) - fig.logo(position="jTR+o0.25c/0.25c+w7.5c", box=True) + fig.logo(position=Position("TR", offset=(0.25, 0.25)), width="7.5c", box=True) return fig + + +@pytest.mark.mpl_image_compare(filename="test_logo_on_a_map.png") +def test_logo_position_deprecated_syntax(): + """ + Test that passing the deprecated GMT CLI syntax string to 'position' works. + """ + fig = Figure() + fig.basemap(region=[-90, -70, 0, 20], projection="M15c", frame=True) + fig.logo(position="jTR+o0.25/0.25+w7.5c", box=True) + return fig + + +def test_logo_width_and_height(): + """ + Test that an error is raised when both width and height are specified. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + fig.logo(width="5c", height="5c") + + +def test_logo_position_mixed_syntax(): + """ + Test that an error is raised when mixing new and deprecated syntax in 'position'. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + fig.logo(position="jTL", width="5c") + with pytest.raises(GMTInvalidInput): + fig.logo(position="jTL", height="6c")