Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions examples/gallery/images/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@
many formats (e.g., png, jpg, eps, pdf) on a figure. We must specify the filename via
the ``imagefile`` parameter or simply use the filename as the first argument. You can
also use a full URL pointing to your desired image. The ``position`` parameter allows
us to set a reference point on the map for the image.
us to place the image at a specific location on the plot.
"""

# %%
from pathlib import Path

import pygmt
from pygmt.params import Position

fig = pygmt.Figure()
fig.basemap(region=[0, 2, 0, 2], projection="X10c", frame=True)

# Place and center ("+jCM") the image "needle.jpg" provided by GMT to the position
# ("+g") 1/1 on the current plot, scale it to a width of 8 centimeters ("+w") and draw
# a rectangular border around it
# Place the center of the image "needle.jpg" provided by GMT to the position (1, 1) on
# the current plot, scale it to a width of 8 centimeters and draw a rectangular border
# around it.
fig.image(
imagefile="https://oceania.generic-mapping-tools.org/cache/needle.jpg",
position="g1/1+w8c+jCM",
position=Position((1, 1), cstype="mapcoords", anchor="MC"),
width="8c",
box=True,
)

Expand Down
69 changes: 58 additions & 11 deletions pygmt/src/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@
from collections.abc import Sequence
from typing import Literal

from pygmt._typing import PathLike
from pygmt._typing import AnchorCode, PathLike
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 Box
from pygmt.params import Box, Position
from pygmt.src._common import _parse_position


@fmt_docstring
@use_alias(D="position", G="bitcolor")
@use_alias(G="bitcolor")
def image( # noqa: PLR0913
self,
imagefile: PathLike,
position: Position | Sequence[float | str] | AnchorCode | None = None,
width: float | str | None = None,
height: float | str | None = None,
dpi: float | str | None = None,
replicate: int | Sequence[int] | None = None,
box: Box | bool = False,
monochrome: bool = False,
invert: bool = False,
Expand All @@ -33,10 +39,10 @@ def image( # noqa: PLR0913
r"""
Plot raster or EPS images.

Reads Encapsulated PostScript (EPS) or raster image files and plots them. The
image can be scaled arbitrarily, and 1-bit raster images can be:
Reads an Encapsulated PostScript file or a raster image file and plot it on a map.
The image can be scaled arbitrarily, and 1-bit raster images can be:

- inverted, i.e., black pixels (on) becomes white (off) and vice versa.
- inverted, i.e., black pixels (on) become white (off) and vice versa.
- colorized, by assigning different foreground and background colors.
- made transparent where either the back- or foreground is painted.

