Skip to content
Open
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
3 changes: 1 addition & 2 deletions examples/gallery/embellishments/gmt_logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@

# 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="TR", position_type="inside", anchor_offset=(0.3, 0.6), width="3c")
fig.show()
5 changes: 2 additions & 3 deletions pygmt/src/inset.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,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="BR", position_type="inside", offset=0.2, width="3c")
>>> fig.show()
"""
self._activate_figure()
Expand Down
113 changes: 101 additions & 12 deletions pygmt/src/logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@
logo - Plot the GMT logo.
"""

from collections.abc import Sequence
from typing import Literal

from pygmt._typing import AnchorCode
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias
from pygmt.params import Box


@fmt_docstring
@use_alias(R="region", D="position", S="style")
@use_alias(R="region", S="style")
@kwargs_to_strings(R="sequence", p="sequence")
def logo(
def logo( # noqa: PLR0913
self,
position: Sequence[str | float] | AnchorCode | None = None,
position_type: Literal[
"mapcoords", "boxcoords", "plotcoords", "inside", "outside"
] = "plotcoords",
anchor: AnchorCode | None = None,
anchor_offset: Sequence[float | str] | None = None,
height: float | str | None = None,
width: float | str | None = None,
projection=None,
box: Box | bool = False,
verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"]
Expand All @@ -26,14 +37,21 @@ def logo(
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.5/_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. The position can be changed by specifying the
reference point (via ``position_type`` and ``position``) and anchor point (via
``anchor`` and ``anchor_offset``). Refer to :doc:`/techref/reference_anchor_points`
for details about the positioning.

Full GMT docs at :gmt-docs:`gmtlogo.html`.

{aliases}
- D = position/position_type/anchor/anchor_offset/width/height
- F = box
- J = projection
- V = verbose
Expand All @@ -42,17 +60,51 @@ 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/position_type
Specify the reference point on the plot for the GMT logo. The method of
defining the the reference point is controlled by **position_type**, and the
exact location is set by **position**.

The **position_type** parameter can take one of the following values:

- ``"mapcoords"``: **position** is specified as (*longitude*, *latitude*) in map
coordinates. Example: (120, -45) places the reference point at 120°E, 45°S.
- ``"boxcoords"``: **position** is specified as (*nx*, *ny*) in normalized
coordinates, i.e., fractional values between 0 and 1 along the x- and y-axes.
Example: (0, 0) corresponds to the lower-left corner, and (1, 1) to the
upper-right corner of the plot bounding box.
- ``"plotcoords"``: **position** is specified as (*x*, *y*) in plot coordinates,
i.e., distances from the lower-left plot origin given in inches, centimeters,
or points. Example: ("1c", "2c") places the reference point 1 cm to the right
and 2 cm above the plot origin.
- ``"inside"`` or ``"outside"``: **position** is one of the nine
:doc:two-character justification codes </techref/justification_codes>,
indicating a specific location relative to the plot bounding box. Example:
``"TL"`` places the reference point at the top-left corner, either inside or
outside the bounding box.
anchor
Specify the anchor point of the GMT logo, using one of the
:doc:`2-character justification codes </techref/justification_codes>`.
The default value depends on **position_type**.

- ``position_type="inside"``: **anchor** defaults to the same as **position**.
- ``position_type="outside"``: **anchor** defaults to the mirror opposite of
**position**.
- Otherwise, **anchor** defaults to ``"MC"`` (middle center).
anchor_offset
Specifies an offset for the anchor point as *offset* or
(*offset_x*, *offset_y*). If a single value *offset* is given, both *offset_x*
and *offset_y* are set to *offset*.
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,
pass a :class:`pygmt.params.Box` object to control style, fill, pen, and other
box properties.
{projection}
{region}
style : str
[**l**\|\ **n**\|\ **u**].
Control what is written beneath the map portion of the logo.
Expand All @@ -61,13 +113,50 @@ def logo(
[Default]
- **n** to skip the label placement
- **u** to place the URL to the GMT site
{projection}
{region}
{verbose}
{panel}
{transparency}
"""
self._activate_figure()

# 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)

# Mapping position_type to GMT single-letter code.
_position_type = {
"mapcoords": "g",
"boxcoords": "n",
"plotcoords": "x",
"inside": "j",
"outside": "J",
}[position_type]

# Prior PyGMT v0.17.0, 'position' was aliased to the -D option.
# For backward compatibility, we need to check if users pass a string with the GMT
# CLI syntax to 'position', i.e., a string starting with one of the leading
# single-letter or contains modifiers with "+".
if isinstance(position, str) and (position[0] in "gnxjJ" or "+" in position):
if any(v is not None for v in (anchor, anchor_offset, height, width)):
msg = (
"Parameter 'position' is given with a raw GMT CLI syntax, and conflicts "
"with other parameters (anchor, anchor_offset, height, width). "
"Please refer to the documentation for the recommended usage."
)
raise GMTInvalidInput(msg)
_position_type = "" # Unset _position_type to an empty string.

aliasdict = AliasSystem(
D=[
Alias(position, name="position", sep="/", size=2, prefix=_position_type),
Alias(anchor, name="anchor", prefix="+j"),
Alias(anchor_offset, name="anchor_offset", prefix="+o", sep="/", size=2),
Alias(height, name="height", prefix="+h"),
Alias(width, name="width", prefix="+w"),
],
F=Alias(box, name="box"),
).add_common(
J=projection,
Expand Down
5 changes: 5 additions & 0 deletions pygmt/tests/baseline/test_logo_position_type.png.dvc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
outs:
- md5: 9b9d740219df9edb298dbc49e7ef1351
size: 232635
hash: md5
path: test_logo_position_type.png
61 changes: 60 additions & 1 deletion pygmt/tests/test_logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput


@pytest.mark.benchmark
Expand All @@ -24,5 +25,63 @@ 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_type="inside",
position="TR",
anchor_offset=(0.25, 0.25),
width="7.5c",
box=True,
)
return fig


@pytest.mark.mpl_image_compare
def test_logo_position_type():
"""
Test that the new group of parameters works as expected.
"""
fig = Figure()
fig.basemap(region=[-90, -70, 0, 20], projection="M15c", frame=True)
fig.logo(position_type="inside", position="TL")
fig.logo(position_type="outside", position="TR")
fig.logo(position_type="mapcoords", position=(-80, 15))
fig.logo(position_type="boxcoords", position=(0, 0.5))
fig.logo(position_type="plotcoords", position=("2c", "0c"), width="5c")
fig.logo(position=("8c", "0c")) # Default position_type is "plotcoords".
return fig


@pytest.mark.mpl_image_compare(filename="test_logo_position_type.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="jTL")
fig.logo(position="JTR")
fig.logo(position="g-80/15")
fig.logo(position="n0/0.5")
fig.logo(position="x2c/0c+w5c")
fig.logo(position="8c/0c")
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", anchor="BR")
Loading