Skip to content

Commit 9af5ba1

Browse files
authored
Merge branch 'main' into alias-system
2 parents 3ceea79 + aa7c658 commit 9af5ba1

File tree

15 files changed

+247
-161
lines changed

15 files changed

+247
-161
lines changed

examples/gallery/histograms/blockm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
# Set the region for the plot
2020
region = [130, 152.5, 32.5, 52.5]
21-
# Define spacing in x- and y-direction (150x150 arc-minute blocks)
21+
# Define spacing in x- and y-directions (150x150 arc-minute blocks)
2222
spacing = "150m"
2323

2424
fig = pygmt.Figure()

examples/gallery/images/grdlandmask.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# Assign a value of 0 for all water masses and a value of 1 for all land
1818
# masses.
1919
# Use shoreline data with (l)ow resolution and set the grid spacing to
20-
# 5 arc-minutes in x- and y-direction.
20+
# 5 arc-minutes in x- and y-directions.
2121
grid = pygmt.grdlandmask(region=region, spacing="5m", maskvalues=[0, 1], resolution="l")
2222

2323
# Plot clipped grid

examples/tutorials/advanced/3d_perspective_image.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
# Sets the view azimuth as 130 degrees, and the view elevation as 30
2828
# degrees
2929
perspective=[130, 30],
30-
# Sets the x- and y-axis labels, and annotates the west, south, and east
31-
# axes
30+
# Sets the x- and y-axis labels, and annotates the west, south, and east axes
3231
frame=["xa", "ya", "WSnE"],
3332
# Sets a Mercator projection on a 15-centimeter figure
3433
projection="M15c",

examples/tutorials/advanced/cartesian_histograms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
fill="red3",
8080
pen="1p,darkgray,solid",
8181
histtype=0,
82-
# Use horizontal bars. Note that the x- and y-axis are flipped, with the x-axis
82+
# Use horizontal bars. Note that the x- and y-axes are flipped, with the x-axis
8383
# plotted vertically and the y-axis plotted horizontally.
8484
horizontal=True,
8585
)

examples/tutorials/advanced/insets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
# %%
5858
# When using **j** to set the anchor of the inset, the default location is in
5959
# contact with the nearby axis or axes. The offset of the inset can be set with
60-
# **+o**, followed by the offsets along the x- and y-axis. If only one offset
60+
# **+o**, followed by the offsets along the x- and y-axes. If only one offset
6161
# is passed, it is applied to both axes. Each offset can have its own unit. In
6262
# the example below, the inset is shifted 0.5 centimeters on the x-axis and
6363
# 0.2 centimeters on the y-axis.

examples/tutorials/basics/coastlines.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
# 3. island-in-lake shore
2828
# 4. lake-in-island-in-lake shore
2929
#
30-
# You can specify which level you want to plot by passing the level number and
31-
# a GMT pen configuration. For example, to plot just the coastlines with 0.5p
32-
# thickness and black lines:
30+
# You can specify which level you want to plot by passing the level number and a GMT
31+
# pen configuration. For example, to plot just the coastlines with 0.5p thickness and
32+
# black lines:
3333

3434
fig = pygmt.Figure()
3535
fig.basemap(region="g", projection="W15c", frame=True)
@@ -50,18 +50,14 @@
5050
# Resolutions
5151
# -----------
5252
#
53-
# The coastline database comes with 5 resolutions. The resolution drops by 80%
54-
# between levels:
55-
#
56-
# 1. ``"c"``: crude
57-
# 2. ``"l"``: low (default)
58-
# 3. ``"i"``: intermediate
59-
# 4. ``"h"``: high
60-
# 5. ``"f"``: full
53+
# The coastline database comes with 5 resolutions: ``"full"``, ``"high"``,
54+
# ``"intermediate"``, ``"low"``, and ``"crude"``. The resolution drops by 80% between
55+
# levels. The ``resolution`` parameter defaults to ``"auto"`` to automatically select
56+
# the best resolution given the chosen map scale.
6157

6258
oahu = [-158.3, -157.6, 21.2, 21.8]
6359
fig = pygmt.Figure()
64-
for res in ["c", "l", "i", "h", "f"]:
60+
for res in ["crude", "low", "intermediate", "high", "full"]:
6561
fig.coast(resolution=res, shorelines="1p", region=oahu, projection="M5c")
6662
fig.shift_origin(xshift="5c")
6763
fig.show()
@@ -71,9 +67,9 @@
7167
# Land and water
7268
# --------------
7369
#
74-
# Use the ``land`` and ``water`` parameters to specify a fill color for land
75-
# and water bodies. The colors can be given by name or hex codes (like the ones
76-
# used in HTML and CSS):
70+
# Use the ``land`` and ``water`` parameters to specify a fill color for land and water
71+
# bodies. The colors can be given by name or hex codes (like the ones used in HTML and
72+
# CSS):
7773

