Skip to content

Commit 4f8709e

Browse files
authored
Figure.image: Add parameter fgcolor/bgcolor/transparent_color and deprecate bitcolor [Will be removed in 0.24.0] (#4872)
1 parent f87b1bd commit 4f8709e

4 files changed

Lines changed: 158 additions & 13 deletions

File tree

‎pygmt/helpers/caching.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,6 @@ def cache_data() -> None:
134134
"@tut_quakes.ngdc",
135135
"@tut_ship.xyz",
136136
"@usgs_quakes_22.txt",
137+
"@vader1.png",
137138
]
138139
which(fname=datasets, download="auto")

‎pygmt/src/image.py‎

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
image - Plot raster or EPS images.
33
"""
44

5+
import warnings
56
from collections.abc import Sequence
67
from typing import Literal
78

89
from pygmt._typing import AnchorCode, PathLike
910
from pygmt.alias import Alias, AliasSystem
1011
from pygmt.clib import Session
11-
from pygmt.helpers import build_arg_list, fmt_docstring, use_alias
12+
from pygmt.exceptions import GMTParameterError, GMTValueError
13+
from pygmt.helpers import build_arg_list, fmt_docstring
1214
from pygmt.params import Axis, Box, Frame, Position
1315
from pygmt.src._common import _parse_position
1416

1517

1618
@fmt_docstring
17-
@use_alias(G="bitcolor")
1819
def image(
1920
self,
2021
imagefile: PathLike,
@@ -26,6 +27,10 @@ def image(
2627
box: Box | bool = False,
2728
monochrome: bool = False,
2829
invert: bool = False,
30+
bgcolor: str | None = None,
31+
fgcolor: str | None = None,
32+
transparent_color: str | None = None,
33+
bitcolor: str | Sequence[str] | None = None,
2934
projection: str | None = None,
3035
region: Sequence[float | str] | str | None = None,
3136
frame: Frame | Axis | Literal["none"] | str | Sequence[str] | bool = False,
@@ -54,10 +59,15 @@ def image(
5459
5560
Full GMT docs at :gmt-docs:`image.html`.
5661
57-
$aliases
62+
**Aliases:**
63+
64+
.. hlist::
65+
:columns: 3
66+
5867
- B = frame
5968
- D = position, **+w**: width/height, **+r**: dpi, **+n**: replicate
6069
- F = box
70+
- G = **+b**: bgcolor, **+f**: fgcolor, **+t**: transparent_color, bitcolor
6171
- I = invert
6272
- J = projection
6373
- M = monochrome
@@ -74,7 +84,7 @@ def image(
7484
contain an appropriate BoundingBox. A raster file can have a depth of 1, 8, 24,
7585
or 32 bits and is read via GDAL.
7686
position
77-
Position of the GMT logo on the plot. It can be specified in multiple ways:
87+
Position of the image on the plot. It can be specified in multiple ways:
7888
7989
- A :class:`pygmt.params.Position` object to fully control the reference point,
8090
anchor point, and offset.
@@ -106,14 +116,25 @@ def image(
106116
box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box appearance,
107117
pass a :class:`pygmt.params.Box` object to control style, fill, pen, and other
108118
box properties.
109-
bitcolor : str or list
119+
bgcolor
120+
fgcolor
121+
For 1-bit images, set the background and foreground colors [Default is black and
122+
white, respectively]. Setting either to an empty string makes those pixels
123+
transparent. Cannot be both empty.
124+
transparent_color
125+
For color images, set a single color that should be made transparent.
126+
bitcolor
110127
[*color*][**+b**\|\ **f**\|\ **t**].
111-
Change certain pixel values to another color or make them transparent.
112-
For 1-bit images you can specify an alternate *color* for the
113-
background (**+b**) or the foreground (**+f**) pixels, or give no color
114-
to make those pixels transparent. Can be repeated with different
115-
settings. Alternatively, for color images you can select a single
116-
*color* that should be made transparent instead (**+t**).
128+
Change certain pixel values to another color or make them transparent. For 1-bit
129+
images you can specify an alternate *color* for the background (**+b**) or the
130+
foreground (**+f**) pixels, or give no color to make those pixels transparent.
131+
Alternatively, for color images you can select a single *color* that should be
132+
made transparent instead (**+t**). Pass a list to specify multiple settings.
133+
134+
.. deprecated:: 0.20.0
135+
136+
Use ``bgcolor``, ``fgcolor``, or ``transparent_color`` instead. Will be
137+
removed in 0.24.0.
117138
monochrome
118139
Convert color image to monochrome grayshades using the (television)
119140
YIQ-transformation.
@@ -142,6 +163,38 @@ def image(
142163
if width is None and height is not None:
143164
width = 0
144165

166+
# TODO(PyGMT>=0.24.0): Remove the deprecated "bitcolor" parameter.
167+
if bitcolor is not None:
168+
msg = (
169+
"The 'bitcolor' parameter has been deprecated since v0.20.0 and will be "
170+
"removed in v0.24.0. Use 'bgcolor', 'fgcolor' or 'transparent_color' "
171+
"instead."
172+
)
173+
warnings.warn(msg, category=FutureWarning, stacklevel=2)
174+
if any(v is not None for v in [bgcolor, fgcolor, transparent_color]):
175+
raise GMTParameterError(
176+
conflicts_with=(
177+
"bitcolor",
178+
["bgcolor", "fgcolor", "transparent_color"],
179+
),
180+
)
181+
182+
# 'bgcolor' and 'fgcolor' cannot both be empty.
183+
if bgcolor == "" and fgcolor == "":
184+
_value = f"{bgcolor=}, {fgcolor=}"
185+
raise GMTValueError(
186+
_value,
187+
description="bgcolor and fgcolor",
188+
reason="'bgcolor' and 'fgcolor' cannot both be empty.",
189+
)
190+
# GMT requires a color for the "+t" modifier.
191+
if transparent_color == "":
192+
raise GMTValueError(
193+
transparent_color,
194+
description="value for 'transparent_color'",
195+
reason="'transparent_color' cannot be empty.",
196+
)
197+
145198
aliasdict = AliasSystem(
146199
D=[
147200
Alias(position, name="position"),
@@ -151,6 +204,13 @@ def image(
151204
Alias(dpi, name="dpi", prefix="+r"),
152205
],
153206
F=Alias(box, name="box"),
207+
G=[
208+
Alias(bgcolor, name="bgcolor", suffix="+b"),
209+
Alias(fgcolor, name="fgcolor", suffix="+f"),
210+
Alias(transparent_color, name="transparent_color", suffix="+t"),
211+
]
212+
if bitcolor is None
213+
else Alias(bitcolor, name="bitcolor"),
154214
M=Alias(monochrome, name="monochrome"),
155215
I=Alias(invert, name="invert"),
156216
).add_common(
Lines changed: 3 additions & 0 deletions
Loading

‎pygmt/tests/test_image.py‎

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import pytest
66
from pygmt import Figure
7-
from pygmt.exceptions import GMTParameterError
8-
from pygmt.params import Box, Position
7+
from pygmt.exceptions import GMTParameterError, GMTValueError
8+
from pygmt.params import Box, Frame, Position
99

1010

1111
@pytest.mark.mpl_image_compare
@@ -74,3 +74,84 @@ def test_image_position_mixed_syntax():
7474
fig.image(imagefile="@circuit.png", position="x0/0", dpi="300")
7575
with pytest.raises(GMTParameterError):
7676
fig.image(imagefile="@circuit.png", position="x0/0", replicate=(2, 1))
77+
78+
79+
@pytest.mark.mpl_image_compare
80+
def test_image_bgcolor_fgcolor():
81+
"""
82+
Test setting the background and foreground colors of a 1-bit image.
83+
"""
84+
fig = Figure()
85+
fig.basemap(region=[0, 8, 0, 4], projection="x1c/1c", frame=Frame(fill="gray"))
86+
fig.image(imagefile="@vader1.png", position=(0, 0), width="2c")
87+
fig.image(imagefile="@vader1.png", position=(2, 0), width="2c", bgcolor="")
88+
fig.image(imagefile="@vader1.png", position=(4, 0), width="2c", fgcolor="")
89+
fig.image(imagefile="@vader1.png", position=(6, 0), width="2c", bgcolor="red")
90+
fig.image(imagefile="@vader1.png", position=(0, 2), width="2c", fgcolor="blue")
91+
fig.image(
92+
imagefile="@vader1.png",
93+
position=(2, 2),
94+
width="2c",
95+
bgcolor="red",
96+
fgcolor="blue",
97+
)
98+
fig.image(
99+
imagefile="@vader1.png",
100+
position=(4, 2),
101+
width="2c",
102+
bgcolor="red",
103+
fgcolor="",
104+
)
105+
fig.image(
106+
imagefile="@vader1.png",
107+
position=(6, 2),
108+
width="2c",
109+
bgcolor="",
110+
fgcolor="blue",
111+
)
112+
return fig
113+
114+
115+
def test_image_invalid_colors():
116+
"""
117+
Test that invalid 'bgcolor'/'fgcolor'/'transparent_color' values raise an error.
118+
"""
119+
fig = Figure()
120+
# Making both the background and the foreground transparent leaves nothing to paint.
121+
with pytest.raises(GMTValueError):
122+
fig.image(imagefile="@circuit.png", bgcolor="", fgcolor="")
123+
# GMT requires a color for the "+t" modifier.
124+
with pytest.raises(GMTValueError):
125+
fig.image(imagefile="@circuit.png", transparent_color="")
126+
127+
128+
# TODO(PyGMT>=0.24.0): Remove the test for the deprecated "bitcolor" parameter.
129+
def test_image_bitcolor_deprecated():
130+
"""
131+
Test that the deprecated 'bitcolor' parameter still works but warns.
132+
"""
133+
fig = Figure()
134+
with pytest.warns(FutureWarning):
135+
fig.image(imagefile="@circuit.png", bitcolor="red+b")
136+
with pytest.warns(FutureWarning):
137+
fig.image(imagefile="@circuit.png", bitcolor=["red+b", "blue+f"])
138+
139+
140+
# TODO(PyGMT>=0.24.0): Remove the test for the deprecated "bitcolor" parameter.
141+
def test_image_bitcolor_conflict():
142+
"""
143+
Test that the deprecated 'bitcolor' parameter raises an error when used with
144+
'bgcolor', 'fgcolor', or 'transparent_color'.
145+
"""
146+
fig = Figure()
147+
with pytest.raises(GMTParameterError):
148+
with pytest.warns(FutureWarning):
149+
fig.image(imagefile="@circuit.png", bitcolor="red+b", bgcolor="blue")
150+
with pytest.raises(GMTParameterError):
151+
with pytest.warns(FutureWarning):
152+
fig.image(imagefile="@circuit.png", bitcolor="red+b", fgcolor="blue")
153+
with pytest.raises(GMTParameterError):
154+
with pytest.warns(FutureWarning):
155+
fig.image(
156+
imagefile="@circuit.png", bitcolor="red+b", transparent_color="blue"
157+
)

0 commit comments

Comments
 (0)