diff --git a/doc/api/index.rst b/doc/api/index.rst index 75dedbf737d..c0e5bcf472c 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -217,6 +217,7 @@ Class-style Parameters Box Frame Pattern + Perspective Position Enums diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 3374af11d29..297d1b53f3e 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -276,22 +276,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*: the 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]. + at the bottom of the selected 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..ad37701d78e --- /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 selected axis. + level: float | None = None + + #: Set which constant-coordinate plane is used as the plotting plane. Use ``"x"``, + #: ``"y"``, or ``"z"`` for the x-plane, y-plane, or horizontal z-plane, + #: respectively [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 c209cf016b1..fad1b736966 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( 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 93e45ebc15a..73966e70fc5 100644 --- a/pygmt/src/coast.py +++ b/pygmt/src/coast.py @@ -15,7 +15,7 @@ is_nonstr_iter, use_alias, ) -from pygmt.params import Axis, Box, Frame +from pygmt.params import Axis, Box, Frame, Perspective __doctest_skip__ = ["coast"] @@ -90,7 +90,7 @@ def coast( 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 9d264e42787..bb56d81a1ab 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"] @@ -278,7 +278,7 @@ def colorbar( 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 2123acba3b8..1ab946e9e80 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( | 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 8a964c10a44..5c29f80a8e3 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 6a4a23b6b2b..80283af23d0 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( 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 d659fa9d3ab..dd8487bc112 100644 --- a/pygmt/src/grdcontour.py +++ b/pygmt/src/grdcontour.py @@ -16,7 +16,7 @@ kwargs_to_strings, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective __doctest_skip__ = ["grdcontour"] @@ -43,7 +43,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 66b9990d2c5..42cda0b6e10 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( 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 580b03fc859..cb54e80c74a 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -17,7 +17,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"] @@ -141,7 +141,7 @@ def grdview( 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 67d9b6d1009..b60a66dc133 100644 --- a/pygmt/src/histogram.py +++ b/pygmt/src/histogram.py @@ -15,7 +15,7 @@ kwargs_to_strings, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -52,7 +52,7 @@ def histogram( | 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 1c0ceb9b7f2..6ca7fb5478d 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( 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 5e5f320e3ac..0e7d8346c46 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( 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 9733c185fbc..26f7d40e376 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( 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 b670d6d013a..aed779aa6d2 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( 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 a2c84cd641a..9b9e8880a70 100644 --- a/pygmt/src/meca.py +++ b/pygmt/src/meca.py @@ -17,7 +17,7 @@ fmt_docstring, use_alias, ) -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective from pygmt.src._common import _FocalMechanismConvention @@ -140,7 +140,7 @@ def meca( | 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 bb2d5097e76..d92cedfb94e 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( # ruff: ignore[too-many-branches] | 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 00aae4b1e18..4c5a2f91edf 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( # ruff: ignore[too-many-branches] | 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 1c59d87fc68..93059702e77 100644 --- a/pygmt/src/rose.py +++ b/pygmt/src/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, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -46,7 +46,7 @@ def rose( | 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 fe3b6961138..79ab25a0290 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( 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 a8f3c959689..d8047fd3fad 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( 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 aafa0a07609..1b2b27f7dc7 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 @@ -125,7 +125,7 @@ def ternary( 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 4e9bbf5177d..428e31ca479 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( # ruff: ignore[too-many-branches, too-many-statements] 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 706dd607bb6..37c1754419a 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 @@ -37,7 +37,7 @@ def tilemap( 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 71d09dee444..9646845e805 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, fmt_docstring, use_alias -from pygmt.params import Axis, Frame +from pygmt.params import Axis, Frame, Perspective @fmt_docstring @@ -43,7 +43,7 @@ def velo( | 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 641bd73385c..61a7731a6bf 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, 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 @@ -45,7 +45,7 @@ def wiggle( 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"))