7874
fig = pygmt.Figure()
7975
fig.basemap(region="g", projection="W15c", frame=True)

pygmt/helpers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
is_nonstr_iter,
2424
launch_external_viewer,
2525
non_ascii_to_octal,
26+
sequence_join,
2627
)
2728
from pygmt.helpers.validators import validate_output_table_type

pygmt/helpers/utils.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,120 @@ def args_in_kwargs(args: Sequence[str], kwargs: dict[str, Any]) -> bool:
711711
return any(
712712
kwargs.get(arg) is not None and kwargs.get(arg) is not False for arg in args
713713
)
714+
715+
716+
def sequence_join(
717+
value: Any,
718+
separator: str = "/",
719+
size: int | Sequence[int] | None = None,
720+
ndim: int = 1,
721+
name: str | None = None,
722+
) -> str | list[str] | None | Any:
723+
"""
724+
Join a sequence of values into a string separated by a separator.
725+
726+
A 1-D sequence will be joined into a single string. A 2-D sequence will be joined
727+
into a list of strings. Non-sequence values will be returned as is.
728+
729+
Parameters
730+
----------
731+
value
732+
The 1-D or 2-D sequence of values to join.
733+
separator
734+
The separator to join the values.
735+
size
736+
Expected size of the 1-D sequence. It can be either an integer or a sequence of
737+
integers. If an integer, it is the expected size of the 1-D sequence. If it is a
738+
sequence, it is the allowed sizes of the 1-D sequence.
739+
ndim
740+
The expected maximum number of dimensions of the sequence.
741+
name
742+
The name of the parameter to be used in the error message.
743+
744+
Returns
745+
-------
746+
joined_value
747+
The joined string or list of strings.
748+
749+
Examples
750+
--------
751+
>>> sequence_join("1/2/3/4")
752+
'1/2/3/4'
753+
>>> sequence_join(None)
754+
>>> sequence_join(True)
755+
True
756+
>>> sequence_join(False)
757+
False
758+
759+
>>> sequence_join([])
760+
Traceback (most recent call last):
761+
...
762+
pygmt.exceptions.GMTInvalidInput: Expected a sequence but got an empty sequence.
763+
764+
>>> sequence_join([1, 2, 3, 4])
765+
'1/2/3/4'
766+
>>> sequence_join([1, 2, 3, 4], separator=",")
767+
'1,2,3,4'
768+
>>> sequence_join([1, 2, 3, 4], separator="/", size=4)
769+
'1/2/3/4'
770+
>>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4])
771+
'1/2/3/4'
772+
>>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4], ndim=2)
773+
'1/2/3/4'
774+
>>> sequence_join([1, 2, 3, 4], separator="/", size=2)
775+
Traceback (most recent call last):
776+
...
777+
pygmt.exceptions.GMTInvalidInput: Expected a sequence of 2 values, but got 4 values.
778+
>>> sequence_join([1, 2, 3, 4, 5], separator="/", size=[2, 4], name="parname")
779+
Traceback (most recent call last):
780+
...
781+
pygmt.exceptions.GMTInvalidInput: Parameter 'parname': Expected ...
782+
783+
>>> sequence_join([[1, 2], [3, 4]], separator="/")
784+
Traceback (most recent call last):
785+
...
786+
pygmt.exceptions.GMTInvalidInput: Expected a 1-D ..., but a 2-D sequence is given.
787+
>>> sequence_join([[1, 2], [3, 4]], separator="/", ndim=2)
788+
['1/2', '3/4']
789+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=2, ndim=2)
790+
['1/2', '3/4']
791+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=4, ndim=2)
792+
Traceback (most recent call last):
793+
...
794+
pygmt.exceptions.GMTInvalidInput: Expected a sequence of 4 values.
795+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=[2, 4], ndim=2)
796+
['1/2', '3/4']
797+
"""
798+
# Return the original value if it is not a sequence (e.g., None, bool, or str).
799+
if not is_nonstr_iter(value):
800+
return value
801+
# Now it must be a sequence.
802+
803+
# Change size to a list to simplify the checks.
804+
size = [size] if isinstance(size, int) else size
805+
errmsg = {
806+
"name": f"Parameter '{name}': " if name else "",
807+
"sizes": ", ".join(str(s) for s in size) if size is not None else "",
808+
}
809+
810+
if len(value) == 0:
811+
msg = f"{errmsg['name']}Expected a sequence but got an empty sequence."
812+
raise GMTInvalidInput(msg)
813+
814+
if not is_nonstr_iter(value[0]): # 1-D sequence.
815+
if size is not None and len(value) not in size:
816+
msg = (
817+
f"{errmsg['name']}Expected a sequence of {errmsg['sizes']} values, "
818+
f"but got {len(value)} values."
819+
)
820+
raise GMTInvalidInput(msg)
821+
return separator.join(str(v) for v in value)
822+
823+
# Now it must be a 2-D sequence.
824+
if ndim == 1:
825+
msg = f"{errmsg['name']}Expected a 1-D sequence, but a 2-D sequence is given."
826+
raise GMTInvalidInput(msg)
827+
if size is not None and any(len(i) not in size for i in value):
828+
msg = f"{errmsg['name']}Expected a sequence of {errmsg['sizes']} values."
829+
raise GMTInvalidInput(msg)
830+
return [separator.join(str(j) for j in sub) for sub in value]

