From 7ed3b49ac610ba24a5356f7e62bea5dec52b3e47 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 17 Feb 2021 19:28:53 +0100 Subject: [PATCH 01/17] Add new modules to plot horizontal and vertical lines 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. --- pygmt/src/hlines.py | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 pygmt/src/hlines.py diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py new file mode 100644 index 00000000000..37a69dcd11f --- /dev/null +++ b/pygmt/src/hlines.py @@ -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) From 096b9de601c68bb17d3ac3e2e9796e881679b600 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 17 Feb 2021 19:42:07 +0100 Subject: [PATCH 02/17] added further content --- doc/api/index.rst | 1 + examples/gallery/line/horizontal-lines.py | 29 ++++ pygmt/figure.py | 1 + pygmt/src/__init__.py | 1 + pygmt/src/hlines.py | 161 ++++++++++++---------- 5 files changed, 117 insertions(+), 76 deletions(-) create mode 100644 examples/gallery/line/horizontal-lines.py diff --git a/doc/api/index.rst b/doc/api/index.rst index 0ef0b359b39..f56e3d0b3e1 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -31,6 +31,7 @@ Plotting data and laying out the map: Figure.grdview Figure.image Figure.inset + Figure.hlines Figure.legend Figure.logo Figure.meca diff --git a/examples/gallery/line/horizontal-lines.py b/examples/gallery/line/horizontal-lines.py new file mode 100644 index 00000000000..cfbe3066586 --- /dev/null +++ b/examples/gallery/line/horizontal-lines.py @@ -0,0 +1,29 @@ +""" +Plot horizontal lines +--------------------- + +The :meth:`pygmt.Figure.hlines` method can plot horizontal lines based on +a given y value. Optionally, the lower and upper limits of the lines can be +defined, otherwise the current map boundaries are taken. + +""" + +import pygmt + +fig = pygmt.Figure() +fig.basemap(region=[0, 10, 0, 11], projection="X10c/10c", frame=True) + +fig.hlines(1, label="line1") +fig.hlines([2, 3], pen="2p,dodgerblue4", label="line2") +fig.hlines([4, 5], xmin=2, xmax=8, pen="2p,red3", label="line3") +fig.hlines([6, 7], xmin=[1, 3], xmax=[8, 7], pen="3p,seagreen", label="line4") +fig.hlines( + [8, 9, 10], + xmin=[1.3, 3, 2], + xmax=[6.5, 7, 5], + pen=["4p,darkmagenta", "2p,gold,--", "3.5p,blue,."], + label=["line5", "line6", "line7"], +) + +fig.legend() +fig.show() diff --git a/pygmt/figure.py b/pygmt/figure.py index 92699cd8632..72df6e40c4a 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -383,6 +383,7 @@ def _repr_html_(self): grdcontour, grdimage, grdview, + hlines, image, inset, legend, diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 3ef9af36070..2b0e0c2329b 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -15,6 +15,7 @@ from pygmt.src.grdinfo import grdinfo from pygmt.src.grdtrack import grdtrack from pygmt.src.grdview import grdview +from pygmt.src.hlines import hlines from pygmt.src.image import image from pygmt.src.info import info from pygmt.src.inset import inset diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 37a69dcd11f..31d6250d6a8 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -4,42 +4,50 @@ 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 +from pygmt.helpers import ( + build_arg_string, + data_kind, + fmt_docstring, + kwargs_to_strings, + use_alias, +) + @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", - ) + 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 + """ + " Plot one or a collection of horizontal lines. - Takes a single y value or a list of individual y values and optionally + 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**). + 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 ---------- @@ -73,83 +81,84 @@ def hlines(self, y=None, xmin=None, xmax=None, pen=None, label=None, **kwargs): {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"``). + 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: + 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) + 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: + 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) + 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 + 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 + kwargs["l"][0] = label2use else: - kwargs["l"] = np.repeat(None, list_length) - - - # prepare pens + 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(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 + # 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) + 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) From 92fa9c078315c0323e247c7de54bdb3f98899c03 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 17 Feb 2021 19:49:19 +0100 Subject: [PATCH 03/17] added some first tests --- pygmt/tests/test_hlines.py | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 pygmt/tests/test_hlines.py diff --git a/pygmt/tests/test_hlines.py b/pygmt/tests/test_hlines.py new file mode 100644 index 00000000000..021e936ba53 --- /dev/null +++ b/pygmt/tests/test_hlines.py @@ -0,0 +1,73 @@ +""" +Tests for hline. +""" +from pygmt import Figure +from pygmt.helpers.testing import check_figures_equal + + +@check_figures_equal() +def test_hlines_value_sets(): + """ + Passing sets of y, xmin and xmax. + """ + + fig_ref, fig_test = Figure(), Figure() + + fig_ref.hlines( + region=[0, 10, 0, 20], + projection="X10c/10c", + frame=True, + y=[5.5, 10, 6, 11], + xmin=[3.1, 6, 0, 1], + xmax=[5.5, 7.8, 10, 9], + label="test2", + pen="4p,green", + ) + + fig_test.hlines( + region="0/10/0/20", + projection="X10c/10c", + frame=True, + y=[5.5, 10, 6, 11], + xmin=[3.1, 6, 0, 1], + xmax=[5.5, 7.8, 10, 9], + label="test2", + pen="4p,green", + ) + + return fig_ref, fig_test + + +@check_figures_equal() +def test_hlines_zvalue(): + """ + Passing in color via zvalue. + """ + + fig_ref, fig_test = Figure(), Figure() + + fig_ref.hlines( + region=[0, 10, 0, 20], + projection="X10c/10c", + frame=True, + y=[9.9], + xmin=2, + xmax=6, + pen="5p,+z", + zvalue="0.5", + cmap="magma", + ) + + fig_test.hlines( + region="0/10/0/20", + projection="X10c/10c", + frame=True, + y=[9.9], + xmin=2, + xmax=6, + pen="5p,+z", + zvalue="0.5", + cmap="magma", + ) + + return fig_ref, fig_test From 975b3050c81fc85c7329fd93ebcf12df5f31f616 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 17 Feb 2021 19:58:06 +0100 Subject: [PATCH 04/17] some formatting --- pygmt/src/hlines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 31d6250d6a8..058b83c0af6 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -32,7 +32,7 @@ t="transparency", ) @kwargs_to_strings(R="sequence", p="sequence") -def hlines(self, y=None, xmin=None, xmax=None, pen=None, label=None, **kwargs): +def hlines(self, y=None, xmin=None, xmax=None, **kwargs): """ " Plot one or a collection of horizontal lines. @@ -111,7 +111,7 @@ def hlines(self, y=None, xmin=None, xmax=None, pen=None, label=None, **kwargs): else: # if only a single xmin and xmax without [], repeat to fit size of y - if isinstance(xmin, int) or isinstance(xmin, float): + if isinstance(xmin, (int, float)): x = np.array([[xmin], [xmax]]) x = np.repeat(x, list_length, axis=1) else: From f25a89c4d0a38fedfffeb5cf9da98ef16ff7e17f Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 17 Feb 2021 20:07:07 +0100 Subject: [PATCH 05/17] corrected typo --- pygmt/src/hlines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 058b83c0af6..95a6ca43f64 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -34,7 +34,7 @@ @kwargs_to_strings(R="sequence", p="sequence") def hlines(self, y=None, xmin=None, xmax=None, **kwargs): """ - " Plot one or a collection of horizontal lines. + 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. From d0bc35c0a3528f95540b6ab7c6a0163f1769b372 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 7 Apr 2021 18:57:57 +0200 Subject: [PATCH 06/17] moved gallery example to new subfolder /lines --- examples/gallery/{line => lines}/horizontal-lines.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/gallery/{line => lines}/horizontal-lines.py (100%) diff --git a/examples/gallery/line/horizontal-lines.py b/examples/gallery/lines/horizontal-lines.py similarity index 100% rename from examples/gallery/line/horizontal-lines.py rename to examples/gallery/lines/horizontal-lines.py From f75fbe1797a2ac24dc51ba79dbb65e2768b83a73 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 7 Apr 2021 19:04:43 +0200 Subject: [PATCH 07/17] disable pylint warnings --- pygmt/src/hlines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 95a6ca43f64..c2f96819bb2 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -93,7 +93,7 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): for lines. """ - kwargs = self._preprocess(**kwargs) + kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access list_length = len(np.atleast_1d(y)) From a375a778482f9b4ddfbb0cb32300a0d4f9985a33 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 7 Apr 2021 19:08:43 +0200 Subject: [PATCH 08/17] replace hyphen by underscore in gallery example file name --- .../gallery/lines/{horizontal-lines.py => horizontal_lines.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/gallery/lines/{horizontal-lines.py => horizontal_lines.py} (100%) diff --git a/examples/gallery/lines/horizontal-lines.py b/examples/gallery/lines/horizontal_lines.py similarity index 100% rename from examples/gallery/lines/horizontal-lines.py rename to examples/gallery/lines/horizontal_lines.py From 71113dd38aa9d9b57fc71dc5d7c90c65dc04a9cf Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 7 Apr 2021 19:26:44 +0200 Subject: [PATCH 09/17] disable pylint warnings --- pygmt/src/hlines.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index c2f96819bb2..245ee35d613 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -93,6 +93,10 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): for lines. """ + # appearing pylint warnings that need to be fixed + # pylint: disable=too-many-locals + # pylint: disable=too-many-branches + kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access list_length = len(np.atleast_1d(y)) From 1438d4340e5911f927189ad20fb86a1713e1c23b Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 7 Apr 2021 19:29:23 +0200 Subject: [PATCH 10/17] formatting --- pygmt/src/hlines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 245ee35d613..7f4d8caae05 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -97,7 +97,7 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): # pylint: disable=too-many-locals # pylint: disable=too-many-branches - kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access + kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access list_length = len(np.atleast_1d(y)) From 65eb85d2bcd92d21a2663cc4af0d2566562c6ce3 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Tue, 10 Aug 2021 21:03:17 +0200 Subject: [PATCH 11/17] update hlines module --- examples/gallery/lines/horizontal_lines.py | 15 ++- pygmt/src/hlines.py | 139 ++++++++------------- 2 files changed, 61 insertions(+), 93 deletions(-) diff --git a/examples/gallery/lines/horizontal_lines.py b/examples/gallery/lines/horizontal_lines.py index cfbe3066586..5e027f2fe20 100644 --- a/examples/gallery/lines/horizontal_lines.py +++ b/examples/gallery/lines/horizontal_lines.py @@ -17,13 +17,12 @@ fig.hlines([2, 3], pen="2p,dodgerblue4", label="line2") fig.hlines([4, 5], xmin=2, xmax=8, pen="2p,red3", label="line3") fig.hlines([6, 7], xmin=[1, 3], xmax=[8, 7], pen="3p,seagreen", label="line4") -fig.hlines( - [8, 9, 10], - xmin=[1.3, 3, 2], - xmax=[6.5, 7, 5], - pen=["4p,darkmagenta", "2p,gold,--", "3.5p,blue,."], - label=["line5", "line6", "line7"], -) +fig.hlines([8, 9, 10], + xmin=[1.3, 3, 2], + xmax=[6.5, 7, 5], + pen=["4p,darkmagenta","2p,gold,--","3.5p,blue,."], + label=["line5", "line6", "line7"], + transparency = 50) fig.legend() -fig.show() +fig.show() \ No newline at end of file diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 7f4d8caae05..54ecf756936 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -12,35 +12,22 @@ use_alias, ) - @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") + ) +@kwargs_to_strings(p="sequence") def hlines(self, y=None, xmin=None, xmax=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 @@ -48,21 +35,11 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): 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 @@ -76,87 +53,80 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): ``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. - """ - # appearing pylint warnings that need to be fixed - # pylint: disable=too-many-locals - # pylint: disable=too-many-branches + """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access - list_length = len(np.atleast_1d(y)) + y = np.atleast_1d(y) + list_length = len(y) + + # prepare x values + def prep_data(xmin, xmax, list_length): + + if xmin is None and xmax is None: + with Session() as lib: + # get limits from current map boundings if not given via xmin, xmax + x = np.array([[lib.extract_region()[0]], [lib.extract_region()[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." + ) - # 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, 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.") + # if only a single xmin and xmax without [], repeat to fit size of y + if isinstance(xmin, (int, float)): + x = np.array([[xmin], [xmax]]) + x = np.repeat(x, list_length, axis=1) 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"] + if len(xmin) != len(xmax): + GMTInvalidInput("Must provide same length for xmin and xmax.") + else: + x = np.array([xmin, xmax]) + + return np.atleast_1d(x) + + def prep_style(kwargs, list_length): + + # prepare labels + if "l" in kwargs: + # if several lines belong to the same label, first set all to the + # label given via "l", then reset all entries except the first + if not isinstance(kwargs["l"], list): + kwargs["l"] = np.repeat(kwargs["l"], list_length) + kwargs["l"][1:list_length] = None + else: 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) + + # prepare pens + if "W" in kwargs: + # select pen, no series + if not isinstance(kwargs["W"], list): + kwargs["W"] = np.repeat(kwargs["W"], list_length) + else: # use as default if no pen is given (neither single nor series) + kwargs["W"] = np.repeat("1p,black", list_length) + + return kwargs # loop over entries + x = prep_data(xmin, xmax, list_length) + kwargs = prep_style(kwargs, list_length) 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": + if data_kind(None, [x[0][index], x[1][index]], [y[index], y[index]]) == "vectors": file_context = lib.virtualfile_from_vectors( - np.atleast_1d(x2plt), np.atleast_1d(y2plt) - ) + np.atleast_1d([x[0][index], x[1][index]]), [y[index], y[index]]) else: raise GMTInvalidInput("Unrecognized data type.") @@ -164,5 +134,4 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): 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) + lib.call_module("plot", " ".join([fname, build_arg_string(kwargs)])) From 49008eda7da1536188be47024a5d185a52d4c8c0 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Tue, 10 Aug 2021 21:08:20 +0200 Subject: [PATCH 12/17] formatting --- examples/gallery/lines/horizontal_lines.py | 16 +++++++++------- pygmt/figure.py | 2 +- pygmt/src/__init__.py | 2 +- pygmt/src/hlines.py | 11 ++++++++--- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/examples/gallery/lines/horizontal_lines.py b/examples/gallery/lines/horizontal_lines.py index 5e027f2fe20..0ce5490c7ee 100644 --- a/examples/gallery/lines/horizontal_lines.py +++ b/examples/gallery/lines/horizontal_lines.py @@ -17,12 +17,14 @@ fig.hlines([2, 3], pen="2p,dodgerblue4", label="line2") fig.hlines([4, 5], xmin=2, xmax=8, pen="2p,red3", label="line3") fig.hlines([6, 7], xmin=[1, 3], xmax=[8, 7], pen="3p,seagreen", label="line4") -fig.hlines([8, 9, 10], - xmin=[1.3, 3, 2], - xmax=[6.5, 7, 5], - pen=["4p,darkmagenta","2p,gold,--","3.5p,blue,."], - label=["line5", "line6", "line7"], - transparency = 50) +fig.hlines( + [8, 9, 10], + xmin=[1.3, 3, 2], + xmax=[6.5, 7, 5], + pen=["4p,darkmagenta", "2p,gold,--", "3.5p,blue,."], + label=["line5", "line6", "line7"], + transparency=50, +) fig.legend() -fig.show() \ No newline at end of file +fig.show() diff --git a/pygmt/figure.py b/pygmt/figure.py index 759b2c1f48d..9092f977684 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -422,8 +422,8 @@ def _repr_html_(self): grdcontour, grdimage, grdview, - hlines, histogram, + hlines, image, inset, legend, diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 2aebe291ca1..936b6875e47 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -22,8 +22,8 @@ from pygmt.src.grdsample import grdsample from pygmt.src.grdtrack import grdtrack from pygmt.src.grdview import grdview -from pygmt.src.hlines import hlines from pygmt.src.histogram import histogram +from pygmt.src.hlines import hlines from pygmt.src.image import image from pygmt.src.info import info from pygmt.src.inset import inset diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 54ecf756936..106ac5e45e8 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -12,6 +12,7 @@ use_alias, ) + @fmt_docstring @use_alias( N="no_clip", @@ -20,7 +21,7 @@ l="label", p="perspective", t="transparency", - ) +) @kwargs_to_strings(p="sequence") def hlines(self, y=None, xmin=None, xmax=None, **kwargs): """ @@ -124,9 +125,13 @@ def prep_style(kwargs, list_length): for index in range(list_length): with Session() as lib: - if data_kind(None, [x[0][index], x[1][index]], [y[index], y[index]]) == "vectors": + if ( + data_kind(None, [x[0][index], x[1][index]], [y[index], y[index]]) + == "vectors" + ): file_context = lib.virtualfile_from_vectors( - np.atleast_1d([x[0][index], x[1][index]]), [y[index], y[index]]) + np.atleast_1d([x[0][index], x[1][index]]), [y[index], y[index]] + ) else: raise GMTInvalidInput("Unrecognized data type.") From 457bf72ddc4519c95b006640bf62161cc75aa6f3 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Tue, 10 Aug 2021 21:12:29 +0200 Subject: [PATCH 13/17] adjust docstring --- pygmt/src/hlines.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 106ac5e45e8..6c1caae43bb 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -74,7 +74,8 @@ def prep_data(xmin, xmax, list_length): if xmin is None and xmax is None: with Session() as lib: - # get limits from current map boundings if not given via xmin, xmax + # get limits from current map boundings if not given + # via xmin, xmax x = np.array([[lib.extract_region()[0]], [lib.extract_region()[1]]]) x = np.repeat(x, list_length, axis=1) elif xmin is None or xmax is None: @@ -83,7 +84,8 @@ def prep_data(xmin, xmax, list_length): ) else: - # if only a single xmin and xmax without [], repeat to fit size of y + # if only a single xmin and xmax without [], repeat to fit size + # of y if isinstance(xmin, (int, float)): x = np.array([[xmin], [xmax]]) x = np.repeat(x, list_length, axis=1) From f2c84a7bf8e1cf6c1ab779a3dab14a4ea73f5a1a Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Tue, 10 Aug 2021 21:26:51 +0200 Subject: [PATCH 14/17] adjust priliminary tests --- pygmt/tests/test_hlines.py | 43 ++------------------------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/pygmt/tests/test_hlines.py b/pygmt/tests/test_hlines.py index 021e936ba53..896aa68b14a 100644 --- a/pygmt/tests/test_hlines.py +++ b/pygmt/tests/test_hlines.py @@ -13,10 +13,8 @@ def test_hlines_value_sets(): fig_ref, fig_test = Figure(), Figure() + fig_ref.basemap(region=[0, 10, 0, 20], projection="X10c/10c", frame=True) fig_ref.hlines( - region=[0, 10, 0, 20], - projection="X10c/10c", - frame=True, y=[5.5, 10, 6, 11], xmin=[3.1, 6, 0, 1], xmax=[5.5, 7.8, 10, 9], @@ -24,10 +22,8 @@ def test_hlines_value_sets(): pen="4p,green", ) + fig_test.basemap(region="0/10/0/20", projection="X10c/10c", frame=True) fig_test.hlines( - region="0/10/0/20", - projection="X10c/10c", - frame=True, y=[5.5, 10, 6, 11], xmin=[3.1, 6, 0, 1], xmax=[5.5, 7.8, 10, 9], @@ -36,38 +32,3 @@ def test_hlines_value_sets(): ) return fig_ref, fig_test - - -@check_figures_equal() -def test_hlines_zvalue(): - """ - Passing in color via zvalue. - """ - - fig_ref, fig_test = Figure(), Figure() - - fig_ref.hlines( - region=[0, 10, 0, 20], - projection="X10c/10c", - frame=True, - y=[9.9], - xmin=2, - xmax=6, - pen="5p,+z", - zvalue="0.5", - cmap="magma", - ) - - fig_test.hlines( - region="0/10/0/20", - projection="X10c/10c", - frame=True, - y=[9.9], - xmin=2, - xmax=6, - pen="5p,+z", - zvalue="0.5", - cmap="magma", - ) - - return fig_ref, fig_test From e7726c0d172c84469a464f1c9868b4a7b5f0448a Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 11 Aug 2021 18:32:05 +0200 Subject: [PATCH 15/17] update --- pygmt/src/hlines.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 6c1caae43bb..0c9e3153ee4 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -74,8 +74,7 @@ def prep_data(xmin, xmax, list_length): if xmin is None and xmax is None: with Session() as lib: - # get limits from current map boundings if not given - # via xmin, xmax + # get limits from current map boundings if not given via xmin, xmax x = np.array([[lib.extract_region()[0]], [lib.extract_region()[1]]]) x = np.repeat(x, list_length, axis=1) elif xmin is None or xmax is None: @@ -84,8 +83,7 @@ def prep_data(xmin, xmax, list_length): ) else: - # if only a single xmin and xmax without [], repeat to fit size - # of y + # if only a single xmin and xmax without [], repeat to fit size of y if isinstance(xmin, (int, float)): x = np.array([[xmin], [xmax]]) x = np.repeat(x, list_length, axis=1) @@ -101,11 +99,13 @@ def prep_style(kwargs, list_length): # prepare labels if "l" in kwargs: - # if several lines belong to the same label, first set all to the - # label given via "l", then reset all entries except the first + # if several lines belong to the same label, first set all to None + # then replace first entry by the label given via "l" if not isinstance(kwargs["l"], list): - kwargs["l"] = np.repeat(kwargs["l"], list_length) - kwargs["l"][1:list_length] = None + label2use = kwargs["l"] + kwargs["l"] = np.repeat(None, list_length) + kwargs["l"][0] = label2use + else: kwargs["l"] = np.repeat(None, list_length) From 46f87023467de92dbe2e0cd6de60c39add2af316 Mon Sep 17 00:00:00 2001 From: Michael Grund Date: Wed, 11 Aug 2021 18:35:52 +0200 Subject: [PATCH 16/17] formatting --- pygmt/src/hlines.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 0c9e3153ee4..80160846859 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -74,7 +74,8 @@ def prep_data(xmin, xmax, list_length): if xmin is None and xmax is None: with Session() as lib: - # get limits from current map boundings if not given via xmin, xmax + # get limits from current map boundings if not given + # via xmin, xmax x = np.array([[lib.extract_region()[0]], [lib.extract_region()[1]]]) x = np.repeat(x, list_length, axis=1) elif xmin is None or xmax is None: @@ -83,7 +84,8 @@ def prep_data(xmin, xmax, list_length): ) else: - # if only a single xmin and xmax without [], repeat to fit size of y + # if only a single xmin and xmax without [], repeat to fit + # size of y if isinstance(xmin, (int, float)): x = np.array([[xmin], [xmax]]) x = np.repeat(x, list_length, axis=1) From d493a1c1b26cfc2efada79743ca8583d5c632cfd Mon Sep 17 00:00:00 2001 From: Michael Grund <23025878+michaelgrund@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:42:50 +0200 Subject: [PATCH 17/17] Update pygmt/src/hlines.py Co-authored-by: Dongdong Tian --- pygmt/src/hlines.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 80160846859..a8d3f237272 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -73,11 +73,9 @@ def hlines(self, y=None, xmin=None, xmax=None, **kwargs): def prep_data(xmin, xmax, list_length): if xmin is None and xmax is None: - with Session() as lib: - # get limits from current map boundings if not given - # via xmin, xmax - x = np.array([[lib.extract_region()[0]], [lib.extract_region()[1]]]) - x = np.repeat(x, list_length, axis=1) + # get limits from current map boundings if not given via xmin, xmax + xmin, xmax = fig.region[0:2] + x = np.repeat([[xmin], [xmax]], 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."