From 2f32c369633ee8d7f3bee9924735ea922343d35e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 2 Dec 2025 14:11:30 +0800 Subject: [PATCH 01/16] Figure.grdview: Pythonic syntax for surftype parameter --- pygmt/src/grdview.py | 76 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 06b5ef1ab55..e6bc027cf4e 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -9,6 +9,7 @@ from pygmt._typing import PathLike 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, use_alias __doctest_skip__ = ["grdview"] @@ -19,7 +20,6 @@ C="cmap", G="drapegrid", N="plane", - Q="surftype", Wc="contourpen", Wm="meshpen", Wf="facadepen", @@ -30,6 +30,14 @@ def grdview( # noqa: PLR0913 self, grid: PathLike | xr.DataArray, + surftype: Literal[ + "mesh", "surface", "surface+mesh", "image", "waterfallx", "waterfally" + ] + | None = None, + dpi: int | None = None, + meshfill: float | None = None, + monochrome: bool = False, + nan_transparent: bool = False, projection: str | None = None, zscale: float | str | None = None, zsize: float | str | None = None, @@ -59,6 +67,7 @@ def grdview( # noqa: PLR0913 - Jz = zscale - JZ = zsize - R = region + - Q = surftype - V = verbose - c = panel - p = perspective @@ -89,18 +98,24 @@ def grdview( # noqa: PLR0913 Draw a plane at this z-level. If the optional color is provided via the **+g** modifier, and the projection is not oblique, the frontal facade between the plane and the data perimeter is colored. - surftype : str - Specify cover type of the grid. Select one of following settings: - - - **m**: mesh plot [Default]. - - **mx** or **my**: waterfall plots (row or column profiles). - - **s**: surface plot, and optionally append **m** to have mesh lines drawn on - top of the surface. - - **i**: image plot. - - **c**: Same as **i** but will make nodes with z = NaN transparent. - - For any of these choices, you may force a monochrome image by appending the - modifier **+m**. + surftype + Specify cover type of the grid. Valid values are: + + - ``"mesh"``: mesh plot [Default]. + - ``"surface``: surface plot. + - ``"surface+mesh"``: surface plot with mesh lines drawn on top of the surface. + - ``"image"``: image plot. + - ``"waterfall_x"``/``"waterfall_y"``: waterfall plots (row or column profiles). + dpi + Effective dots-per-unit resolution for the rasterization for image plots (i.e., + ``surftype="image"``) [Default is :gmt-term:`GMT_GRAPHICS_DPU`] + meshfill + For mesh plot or waterfall plots, set the mesh fill [Default is white]. + monochrome + Force conversion to monochrome image using the (television) YIQ transformation. + nan_transparent + Make grid nodes with z = NaN transparent, using the color-masking feature in + PostScript Level 3. Only applies when ``surftype="image"``. contourpen : str Draw contour lines on top of surface or mesh (not image). Append pen attributes used for the contours. @@ -159,9 +174,44 @@ def grdview( # noqa: PLR0913 """ self._activate_figure() + if dpi is not None and surftype != "image": + msg = "Parameter 'dpi' can only be used when 'surftype' is 'image'." + raise GMTInvalidInput(msg) + if nan_transparent and surftype != "image": + msg = "Parameter 'nan_transparent' can only be used when 'surftype' is 'image'." + raise GMTInvalidInput(msg) + if meshfill is not None and surftype not in {"mesh", "waterfallx", "waterfally"}: + msg = ( + "Parameter 'meshfill' can only be used when 'surftype' is " + "'mesh', 'waterfallx', or 'waterfally'." + ) + raise GMTInvalidInput(msg) + + _surtype_mapping = { + "surface": "s", + "mesh": "m", + "surface+mesh": "sm", + "image": "c" if nan_transparent is True else "i", + "waterfallx": "mx", + "waterfally": "my", + } + + # surftype was aliased to Q previously. + _old_surftype_syntax = surftype not in _surtype_mapping and surftype is not None + aliasdict = AliasSystem( Jz=Alias(zscale, name="zscale"), JZ=Alias(zsize, name="zsize"), + Q=[ + Alias( + surftype, + name="surftype", + mapping=_surtype_mapping if not _old_surftype_syntax else None, + ), + Alias(dpi, name="dpi"), + Alias(meshfill, name="meshfill"), + Alias(monochrome, name="monochrome", prefix="+m"), + ], ).add_common( B=frame, J=projection, From 25af727089b1737bbe08cea7a081244023632caf Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 2 Dec 2025 16:11:56 +0800 Subject: [PATCH 02/16] Also migrate the meshpen parameter to the new alias system --- pygmt/src/grdview.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index e6bc027cf4e..4c7ec3ede99 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -21,7 +21,6 @@ G="drapegrid", N="plane", Wc="contourpen", - Wm="meshpen", Wf="facadepen", I="shading", f="coltypes", @@ -36,6 +35,7 @@ def grdview( # noqa: PLR0913 | None = None, dpi: int | None = None, meshfill: float | None = None, + meshpen: str | None = None, monochrome: bool = False, nan_transparent: bool = False, projection: str | None = None, @@ -69,6 +69,7 @@ def grdview( # noqa: PLR0913 - R = region - Q = surftype - V = verbose + - Wm = meshpen - c = panel - p = perspective - t = transparency @@ -110,7 +111,9 @@ def grdview( # noqa: PLR0913 Effective dots-per-unit resolution for the rasterization for image plots (i.e., ``surftype="image"``) [Default is :gmt-term:`GMT_GRAPHICS_DPU`] meshfill - For mesh plot or waterfall plots, set the mesh fill [Default is white]. + Set the mesh fill [Default is white] for mesh or waterfall plots. + meshpen + Set the pen attributes for the mesh lines, for mesh, or waterfall plots. monochrome Force conversion to monochrome image using the (television) YIQ transformation. nan_transparent @@ -119,9 +122,6 @@ def grdview( # noqa: PLR0913 contourpen : str Draw contour lines on top of surface or mesh (not image). Append pen attributes used for the contours. - meshpen : str - Set the pen attributes used for the mesh. You must also select ``surftype`` of - **m** or **sm** for meshlines to be drawn. facadepen :str Set the pen attributes used for the facade. You must also select ``plane`` for the facade outline to be drawn. @@ -212,6 +212,7 @@ def grdview( # noqa: PLR0913 Alias(meshfill, name="meshfill"), Alias(monochrome, name="monochrome", prefix="+m"), ], + Wm=Alias(meshpen, name="meshpen"), ).add_common( B=frame, J=projection, From af379166f21a43e4921d8e5afcaeab2408ee497a Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 22:26:24 +0800 Subject: [PATCH 03/16] Revert "Also migrate the meshpen parameter to the new alias system" This reverts commit 25af727089b1737bbe08cea7a081244023632caf. --- pygmt/src/grdview.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 4c7ec3ede99..e6bc027cf4e 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -21,6 +21,7 @@ G="drapegrid", N="plane", Wc="contourpen", + Wm="meshpen", Wf="facadepen", I="shading", f="coltypes", @@ -35,7 +36,6 @@ def grdview( # noqa: PLR0913 | None = None, dpi: int | None = None, meshfill: float | None = None, - meshpen: str | None = None, monochrome: bool = False, nan_transparent: bool = False, projection: str | None = None, @@ -69,7 +69,6 @@ def grdview( # noqa: PLR0913 - R = region - Q = surftype - V = verbose - - Wm = meshpen - c = panel - p = perspective - t = transparency @@ -111,9 +110,7 @@ def grdview( # noqa: PLR0913 Effective dots-per-unit resolution for the rasterization for image plots (i.e., ``surftype="image"``) [Default is :gmt-term:`GMT_GRAPHICS_DPU`] meshfill - Set the mesh fill [Default is white] for mesh or waterfall plots. - meshpen - Set the pen attributes for the mesh lines, for mesh, or waterfall plots. + For mesh plot or waterfall plots, set the mesh fill [Default is white]. monochrome Force conversion to monochrome image using the (television) YIQ transformation. nan_transparent @@ -122,6 +119,9 @@ def grdview( # noqa: PLR0913 contourpen : str Draw contour lines on top of surface or mesh (not image). Append pen attributes used for the contours. + meshpen : str + Set the pen attributes used for the mesh. You must also select ``surftype`` of + **m** or **sm** for meshlines to be drawn. facadepen :str Set the pen attributes used for the facade. You must also select ``plane`` for the facade outline to be drawn. @@ -212,7 +212,6 @@ def grdview( # noqa: PLR0913 Alias(meshfill, name="meshfill"), Alias(monochrome, name="monochrome", prefix="+m"), ], - Wm=Alias(meshpen, name="meshpen"), ).add_common( B=frame, J=projection, From fcf69e6590cdff232d04b03996bfb61026b0e2ec Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 22:51:04 +0800 Subject: [PATCH 04/16] Improve docstrings and add more checks --- pygmt/src/grdview.py | 45 ++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index e6bc027cf4e..97ab7d93a68 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -31,13 +31,13 @@ def grdview( # noqa: PLR0913 self, grid: PathLike | xr.DataArray, surftype: Literal[ - "mesh", "surface", "surface+mesh", "image", "waterfallx", "waterfally" + "mesh", "surface", "surface+mesh", "image", "waterfall_x", "waterfall_y" ] | None = None, dpi: int | None = None, - meshfill: float | None = None, - monochrome: bool = False, + mesh_fill: float | None = None, nan_transparent: bool = False, + monochrome: bool = False, projection: str | None = None, zscale: float | str | None = None, zsize: float | str | None = None, @@ -67,7 +67,7 @@ def grdview( # noqa: PLR0913 - Jz = zscale - JZ = zsize - R = region - - Q = surftype + - Q = surftype, dpi, mesh_fill, nan_transparent, monochrome - V = verbose - c = panel - p = perspective @@ -99,7 +99,7 @@ def grdview( # noqa: PLR0913 modifier, and the projection is not oblique, the frontal facade between the plane and the data perimeter is colored. surftype - Specify cover type of the grid. Valid values are: + Specify surface type of the grid. Valid values are: - ``"mesh"``: mesh plot [Default]. - ``"surface``: surface plot. @@ -109,13 +109,13 @@ def grdview( # noqa: PLR0913 dpi Effective dots-per-unit resolution for the rasterization for image plots (i.e., ``surftype="image"``) [Default is :gmt-term:`GMT_GRAPHICS_DPU`] - meshfill - For mesh plot or waterfall plots, set the mesh fill [Default is white]. - monochrome - Force conversion to monochrome image using the (television) YIQ transformation. + mesh_fill + Set the mesh fill in mesh plot or waterfall plots [Default is white]. nan_transparent Make grid nodes with z = NaN transparent, using the color-masking feature in PostScript Level 3. Only applies when ``surftype="image"``. + monochrome + Force conversion to monochrome image using the (television) YIQ transformation. contourpen : str Draw contour lines on top of surface or mesh (not image). Append pen attributes used for the contours. @@ -165,7 +165,7 @@ def grdview( # noqa: PLR0913 ... # Set the vertical scale (z-axis) to 2 cm ... zsize="2c", ... # Set "surface plot" to color the surface via a CPT - ... surftype="s", + ... surftype="surface", ... # Specify CPT to "geo" ... cmap="geo", ... ) @@ -180,10 +180,10 @@ def grdview( # noqa: PLR0913 if nan_transparent and surftype != "image": msg = "Parameter 'nan_transparent' can only be used when 'surftype' is 'image'." raise GMTInvalidInput(msg) - if meshfill is not None and surftype not in {"mesh", "waterfallx", "waterfally"}: + if mesh_fill is not None and surftype not in {"mesh", "waterfall_x", "waterfall_y"}: msg = ( - "Parameter 'meshfill' can only be used when 'surftype' is " - "'mesh', 'waterfallx', or 'waterfally'." + "Parameter 'mesh_fill' can only be used when 'surftype' is 'mesh', " + "'waterfall_x', or 'waterfall_y'." ) raise GMTInvalidInput(msg) @@ -192,12 +192,21 @@ def grdview( # noqa: PLR0913 "mesh": "m", "surface+mesh": "sm", "image": "c" if nan_transparent is True else "i", - "waterfallx": "mx", - "waterfally": "my", + "waterfall_x": "mx", + "waterfall_y": "my", } - # surftype was aliased to Q previously. - _old_surftype_syntax = surftype not in _surtype_mapping and surftype is not None + # Previously, 'surftype' was aliased to Q. + _old_surftype_syntax = surftype is not None and surftype not in _surtype_mapping + + if _old_surftype_syntax and any( + v not in {None, False} for v in (dpi, mesh_fill, monochrome, nan_transparent) + ): + msg = ( + "Parameter 'surftype' is given with a raw GMT command string, and conflicts " + "with parameters 'dpi', 'mesh_fill', 'monochrome', or 'nan_transparent'." + ) + raise GMTInvalidInput(msg) aliasdict = AliasSystem( Jz=Alias(zscale, name="zscale"), @@ -209,7 +218,7 @@ def grdview( # noqa: PLR0913 mapping=_surtype_mapping if not _old_surftype_syntax else None, ), Alias(dpi, name="dpi"), - Alias(meshfill, name="meshfill"), + Alias(mesh_fill, name="mesh_fill"), Alias(monochrome, name="monochrome", prefix="+m"), ], ).add_common( From 19cd54433ef6ee9d1ddfce51da447171efa5e4e0 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 22:56:30 +0800 Subject: [PATCH 05/16] Update existing tests --- pygmt/tests/test_grdview.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 40c40190d21..3ceb97bea6d 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -104,7 +104,7 @@ def test_grdview_with_cmap_for_image_plot(xrgrid): Run grdview by passing in a grid and setting a colormap for producing an image plot. """ fig = Figure() - fig.grdview(grid=xrgrid, cmap="oleron", surftype="i") + fig.grdview(grid=xrgrid, cmap="oleron", surftype="image") return fig @@ -115,7 +115,7 @@ def test_grdview_with_cmap_for_surface_monochrome_plot(xrgrid): monochrome plot. """ fig = Figure() - fig.grdview(grid=xrgrid, cmap="oleron", surftype="s+m") + fig.grdview(grid=xrgrid, cmap="oleron", surftype="surface", monochrome=True) return fig @@ -127,7 +127,11 @@ def test_grdview_with_cmap_for_perspective_surface_plot(xrgrid): """ fig = Figure() fig.grdview( - grid=xrgrid, cmap="oleron", surftype="s", perspective=[225, 30], zscale=0.005 + grid=xrgrid, + cmap="oleron", + surftype="surface", + perspective=[225, 30], + zscale=0.005, ) return fig @@ -179,7 +183,9 @@ def test_grdview_surface_plot_styled_with_contourpen(xrgrid): surface plot. """ fig = Figure() - fig.grdview(grid=xrgrid, cmap="relief", surftype="s", contourpen="0.5p,black,dash") + fig.grdview( + grid=xrgrid, cmap="relief", surftype="surface", contourpen="0.5p,black,dash" + ) return fig @@ -190,7 +196,9 @@ def test_grdview_surface_mesh_plot_styled_with_meshpen(xrgrid): mesh plot. """ fig = Figure() - fig.grdview(grid=xrgrid, cmap="relief", surftype="sm", meshpen="0.5p,black,dash") + fig.grdview( + grid=xrgrid, cmap="relief", surftype="surface+mesh", meshpen="0.5p,black,dash" + ) return fig @@ -226,7 +234,12 @@ def test_grdview_drapegrid_dataarray(xrgrid): fig = Figure() fig.grdview( - grid=xrgrid, drapegrid=drapegrid, cmap="oleron", surftype="c", frame=True + grid=xrgrid, + drapegrid=drapegrid, + cmap="oleron", + surftype="image", + nan_transparent=True, + frame=True, ) return fig From 831d5c2da2a9a81fe7a31fae6e0a08e9efb74f7e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 22:56:44 +0800 Subject: [PATCH 06/16] Update existing gallery examples --- examples/gallery/3d_plots/grdview_surface.py | 22 +++++++++---------- .../advanced/3d_perspective_image.py | 10 ++++----- .../advanced/draping_on_3d_surface.py | 4 ++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/examples/gallery/3d_plots/grdview_surface.py b/examples/gallery/3d_plots/grdview_surface.py index 9b5bbdede1d..ea6ca03115c 100644 --- a/examples/gallery/3d_plots/grdview_surface.py +++ b/examples/gallery/3d_plots/grdview_surface.py @@ -3,15 +3,14 @@ ================== The :meth:`pygmt.Figure.grdview()` method can plot 3-D surfaces with -``surftype="s"``. Here, we supply the data as an :class:`xarray.DataArray` with -the coordinate vectors ``x`` and ``y`` defined. Note that the ``perspective`` -parameter here controls the azimuth and elevation angle of the view. We provide -a list of two arguments to ``frame`` - the first argument specifies the -:math:`x`- and :math:`y`-axes frame attributes and the second argument, -prepended with ``"z"``, specifies the :math:`z`-axis frame attributes. -Specifying the same scale for the ``projection`` and ``zscale`` parameters -ensures equal axis scaling. The ``shading`` parameter specifies illumination; -here we choose an azimuth of 45° with ``shading="+a45"``. +``surftype="surface"``. Here, we supply the data as an :class:`xarray.DataArray` with +the coordinate vectors ``x`` and ``y`` defined. Note that the ``perspective`` parameter +here controls the azimuth and elevation angle of the view. We provide a list of two +arguments to ``frame`` - the first argument specifies the :math:`x`- and :math:`y`-axes +frame attributes and the second argument, prepended with ``"z"``, specifies the +:math:`z`-axis frame attributes. Specifying the same scale for the ``projection`` and +``zscale`` parameters ensures equal axis scaling. The ``shading`` parameter specifies +illumination; here we choose an azimuth of 45° with ``shading="+a45"``. """ # %% @@ -46,12 +45,11 @@ def ackley(x, y): SCALE = 0.5 # in centimeters fig.grdview( data, - # Set annotations and gridlines in steps of five, and - # tick marks in steps of one + # Set annotations and gridlines in steps of five, and tick marks in steps of one frame=["a5f1g5", "za5f1g5"], projection=f"x{SCALE}c", zscale=f"{SCALE}c", - surftype="s", + surftype="surface", cmap="roma", perspective=[135, 30], # Azimuth southeast (135°), at elevation 30° shading="+a45", diff --git a/examples/tutorials/advanced/3d_perspective_image.py b/examples/tutorials/advanced/3d_perspective_image.py index c1c3bba36f9..0a4603d8052 100644 --- a/examples/tutorials/advanced/3d_perspective_image.py +++ b/examples/tutorials/advanced/3d_perspective_image.py @@ -47,10 +47,8 @@ frame=["xa", "yaf", "WSnE"], projection="M15c", zsize="1.5c", - # Set the surftype to "surface" - surftype="s", - # Set the CPT to "geo" - cmap="geo", + surftype="surface", + cmap="geo", # Set the CPT to "geo" ) fig.show() @@ -65,7 +63,7 @@ frame=["xa", "yaf", "WSnE"], projection="M15c", zsize="1.5c", - surftype="s", + surftype="surface", cmap="geo", # Set the plane elevation to 1,000 meters and make the fill "gray" plane="1000+ggray", @@ -88,7 +86,7 @@ frame=["xaf", "yaf", "WSnE"], projection="M15c", zsize="1.5c", - surftype="s", + surftype="surface", cmap="geo", plane="1000+ggrey", # Set the contour pen thickness to "0.1p" diff --git a/examples/tutorials/advanced/draping_on_3d_surface.py b/examples/tutorials/advanced/draping_on_3d_surface.py index eaa85cd367e..83932be6084 100644 --- a/examples/tutorials/advanced/draping_on_3d_surface.py +++ b/examples/tutorials/advanced/draping_on_3d_surface.py @@ -58,7 +58,7 @@ grid=grd_relief, # Use elevation grid for z values drapegrid=grd_age, # Use crustal age grid for color-coding cmap=True, # Use colormap created for the crustal age - surftype="i", # Create an image plot + surftype="image", # Create an image plot # Use an illumination from the azimuthal directions 0° (north) and 270° # (west) with a normalization via a cumulative Laplace distribution for # the shading @@ -122,7 +122,7 @@ grid=grd_relief, # Use elevation grid for z values drapegrid=drapegrid, # Drap image grid for the EU flag on top cmap=True, # Use colormap defined for the EU flag - surftype="i", # Create an image plot + surftype="image", # Create an image plot # Use an illumination from the azimuthal directions 0° (north) and 270° (west) with # a normalization via a cumulative Laplace distribution for the shading shading="+a0/270+ne0.6", From 16051a03d6feda261053536f5a7ac39119994b29 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 23:02:54 +0800 Subject: [PATCH 07/16] Add more tests --- pygmt/tests/test_grdview.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 3ceb97bea6d..75c36099fbc 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -4,7 +4,7 @@ import pytest from pygmt import Figure, grdcut -from pygmt.exceptions import GMTTypeError +from pygmt.exceptions import GMTInvalidInput, GMTTypeError from pygmt.helpers import GMTTempFile from pygmt.helpers.testing import load_static_earth_relief @@ -252,3 +252,34 @@ def test_grdview_wrong_kind_of_drapegrid(xrgrid): fig = Figure() with pytest.raises(GMTTypeError): fig.grdview(grid=xrgrid, drapegrid=dataset) + + +def test_grdview_invalid_surftype(gridfile): + """ + Test grdview with an invalid surftype or invalid combination of surftype and other + parameters. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, surftype="invalid_surftype") + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, surftype="surface", dpi=300) + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, surftype="surface", nan_transparent=True) + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, surftype="surface", mesh_fill="red") + + +def test_grdview_mixed_syntax(gridfile): + """ + Run grdview using grid as a file and drapegrid as an xarray.DataArray. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, cmap="oleron", surftype="i", dpi=300) + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, cmap="oleron", surftype="m", mesh_fill="red") + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, cmap="oleron", surftype="s", monochrome=True) + with pytest.raises(GMTInvalidInput): + fig.grdview(grid=gridfile, cmap="oleron", surftype="i", nan_transparent=True) From af8da8f2094884548b2d9c38357dff31a6a0f922 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 23:20:20 +0800 Subject: [PATCH 08/16] Update examples/gallery/3d_plots/grdview_surface.py --- examples/gallery/3d_plots/grdview_surface.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/gallery/3d_plots/grdview_surface.py b/examples/gallery/3d_plots/grdview_surface.py index ea6ca03115c..6253087f064 100644 --- a/examples/gallery/3d_plots/grdview_surface.py +++ b/examples/gallery/3d_plots/grdview_surface.py @@ -3,14 +3,15 @@ ================== The :meth:`pygmt.Figure.grdview()` method can plot 3-D surfaces with -``surftype="surface"``. Here, we supply the data as an :class:`xarray.DataArray` with -the coordinate vectors ``x`` and ``y`` defined. Note that the ``perspective`` parameter -here controls the azimuth and elevation angle of the view. We provide a list of two -arguments to ``frame`` - the first argument specifies the :math:`x`- and :math:`y`-axes -frame attributes and the second argument, prepended with ``"z"``, specifies the -:math:`z`-axis frame attributes. Specifying the same scale for the ``projection`` and -``zscale`` parameters ensures equal axis scaling. The ``shading`` parameter specifies -illumination; here we choose an azimuth of 45° with ``shading="+a45"``. +``surftype="s"``. Here, we supply the data as an :class:`xarray.DataArray` with +the coordinate vectors ``x`` and ``y`` defined. Note that the ``perspective`` +parameter here controls the azimuth and elevation angle of the view. We provide +a list of two arguments to ``frame`` - the first argument specifies the +:math:`x`- and :math:`y`-axes frame attributes and the second argument, +prepended with ``"z"``, specifies the :math:`z`-axis frame attributes. +Specifying the same scale for the ``projection`` and ``zscale`` parameters +ensures equal axis scaling. The ``shading`` parameter specifies illumination; +here we choose an azimuth of 45° with ``shading="+a45"``. """ # %% From e1b77408c9c471504482c20be90bb9905161070c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 23:21:55 +0800 Subject: [PATCH 09/16] Fix a typo --- pygmt/src/grdview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 97ab7d93a68..cf521dc1b2f 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -187,7 +187,7 @@ def grdview( # noqa: PLR0913 ) raise GMTInvalidInput(msg) - _surtype_mapping = { + _surftype_mapping = { "surface": "s", "mesh": "m", "surface+mesh": "sm", @@ -197,7 +197,7 @@ def grdview( # noqa: PLR0913 } # Previously, 'surftype' was aliased to Q. - _old_surftype_syntax = surftype is not None and surftype not in _surtype_mapping + _old_surftype_syntax = surftype is not None and surftype not in _surftype_mapping if _old_surftype_syntax and any( v not in {None, False} for v in (dpi, mesh_fill, monochrome, nan_transparent) @@ -215,7 +215,7 @@ def grdview( # noqa: PLR0913 Alias( surftype, name="surftype", - mapping=_surtype_mapping if not _old_surftype_syntax else None, + mapping=_surftype_mapping if not _old_surftype_syntax else None, ), Alias(dpi, name="dpi"), Alias(mesh_fill, name="mesh_fill"), From 40beb74f11113992e83d1ae3ab9ec3a004cf14b8 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 9 Dec 2025 23:37:37 +0800 Subject: [PATCH 10/16] Do not check invalid surftype --- pygmt/tests/test_grdview.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 75c36099fbc..20250821b81 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -260,8 +260,6 @@ def test_grdview_invalid_surftype(gridfile): parameters. """ fig = Figure() - with pytest.raises(GMTInvalidInput): - fig.grdview(grid=gridfile, surftype="invalid_surftype") with pytest.raises(GMTInvalidInput): fig.grdview(grid=gridfile, surftype="surface", dpi=300) with pytest.raises(GMTInvalidInput): From aedfd1c5e93d7a733916a8b771ec62e4986759df Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 10 Dec 2025 22:16:57 +0800 Subject: [PATCH 11/16] Update docstrings for mesh_pen --- pygmt/src/grdview.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 212aec3ea3e..7aea3f1e7a8 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -129,8 +129,8 @@ def grdview( # noqa: PLR0913 Set the pen attributes used for the facade. You must also select ``plane`` for the facade outline to be drawn. mesh_pen - Set the pen attributes used for the mesh. You must also select ``surftype`` of - **m** or **sm** for meshlines to be drawn. + Set the pen attributes used for the mesh. Need to set ``surftype`` to + ``"mesh"``, or ``"surface+mesh"`` to draw meshlines. shading : str Provide the name of a grid file with intensities in the (-1,+1) range, or a constant intensity to apply everywhere (affects the ambient light). From d2accb9f7caf7670e298aa62b6764ab8eb97f937 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 18 Dec 2025 13:01:33 +0800 Subject: [PATCH 12/16] Add one test for surftype --- pygmt/src/grdview.py | 38 ++++++++----------- .../baseline/test_grdview_surftype.png.dvc | 5 +++ pygmt/tests/test_grdview.py | 31 +++++++++++++++ 3 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 pygmt/tests/baseline/test_grdview_surftype.png.dvc diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 04b2db59cca..df42c5f4a65 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -22,13 +22,7 @@ @deprecate_parameter("facadepen", "facade_pen", "v0.18.0", remove_version="v0.20.0") @deprecate_parameter("meshpen", "mesh_pen", "v0.18.0", remove_version="v0.20.0") @deprecate_parameter("drapegrid", "drape_grid", "v0.18.0", remove_version="v0.20.0") -@use_alias( - C="cmap", - G="drape_grid", - I="shading", - f="coltypes", - n="interpolation", -) +@use_alias(C="cmap", G="drape_grid", I="shading", f="coltypes", n="interpolation") def grdview( # noqa: PLR0913 self, grid: PathLike | xr.DataArray, @@ -37,10 +31,10 @@ def grdview( # noqa: PLR0913 ] | None = None, dpi: int | None = None, - mesh_fill: float | None = None, nan_transparent: bool = False, monochrome: bool = False, contour_pen: str | None = None, + mesh_fill: float | None = None, mesh_pen: str | None = None, plane: float | bool = False, facade_fill: str | None = None, @@ -48,13 +42,13 @@ def grdview( # noqa: PLR0913 projection: str | None = None, zscale: float | str | None = None, zsize: float | str | None = None, - frame: str | Sequence[str] | bool = False, region: Sequence[float | str] | str | None = None, + frame: str | Sequence[str] | bool = False, verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - transparency: float | None = None, perspective: float | Sequence[float] | str | bool = False, + transparency: float | None = None, **kwargs, ): r""" @@ -87,15 +81,6 @@ def grdview( # noqa: PLR0913 Parameters ---------- $grid - region : str or list - *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. - Specify the :doc:`region ` of interest. When used - with ``perspective``, optionally append */zmin/zmax* to indicate the range to - use for the 3-D axes [Default is the region given by the input grid]. - $projection - zscale/zsize - Set z-axis scaling or z-axis size. - $frame cmap : str The name of the color palette table to use. drape_grid : str or :class:`xarray.DataArray` @@ -105,7 +90,7 @@ def grdview( # noqa: PLR0913 provides the information pertaining to colors, which (if ``drape_grid`` is a grid) will be looked-up via the CPT (see ``cmap``). surftype - Specify surface type of the grid. Valid values are: + Specify surface type for the grid. Valid values are: - ``"mesh"``: mesh plot [Default]. - ``"surface``: surface plot. @@ -115,8 +100,6 @@ def grdview( # noqa: PLR0913 dpi Effective dots-per-unit resolution for the rasterization for image plots (i.e., ``surftype="image"``) [Default is :gmt-term:`GMT_GRAPHICS_DPU`] - mesh_fill - Set the mesh fill in mesh plot or waterfall plots [Default is white]. nan_transparent Make grid nodes with z = NaN transparent, using the color-masking feature in PostScript Level 3. Only applies when ``surftype="image"``. @@ -128,6 +111,8 @@ def grdview( # noqa: PLR0913 mesh_pen Set the pen attributes used for the mesh. Need to set ``surftype`` to ``"mesh"``, or ``"surface+mesh"`` to draw meshlines. + mesh_fill + Set the mesh fill in mesh plot or waterfall plots [Default is white]. plane Draw a plane at the specified z-level. If ``True``, defaults to the minimum value in the grid. However, if ``region`` was used to set *zmin/zmax* then @@ -148,6 +133,15 @@ def grdview( # noqa: PLR0913 **+m**\ *ambient* to specify azimuth, intensity, and ambient arguments for that function, or just give **+d** to select the default arguments [Default is ``"+a-45+nt1+m0"``]. + $projection + zscale/zsize + Set z-axis scaling or z-axis size. + region : str or list + *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. + Specify the :doc:`region ` of interest. When used + with ``perspective``, optionally append */zmin/zmax* to indicate the range to + use for the 3-D axes [Default is the region given by the input grid]. + $frame $verbose $panel $coltypes diff --git a/pygmt/tests/baseline/test_grdview_surftype.png.dvc b/pygmt/tests/baseline/test_grdview_surftype.png.dvc new file mode 100644 index 00000000000..e76f60713a3 --- /dev/null +++ b/pygmt/tests/baseline/test_grdview_surftype.png.dvc @@ -0,0 +1,5 @@ +outs: +- md5: 3c4e5e2aba5d541909ad94e4ec203186 + size: 225575 + hash: md5 + path: test_grdview_surftype.png diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 5dddd3cd6fd..11297fd1005 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -53,6 +53,37 @@ def test_grdview_grid_dataarray(xrgrid): return fig +@pytest.mark.mpl_image_compare +def test_grdview_surftype(grid): + """ + Test grdview with different surftype values. + """ + args = { + "grid": grid, + "projection": "M?", + "frame": True, + "panel": True, + "perspective": (-150, 25), + "zsize": "1.0c", + } + + fig = Figure() + with fig.subplot(nrows=2, ncols=3, subsize=("5c", "5c"), margins=(0, -0.5)): + for surftype in [ + "mesh", + "surface", + "surface+mesh", + "image", + "waterfall_x", + "waterfall_y", + ]: + if surftype in {"surface", "surface+mesh"}: + fig.grdview(surftype=surftype, cmap="SCM/oleron", **args) + else: + fig.grdview(surftype=surftype, **args) + return fig + + def test_grdview_wrong_kind_of_grid(xrgrid): """ Run grdview using grid input that is not an xarray.DataArray or file. From ec9d3ec853e5945609a348d044f28cfef4011acd Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 18 Dec 2025 13:13:34 +0800 Subject: [PATCH 13/16] Add one more test for dpi --- .../baseline/test_grdview_image_dpi.png.dvc | 5 +++++ pygmt/tests/test_grdview.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 pygmt/tests/baseline/test_grdview_image_dpi.png.dvc diff --git a/pygmt/tests/baseline/test_grdview_image_dpi.png.dvc b/pygmt/tests/baseline/test_grdview_image_dpi.png.dvc new file mode 100644 index 00000000000..8864be6e72f --- /dev/null +++ b/pygmt/tests/baseline/test_grdview_image_dpi.png.dvc @@ -0,0 +1,5 @@ +outs: +- md5: d384910b17de5a2a842cd2625761c821 + size: 224724 + hash: md5 + path: test_grdview_image_dpi.png diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 11297fd1005..84cf1d0fb06 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -84,6 +84,25 @@ def test_grdview_surftype(grid): return fig +@pytest.mark.mpl_image_compare +def test_grdview_image_dpi(grid): + """ + Test grdview with surftype="image" and dpi parameter. + """ + fig = Figure() + for dpi in [None, 10, 100]: + fig.grdview( + grid=grid, + projection="M4c", + surftype="image", + dpi=dpi, + frame=["af", f"WSen+tdpi={dpi}"], + perspective=(225, 30), + ) + fig.shift_origin(xshift="7c") + return fig + + def test_grdview_wrong_kind_of_grid(xrgrid): """ Run grdview using grid input that is not an xarray.DataArray or file. From f1ea73f81f59642a4b728be2098d8833f834a39e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 18 Dec 2025 13:24:46 +0800 Subject: [PATCH 14/16] Add one more test for mesh_pen/mesh_fill --- ...est_grdview_mesh_pen_and_mesh_fill.png.dvc | 5 ++ pygmt/tests/test_grdview.py | 49 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 pygmt/tests/baseline/test_grdview_mesh_pen_and_mesh_fill.png.dvc diff --git a/pygmt/tests/baseline/test_grdview_mesh_pen_and_mesh_fill.png.dvc b/pygmt/tests/baseline/test_grdview_mesh_pen_and_mesh_fill.png.dvc new file mode 100644 index 00000000000..e3c16423c6d --- /dev/null +++ b/pygmt/tests/baseline/test_grdview_mesh_pen_and_mesh_fill.png.dvc @@ -0,0 +1,5 @@ +outs: +- md5: 9158bd6308a9bb59fdcaf56e406954b3 + size: 55008 + hash: md5 + path: test_grdview_mesh_pen_and_mesh_fill.png diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 84cf1d0fb06..cd3d37b2f82 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -103,6 +103,55 @@ def test_grdview_image_dpi(grid): return fig +@pytest.mark.mpl_image_compare +def test_grdview_monochrome(grid): + """ + Test grdview with different surftype values and monochrome=True. + """ + args = { + "grid": grid, + "projection": "M?", + "frame": True, + "panel": True, + "perspective": (-150, 25), + "zsize": "1.0c", + "monochrome": True, + } + fig = Figure() + with fig.subplot(nrows=2, ncols=3, subsize=("5c", "5c"), margins=(0, -0.5)): + for surftype in ["mesh", "surface", "surface+mesh"]: + if surftype in {"surface", "surface+mesh"}: + fig.grdview(surftype=surftype, cmap="SCM/oleron", **args) + else: + fig.grdview(surftype=surftype, **args) + return fig + + +@pytest.mark.mpl_image_compare +def test_grdview_mesh_pen_and_mesh_fill(grid): + """ + Test grdview with mesh_pena and mesh_fill parameters. + """ + args = { + "grid": grid, + "projection": "M?", + "frame": True, + "panel": True, + "perspective": (-150, 25), + "zsize": "1.0c", + "mesh_fill": "lightred", + "mesh_pen": "0.5p,blue", + } + fig = Figure() + with fig.subplot(nrows=1, ncols=3, subsize=("5c", "5c"), margins=0): + for surftype in ["mesh", "waterfall_x", "waterfall_y"]: + if surftype == "surface+mesh": + fig.grdview(surftype=surftype, cmap="SCM/oleron", **args) + else: + fig.grdview(surftype=surftype, **args) + return fig + + def test_grdview_wrong_kind_of_grid(xrgrid): """ Run grdview using grid input that is not an xarray.DataArray or file. From b8f7ffa8e9387d4f85fdacecddbbdd97a0e342db Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 18 Dec 2025 13:25:49 +0800 Subject: [PATCH 15/16] Fix test_grdview_mesh_pen_and_mesh_fill --- pygmt/tests/test_grdview.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index cd3d37b2f82..2f952498620 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -145,10 +145,7 @@ def test_grdview_mesh_pen_and_mesh_fill(grid): fig = Figure() with fig.subplot(nrows=1, ncols=3, subsize=("5c", "5c"), margins=0): for surftype in ["mesh", "waterfall_x", "waterfall_y"]: - if surftype == "surface+mesh": - fig.grdview(surftype=surftype, cmap="SCM/oleron", **args) - else: - fig.grdview(surftype=surftype, **args) + fig.grdview(surftype=surftype, **args) return fig From 073d3b60221d6ef893f8c17715918e92e4783ed4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 18 Dec 2025 13:28:27 +0800 Subject: [PATCH 16/16] Remove duplicated tests --- ...t_grdview_with_cmap_for_image_plot.png.dvc | 4 ---- ...h_cmap_for_surface_monochrome_plot.png.dvc | 4 ---- pygmt/tests/test_grdview.py | 21 ------------------- 3 files changed, 29 deletions(-) delete mode 100644 pygmt/tests/baseline/test_grdview_with_cmap_for_image_plot.png.dvc delete mode 100644 pygmt/tests/baseline/test_grdview_with_cmap_for_surface_monochrome_plot.png.dvc diff --git a/pygmt/tests/baseline/test_grdview_with_cmap_for_image_plot.png.dvc b/pygmt/tests/baseline/test_grdview_with_cmap_for_image_plot.png.dvc deleted file mode 100644 index b85a4ddb08c..00000000000 --- a/pygmt/tests/baseline/test_grdview_with_cmap_for_image_plot.png.dvc +++ /dev/null @@ -1,4 +0,0 @@ -outs: -- md5: 22264dc7db5565465d47092a139377cc - size: 267613 - path: test_grdview_with_cmap_for_image_plot.png diff --git a/pygmt/tests/baseline/test_grdview_with_cmap_for_surface_monochrome_plot.png.dvc b/pygmt/tests/baseline/test_grdview_with_cmap_for_surface_monochrome_plot.png.dvc deleted file mode 100644 index 3f3cbac6d9a..00000000000 --- a/pygmt/tests/baseline/test_grdview_with_cmap_for_surface_monochrome_plot.png.dvc +++ /dev/null @@ -1,4 +0,0 @@ -outs: -- md5: 152085fd81de3ad78b5c7ffb3cf0d080 - size: 95530 - path: test_grdview_with_cmap_for_surface_monochrome_plot.png diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index 2f952498620..b3dbf5e6bb7 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -194,27 +194,6 @@ def test_grdview_with_perspective_and_zsize(xrgrid): return fig -@pytest.mark.mpl_image_compare -def test_grdview_with_cmap_for_image_plot(xrgrid): - """ - Run grdview by passing in a grid and setting a colormap for producing an image plot. - """ - fig = Figure() - fig.grdview(grid=xrgrid, cmap="oleron", surftype="image") - return fig - - -@pytest.mark.mpl_image_compare -def test_grdview_with_cmap_for_surface_monochrome_plot(xrgrid): - """ - Run grdview by passing in a grid and setting a colormap for producing a surface - monochrome plot. - """ - fig = Figure() - fig.grdview(grid=xrgrid, cmap="oleron", surftype="surface", monochrome=True) - return fig - - @pytest.mark.mpl_image_compare def test_grdview_with_cmap_for_perspective_surface_plot(xrgrid): """