diff --git a/doc/api/index.rst b/doc/api/index.rst index 49b8166abb3..38e572cdc7d 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -224,6 +224,7 @@ Class-style Parameters Box Frame Pattern + Perspective Position Enums diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 53c7e1f1d29..ef77d151508 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -277,22 +277,22 @@ perspective Select perspective view and set the azimuth and elevation of the viewpoint. - Accepts a single value or a sequence of two or three values: *azimuth*, - (*azimuth*, *elevation*), or (*azimuth*, *elevation*, *zlevel*). + Accepts a :class:`pygmt.params.Perspective` object, a single value + *azimuth*, a sequence of two values (*azimuth*, *elevation*), a sequence of + three values (*azimuth*, *elevation*, *level*). - *azimuth*: Azimuth angle of the viewpoint in degrees [Default is 180, i.e., looking from south to north]. - *elevation*: Elevation angle of the viewpoint above the horizon [Default is 90, i.e., looking straight down at nadir]. - - *zlevel*: Z-level at which 2-D elements (e.g., the plot frame) are drawn. + - *level*: Z-level at which 2-D elements (e.g., the plot frame) are drawn. Only applied when used together with ``zsize`` or ``zscale``. [Default is at the bottom of the z-axis]. + - :class:`pygmt.params.Perspective`: A class-style way for more control over + the perspective parameters. Alternatively, set ``perspective=True`` to reuse the perspective setting - from the previous plotting method, or pass a string following the full - GMT syntax for finer control (e.g., adding ``+w`` or ``+v`` modifiers to - select an axis location other than the plot origin). See - :gmt-docs:`gmt.html#perspective-full` for details.""", + from the previous plotting method.""", "projection": r""" projection *projcode*\[*projparams*/]\ *width*\|\ *scale*. diff --git a/pygmt/params/__init__.py b/pygmt/params/__init__.py index 43b83ab0131..3d115745aa3 100644 --- a/pygmt/params/__init__.py +++ b/pygmt/params/__init__.py @@ -5,4 +5,5 @@ from pygmt.params.box import Box from pygmt.params.frame import Axis, Frame from pygmt.params.pattern import Pattern +from pygmt.params.perspective import Perspective from pygmt.params.position import Position diff --git a/pygmt/params/perspective.py b/pygmt/params/perspective.py new file mode 100644 index 00000000000..6acde229c42 --- /dev/null +++ b/pygmt/params/perspective.py @@ -0,0 +1,80 @@ +""" +The Perspective class for setting perspective view. +""" + +import dataclasses +from typing import Literal + +from pygmt.alias import Alias +from pygmt.exceptions import GMTValueError +from pygmt.params.base import BaseParam + +__doctest_skip__ = ["Perspective"] + + +@dataclasses.dataclass(repr=False) +class Perspective(BaseParam): + """ + Class for setting perspective view. + + Examples + -------- + >>> import pygmt + >>> from pygmt.params import Perspective + >>> fig = pygmt.Figure() + >>> fig.basemap( + ... region=[0, 10, 0, 10, 0, 20], + ... projection="X3c", + ... zsize="3c", + ... frame=["xafg", "yafg", "zafg", "wSEnZ"], + ... perspective=Perspective(azimuth=135, elevation=40, level=10), + ... ) + >>> fig.show() + """ + + #: Azimuth of the viewpoint in degrees. Default is 180.0 (looking from south to + #: north). + azimuth: float | None = None + + #: Elevation angle of the viewpoint in degrees. Default is 90.0 (looking straight + #: down at nadir). + elevation: float | None = None + + #: The level at which all 2-D material, like the plot frame, is plotted. Only valid + #: when used together with parameters ``zsize``/``zscale``. Default is at the bottom + #: of the z-axis. + level: float | None = None + + #: Set which constant-coordinate plane is used as the plotting plane. Use ``"x"`` + #: for the x-plane, ``"y"`` for the y-plane, or ``"z"`` for the horizontal z-plane + # [Default is ``"z"``]. + plane: Literal["x", "y", "z"] | None = None + + def _validate(self): + """ + Post-initialization processing to validate parameters. + """ + # azimuth is required, so it must be set to the default if not specified. + if self.azimuth is None: + self.azimuth = 180.0 # Default azimuth is 180.0 + + # Set default elevation if level is set but elevation is not. + if self.level is not None and self.elevation is None: + self.elevation = 90.0 # Default elevation is 90.0 + + if self.plane is not None and self.plane not in {"x", "y", "z"}: + raise GMTValueError( + self.plane, description="plane", choices=["x", "y", "z"] + ) + + @property + def _aliases(self): + """ + Aliases for the parameters. + """ + return [ + Alias(self.plane, name="plane"), + Alias(self.azimuth, name="azimuth"), + Alias(self.elevation, name="elevation", prefix="/"), + Alias(self.level, name="level", prefix="/"), + ] diff --git a/pygmt/src/basemap.py b/pygmt/src/basemap.py index ccb24b7b713..074fe3856ad 100644 --- a/pygmt/src/basemap.py +++ b/pygmt/src/basemap.py @@ -9,7 +9,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias -from pygmt.params import Axis, Box, Frame +from pygmt.params import Axis, Box, Frame, Perspective @fmt_docstring @@ -28,7 +28,7 @@ def basemap( # noqa: PLR0913 rose: str | None = None, box: Box | str | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/coast.py b/pygmt/src/coast.py index e359f28662a..1cbfc93271b 100644 --- a/pygmt/src/coast.py +++ b/pygmt/src/coast.py @@ -9,7 +9,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTParameterError from pygmt.helpers import args_in_kwargs, build_arg_list, fmt_docstring, use_alias -from pygmt.params import Axis, Box, Frame +from pygmt.params import Axis, Box, Frame, Perspective __doctest_skip__ = ["coast"] @@ -34,7 +34,7 @@ def coast( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 8b4e4d7d7a3..161d54b2cf0 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -11,7 +11,7 @@ from pygmt.exceptions import GMTValueError from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias from pygmt.helpers.utils import is_nonstr_iter -from pygmt.params import Axis, Box, Frame, Position +from pygmt.params import Axis, Box, Frame, Perspective, Position from pygmt.src._common import _parse_position __doctest_skip__ = ["colorbar"] @@ -277,7 +277,7 @@ def colorbar( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/contour.py b/pygmt/src/contour.py index c0b2878e645..2f255bcb32e 100644 --- a/pygmt/src/contour.py +++ b/pygmt/src/contour.py @@ -14,7 +14,7 @@ is_nonstr_iter, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -46,7 +46,7 @@ def contour( # noqa: PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/directional_rose.py b/pygmt/src/directional_rose.py index fbeab2dc106..e690e7e60b7 100644 --- a/pygmt/src/directional_rose.py +++ b/pygmt/src/directional_rose.py @@ -9,7 +9,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Box, Position +from pygmt.params import Box, Perspective, Position from pygmt.src._common import _parse_position __doctest_skip__ = ["directional_rose"] @@ -26,7 +26,7 @@ def directional_rose( verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, ): """ diff --git a/pygmt/src/fill_between.py b/pygmt/src/fill_between.py index 2b280fe9e0b..dc69c41a5f5 100644 --- a/pygmt/src/fill_between.py +++ b/pygmt/src/fill_between.py @@ -10,7 +10,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTValueError from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective __doctest_skip__ = ["fill_between"] @@ -36,7 +36,7 @@ def fill_between( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, ): """ diff --git a/pygmt/src/grdcontour.py b/pygmt/src/grdcontour.py index 07b9b144cc6..0011432cf93 100644 --- a/pygmt/src/grdcontour.py +++ b/pygmt/src/grdcontour.py @@ -17,7 +17,7 @@ kwargs_to_strings, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective __doctest_skip__ = ["grdcontour"] @@ -45,7 +45,7 @@ def grdcontour( verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/grdimage.py b/pygmt/src/grdimage.py index 59c3c10cdaa..3d875b91907 100644 --- a/pygmt/src/grdimage.py +++ b/pygmt/src/grdimage.py @@ -10,7 +10,7 @@ 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 Axis, Frame +from pygmt.params import Axis, Frame, Perspective __doctest_skip__ = ["grdimage"] @@ -37,7 +37,7 @@ def grdimage( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, cores: int | bool = False, **kwargs, diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 5fae8666fec..6c21eb6b144 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -18,7 +18,7 @@ is_given, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.src.grdinfo import grdinfo __doctest_skip__ = ["grdview"] @@ -148,7 +148,7 @@ def grdview( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/histogram.py b/pygmt/src/histogram.py index f89b0b047ea..c8548bbccc6 100644 --- a/pygmt/src/histogram.py +++ b/pygmt/src/histogram.py @@ -16,7 +16,7 @@ kwargs_to_strings, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -55,7 +55,7 @@ def histogram( # noqa: PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index c5ae316c1e5..39b76dacc38 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -6,6 +6,7 @@ import numpy as np from pygmt.exceptions import GMTValueError +from pygmt.params import Perspective __doctest_skip__ = ["hlines"] @@ -18,7 +19,7 @@ def hlines( pen: str | None = None, label: str | None = None, no_clip: bool = False, - perspective: str | bool | None = None, + perspective: Perspective | float | Sequence[float] | bool | None = None, ): """ Plot one or multiple horizontal line(s). diff --git a/pygmt/src/image.py b/pygmt/src/image.py index 5ed843d0e55..37a2413c4da 100644 --- a/pygmt/src/image.py +++ b/pygmt/src/image.py @@ -9,7 +9,7 @@ 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 Axis, Box, Frame, Position +from pygmt.params import Axis, Box, Frame, Perspective, Position from pygmt.src._common import _parse_position @@ -32,7 +32,7 @@ def image( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/legend.py b/pygmt/src/legend.py index 03cd1929094..a63c13c7526 100644 --- a/pygmt/src/legend.py +++ b/pygmt/src/legend.py @@ -11,7 +11,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTTypeError from pygmt.helpers import build_arg_list, data_kind, fmt_docstring, is_nonstr_iter -from pygmt.params import Axis, Box, Frame, Position +from pygmt.params import Axis, Box, Frame, Perspective, Position from pygmt.src._common import _parse_position @@ -31,7 +31,7 @@ def legend( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/logo.py b/pygmt/src/logo.py index b669ae6497c..1d640ca3293 100644 --- a/pygmt/src/logo.py +++ b/pygmt/src/logo.py @@ -10,7 +10,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTParameterError from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Box, Position +from pygmt.params import Box, Perspective, Position from pygmt.src._common import _parse_position __doctest_skip__ = ["logo"] @@ -29,7 +29,7 @@ def logo( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/magnetic_rose.py b/pygmt/src/magnetic_rose.py index 41e8f751ab1..33f69479a08 100644 --- a/pygmt/src/magnetic_rose.py +++ b/pygmt/src/magnetic_rose.py @@ -10,7 +10,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTParameterError from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Box, Position +from pygmt.params import Box, Perspective, Position from pygmt.src._common import _parse_position __doctest_skip__ = ["magnetic_rose"] @@ -31,7 +31,7 @@ def magnetic_rose( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, ): """ diff --git a/pygmt/src/meca.py b/pygmt/src/meca.py index a0d35b7aa56..6aeb134fc22 100644 --- a/pygmt/src/meca.py +++ b/pygmt/src/meca.py @@ -18,7 +18,7 @@ fmt_docstring, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.src._common import _FocalMechanismConvention @@ -155,7 +155,7 @@ def meca( # noqa: PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, transparency: float | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, **kwargs, ): r""" diff --git a/pygmt/src/plot.py b/pygmt/src/plot.py index 2900296d7f7..92838e9b893 100644 --- a/pygmt/src/plot.py +++ b/pygmt/src/plot.py @@ -16,7 +16,7 @@ is_nonstr_iter, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.src._common import _data_geometry_is_point @@ -59,7 +59,7 @@ def plot( # noqa: PLR0912, PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | Sequence[float] | bool | None = None, **kwargs, ): diff --git a/pygmt/src/plot3d.py b/pygmt/src/plot3d.py index 12ebd3ffa8c..e151ec5b9d6 100644 --- a/pygmt/src/plot3d.py +++ b/pygmt/src/plot3d.py @@ -16,7 +16,7 @@ is_nonstr_iter, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.src._common import _data_geometry_is_point @@ -61,7 +61,7 @@ def plot3d( # noqa: PLR0912, PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | Sequence[float] | bool | None = None, **kwargs, ): diff --git a/pygmt/src/rose.py b/pygmt/src/rose.py index 6f77530f905..ab0b93562eb 100644 --- a/pygmt/src/rose.py +++ b/pygmt/src/rose.py @@ -9,7 +9,7 @@ from pygmt.alias import AliasSystem from pygmt.clib import Session from pygmt.helpers import build_arg_list, fmt_docstring, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -46,7 +46,7 @@ def rose( # noqa: PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/scalebar.py b/pygmt/src/scalebar.py index 918d10bbcd5..186c28e0761 100644 --- a/pygmt/src/scalebar.py +++ b/pygmt/src/scalebar.py @@ -9,7 +9,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Box, Position +from pygmt.params import Box, Perspective, Position from pygmt.src._common import _parse_position __doctest_skip__ = ["scalebar"] @@ -31,7 +31,7 @@ def scalebar( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, ): """ diff --git a/pygmt/src/solar.py b/pygmt/src/solar.py index 93ac9057f11..532ebe31d63 100644 --- a/pygmt/src/solar.py +++ b/pygmt/src/solar.py @@ -10,7 +10,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTValueError from pygmt.helpers import build_arg_list, fmt_docstring -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective __doctest_skip__ = ["solar"] @@ -28,7 +28,7 @@ def solar( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/ternary.py b/pygmt/src/ternary.py index 23e75109689..bf715941682 100644 --- a/pygmt/src/ternary.py +++ b/pygmt/src/ternary.py @@ -10,7 +10,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTValueError from pygmt.helpers import build_arg_list, fmt_docstring, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.params.frame import _Axes @@ -120,7 +120,7 @@ def ternary( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/text.py b/pygmt/src/text.py index ca2bf13ef9e..88c962b231b 100644 --- a/pygmt/src/text.py +++ b/pygmt/src/text.py @@ -19,7 +19,7 @@ non_ascii_to_octal, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -52,7 +52,7 @@ def text( # noqa: PLR0912, PLR0913, PLR0915 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | Sequence[float] | bool | None = None, **kwargs, ): diff --git a/pygmt/src/tilemap.py b/pygmt/src/tilemap.py index 7a7c96c46d0..85dd23ebf30 100644 --- a/pygmt/src/tilemap.py +++ b/pygmt/src/tilemap.py @@ -10,7 +10,7 @@ from pygmt.datasets.tile_map import load_tile_map from pygmt.enums import GridType from pygmt.helpers import build_arg_list, fmt_docstring, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective try: from xyzservices import TileProvider @@ -36,7 +36,7 @@ def tilemap( # noqa: PLR0913 verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/velo.py b/pygmt/src/velo.py index 957ce8cd58b..61473c3ad6c 100644 --- a/pygmt/src/velo.py +++ b/pygmt/src/velo.py @@ -12,7 +12,7 @@ from pygmt.clib import Session from pygmt.exceptions import GMTParameterError, GMTTypeError from pygmt.helpers import build_arg_list, deprecate_parameter, fmt_docstring, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -47,7 +47,7 @@ def velo( # noqa : PLR0913 | bool = False, panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/src/vlines.py b/pygmt/src/vlines.py index 1e99de0b535..377064eb923 100644 --- a/pygmt/src/vlines.py +++ b/pygmt/src/vlines.py @@ -6,6 +6,7 @@ import numpy as np from pygmt.exceptions import GMTValueError +from pygmt.params import Perspective __doctest_skip__ = ["vlines"] @@ -18,7 +19,7 @@ def vlines( pen: str | None = None, label: str | None = None, no_clip: bool = False, - perspective: str | bool | None = None, + perspective: Perspective | float | Sequence[float] | bool | None = None, ): """ Plot one or multiple vertical line(s). diff --git a/pygmt/src/wiggle.py b/pygmt/src/wiggle.py index 290bd9bbed2..bda03c1ba7e 100644 --- a/pygmt/src/wiggle.py +++ b/pygmt/src/wiggle.py @@ -9,7 +9,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.helpers import build_arg_list, deprecate_parameter, fmt_docstring, use_alias -from pygmt.params import Axis, Frame, Position +from pygmt.params import Axis, Frame, Perspective, Position from pygmt.src._common import _parse_position @@ -53,7 +53,7 @@ def wiggle( # noqa: PLR0913 panel: int | Sequence[int] | bool = False, incols: int | str | Sequence[int | str] | None = None, label: str | None = None, - perspective: float | Sequence[float] | str | bool = False, + perspective: Perspective | float | Sequence[float] | bool = False, transparency: float | None = None, **kwargs, ): diff --git a/pygmt/tests/test_params_perspective.py b/pygmt/tests/test_params_perspective.py new file mode 100644 index 00000000000..aed5a1560e9 --- /dev/null +++ b/pygmt/tests/test_params_perspective.py @@ -0,0 +1,36 @@ +""" +Test the Perspective class. +""" + +import pytest +from pygmt.exceptions import GMTValueError +from pygmt.params import Perspective + + +def test_params_perspective(): + """ + Test the Perspective class with various parameters. + """ + # Test azimuth, elevation, and level separately + assert str(Perspective(azimuth=120)) == "120" + assert str(Perspective(elevation=30)) == "180.0/30" + assert str(Perspective(level=1000)) == "180.0/90.0/1000" + + # Test combinations of azimuth, elevation, and level + assert str(Perspective(azimuth=120, elevation=30)) == "120/30" + assert str(Perspective(azimuth=120, elevation=30, level=1000)) == "120/30/1000" + assert str(Perspective(elevation=30, level=1000)) == "180.0/30/1000" + + # Test plane parameter + assert str(Perspective(azimuth=120, elevation=30, plane="x")) == "x120/30" + assert str(Perspective(azimuth=120, elevation=30, plane="y")) == "y120/30" + assert str(Perspective(azimuth=120, elevation=30, plane="z")) == "z120/30" + assert str(Perspective(plane="y")) == "y180.0" + + +def test_params_perspective_invalid_plane(): + """ + Test that an invalid plane raises an error. + """ + with pytest.raises(GMTValueError): + str(Perspective(plane="bad"))