diff --git a/examples/tutorials/basics/coastlines.py b/examples/tutorials/basics/coastlines.py index 5e611b3e2d3..d1e4fa4399d 100644 --- a/examples/tutorials/basics/coastlines.py +++ b/examples/tutorials/basics/coastlines.py @@ -27,9 +27,9 @@ # 3. island-in-lake shore # 4. lake-in-island-in-lake shore # -# You can specify which level you want to plot by passing the level number and -# a GMT pen configuration. For example, to plot just the coastlines with 0.5p -# thickness and black lines: +# You can specify which level you want to plot by passing the level number and a GMT +# pen configuration. For example, to plot just the coastlines with 0.5p thickness and +# black lines: fig = pygmt.Figure() fig.basemap(region="g", projection="W15c", frame=True) @@ -50,18 +50,14 @@ # Resolutions # ----------- # -# The coastline database comes with 5 resolutions. The resolution drops by 80% -# between levels: -# -# 1. ``"c"``: crude -# 2. ``"l"``: low (default) -# 3. ``"i"``: intermediate -# 4. ``"h"``: high -# 5. ``"f"``: full +# The coastline database comes with 5 resolutions: ``"full"``, ``"high"``, +# ``"intermediate"``, ``"low"``, and ``"crude"``. The resolution drops by 80% between +# levels. The ``resolution`` parameter defaults to ``"auto"`` to automatically select +# the best resolution given the chosen map scale. oahu = [-158.3, -157.6, 21.2, 21.8] fig = pygmt.Figure() -for res in ["c", "l", "i", "h", "f"]: +for res in ["crude", "low", "intermediate", "high", "full"]: fig.coast(resolution=res, shorelines="1p", region=oahu, projection="M5c") fig.shift_origin(xshift="5c") fig.show() @@ -71,9 +67,9 @@ # Land and water # -------------- # -# Use the ``land`` and ``water`` parameters to specify a fill color for land -# and water bodies. The colors can be given by name or hex codes (like the ones -# used in HTML and CSS): +# Use the ``land`` and ``water`` parameters to specify a fill color for land and water +# bodies. The colors can be given by name or hex codes (like the ones used in HTML and +# CSS): fig = pygmt.Figure() fig.basemap(region="g", projection="W15c", frame=True) diff --git a/pygmt/src/_common.py b/pygmt/src/_common.py index 408204d050a..53aab7fb66a 100644 --- a/pygmt/src/_common.py +++ b/pygmt/src/_common.py @@ -251,3 +251,56 @@ def from_params( f"{', '.join(params)}." ) raise GMTInvalidInput(msg) + + +def _parse_coastline_resolution( + resolution: Literal["auto", "full", "high", "intermediate", "low", "crude", None], +) -> Literal["a", "f", "h", "i", "l", "c", None]: + """ + Parse the 'resolution' parameter for coastline-related functions. + + Parameters + ---------- + resolution + The resolution of the coastline dataset to use. The available resolutions from + highest to lowest are: ``"full"``, ``"high"``, ``"intermediate"``, ``"low"``, + and ``"crude"``, which drops by 80% between levels. Alternatively, choose + ``"auto"`` to automatically select the most suitable resolution given the chosen + map scale or region. ``None`` means using the default resolution. + + Returns + ------- + The single-letter resolution code or ``None``. + + Raises + ------ + GMTInvalidInput + If the resolution is invalid. + + Examples + -------- + >>> _parse_coastline_resolution("full") + 'f' + >>> _parse_coastline_resolution("f") + 'f' + >>> _parse_coastline_resolution(None) + >>> _parse_coastline_resolution("invalid") + Traceback (most recent call last): + ... + pygmt.exceptions.GMTInvalidInput: Invalid resolution: 'invalid'. Valid values ... + """ + if resolution is None: + return None + + _valid_res = {"auto", "full", "high", "intermediate", "low", "crude"} + + if resolution in _valid_res: # Long-form arguments. + return resolution[0] # type: ignore[return-value] + + if resolution in {_res[0] for _res in _valid_res}: # Short-form arguments. + return resolution # type: ignore[return-value] + + msg = ( + f"Invalid resolution: '{resolution}'. Valid values are {', '.join(_valid_res)}." + ) + raise GMTInvalidInput(msg) diff --git a/pygmt/src/coast.py b/pygmt/src/coast.py index ec55a7acdb3..f898ce8bc13 100644 --- a/pygmt/src/coast.py +++ b/pygmt/src/coast.py @@ -2,6 +2,8 @@ coast - Plot continents, countries, shorelines, rivers, and borders. """ +from typing import Literal + from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( @@ -11,6 +13,7 @@ kwargs_to_strings, use_alias, ) +from pygmt.src._common import _parse_coastline_resolution __doctest_skip__ = ["coast"] @@ -20,7 +23,6 @@ A="area_thresh", B="frame", C="lakes", - D="resolution", E="dcw", F="box", G="land", @@ -37,7 +39,13 @@ t="transparency", ) @kwargs_to_strings(R="sequence", c="sequence_comma", p="sequence") -def coast(self, **kwargs): +def coast( + self, + resolution: Literal[ + "auto", "full", "high", "intermediate", "low", "crude", None + ] = None, + **kwargs, +): r""" Plot continents, countries, shorelines, rivers, and borders. @@ -73,10 +81,12 @@ def coast(self, **kwargs): parameter. Optionally, specify separate fills by appending **+l** for lakes or **+r** for river-lakes, and passing multiple strings in a list. - resolution : str - **f**\|\ **h**\|\ **i**\|\ **l**\|\ **c**. - Select the resolution of the data set to: (**f**\ )ull, (**h**\ )igh, - (**i**\ )ntermediate, (**l**\ )ow, and (**c**\ )rude. + resolution + Select the resolution of the coastline dataset to use. The available resolutions + from highest to lowest are: ``"full"``, ``"high"``, ``"intermediate"``, + ``"low"``, and ``"crude"``, which drops by 80% between levels. Default is + ``"auto"`` to automatically select the most suitable resolution given the chosen + map scale. land : str Select filling of "dry" areas. rivers : int, str, or list @@ -200,5 +210,8 @@ def coast(self, **kwargs): "lakes, land, water, rivers, borders, dcw, Q, or shorelines." ) raise GMTInvalidInput(msg) + + kwargs["D"] = kwargs.get("D", _parse_coastline_resolution(resolution)) + with Session() as lib: lib.call_module(module="coast", args=build_arg_list(kwargs)) diff --git a/pygmt/src/grdlandmask.py b/pygmt/src/grdlandmask.py index e26ed0b360e..08135999906 100644 --- a/pygmt/src/grdlandmask.py +++ b/pygmt/src/grdlandmask.py @@ -2,11 +2,14 @@ grdlandmask - Create a "wet-dry" mask grid from shoreline database. """ +from typing import Literal + import xarray as xr from pygmt._typing import PathLike from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias +from pygmt.src._common import _parse_coastline_resolution __doctest_skip__ = ["grdlandmask"] @@ -14,7 +17,6 @@ @fmt_docstring @use_alias( A="area_thresh", - D="resolution", E="bordervalues", I="spacing", N="maskvalues", @@ -24,7 +26,13 @@ x="cores", ) @kwargs_to_strings(I="sequence", R="sequence", N="sequence", E="sequence") -def grdlandmask(outgrid: PathLike | None = None, **kwargs) -> xr.DataArray | None: +def grdlandmask( + outgrid: PathLike | None = None, + resolution: Literal[ + "auto", "full", "high", "intermediate", "low", "crude", None + ] = None, + **kwargs, +) -> xr.DataArray | None: r""" Create a "wet-dry" mask grid from shoreline database. @@ -44,17 +52,15 @@ def grdlandmask(outgrid: PathLike | None = None, **kwargs) -> xr.DataArray | Non {spacing} {region} {area_thresh} - resolution : str - *res*\[\ **+f**\]. Select the resolution of the data set to use - ((**f**)ull, (**h**)igh, (**i**)ntermediate, (**l**)ow, or - (**c**)rude). The resolution drops off by ~80% between data sets. - [Default is **l**]. Append **+f** to automatically select a lower - resolution should the one requested not be available - [abort if not found]. Alternatively, choose (**a**)uto to automatically - select the best resolution given the chosen region. Note that because - the coastlines differ in details a node in a mask file using one - resolution is not guaranteed to remain inside [or outside] when a - different resolution is selected. + resolution + Select the resolution of the coastline dataset to use. The available resolutions + from highest to lowest are: ``"full"``, ``"high"``, ``"intermediate"``, + ``"low"``, and ``"crude"``, which drops by 80% between levels. Alternatively, + choose ``"auto"`` to automatically select the most suitable resolution given the + chosen region. Note that because the coastlines differ in details, a node in a + mask file using one resolution is not guaranteed to remain inside [or outside] + when a different resolution is selected. If ``None``, the low resolution is used + by default. maskvalues : list Set the values that will be assigned to nodes, in the form of [*wet*, *dry*], or [*ocean*, *land*, *lake*, *island*, *pond*]. Default is ``[0, 1, 0, 1, 0]`` @@ -102,6 +108,8 @@ def grdlandmask(outgrid: PathLike | None = None, **kwargs) -> xr.DataArray | Non msg = "Both 'region' and 'spacing' must be specified." raise GMTInvalidInput(msg) + kwargs["D"] = kwargs.get("D", _parse_coastline_resolution(resolution)) + with Session() as lib: with lib.virtualfile_out(kind="grid", fname=outgrid) as voutgrd: kwargs["G"] = voutgrd diff --git a/pygmt/src/select.py b/pygmt/src/select.py index 1b1adbb5d34..922b0626c59 100644 --- a/pygmt/src/select.py +++ b/pygmt/src/select.py @@ -15,6 +15,7 @@ use_alias, validate_output_table_type, ) +from pygmt.src._common import _parse_coastline_resolution __doctest_skip__ = ["select"] @@ -23,7 +24,6 @@ @use_alias( A="area_thresh", C="dist2pt", - D="resolution", F="polygon", G="gridmask", I="reverse", @@ -49,6 +49,9 @@ def select( data: PathLike | TableLike | None = None, output_type: Literal["pandas", "numpy", "file"] = "pandas", outfile: PathLike | None = None, + resolution: Literal[ + "auto", "full", "high", "intermediate", "low", "crude", None + ] = None, **kwargs, ) -> pd.DataFrame | np.ndarray | None: r""" @@ -117,16 +120,6 @@ def select( ` *polygonfile*. For spherical polygons (lon, lat), make sure no consecutive points are separated by 180 degrees or more in longitude. - resolution : str - *resolution*\ [**+f**]. - Ignored unless ``mask`` is set. Selects the resolution of the coastline - data set to use ((**f**)ull, (**h**)igh, (**i**)ntermediate, (**l**)ow, - or (**c**)rude). The resolution drops off by ~80% between data sets. - [Default is **l**]. Append (**+f**) to automatically select a lower - resolution should the one requested not be available [Default is abort - if not found]. Note that because the coastlines differ in details - it is not guaranteed that a point will remain inside [or outside] when - a different resolution is selected. gridmask : str Pass all locations that are inside the valid data area of the grid *gridmask*. Nodes that are outside are either NaN or zero. @@ -155,6 +148,15 @@ def select( [Default is s/k/s/k/s (i.e., s/k), which passes all points on dry land]. + resolution + Ignored unless ``mask`` is set. Select the resolution of the coastline dataset + to use. The available resolutions from highest to lowest are: ``"full"``, + ``"high"``, ``"intermediate"``, ``"low"``, and ``"crude"``, which drops by 80% + between levels. Alternatively, choose ``"auto"`` to automatically select the + most suitable resolution given the chosen region. Note that because the + coastlines differ in details, a node in a mask file using one resolution is not + guaranteed to remain inside [or outside] when a different resolution is + selected. If ``None``, the low resolution is used by default. {region} {verbose} z_subregion : str or list @@ -206,6 +208,8 @@ def select( >>> # longitudes 246 and 247 and latitudes 20 and 21 >>> out = pygmt.select(data=ship_data, region=[246, 247, 20, 21]) """ + kwargs["D"] = kwargs.get("D", _parse_coastline_resolution(resolution)) + output_type = validate_output_table_type(output_type, outfile=outfile) column_names = None diff --git a/pygmt/tests/test_coast.py b/pygmt/tests/test_coast.py index 780ec63cace..64153e8e36e 100644 --- a/pygmt/tests/test_coast.py +++ b/pygmt/tests/test_coast.py @@ -29,7 +29,7 @@ def test_coast_world_mercator(): projection="M15c", frame="af", land="#aaaaaa", - resolution="c", + resolution="crude", water="white", ) return fig