Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1fd9d4a
Implement the Pattern class for specifying pattern as fills
seisman Sep 7, 2025
30d6b92
Add tests for the Pattern class
seisman Sep 7, 2025
db82469
Use the Pattern class in examples
seisman Sep 7, 2025
3031c59
Improve the gallery example for patterns
seisman Sep 7, 2025
ed1ba89
Remove unnecessary content from the techref for patterns
seisman Sep 7, 2025
57e9c4e
Improve docstrings
seisman Sep 8, 2025
3ba2006
Remove the doc/techref/patterns.md doc
seisman Sep 8, 2025
5dfa0d2
Fix a typo in URL
seisman Sep 8, 2025
0ca48c6
Smaller image size
seisman Sep 8, 2025
43bf9ff
Fix a typo in examples/gallery/symbols/patterns.py
seisman Sep 8, 2025
2a18366
Use the figure directive
seisman Sep 8, 2025
5cda812
Polish examples/gallery/symbols/patterns.py
seisman Sep 8, 2025
dcbcaa8
Fix the text for each pattern
seisman Sep 8, 2025
ff3419c
Fix the image width to show the pattern string
seisman Sep 8, 2025
1b17ab0
Validate that fgcolor and fgcolor cannot both be empty
seisman Sep 8, 2025
994c3be
Fix styling
seisman Sep 8, 2025
8e560c3
Fix typos
seisman Sep 9, 2025
f627d28
Rename id to pattern
seisman Sep 9, 2025
28be123
Rename reversed to invert
seisman Sep 9, 2025
a8af0e7
One more fix for reverse->invert
seisman Sep 9, 2025
56bbf3c
Merge branch 'main' into feature/pattern
seisman Sep 9, 2025
74da7a8
Fix a typo
seisman Sep 9, 2025
e9eca8e
DOC: Fix the default resolution from 1200 to 300 for patterns
seisman Sep 9, 2025
43036c5
Polish docstrings
seisman Sep 9, 2025
4431649
Merge branch 'main' into feature/pattern
seisman Sep 10, 2025
1b453a9
Merge branch 'main' into feature/pattern
seisman Sep 13, 2025
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
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ Class-style Parameters
:toctree: generated

Box
Pattern

Enums
-----
Expand Down
1 change: 0 additions & 1 deletion doc/techref/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ common_parameters.md
projections.md
fonts.md
text_formatting.md
patterns.md
encodings.md
justification_codes.md
environment_variables.md
Expand Down
25 changes: 0 additions & 25 deletions doc/techref/patterns.md

This file was deleted.

52 changes: 25 additions & 27 deletions examples/gallery/symbols/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Bit and hachure patterns
========================

In addition to colors, PyGMT also allows using bit and hachure patterns to fill
symbols, polygons, and other areas, via the ``fill`` parameter or similar parameters.
In addition to colors, PyGMT also allows using bit and hachure patterns to fill symbols,
polygons, and other areas, via the ``fill`` parameter or similar parameters.

Example method parameters that support bit and hachure patterns include:

Expand All @@ -19,49 +19,47 @@
``uncertaintyfill``
- :meth:`pygmt.Figure.wiggle`: Anomalies via ``fillpositive`` and ``fillnegative``

GMT provides 90 predefined patterns that can be used in PyGMT. The patterns are numbered
from 1 to 90, and can be colored and inverted. The resolution of the pattern
can be changed, and the background and foreground colors can be set. For a complete list
of available patterns and the full syntax to specify a pattern, refer to the
:doc:`/techref/patterns`.
GMT provides 90 predefined 1-bit patterns, which are numbered from 1 to 90. In addition,
custom 1-, 8-, or 24-bit image raster files can also be used as patterns.

