Skip to content

Commit

Permalink
Add new modules to plot horizontal and vertical lines
Browse files Browse the repository at this point in the history
As discussed in #670 here's a new module (**hlines**) to plot a single or a set of horizontal lines with only defining the desired y-value(s). For discussion I only add the module for horizontal lines at the moment, however, the adjustments to prepare the same for vertical lines is done very quickly.
  • Loading branch information
michaelgrund committed Feb 17, 2021
1 parent 87cb403 commit 7ed3b49
Showing 1 changed file with 155 additions and 0 deletions.
155 changes: 155 additions & 0 deletions pygmt/src/hlines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"""
hlines - Plot horizontal lines.
"""
import numpy as np
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import build_arg_string, fmt_docstring, use_alias, kwargs_to_strings, data_kind

@fmt_docstring
@use_alias(
B="frame",
C="cmap",
D="offset",
J="projection",
N="no_clip",
R="region",
U="timestamp",
V="verbose",
W="pen",
X="xshift",
Y="yshift",
Z="zvalue",
l="label",
p="perspective",
t="transparency",
)
@kwargs_to_strings(R="sequence", p="sequence")
def hlines(self, y=None, xmin=None, xmax=None, pen=None, label=None, **kwargs):
""""
Plot one or a collection of horizontal lines
Takes a single y value or a list of individual y values and optionally
lower and upper x value limits as input.
Must provide *y*.
If y values are given without x limits then the current map boundaries are
used as lower and upper limits. If only a single set of x limits is given then
all lines will have the same length, otherwise give x limits for each individual
line. If only a single label is given then all lines are grouped under this label
in the legend (if shown). If each line should appear as a single entry in the
legend, give corresponding labels for all lines (same for **pen**).
Parameters
----------
y : float or 1d array
The y coordinates or an array of y coordinates of the
horizontal lines to plot.
{J}
{R}
{B}
{CPT}
offset : str
``dx/dy``.
Offset the line locations by the given amounts
*dx/dy* [Default is no offset]. If *dy* is not given it is set
equal to *dx*.
no_clip : bool or str
``'[c|r]'``.
Do NOT clip lines that fall outside map border [Default plots
lines whose coordinates are strictly inside the map border only].
The option does not apply to lines which are always
clipped to the map region. For periodic (360-longitude) maps we
must plot all lines twice in case they are clipped by the
repeating boundary. ``no_clip=True`` will turn off clipping and not
plot repeating lines. Use ``no_clip="r"`` to turn off clipping
but retain the plotting of such repeating lines, or use
``no_clip="c"`` to retain clipping but turn off plotting of
repeating lines.
{W}
{U}
{V}
{XY}
zvalue : str or float
``value``.
Instead of specifying a line color via **pen**, give it a *value*
via **zvalue** and a color lookup table via **cmap**. Requires appending
**+z** to **pen** (e.g. ``pen = "5p,+z"``, ``zvalue = 0.8``,
* ``cmap = "viridis"``).
label : str
Add a legend entry for the line being plotted.
{p}
{t}
*transparency* can also be a 1d array to set varying transparency
for lines.
"""

kwargs = self._preprocess(**kwargs)

list_length = len(np.atleast_1d(y))

# prepare x vals
if xmin is None and xmax is None:
# get limits from current map boundings if not given via xmin, xmax
with Session() as lib:
mapbnds = lib.extract_region()
x = np.array([[mapbnds[0]], [mapbnds[1]]])
x = np.repeat(x, list_length, axis=1)
elif xmin is None or xmax is None:
raise GMTInvalidInput("Must provide both, xmin and xmax if limits are not set automatically.")

else:
# if only a single xmin and xmax without [], repeat to fit size of y
if isinstance(xmin, int) or isinstance(xmin, float):
x = np.array([[xmin], [xmax]])
x = np.repeat(x, list_length, axis=1)
else:
if len(xmin) != len(xmax):
GMTInvalidInput("Must provide same length for xmin and xmax.")
else:
x = np.array([xmin, xmax])

# prepare labels
if "l" in kwargs:
# if several lines belong to the same label, first take the label,
# then set all to None and reset the first entry to the given label
if not isinstance(kwargs["l"], list):
label2use = kwargs["l"]
kwargs["l"] = np.repeat(None, list_length)
kwargs["l"][0] = label2use
else:
kwargs["l"] = np.repeat(None, list_length)


# prepare pens
if "W" in kwargs:
# select pen, no series
if not isinstance(kwargs["W"], list):
pen2use = kwargs["W"]
kwargs["W"] = np.repeat(pen2use, list_length)
else: # use as default if no pen is given (neither single nor series)
kwargs["W"] = np.repeat("1p,black", list_length)

# loop over entries
kwargs_copy = kwargs.copy()

for index in range(list_length):
y2plt = [np.atleast_1d(y)[index], np.atleast_1d(y)[index]]
x2plt = [np.atleast_1d(x)[0][index], np.atleast_1d(x)[1][index]]
kind = data_kind(None, x2plt, y2plt)

with Session() as lib:
if kind == "vectors":
file_context = lib.virtualfile_from_vectors(
np.atleast_1d(x2plt), np.atleast_1d(y2plt))
else:
raise GMTInvalidInput("Unrecognized data type.")

kwargs["l"] = kwargs_copy["l"][index]
kwargs["W"] = kwargs_copy["W"][index]

with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
lib.call_module("plot", arg_str)

0 comments on commit 7ed3b49

Please sign in to comment.