|
| 1 | +""" |
| 2 | +vlines - Plot vertical lines. |
| 3 | +""" |
| 4 | + |
| 5 | +from collections.abc import Sequence |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +from pygmt.exceptions import GMTInvalidInput |
| 9 | + |
| 10 | +__doctest_skip__ = ["vlines"] |
| 11 | + |
| 12 | + |
| 13 | +def vlines( |
| 14 | + self, |
| 15 | + x: float | Sequence[float], |
| 16 | + ymin: float | Sequence[float] | None = None, |
| 17 | + ymax: float | Sequence[float] | None = None, |
| 18 | + pen: str | None = None, |
| 19 | + label: str | None = None, |
| 20 | + no_clip: bool = False, |
| 21 | + perspective: str | bool | None = None, |
| 22 | +): |
| 23 | + """ |
| 24 | + Plot one or multiple vertical line(s). |
| 25 | +
|
| 26 | + This method is a high-level wrapper around :meth:`pygmt.Figure.plot` that focuses on |
| 27 | + plotting vertical lines at X-coordinates specified by the ``x`` parameter. The ``x`` |
| 28 | + parameter can be a single value (for a single vertical line) or a sequence of values |
| 29 | + (for multiple vertical lines). |
| 30 | +
|
| 31 | + By default, the Y-coordinates of the start and end points of the lines are set to be |
| 32 | + the Y-limits of the current plot, but this can be overridden by specifying the |
| 33 | + ``ymin`` and ``ymax`` parameters. ``ymin`` and ``ymax`` can be either a single value |
| 34 | + or a sequence of values. If a single value is provided, it is applied to all lines. |
| 35 | + If a sequence is provided, the length of ``ymin`` and ``ymax`` must match the length |
| 36 | + of ``x``. |
| 37 | +
|
| 38 | + The term "vertical" lines can be interpreted differently in different coordinate |
| 39 | + systems: |
| 40 | +
|
| 41 | + - **Cartesian** coordinate system: lines are plotted as straight lines. |
| 42 | + - **Polar** projection: lines are plotted as straight lines along radius. |
| 43 | + - **Geographic** projection: lines are plotted as meridians along constant |
| 44 | + longitude. |
| 45 | +
|
| 46 | + Parameters |
| 47 | + ---------- |
| 48 | + x |
| 49 | + X-coordinates to plot the lines. It can be a single value (for a single line) |
| 50 | + or a sequence of values (for multiple lines). |
| 51 | + ymin/ymax |
| 52 | + Y-coordinates of the start/end point(s) of the line(s). If ``None``, defaults to |
| 53 | + the Y-limits of the current plot. ``ymin`` and ``ymax`` can either be a single |
| 54 | + value or a sequence of values. If a single value is provided, it is applied to |
| 55 | + all lines. If a sequence is provided, the length of ``ymin`` and ``ymax`` must |
| 56 | + match the length of ``x``. |
| 57 | + pen |
| 58 | + Pen attributes for the line(s), in the format of *width,color,style*. |
| 59 | + label |
| 60 | + Label for the line(s), to be displayed in the legend. |
| 61 | + no_clip |
| 62 | + If ``True``, do not clip lines outside the plot region. Only makes sense in the |
| 63 | + Cartesian coordinate system. |
| 64 | + perspective |
| 65 | + Select perspective view and set the azimuth and elevation angle of the |
| 66 | + viewpoint. Refer to :meth:`pygmt.Figure.plot` for details. |
| 67 | +
|
| 68 | + Examples |
| 69 | + -------- |
| 70 | + >>> import pygmt |
| 71 | + >>> fig = pygmt.Figure() |
| 72 | + >>> fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True) |
| 73 | + >>> fig.vlines(x=1, pen="1p,black", label="Line at x=1") |
| 74 | + >>> fig.vlines(x=2, ymin=2, ymax=8, pen="1p,red,-", label="Line at x=2") |
| 75 | + >>> fig.vlines(x=[3, 4], ymin=3, ymax=7, pen="1p,black,.", label="Lines at x=3,4") |
| 76 | + >>> fig.vlines(x=[5, 6], ymin=4, ymax=9, pen="1p,red", label="Lines at x=5,6") |
| 77 | + >>> fig.vlines( |
| 78 | + ... x=[7, 8], ymin=[0, 1], ymax=[7, 8], pen="1p,blue", label="Lines at x=7,8" |
| 79 | + ... ) |
| 80 | + >>> fig.legend() |
| 81 | + >>> fig.show() |
| 82 | + """ |
| 83 | + self._preprocess() |
| 84 | + |
| 85 | + # Determine the y limits from the current plot region if not specified. |
| 86 | + if ymin is None or ymax is None: |
| 87 | + ylimits = self.region[2:] |
| 88 | + if ymin is None: |
| 89 | + ymin = ylimits[0] |
| 90 | + if ymax is None: |
| 91 | + ymax = ylimits[1] |
| 92 | + |
| 93 | + # Ensure x/ymin/ymax are 1-D arrays. |
| 94 | + _x = np.atleast_1d(x) |
| 95 | + _ymin = np.atleast_1d(ymin) |
| 96 | + _ymax = np.atleast_1d(ymax) |
| 97 | + |
| 98 | + nlines = len(_x) # Number of lines to plot. |
| 99 | + |
| 100 | + # Check if ymin/ymax are scalars or have the expected length. |
| 101 | + if _ymin.size not in {1, nlines} or _ymax.size not in {1, nlines}: |
| 102 | + msg = ( |
| 103 | + f"'ymin' and 'ymax' are expected to be scalars or have lengths '{nlines}', " |
| 104 | + f"but lengths '{_ymin.size}' and '{_ymax.size}' are given." |
| 105 | + ) |
| 106 | + raise GMTInvalidInput(msg) |
| 107 | + |
| 108 | + # Repeat ymin/ymax to match the length of x if they are scalars. |
| 109 | + if nlines != 1: |
| 110 | + if _ymin.size == 1: |
| 111 | + _ymin = np.repeat(_ymin, nlines) |
| 112 | + if _ymax.size == 1: |
| 113 | + _ymax = np.repeat(_ymax, nlines) |
| 114 | + |
| 115 | + # Call the Figure.plot method to plot the lines. |
| 116 | + for i in range(nlines): |
| 117 | + # Special handling for label. |
| 118 | + # 1. Only specify a label when plotting the first line. |
| 119 | + # 2. The -l option can accept comma-separated labels for labeling multiple lines |
| 120 | + # with auto-coloring enabled. We don't need this feature here, so we need to |
| 121 | + # replace comma with \054 if the label contains commas. |
| 122 | + _label = label.replace(",", "\\054") if label and i == 0 else None |
| 123 | + |
| 124 | + self.plot( |
| 125 | + x=[_x[i], _x[i]], |
| 126 | + y=[_ymin[i], _ymax[i]], |
| 127 | + pen=pen, |
| 128 | + label=_label, |
| 129 | + no_clip=no_clip, |
| 130 | + perspective=perspective, |
| 131 | + straight_line="y", |
| 132 | + ) |
0 commit comments