"""
Base class with plot generating commands.
Does not define any special non-GMT methods (savefig, show, etc).
"""
import contextlib
import csv
import numpy as np
import pandas as pd
from .clib import Session
from .exceptions import GMTInvalidInput
from .helpers import (
build_arg_string,
dummy_context,
data_kind,
fmt_docstring,
GMTTempFile,
use_alias,
kwargs_to_strings,
)
class BasePlotting:
"""
Base class for Figure and Subplot.
Defines the plot generating methods and a hook for subclasses to insert
special arguments (the _preprocess method).
"""
def _preprocess(self, **kwargs): # pylint: disable=no-self-use
"""
Make any changes to kwargs or required actions before plotting.
This method is run before all plotting commands and can be used to
insert special arguments into the kwargs or make any actions that are
required before ``call_module``.
For example, the :class:`pygmt.Figure` needs this to tell the GMT
modules to plot to a specific figure.
This is a dummy method that does nothing.
Returns
-------
kwargs : dict
The same input kwargs dictionary.
Examples
--------
>>> base = BasePlotting()
>>> base._preprocess(resolution='low')
{'resolution': 'low'}
"""
return kwargs
@fmt_docstring
@use_alias(
R="region",
J="projection",
A="area_thresh",
B="frame",
D="resolution",
I="rivers",
L="map_scale",
N="borders",
W="shorelines",
G="land",
S="water",
U="timestamp",
)
@kwargs_to_strings(R="sequence")
def coast(self, **kwargs):
"""
Plot continents, shorelines, rivers, and borders on maps
Plots grayshaded, colored, or textured land-masses [or water-masses] on
maps and [optionally] draws coastlines, rivers, and political
boundaries. Alternatively, it can (1) issue clip paths that will
contain all land or all water areas, or (2) dump the data to an ASCII
table. The data files come in 5 different resolutions: (**f**)ull,
(**h**)igh, (**i**)ntermediate, (**l**)ow, and (**c**)rude. The full
resolution files amount to more than 55 Mb of data and provide great
detail; for maps of larger geographical extent it is more economical to
use one of the other resolutions. If the user selects to paint the
land-areas and does not specify fill of water-areas then the latter
will be transparent (i.e., earlier graphics drawn in those areas will
not be overwritten). Likewise, if the water-areas are painted and no
land fill is set then the land-areas will be transparent.
A map projection must be supplied.
Full option list at :gmt-docs:`coast.html`
{aliases}
Parameters
----------
{J}
{R}
area_thresh : int, float, or str
``'min_area[/min_level/max_level][+ag|i|s|S][+r|l][+ppercent]'``
Features with an area smaller than min_area in km^2 or of
hierarchical level that is lower than min_level or higher than
max_level will not be plotted.
{B}
C : str
Set the shade, color, or pattern for lakes and river-lakes.
resolution : str
Selects the resolution of the data set to use ((f)ull, (h)igh,
(i)ntermediate, (l)ow, and (c)rude).
land : str
Select filling or clipping of “dry” areas.
rivers : str
``'river[/pen]'``
Draw rivers. Specify the type of rivers and [optionally] append pen
attributes.
map_scale : str
``'[g|j|J|n|x]refpoint'``
Draws a simple map scale centered on the reference point specified.
borders : str
``'border[/pen]'``
Draw political boundaries. Specify the type of boundary and
[optionally] append pen attributes
water : str
Select filling or clipping of “wet” areas.
{U}
shorelines : str
``'[level/]pen'``
Draw shorelines [Default is no shorelines]. Append pen attributes.
"""
kwargs = self._preprocess(**kwargs)
with Session() as lib:
lib.call_module("coast", build_arg_string(kwargs))
@fmt_docstring
@use_alias(
R="region",
J="projection",
B="frame",
C="cmap",
D="position",
F="box",
G="truncate",
W="scale",
)
@kwargs_to_strings(R="sequence", G="sequence")
def colorbar(self, **kwargs):
"""
Plot a gray or color scale-bar on maps.
Both horizontal and vertical scales are supported. For CPTs with
gradational colors (i.e., the lower and upper boundary of an interval
have different colors) we will interpolate to give a continuous scale.
Variations in intensity due to shading/illumination may be displayed by
setting the option -I. Colors may be spaced according to a linear
scale, all be equal size, or by providing a file with individual tile
widths.
Full option list at :gmt-docs:`colorbar.html`
{aliases}
Parameters
----------
position : str
``[g|j|J|n|x]refpoint[+wlength[/width]][+e[b|f][length]][+h|v]
[+jjustify][+m[a|c|l|u]][+n[txt]][+odx[/dy]]``. Defines the
reference point on the map for the color scale using one of four
coordinate systems: (1) Use *g* for map (user) coordinates, (2) use
*j* or *J* for setting refpoint via a 2-char justification code
that refers to the (invisible) map domain rectangle, (3) use *n*
for normalized (0-1) coordinates, or (4) use *x* for plot
coordinates (inches, cm, etc.). All but *x* requires both *region*
and *projection* to be specified. Append +w followed by the length
and width of the color bar. If width is not specified then it is
set to 4% of the given length. Give a negative length to reverse
the scale bar. Append +h to get a horizontal scale
[Default is vertical (+v)]. By default, the anchor point on the
scale is assumed to be the bottom left corner (BL), but this can be
changed by appending +j followed by a 2-char justification code
*justify*.
box : bool or str
``[+cclearances][+gfill][+i[[gap/]pen]][+p[pen]][+r[radius]]
[+s[[dx/dy/][shade]]]``. If set to True, draws a rectangular
border around the color scale. Alternatively, specify a different
pen with +ppen. Add +gfill to fill the scale panel [no fill].
Append +cclearance where clearance is either gap, xgap/ygap, or
lgap/rgap/bgap/tgap where these items are uniform, separate in x-
and y-direction, or individual side spacings between scale and
border. Append +i to draw a secondary, inner border as well. We use
a uniform gap between borders of 2p and the MAP_DEFAULTS_PEN unless
other values are specified. Append +r to draw rounded rectangular
borders instead, with a 6p corner radius. You can override this
radius by appending another value. Finally, append +s to draw an
offset background shaded region. Here, dx/dy indicates the shift
relative to the foreground frame [4p/-4p] and shade sets the fill
style to use for shading [gray50].
truncate : list or str
``zlo/zhi`` Truncate the incoming CPT so that the lowest and
highest z-levels are to zlo and zhi. If one of these equal NaN then
we leave that end of the CPT alone. The truncation takes place
before the plotting.
scale : float
Multiply all z-values in the CPT by the provided scale. By default
the CPT is used as is.
"""
kwargs = self._preprocess(**kwargs)
with Session() as lib:
lib.call_module("colorbar", build_arg_string(kwargs))
@fmt_docstring
@use_alias(
A="annotation",
B="frame",
C="interval",
G="label_placement",
J="projection",
L="limit",
Q="cut",
R="region",
S="resample",
U="timestamp",
W="pen",
)
@kwargs_to_strings(R="sequence", L="sequence", A="sequence_plus")
def grdcontour(self, grid, **kwargs):
"""
Convert grids or images to contours and plot them on maps
Takes a grid file name or an xarray.DataArray object as input.
Full option list at :gmt-docs:`grdcontour.html`
{aliases}
Parameters
----------
grid : str or xarray.DataArray
The file name of the input grid or the grid loaded as a DataArray.
interval : str or int
Specify the contour lines to generate.
- The filename of a `CPT` file where the color boundaries will
be used as contour levels.
- The filename of a 2 (or 3) column file containing the contour
levels (col 1), (C)ontour or (A)nnotate (col 2), and optional
angle (col 3)
- A fixed contour interval ``cont_int`` or a single contour with
``+[cont_int]``
annotation : str, int, or list
Specify or disable annotated contour levels, modifies annotated
contours specified in ``-C``.
- Specify a fixed annotation interval ``annot_int`` or a
single annotation level ``+[annot_int]``
- Disable all annotation with ``'-'``
- Optional label modifiers can be specified as a single string
``'[annot_int]+e'`` or with a list of options
``([annot_int], 'e', 'f10p', 'gred')``.
limit : str or list of 2 ints
Do no draw contours below `low` or above `high`, specify as string
``'[low]/[high]'`` or list ``[low,high]``.
cut : str or int
Do not draw contours with less than `cut` number of points.
resample : str or int
Resample smoothing factor.
{J}
{R}
{B}
{G}
{U}
{W}
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(grid, None, None)
with Session() as lib:
if kind == "file":
file_context = dummy_context(grid)
elif kind == "grid":
file_context = lib.virtualfile_from_grid(grid)
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid)))
with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("grdcontour", arg_str)
@fmt_docstring
@use_alias(R="region", J="projection", W="pen", B="frame", I="shading", C="cmap")
@kwargs_to_strings(R="sequence")
def grdimage(self, grid, **kwargs):
"""
Project grids or images and plot them on maps.
Takes a grid file name or an xarray.DataArray object as input.
Full option list at :gmt-docs:`grdimage.html`
{aliases}
Parameters
----------
grid : str or xarray.DataArray
The file name of the input grid or the grid loaded as a DataArray.
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(grid, None, None)
with Session() as lib:
if kind == "file":
file_context = dummy_context(grid)
elif kind == "grid":
file_context = lib.virtualfile_from_grid(grid)
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid)))
with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("grdimage", arg_str)
@fmt_docstring
@use_alias(
R="region",
J="projection",
Jz="zscale",
JZ="zsize",
B="frame",
C="cmap",
G="drapegrid",
N="plane",
Q="surftype",
Wc="contourpen",
Wm="meshpen",
Wf="facadepen",
p="perspective",
)
@kwargs_to_strings(R="sequence", p="sequence")
def grdview(self, grid, **kwargs):
"""
Create 3-D perspective image or surface mesh from a grid.
Reads a 2-D grid file and produces a 3-D perspective plot by drawing a
mesh, painting a colored/gray-shaded surface made up of polygons, or by
scanline conversion of these polygons to a raster image. Options
include draping a data set on top of a surface, plotting of contours on
top of the surface, and apply artificial illumination based on
intensities provided in a separate grid file.
Full option list at :gmt-docs:`grdview.html`
{aliases}
Parameters
----------
grid : str or xarray.DataArray
The file name of the input relief grid or the grid loaded as a
DataArray.
zscale/zsize : float or str
Set z-axis scaling or z-axis size.
cmap : str
The name of the color palette table to use.
drapegrid : str or xarray.DataArray
The file name or a DataArray of the image grid to be draped on top
of the relief provided by grid. [Default determines colors from
grid]. Note that -Jz and -N always refers to the grid. The
drapegrid only provides the information pertaining to colors, which
(if drapegrid is a grid) will be looked-up via the CPT (see -C).
plane : float or str
``level[+gfill]``.
Draws a plane at this z-level. If the optional color is provided
via the +g modifier, and the projection is not oblique, the frontal
facade between the plane and the data perimeter is colored.
surftype : str
Specifies cover type of the grid.
Select one of following settings:
1. 'm' for mesh plot [Default].
2. 'mx' or 'my' for waterfall plots (row or column profiles).
3. 's' for surface plot.
4. 'i' for image plot.
5. 'c'. Same as 'i' but will make nodes with z = NaN transparent.
For any of these choices, you may force a monochrome image by
appending the modifier +m.
contourpen : str
Draw contour lines on top of surface or mesh (not image). Append
pen attributes used for the contours.
meshpen : str
Sets the pen attributes used for the mesh. You must also select -Qm
or -Qsm for meshlines to be drawn.
facadepen :str
Sets the pen attributes used for the facade. You must also select
-N for the facade outline to be drawn.
perspective : list or str
``'[x|y|z]azim[/elev[/zlevel]][+wlon0/lat0[/z0]][+vx0/y0]'``.
Select perspective view.
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(grid, None, None)
with Session() as lib:
if kind == "file":
file_context = dummy_context(grid)
elif kind == "grid":
file_context = lib.virtualfile_from_grid(grid)
else:
raise GMTInvalidInput(f"Unrecognized data type for grid: {type(grid)}")
with contextlib.ExitStack() as stack:
fname = stack.enter_context(file_context)
if "G" in kwargs:
drapegrid = kwargs["G"]
if data_kind(drapegrid) in ("file", "grid"):
if data_kind(drapegrid) == "grid":
drape_context = lib.virtualfile_from_grid(drapegrid)
drapefile = stack.enter_context(drape_context)
kwargs["G"] = drapefile
else:
raise GMTInvalidInput(
f"Unrecognized data type for drapegrid: {type(drapegrid)}"
)
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("grdview", arg_str)
@fmt_docstring
@use_alias(
R="region",
J="projection",
B="frame",
S="style",
G="color",
W="pen",
i="columns",
l="label",
C="cmap",
U="timestamp",
)
@kwargs_to_strings(R="sequence", i="sequence_comma")
def plot(self, x=None, y=None, data=None, sizes=None, direction=None, **kwargs):
"""
Plot lines, polygons, and symbols on maps.
Used to be psxy.
Takes a matrix, (x,y) pairs, or a file name as input and plots lines,
polygons, or symbols at those locations on a map.
Must provide either *data* or *x* and *y*.
If providing data through *x* and *y*, *color* (G) can be a 1d array
that will be mapped to a colormap.
If a symbol is selected and no symbol size given, then psxy will
interpret the third column of the input data as symbol size. Symbols
whose size is <= 0 are skipped. If no symbols are specified then the
symbol code (see *S* below) must be present as last column in the
input. If *S* is not used, a line connecting the data points will be
drawn instead. To explicitly close polygons, use *L*. Select a fill
with *G*. If *G* is set, *W* will control whether the polygon outline
is drawn or not. If a symbol is selected, *G* and *W* determines the
fill and outline/no outline, respectively.
Full option list at :gmt-docs:`plot.html`
{aliases}
Parameters
----------
x/y : float or 1d arrays
The x and y coordinates, or arrays of x and y coordinates of the
data points
data : str or 2d array
Either a data file name or a 2d numpy array with the tabular data.
Use option *columns* (i) to choose which columns are x, y, color,
and size, respectively.
sizes : 1d array
The sizes of the data points in units specified in *style* (S).
Only valid if using *x* and *y*.
direction : list of two 1d arrays
If plotting vectors (using ``style='V'`` or ``style='v'``), then
should be a list of two 1d arrays with the vector directions. These
can be angle and length, azimuth and length, or x and y components,
depending on the style options chosen.
{J}
{R}
A : bool or str
``'[m|p|x|y]'``
By default, geographic line segments are drawn as great circle
arcs. To draw them as straight lines, use *A*.
{B}
{CPT}
D : str
``'dx/dy'``: Offset the plot symbol or line locations by the given
amounts dx/dy.
E : bool or str
``'[x|y|X|Y][+a][+cl|f][+n][+wcap][+ppen]'``.
Draw symmetrical error bars.
{G}
style : str
Plot symbols (including vectors, pie slices, fronts, decorated or
quoted lines).
{W}
{U}
label : str
Add a legend entry for the symbol or line being plotted.
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(data, x, y)
extra_arrays = []
if "S" in kwargs and kwargs["S"][0] in "vV" and direction is not None:
extra_arrays.extend(direction)
if "G" in kwargs and not isinstance(kwargs["G"], str):
if kind != "vectors":
raise GMTInvalidInput(
"Can't use arrays for color if data is matrix or file."
)
extra_arrays.append(kwargs["G"])
del kwargs["G"]
if sizes is not None:
if kind != "vectors":
raise GMTInvalidInput(
"Can't use arrays for sizes if data is matrix or file."
)
extra_arrays.append(sizes)
with Session() as lib:
# Choose how data will be passed in to the module
if kind == "file":
file_context = dummy_context(data)
elif kind == "matrix":
file_context = lib.virtualfile_from_matrix(data)
elif kind == "vectors":
file_context = lib.virtualfile_from_vectors(
np.atleast_1d(x), np.atleast_1d(y), *extra_arrays
)
with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("plot", arg_str)
@fmt_docstring
@use_alias(
R="region",
J="projection",
B="frame",
S="skip",
G="label_placement",
W="pen",
L="triangular_mesh_pen",
i="columns",
C="levels",
)
@kwargs_to_strings(R="sequence", i="sequence_comma")
def contour(self, x=None, y=None, z=None, data=None, **kwargs):
"""
Contour table data by direct triangulation.
Takes a matrix, (x,y,z) pairs, or a file name as input and plots lines,
polygons, or symbols at those locations on a map.
Must provide either *data* or *x*, *y*, and *z*.
[TODO: Insert more documentation]
Full option list at :gmt-docs:`contour.html`
{aliases}
Parameters
----------
x/y/z : 1d arrays
Arrays of x and y coordinates and values z of the data points.
data : str or 2d array
Either a data file name or a 2d numpy array with the tabular data.
{J}
{R}
A : bool or str
``'[m|p|x|y]'``
By default, geographic line segments are drawn as great circle
arcs. To draw them as straight lines, use *A*.
{B}
levels : str
Contour file or level(s)
D : str
Dump contour coordinates
E : str
Network information
label_placement : str
Placement of labels
I : bool
Color the triangles using CPT
triangular_mesh_pen : str
Pen to draw the underlying triangulation (default none)
N : bool
Do not clip contours
Q : float or str
Do not draw contours with less than cut number of points.
``'[cut[unit]][+z]'``
skip : bool or str
Skip input points outside region ``'[p|t]'``
{W}
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(data, x, y, z)
if kind == "vectors" and z is None:
raise GMTInvalidInput("Must provided both x, y, and z.")
with Session() as lib:
# Choose how data will be passed in to the module
if kind == "file":
file_context = dummy_context(data)
elif kind == "matrix":
file_context = lib.virtualfile_from_matrix(data)
elif kind == "vectors":
file_context = lib.virtualfile_from_vectors(x, y, z)
with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("contour", arg_str)
@fmt_docstring
@use_alias(
R="region",
J="projection",
B="frame",
L="map_scale",
Td="rose",
Tm="compass",
U="timestamp",
)
@kwargs_to_strings(R="sequence")
def basemap(self, **kwargs):
"""
Produce a basemap for the figure.
Several map projections are available, and the user may specify
separate tick-mark intervals for boundary annotation, ticking, and
[optionally] gridlines. A simple map scale or directional rose may also
be plotted.
At least one of the options *frame*, *map_scale*, *rose* or *compass*
must be specified.
Full option list at :gmt-docs:`basemap.html`
{aliases}
Parameters
----------
{J}
{R}
{B}
map_scale : str
``'[g|j|J|n|x]refpoint'``
Draws a simple map scale centered on the reference point specified.
rose : str
Draws a map directional rose on the map at the location defined by
the reference and anchor points.
compass : str
Draws a map magnetic rose on the map at the location defined by the
reference and anchor points
{U}
"""
kwargs = self._preprocess(**kwargs)
if not ("B" in kwargs or "L" in kwargs or "T" in kwargs):
raise GMTInvalidInput("At least one of B, L, or T must be specified.")
with Session() as lib:
lib.call_module("basemap", build_arg_string(kwargs))
@fmt_docstring
@use_alias(R="region", J="projection", U="timestamp", D="position", F="box")
@kwargs_to_strings(R="sequence")
def logo(self, **kwargs):
"""
Place the GMT graphics logo on a map.
By default, the GMT logo is 2 inches wide and 1 inch high and
will be positioned relative to the current plot origin.
Use various options to change this and to place a transparent or
opaque rectangular map panel behind the GMT logo.
Full option list at :gmt-docs:`logo.html`
{aliases}
Parameters
----------
{J}
{R}
position : str
``'[g|j|J|n|x]refpoint+wwidth[+jjustify][+odx[/dy]]'``.
Sets reference point on the map for the image.
box : bool or str
Without further options, draws a rectangular border around the
GMT logo.
{U}
"""
kwargs = self._preprocess(**kwargs)
if "D" not in kwargs:
raise GMTInvalidInput("Option D must be specified.")
with Session() as lib:
lib.call_module("logo", build_arg_string(kwargs))
@fmt_docstring
@use_alias(R="region", J="projection", D="position", F="box", M="monochrome")
@kwargs_to_strings(R="sequence")
def image(self, imagefile, **kwargs):
"""
Place images or EPS files on maps.
Reads an Encapsulated PostScript file or a raster image file and plots
it on a map.
Full option list at :gmt-docs:`image.html`
{aliases}
Parameters
----------
imagefile : str
This must be an Encapsulated PostScript (EPS) file or a raster
image. 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. Note: If GDAL was not configured during GMT installation
then only EPS files are supported.
{J}
{R}
position : str
``'[g|j|J|n|x]refpoint+rdpi+w[-]width[/height][+jjustify]
[+nnx[/ny]][+odx[/dy]]'`` Sets reference point on the map for the
image.
box : bool or str
``'[+cclearances][+gfill][+i[[gap/]pen]][+p[pen]][+r[radius]]
[+s[[dx/dy/][shade]]]'`` Without further options, draws a
rectangular border around the image using **MAP_FRAME_PEN**.
monochrome : bool
Convert color image to monochrome grayshades using the (television)
YIQ-transformation.
"""
kwargs = self._preprocess(**kwargs)
with Session() as lib:
arg_str = " ".join([imagefile, build_arg_string(kwargs)])
lib.call_module("image", arg_str)
@fmt_docstring
@use_alias(R="region", J="projection", D="position", F="box")
@kwargs_to_strings(R="sequence")
def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwargs):
"""
Plot legends on maps.
Makes legends that can be overlaid on maps. Reads specific
legend-related information from an input file, or automatically creates
legend entries from plotted symbols that have labels. Unless otherwise
noted, annotations will be made using the primary annotation font and
size in effect (i.e., FONT_ANNOT_PRIMARY).
Full option list at :gmt-docs:`legend.html`
{aliases}
Parameters
----------
spec : None or str
Either None (default) for using the automatically generated legend
specification file, or a filename pointing to the legend
specification file.
{J}
{R}
position : str
``'[g|j|J|n|x]refpoint+wwidth[/height][+jjustify][+lspacing]
[+odx[/dy]]'`` Defines the reference point on the map for the
legend. By default, uses 'JTR+jTR+o0.2c' which places the legend at
the top-right corner inside the map frame, with a 0.2 cm offset.
box : bool or str
``'[+cclearances][+gfill][+i[[gap/]pen]][+p[pen]][+r[radius]]
[+s[[dx/dy/][shade]]]'`` Without further options, draws a
rectangular border around the legend using **MAP_FRAME_PEN**. By
default, uses '+gwhite+p1p' which draws a box around the legend
using a 1 point black pen and adds a white background.
"""
kwargs = self._preprocess(**kwargs)
if "D" not in kwargs:
kwargs["D"] = position
if "F" not in kwargs:
kwargs["F"] = box
with Session() as lib:
if spec is None:
specfile = ""
elif data_kind(spec) == "file":
specfile = spec
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(spec)))
arg_str = " ".join([specfile, build_arg_string(kwargs)])
lib.call_module("legend", arg_str)
@fmt_docstring
@use_alias(
R="region",
J="projection",
B="frame",
C="clearance",
D="offset",
G="fill",
W="pen",
)
@kwargs_to_strings(
R="sequence",
textfiles="sequence_space",
angle="sequence_comma",
font="sequence_comma",
justify="sequence_comma",
)
def text(
self,
textfiles=None,
x=None,
y=None,
position=None,
text=None,
angle=None,
font=None,
justify=None,
**kwargs,
):
"""
Plot or typeset text strings of variable size, font type, and
orientation.
Must provide at least one of the following combinations as input:
- *textfiles*
- *x*, *y*, and *text*
- *position* and *text*
Full option list at :gmt-docs:`text.html`
{aliases}
Parameters
----------
textfiles : str or list
A text data file name, or a list of filenames containing 1 or more
records with (x, y[, angle, font, justify], text).
x/y : float or 1d arrays
The x and y coordinates, or an array of x and y coordinates to plot
the text
position : str
Sets reference point on the map for the text by using x,y
coordinates extracted from *region* instead of providing them
through *x* and *y*. Specify with a two letter (order independent)
code, chosen from:
* Horizontal: L(eft), C(entre), R(ight)
* Vertical: T(op), M(iddle), B(ottom)
For example, position="TL" plots the text at the Upper Left corner
of the map.
text : str or 1d array
The text string, or an array of strings to plot on the figure
angle: int, float, str or bool
Set the angle measured in degrees counter-clockwise from
horizontal. E.g. 30 sets the text at 30 degrees. If no angle is
explicitly given (i.e. angle=True) then the input textfile(s) must
have this as a column.
font : str or bool
Set the font specification with format "size,font,color" where size
is text size in points, font is the font to use, and color sets the
font color. E.g. "12p,Helvetica-Bold,red" selects a 12p red
Helvetica-Bold font. If no font info is explicitly given (i.e.
font=True), then the input textfile(s) must have this information
in one of its columns.
justify : str or bool
Set the alignment which refers to the part of the text string that
will be mapped onto the (x,y) point. Choose a 2 character
combination of L, C, R (for left, center, or right) and T, M, B for
top, middle, or bottom. E.g., BL for lower left. If no
justification is explicitly given (i.e. justify=True), then the
input textfile(s) must have this as a column.
{J}
{R}
clearance : str
``[dx/dy][+to|O|c|C]``
Adjust the clearance between the text and the surrounding box
[15%]. Only used if *pen* or *fill* are specified. Append the unit
you want ('c' for cm, 'i' for inch, or 'p' for point; if not given
we consult 'PROJ_LENGTH_UNIT') or '%' for a percentage of the
font size. Optionally, use modifier '+t' to set the shape of the
textbox when using *fill* and/or *pen*. Append lower case 'o' to
get a straight rectangle [Default]. Append upper case 'O' to get a
rounded rectangle. In paragraph mode (*paragraph*) you can also
append lower case 'c' to get a concave rectangle or append upper
case 'C' to get a convex rectangle.
fill : str
Sets the shade or color used for filling the text box [Default is
no fill].
offset : str
``[j|J]dx[/dy][+v[pen]]``
Offsets the text from the projected (x,y) point by dx,dy [0/0]. If
dy is not specified then it is set equal to dx. Use offset='j' to
offset the text away from the point instead (i.e., the text
justification will determine the direction of the shift). Using
offset='J' will shorten diagonal offsets at corners by sqrt(2).
Optionally, append '+v' which will draw a line from the original
point to the shifted point; append a pen to change the attributes
for this line.
pen : str
Sets the pen used to draw a rectangle around the text string
(see *clearance*) [Default is width = default, color = black,
style = solid].
"""
kwargs = self._preprocess(**kwargs)
# Ensure inputs are either textfiles, x/y/text, or position/text
if position is None:
kind = data_kind(textfiles, x, y, text)
else:
if x is not None or y is not None:
raise GMTInvalidInput(
"Provide either position only, or x/y pairs, not both"
)
kind = "vectors"
if kind == "vectors" and text is None:
raise GMTInvalidInput("Must provide text with x/y pairs or position")
# Build the `-F` argument in gmt text.
if "F" not in kwargs.keys() and (
(
position is not None
or angle is not None
or font is not None
or justify is not None
)
):
kwargs.update({"F": ""})
if angle is not None and isinstance(angle, (int, float, str)):
kwargs["F"] += f"+a{str(angle)}"
if font is not None and isinstance(font, str):
kwargs["F"] += f"+f{font}"
if justify is not None and isinstance(justify, str):
kwargs["F"] += f"+j{justify}"
if position is not None and isinstance(position, str):
kwargs["F"] += f'+c{position}+t"{text}"'
with GMTTempFile(suffix=".txt") as tmpfile:
with Session() as lib:
fname = textfiles if kind == "file" else ""
if kind == "vectors":
if position is not None:
fname = ""
else:
pd.DataFrame.from_dict(
{
"x": np.atleast_1d(x),
"y": np.atleast_1d(y),
"text": np.atleast_1d(text),
}
).to_csv(
tmpfile.name,
sep="\t",
header=False,
index=False,
quoting=csv.QUOTE_NONE,
)
fname = tmpfile.name
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("text", arg_str)
@fmt_docstring
@use_alias(R="region", J="projection", B="frame", S="convention")
@kwargs_to_strings(R="sequence",)
def meca(
self,
lon=None,
lat=None,
depth=None,
spec=None,
convention=None,
plot_lon=None, # FIXME: implement -C flag if plot location is specified
plot_lat=None,
text=None,
scale=None, # FIXME: implement
**kwargs,
):
"""
Plot focal mechanisms.
Parameters
----------
lon: int or float
Longitude of event location.
lat: int or float
Latitude of event location.
depth: int or float
Depth of event location in kilometers.
spec: dict, 1D array, 2D array, or str
Object specifying event parameters that are consistent with the specified convention.
If a string specifying a filename is assigned to spec, no other parameters are required,
as those parameters are assumed to exist in the file.
List of required spec parameters for different conventions:
a (Aki & Rickards): strike, dip, rake, magnitude
c (global CMT): strike1, dip1, rake1, strike2, dip2, rake2, mantissa, exponent
m (seismic moment tensor): mrr, mtt, mff, mrt, mrf, mtf, exponent
p (partial focal mechanism): strike1, dip1, strike2, fault_type, magnitude
x (principal axis): t_exponent, t_azimuth, t_plunge, n_exponent, n_azimuth, n_plunge, p_exponent, p_azimuth, p_plunge, exponent
convention: str
a (Aki & Richards), c (global CMT), m (seismic moment tensor), p (partial focal mechanism), x (principal axis)
Optional (auto-detected) if spec is defined as a dict.
plot_lon: (optional) int or float
Longitude at which to place beachball
plot_lat: (optional) int or float
Latitude at which to place beachball
text: (optional) str
Text string to appear near the beachball
Examples
--------
fig = pygmt.Figure()
fig.meca(lon=239.384, lat=34.556, depth=12.0,
spec=dict(strike1=180, dip1=18, rake1=-88, strike2=0,
dip2=72, rake2=-90, mantissa=5.5, exponent=0),
region=[239, 240, 34, 35.2], projection='m4c')
fig.show()
fig = pygmt.Figure()
fig.meca(spec='focal_mechanisms.psmeca')
fig.show()
"""
# Check the spec and parse the data according to the specified convention
if type(spec) is dict:
if (
(lon is None) or (lat is None) or (depth is None)
): # if no specified location
raise Error("Location not fully specified.")
# These are constants related to GMT and could be defined elsewhere (top of file?)
AKI_PARAMS = ["strike", "dip", "rake", "magnitude"]
GCMT_PARAMS = [
"strike1",
"dip1",
"rake1",
"strike2",
"dip2",
"rake2",
"mantissa",
"exponent",
]
# FIXME: allow specification of moment instead of mantissa and exponent.
MT_PARAMS = ["mrr", "mtt", "mff", "mrt", "mrf", "mtf", "exponent"]
PFM_PARAMS = ["strike1", "dip1", "strike2", "fault_type", "magnitude"]
PA_PARAMS = [
"t_exponent",
"t_azimuth",
"t_plunge",
"n_exponent",
"n_azimuth",
"n_plunge",
"p_exponent",
"p_azimuth",
"p_plunge",
"exponent",
]
# Aki and Richards convention: -Sa in GMT
if set(spec.keys()) == set(AKI_PARAMS):
if (convention != "a") and (convention is not None):
print(
f"Specified convention {convention} is incompatible with data specified in spec dict. Proceeding using Aki and Richards convention."
)
convention = "a"
foc_params = AKI_PARAMS
# Global CMT convention: -Sc in GMT
elif set(spec.keys()) == set(GCMT_PARAMS):
if (convention != "c") and (convention is not None):
print(
f"Specified convention {convention} is incompatible with data specified in spec dict. Proceeding using global CMT convention."
)
convention = "c"
foc_params = GCMT_PARAMS
# Seismic moment tensor convention: -Sm in GMT
elif set(spec.keys()) == set(MT_PARAMS):
if (convention != "m") and (convention is not None):
print(
f"Specified convention {convention} is incompatible with data specified in spec dict. Proceeding using seismic moment tensor convention."
)
convention = "m"
foc_params = MT_PARAMS
# Partial focal mechanism convention: -Sp in GMT
elif set(spec.keys()) == set(PFM_PARAMS):
if (convention != "p") and (convention is not None):
print(
f"Specified convention {convention} is incompatible with data specified in spec dict. Proceeding using partial focal mechanism convention."
)
convention = "p"
foc_params = PFM_PARAMS
# Principal axis convention: -Sx in GMT
elif set(spec.keys()) == set(PFM_PARAMS):
if (convention != "x") and (convention is not None):
print(
f"Specified convention {convention} is incompatible with data specified in spec dict. Proceeding using principal axis convention."
)
convention = "x"
foc_params = PA_PARAMS
else:
raise Error("Parameters in spec dict do not match known conventions.")
# Construct the vector (note that order matters here, hence the list comprehension!)
spec = [lon, lat, depth] + [spec[key] for key in foc_params]
# Add in plotting options, if given, otherwise add 0s as required by GMT
for arg in plot_lon, plot_lat, text:
if arg is None:
spec.append(0)
else:
spec.append(arg)
elif convention is None:
raise Error("We need a convention to know how to interpret the input!")
# if spec is not a dict it is handled here
kind = data_kind(spec)
with Session() as lib:
if kind == "matrix":
file_context = lib.virtualfile_from_matrix(
np.atleast_2d(spec)
) # np.atleast_2d allows 1D and 2D arrays
elif kind == "file":
file_context = dummy_context(spec)
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(spec)))
with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("meca", arg_str)