Expand All @@ -50,6 +56,7 @@ def image( # noqa: PLR0913

$aliases
- B = frame
- D = position, **+w**: width/height, **+r**: dpi, **+n**: replicate
- F = box
- I = invert
- J = projection
Expand All @@ -66,11 +73,34 @@ def image( # noqa: PLR0913
An Encapsulated PostScript (EPS) file or a raster image file. An EPS file must
contain an appropriate BoundingBox. A raster file can have a depth of 1, 8, 24,
or 32 bits and is read via GDAL.
position : str
[**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\ **+r**\ *dpi*\
**+w**\ [**-**]\ *width*\ [/*height*]\ [**+j**\ *justify*]\
[**+n**\ *nx*\ [/*ny*]]\ [**+o**\ *dx*\ [/*dy*]].
Set reference point on the map for the image.
position
Position of the GMT logo on the plot. It can be specified in multiple ways:

- A :class:`pygmt.params.Position` object to fully control the reference point,
anchor point, and offset.
- A sequence of two values representing the x- and y-coordinates in plot
coordinates, e.g., ``(1, 2)`` or ``("1c", "2c")``.
- A :doc:`2-character justification code </techref/justification_codes>` for a
position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.

If not specified, defaults to the Bottom Left corner of the plot (position
``(0, 0)`` with anchor ``"BL"``).
width
height
Width (and height) of the image in plot coordinates (inches, cm, etc.). If
``height`` (or ``width``) is set to 0, then the original aspect ratio of the
image is maintained. If ``width`` (or ``height``) is negative, the absolute
value is used to interpolate image to the device resolution using the PostScript
image operator. If neither dimensions nor ``dpi`` are set then revert to the
default dpi [:gmt-term:`GMT_GRAPHICS_DPU`].
dpi
Set the dpi of the image in dots per inch, or append **c** to indicate this is
dots per cm.
replicate
*nx* or (*nx*, *ny*).
Replicate the (scaled) image *nx* times in the horizontal direction, and *ny*
times in the vertical direction. If a single integer *nx* is given, *ny* = *nx*.
[Default is (1, 1)].
box
Draw a background box behind the image. If set to ``True``, a simple rectangular
box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box appearance,
Expand Down Expand Up @@ -104,7 +134,24 @@ def image( # noqa: PLR0913
"""
self._activate_figure()

position = _parse_position(
position,
kwdict={"width": width, "height": height, "dpi": dpi, "replicate": replicate},
default=Position((0, 0), cstype="plotcoords"), # Default to (0,0) in plotcoords
)

# width is required when only height is given.
if width is None and height is not None:
width = 0

aliasdict = AliasSystem(
D=[
Alias(position, name="position"),
Alias(width, name="width", prefix="+w"), # +wwidth/height
Alias(height, name="height", prefix="/"),
Alias(replicate, name="replicate", prefix="+n", sep="/", size=2),
Alias(dpi, name="dpi", prefix="+r"),
],
F=Alias(box, name="box"),
M=Alias(monochrome, name="monochrome"),
I=Alias(invert, name="invert"),
Expand Down
5 changes: 3 additions & 2 deletions pygmt/tests/baseline/test_image.png.dvc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
outs:
- md5: d7d0d71a44a232d5907dbd44f7a08f18
size: 30811
- md5: 3bafd31eb0374ec175c1283c95ab0530
size: 24065
path: test_image.png
hash: md5
5 changes: 5 additions & 0 deletions pygmt/tests/baseline/test_image_complete.png.dvc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
outs:
- md5: b2984015b085a78cda9c90fbd500b97e
size: 64696
hash: md5
path: test_image_complete.png
5 changes: 5 additions & 0 deletions pygmt/tests/baseline/test_image_height_no_width.png.dvc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
outs:
- md5: 510cf7715a4059cf65cb0917aabacc8d
size: 34733
hash: md5
path: test_image_height_no_width.png
63 changes: 61 additions & 2 deletions pygmt/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import pytest
from pygmt import Figure
from pygmt.params import Box
from pygmt.exceptions import GMTInvalidInput
from pygmt.params import Box, Position


@pytest.mark.mpl_image_compare
Expand All @@ -13,5 +14,63 @@ def test_image():
Place images on map.
"""
fig = Figure()
fig.image(imagefile="@circuit.png", position="x0/0+w2c", box=Box(pen="thin,blue"))
fig.image(imagefile="@circuit.png")
return fig


@pytest.mark.mpl_image_compare
def test_image_complete():
"""
Test all parameters of image.
"""
fig = Figure()
fig.image(
imagefile="@circuit.png",
position=Position((0, 0)),
width="4c",
height="0",
replicate=(2, 1),
dpi=300,
box=Box(pen="thin,blue"),
)
return fig


@pytest.mark.mpl_image_compare
def test_image_height_no_width():
"""
Test all parameters of image.
"""
fig = Figure()
fig.image(imagefile="@circuit.png", height=2)
return fig


@pytest.mark.mpl_image_compare(filename="test_image_complete.png")
def test_image_position_deprecated_syntax():
"""
Test that passing the deprecated GMT CLI syntax string to 'position' works.
"""
fig = Figure()
fig.image(
imagefile="@circuit.png",
position="x0/0+w4c/0c+n2/1+r300",
box=Box(pen="thin,blue"),
)
return fig


def test_image_position_mixed_syntax():
"""
Test that an error is raised when 'position' is given as a raw GMT CLI string
and conflicts with other parameters.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
fig.image(imagefile="@circuit.png", position="x0/0", width="4c")
with pytest.raises(GMTInvalidInput):
fig.image(imagefile="@circuit.png", position="x0/0", height="3c")
with pytest.raises(GMTInvalidInput):
fig.image(imagefile="@circuit.png", position="x0/0", dpi="300")
with pytest.raises(GMTInvalidInput):
fig.image(imagefile="@circuit.png", position="x0/0", replicate=(2, 1))
Loading