From a3928a37fd6e8deb1c0ac2171f5f9243a64ec5a9 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Thu, 25 Apr 2024 20:15:44 -0700 Subject: [PATCH 1/2] Hide invisible axes while still displaying colorbars This at least allows a workaround for #606 --- src/tikzplotlib/_axes.py | 3 ++- src/tikzplotlib/_save.py | 8 ++++++-- tests/test_colorbars.py | 12 +++++++++++- tests/test_colorbars_reference.tex | 25 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/tikzplotlib/_axes.py b/src/tikzplotlib/_axes.py index fa84db2f..b4c6d005 100644 --- a/src/tikzplotlib/_axes.py +++ b/src/tikzplotlib/_axes.py @@ -32,9 +32,10 @@ def __init__(self, data, obj): # noqa: C901 self._subplot(obj, data) self.axis_options = [] + self.is_visible = obj.get_visible() # check if axes need to be displayed at all - if not obj.axison: + if not (obj.axison and self.is_visible): self.axis_options.append("hide x axis") self.axis_options.append("hide y axis") diff --git a/src/tikzplotlib/_save.py b/src/tikzplotlib/_save.py index d89cadfa..96ceabd0 100644 --- a/src/tikzplotlib/_save.py +++ b/src/tikzplotlib/_save.py @@ -348,8 +348,12 @@ def _recurse(data, obj): data["current mpl axes obj"] = child data["current axes"] = ax - # Run through the child objects, gather the content. - data, children_content = _recurse(data, child) + if ax.is_visible: + # Run through the child objects, gather the content. + data, children_content = _recurse(data, child) + else: + # we may still display the colorbar + children_content = [] # populate content and add axis environment if desired if data["add axis environment"]: diff --git a/tests/test_colorbars.py b/tests/test_colorbars.py index 3d384f1f..8fb7e730 100644 --- a/tests/test_colorbars.py +++ b/tests/test_colorbars.py @@ -4,7 +4,7 @@ def plot(): # Make a figure and axes with dimensions as desired. - fig, ax = plt.subplots(3) + fig, ax = plt.subplots(4) # Set the colormap and norm to correspond to the data for which the colorbar will be # used. @@ -69,6 +69,16 @@ def plot(): ) cb3.set_label("Custom extension lengths, some other units") + # Set the colormap and norm to correspond to the data for which the colorbar will be + # used. This time attach the colorbar to axes. + cmap = mpl.cm.cool + norm = mpl.colors.Normalize(vmin=-5, vmax=10) + + img = ax[3].imshow([[0, 1]], cmap=cmap) + ax[3].set_visible(False) + cax = fig.add_axes([0.1, 1, 0.8, 0.1]) + cb4 = fig.colorbar(img, cax=cax, orientation="horizontal", label="Some Units") + return fig diff --git a/tests/test_colorbars_reference.tex b/tests/test_colorbars_reference.tex index dec2184e..6c25b552 100644 --- a/tests/test_colorbars_reference.tex +++ b/tests/test_colorbars_reference.tex @@ -1,3 +1,28 @@ \begin{tikzpicture} +\definecolor{darkgray176}{RGB}{176,176,176} + +\begin{groupplot}[group style={group size=1 by 4}] +\nextgroupplot[ +colorbar horizontal, +colormap={mymap}{[1pt] + rgb(0pt)=(0,1,1); + rgb(1pt)=(1,0,1) +}, +hide x axis, +hide y axis, +point meta max=1, +point meta min=0, +tick align=outside, +tick pos=left, +x grid style={darkgray176}, +xmin=-0.5, xmax=1.5, +xtick style={color=black}, +y dir=reverse, +y grid style={darkgray176}, +ymin=-0.5, ymax=0.5, +ytick style={color=black} +] +\end{groupplot} + \end{tikzpicture} From 702dc4dba7c03c14735aa6113f0fd6955a5c479b Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Thu, 25 Apr 2024 20:45:56 -0700 Subject: [PATCH 2/2] Emit axes for unbound colorbars Fixes #606 Not sure if this is the best way to do it, since it seems a bit kludgy, but it seems to work. --- src/tikzplotlib/_axes.py | 32 +++++++++------ src/tikzplotlib/_save.py | 10 ++++- tests/test_colorbars_reference.tex | 64 ++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/src/tikzplotlib/_axes.py b/src/tikzplotlib/_axes.py index b4c6d005..be881eb3 100644 --- a/src/tikzplotlib/_axes.py +++ b/src/tikzplotlib/_axes.py @@ -17,25 +17,26 @@ def __init__(self, data, obj): # noqa: C901 """Returns the PGFPlots code for an axis environment.""" self.content = [] - # Are we dealing with an axis that hosts a colorbar? Skip then, those are - # treated implicitily by the associated axis. - self.is_colorbar = _is_colorbar_heuristic(obj) - if self.is_colorbar: - return - # instantiation self.nsubplots = 1 self.subplot_index = 0 self.is_subplot = False - if isinstance(obj, mpl.axes.Subplot): + self.axis_options = [] + + # Are we dealing with an axis that hosts a colorbar? Skip then, those are + # treated implicitily by the associated axis. + self.is_colorbar = _is_colorbar_heuristic(obj) + + if isinstance(obj, mpl.axes.Subplot) and not self.is_colorbar: self._subplot(obj, data) self.axis_options = [] self.is_visible = obj.get_visible() # check if axes need to be displayed at all - if not (obj.axison and self.is_visible): + # unassociated colorbars should have hidden axes; colorbars associated to axes will be printed by the axis + if not (obj.axison and self.is_visible) or self.is_colorbar: self.axis_options.append("hide x axis") self.axis_options.append("hide y axis") @@ -154,10 +155,9 @@ def __init__(self, data, obj): # noqa: C901 if col != "white": self.axis_options.append(f"axis background/.style={{fill={col}}}") - # find color bar - colorbar = _find_associated_colorbar(obj) - if colorbar: - self._colorbar(colorbar, data) + self.colorbar = _find_associated_colorbar(obj) + if self.colorbar: + self._colorbar(self.colorbar, data) if self.is_subplot: self.content.append("\n\\nextgroupplot") @@ -801,7 +801,7 @@ def _handle_listed_color_map(cmap, data): if cmap.N is None or cmap.N == len(cmap.colors): colors = [ f"rgb({k}{unit})=({rgb[0]:{ff}},{rgb[1]:{ff}},{rgb[2]:{ff}})" - for k, rgb in enumerate(cmap.colors) + for k, rgb in enumerate(map(mpl.colors.to_rgb, cmap.colors)) ] else: reps = int(float(cmap.N) / len(cmap.colors) - 0.5) + 1 @@ -859,6 +859,12 @@ def _find_associated_colorbar(obj): next axis environment, and see if it is de facto a color bar; if yes, return the color bar object. """ + try: + cbar = obj._colorbar + if cbar is not None: + return cbar + except AttributeError: + pass for child in obj.get_children(): try: cbar = child.colorbar diff --git a/src/tikzplotlib/_save.py b/src/tikzplotlib/_save.py index 96ceabd0..b55b12ed 100644 --- a/src/tikzplotlib/_save.py +++ b/src/tikzplotlib/_save.py @@ -328,6 +328,9 @@ def _recurse(data, obj): """Iterates over all children of the current object, gathers the contents contributing to the resulting PGFPlots file, and returns those. """ + # bound_colorbars holds colorbars that are associated to axes + # we don't add axes from colorbars if they come after the axis to which they were associated + bound_colorbars = [] content = _ContentManager() for child in obj.get_children(): # Some patches are Spines, too; skip those entirely. @@ -338,9 +341,12 @@ def _recurse(data, obj): if isinstance(child, mpl.axes.Axes): ax = _axes.Axes(data, child) - if ax.is_colorbar: + if ax.is_colorbar and any(ax.colorbar is cb for cb in bound_colorbars): continue + if not ax.is_colorbar and ax.colorbar is not None: + bound_colorbars.append(ax.colorbar) + # add extra axis options if data["extra axis options [base]"]: ax.axis_options.extend(data["extra axis options [base]"]) @@ -348,7 +354,7 @@ def _recurse(data, obj): data["current mpl axes obj"] = child data["current axes"] = ax - if ax.is_visible: + if ax.is_visible and not ax.is_colorbar: # Run through the child objects, gather the content. data, children_content = _recurse(data, child) else: diff --git a/tests/test_colorbars_reference.tex b/tests/test_colorbars_reference.tex index 6c25b552..304db4e6 100644 --- a/tests/test_colorbars_reference.tex +++ b/tests/test_colorbars_reference.tex @@ -2,6 +2,70 @@ \definecolor{darkgray176}{RGB}{176,176,176} +\begin{axis}[ +colorbar horizontal, +colormap={mymap}{[1pt] + rgb(0pt)=(0,1,1); + rgb(1pt)=(1,0,1) +}, +hide x axis, +hide y axis, +point meta max=10, +point meta min=-5, +tick align=outside, +tick pos=left, +x grid style={darkgray176}, +xlabel={Some Units}, +xmin=-5, xmax=10, +xtick style={color=black}, +ymin=0, ymax=1 +] +\end{axis} + +\begin{axis}[ +colorbar horizontal, +colormap={mymap}{[1pt] + rgb(0pt)=(1,0,0); + rgb(1pt)=(0,0.5,0); + rgb(2pt)=(0,0,1); + rgb(3pt)=(0,0.75,0.75) +}, +hide x axis, +hide y axis, +point meta max=8, +point meta min=1, +tick align=outside, +tick pos=left, +x grid style={darkgray176}, +xlabel={Discrete intervals, some other units}, +xmin=1, xmax=8, +xtick style={color=black}, +ymin=0, ymax=1 +] +\end{axis} + +\begin{axis}[ +colorbar horizontal, +colormap={mymap}{[1pt] + rgb(0pt)=(0,0.4,1); + rgb(1pt)=(0,0.8,1); + rgb(2pt)=(1,0.8,0); + rgb(3pt)=(1,0.4,0) +}, +hide x axis, +hide y axis, +point meta max=1, +point meta min=-1, +tick align=outside, +tick pos=left, +x grid style={darkgray176}, +xlabel={Custom extension lengths, some other units}, +xmin=-1, xmax=1, +xtick style={color=black}, +ymin=0, ymax=1 +] +\end{axis} + \begin{groupplot}[group style={group size=1 by 4}] \nextgroupplot[ colorbar horizontal,