These patterns can be specified via the :class:`pygmt.params.Pattern` class. The
patterns can be customized with different resolution and different foreground and
background colors. The foreground and background colors can also be inverted.
"""

# %%
import pygmt
from pygmt.params import Pattern

# A list of patterns that will be demonstrated.
# To use a pattern as fill append "p" and the number of the desired pattern.
# By default, the pattern is plotted in black and white with a resolution of 300 dpi.
# By default, a pattern is plotted in black and white with a resolution of 300 dpi.
patterns = [
# Plot a hachted pattern via pattern number 8
"p8",
# Plot a dotted pattern via pattern number 19
"p19",
# Set the background color ("+b") to "red3" and the foreground color ("+f") to
# "lightgray"
"p19+bred3+flightbrown",
# Invert the pattern by using a capitalized "P"
"P19+bred3+flightbrown",
# Change the resolution ("+r") to 100 dpi
"p19+bred3+flightbrown+r100",
# Make the background transparent by not giving a color after "+b";
# works analogous for the foreground
"p19+b+flightbrown+r100",
# Predefined 1-bit pattern 8.
Pattern(8),
# Predefined 1-bit pattern 19.
Pattern(19),
# Pattern 19 with custom background ("red3") and foreground ("lightbrown").
Pattern(19, bgcolor="red3", fgcolor="lightbrown"),
# Invert the background and foreground.
Pattern(19, invert=True, bgcolor="red3", fgcolor="lightbrown"),
# Same as above, but with a 100 dpi resolution.
Pattern(19, bgcolor="red3", fgcolor="lightbrown", dpi=100),
# Same as above, but with a transparent background by setting bgcolor to "".
Pattern(19, bgcolor="", fgcolor="lightbrown", dpi=100),
]

fig = pygmt.Figure()
fig.basemap(
region=[0, 10, 0, 12],
projection="X10c",
projection="X18c/10c",
frame="rlbt+glightgray+tBit and Hachure Patterns",
)

y = 11
for pattern in patterns:
# Plot a square with the pattern as fill.
# The square has a size of 2 centimeters with a 1 point thick, black outline.
fig.plot(x=2, y=y, style="s2c", pen="1p,black", fill=pattern)
fig.plot(x=1, y=y, style="s2c", pen="1p,black", fill=pattern)
# Add a description of the pattern.
fig.text(x=4, y=y, text=pattern, font="Courier-Bold", justify="ML")
fig.text(x=2, y=y, text=str(repr(pattern)), font="Courier-Bold", justify="ML")
y -= 2
fig.show()
7 changes: 3 additions & 4 deletions examples/tutorials/advanced/cartesian_histograms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# Import the required packages
import numpy as np
import pygmt
from pygmt.params import Pattern

# %%
# Generate random data from a normal distribution:
Expand Down Expand Up @@ -204,10 +205,8 @@
frame=["wSnE", "xaf10", "ya5f1+lCumulative counts"],
data=data01,
series=10,
# Use pattern ("p") number 8 as fill for the bars
# Set the background ("+b") to white [Default]
# Set the foreground ("+f") to black [Default]
fill="p8+bwhite+fblack",
# Fill bars with GMT pattern 8, with white background and black foreground.
fill=Pattern(8, bgcolor="white", fgcolor="black"),
pen="1p,darkgray,solid",
histtype=0,
# Show cumulative counts
Expand Down
10 changes: 4 additions & 6 deletions examples/tutorials/advanced/focal_mechanisms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# %%
import pandas as pd
import pygmt
from pygmt.params import Pattern

# Set up arguments for basemap
region = [-5, 5, -5, 5]
Expand Down Expand Up @@ -99,10 +100,7 @@
# ---------------------
#
# Use the parameters ``compressionfill`` and ``extensionfill`` to fill the quadrants
# with different colors or patterns. Regarding patterns see the gallery example
# :doc:`Bit and hachure patterns </gallery/symbols/patterns>` and the Technical
# Reference :doc:`Bit and hachure patterns </techref/patterns>`.

# with different colors or :class:`patterns <pygmt.params.Pattern>`.
fig = pygmt.Figure()
fig.basemap(region=region, projection=projection, frame=frame)

Expand All @@ -122,8 +120,8 @@
longitude=2,
latitude=0,
depth=0,
compressionfill="p8",
extensionfill="p31",
compressionfill=Pattern(8),
extensionfill=Pattern(31),
outline=True,
)

Expand Down
4 changes: 2 additions & 2 deletions examples/tutorials/basics/polygons.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@

# %%
# Use the ``fill`` parameter to fill the polygon with a color or
# :doc:`pattern </techref/patterns>`. Note, that there are no lines drawn between the
# data points by default if ``fill`` is used. Use the ``pen`` parameter to add an
# :class:`pattern <pygmt.params.Pattern>`. Note, that there are no lines drawn between
# the data points by default if ``fill`` is used. Use the ``pen`` parameter to add an
# outline around the polygon.

fig = pygmt.Figure()
Expand Down
1 change: 1 addition & 0 deletions pygmt/params/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
"""

from pygmt.params.box import Box
from pygmt.params.pattern import Pattern
111 changes: 111 additions & 0 deletions pygmt/params/pattern.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
The Pattern class for specifying bit and hachure patterns.
"""

import dataclasses

from pygmt._typing import PathLike
from pygmt.alias import Alias
from pygmt.exceptions import GMTValueError
from pygmt.params.base import BaseParam

__doctest_skip__ = ["Pattern"]


@dataclasses.dataclass(repr=False)
class Pattern(BaseParam):
"""
Class for specifying bit and hachure patterns.