pygmt/src/_common.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,56 @@ def from_params(
251251
f"{', '.join(params)}."
252252
)
253253
raise GMTInvalidInput(msg)
254+
255+
256+
def _parse_coastline_resolution(
257+
resolution: Literal["auto", "full", "high", "intermediate", "low", "crude", None],
258+
) -> Literal["a", "f", "h", "i", "l", "c", None]:
259+
"""
260+
Parse the 'resolution' parameter for coastline-related functions.
261+
262+
Parameters
263+
----------
264+
resolution
265+
The resolution of the coastline dataset to use. The available resolutions from
266+
highest to lowest are: ``"full"``, ``"high"``, ``"intermediate"``, ``"low"``,
267+
and ``"crude"``, which drops by 80% between levels. Alternatively, choose
268+
``"auto"`` to automatically select the most suitable resolution given the chosen
269+
map scale or region. ``None`` means using the default resolution.
270+
271+
Returns
272+
-------
273+
The single-letter resolution code or ``None``.
274+
275+
Raises
276+
------
277+
GMTInvalidInput
278+
If the resolution is invalid.
279+
280+
Examples
281+
--------
282+
>>> _parse_coastline_resolution("full")
283+
'f'
284+
>>> _parse_coastline_resolution("f")
285+
'f'
286+
>>> _parse_coastline_resolution(None)
287+
>>> _parse_coastline_resolution("invalid")
288+
Traceback (most recent call last):
289+
...
290+
pygmt.exceptions.GMTInvalidInput: Invalid resolution: 'invalid'. Valid values ...
291+
"""
292+
if resolution is None:
293+
return None
294+
295+
_valid_res = {"auto", "full", "high", "intermediate", "low", "crude"}
296+
297+
if resolution in _valid_res: # Long-form arguments.
298+
return resolution[0] # type: ignore[return-value]
299+
300+
if resolution in {_res[0] for _res in _valid_res}: # Short-form arguments.
301+
return resolution # type: ignore[return-value]
302+
303+
msg = (
304+
f"Invalid resolution: '{resolution}'. Valid values are {', '.join(_valid_res)}."
305+
)
306+
raise GMTInvalidInput(msg)

pygmt/src/basemap.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ def basemap(self, frame=None, **kwargs):
6060
**+p**\ *pen*. Add **+g**\ *fill* to fill the scale panel [Default is
6161
no fill]. Append **+c**\ *clearance* where *clearance* is either gap,
6262
xgap/ygap, or lgap/rgap/bgap/tgap where these items are uniform,
63-
separate in x- and y-direction, or individual side spacings between
64-
scale and border. Append **+i** to draw a secondary, inner border as
65-
well. We use a uniform gap between borders of 2p and the
63+
separate x and y, or individual side spacings between scale and
64+
border. Append **+i** to draw a secondary, inner border as well.
65+
We use a uniform gap between borders of 2 points and the
6666
:gmt-term:`MAP_DEFAULTS_PEN` unless other values are specified. Append
67-
**+r** to draw rounded rectangular borders instead, with a 6p corner
68-
radius. You can override this radius by appending another value.
67+
**+r** to draw rounded rectangular borders instead, with a 6-points
68+
corner radius. You can override this radius by appending another value.
6969
Finally, append **+s** to draw an offset background shaded region.
7070
Here, *dx/dy* indicates the shift relative to the foreground frame
7171
[Default is ``"4p/-4p"``] and shade sets the fill style to use for

0 commit comments

Comments
 (0)