This class allows users to specify predefined bit-patterns or custom 1-, 8-, or
24-bit image raster files to fill symbols and polygons in various PyGMT plotting
methods. The patterns can be customized with different resolution and different
foreground and background colors. The foreground and background colors can also be
inverted.

GMT provides 90 predefined patterns that can be used in PyGMT. The patterns are
numbered from 1 to 90, and shown below:

.. figure:: https://docs.generic-mapping-tools.org/6.5/_images/GMT_App_E.png
:alt: The 90 predefined bit-patterns provided with GMT
:width: 75%
:align: center

Parameters
----------
pattern
The pattern to use. It can be specified in two forms:

- An integer in the range of 1-90, corresponding to one of 90 predefined 64x64
bit-patterns
- Name of a 1-, 8-, or 24-bit image raster file, to create customized, repeating
images using image raster files.
dpi
Resolution of the pattern in dots per inch (DPI) [Default is 300].
bgcolor/fgcolor
The background/foreground color for predefined bit-patterns or 1-bit images.
Setting either to an empty string will yield a transparent background/foreground
where only the foreground/background pixels will be painted. [Default is white
for background and black for foreground].
invert
If ``True``, the pattern will be bit-inverted, i.e., white and black areas will
be interchanged (only applies to predefined bit-patterns or 1-bit images).

Examples
--------
Draw a global map with land areas filled with pattern 15 in a light red background
and 200 dpi resolution:

>>> import pygmt
>>> from pygmt.params import Pattern
>>> fig = pygmt.Figure()
>>> fig.coast(
... region="g",
... projection="H10c",
... frame=True,
... land=Pattern(15, bgcolor="lightred", dpi=200),
... shorelines=True,
... )
>>> fig.show()
"""

pattern: int | PathLike
dpi: int | None = None
bgcolor: str | None = None
fgcolor: str | None = None
invert: bool = False

def _validate(self):
"""
Validate the parameters.
"""
# Integer pattern number must be in the range 1-90.
if isinstance(self.pattern, int) and not (1 <= self.pattern <= 90):
raise GMTValueError(
self.pattern,
description="pattern number",
reason=(
"Parameter 'pattern' must be an integer in the range 1-90 "
"or the name of a 1-, 8-, or 24-bit image raster file."
),
)
# fgcolor and bgcolor cannot both be empty.
if self.fgcolor == "" and self.bgcolor == "":
_value = f"{self.fgcolor=}, {self.bgcolor=}"
raise GMTValueError(
_value,
description="fgcolor and bgcolor",
reason="fgcolor and bgcolor cannot both be empty.",
)

@property
def _aliases(self):
"""
Aliases for the Pattern class.
"""
return [
Alias(self.pattern, name="pattern", prefix="P" if self.invert else "p"),
Alias(self.bgcolor, name="bgcolor", prefix="+b"),
Alias(self.fgcolor, name="fgcolor", prefix="+f"),
Alias(self.dpi, name="dpi", prefix="+r"),
]
51 changes: 51 additions & 0 deletions pygmt/tests/test_params_pattern.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Test the Pattern class.
"""

import pytest
from pygmt.exceptions import GMTValueError
from pygmt.params import Pattern


def test_pattern():
"""
Test the Pattern class.
"""
assert str(Pattern(1)) == "p1"
assert str(Pattern(pattern=1)) == "p1"

assert str(Pattern("pattern.png")) == "ppattern.png"

assert str(Pattern(10, bgcolor="red")) == "p10+bred"
assert str(Pattern(20, fgcolor="blue")) == "p20+fblue"
assert str(Pattern(30, bgcolor="red", fgcolor="blue")) == "p30+bred+fblue"
assert str(Pattern(30, fgcolor="blue", bgcolor="")) == "p30+b+fblue"
assert str(Pattern(30, fgcolor="", bgcolor="red")) == "p30+bred+f"

assert str(Pattern(40, dpi=300)) == "p40+r300"

assert str(Pattern(50, invert=True)) == "P50"

pattern = Pattern(90, invert=True, bgcolor="red", fgcolor="blue", dpi=300)
assert str(pattern) == "P90+bred+fblue+r300"

pattern = Pattern("pattern.png", bgcolor="red", fgcolor="blue", dpi=300)
assert str(pattern) == "ppattern.png+bred+fblue+r300"


def test_pattern_invalid_pattern():
"""
Test that an invalid pattern number raises a GMTValueError.
"""
with pytest.raises(GMTValueError):
_ = str(Pattern(0))
with pytest.raises(GMTValueError):
_ = str(Pattern(91))


def test_pattern_invalid_colors():
"""
Test that both fgcolor and bgcolor cannot be empty strings.
"""
with pytest.raises(GMTValueError):
_ = str(Pattern(10, fgcolor="", bgcolor=""))
Loading