diff --git a/doc/api/axis_api.rst b/doc/api/axis_api.rst index 3889b86498cb..8341d53448c1 100644 --- a/doc/api/axis_api.rst +++ b/doc/api/axis_api.rst @@ -60,7 +60,7 @@ Formatters and Locators Axis.set_major_locator Axis.set_minor_formatter Axis.set_minor_locator - + Axis.remove_overlapping_locs Axis Label ---------- diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 3569187e0fcf..1855af151f27 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -723,6 +723,8 @@ def __init__(self, axes, pickradius=15): `.Axis.contains`. """ martist.Artist.__init__(self) + self._remove_overlapping_locs = True + self.set_figure(axes.figure) self.isDefault_label = True @@ -754,6 +756,17 @@ def __init__(self, axes, pickradius=15): majorTicks = _LazyTickList(major=True) minorTicks = _LazyTickList(major=False) + def get_remove_overlapping_locs(self): + return self._remove_overlapping_locs + + def set_remove_overlapping_locs(self, val): + self._remove_overlapping_locs = bool(val) + + remove_overlapping_locs = property( + get_remove_overlapping_locs, set_remove_overlapping_locs, + doc=('If minor ticker locations that overlap with major ' + 'ticker locations should be trimmed.')) + def set_label_coords(self, x, y, transform=None): """ Set the coordinates of the label. @@ -1064,23 +1077,29 @@ def _update_ticks(self): Update ticks (position and labels) using the current data interval of the axes. Return the list of ticks that will be drawn. """ - - major_locs = self.major.locator() - major_ticks = self.get_major_ticks(len(major_locs)) + major_locs = self.get_majorticklocs() major_labels = self.major.formatter.format_ticks(major_locs) + major_ticks = self.get_major_ticks(len(major_locs)) + self.major.formatter.set_locs(major_locs) for tick, loc, label in zip(major_ticks, major_locs, major_labels): tick.update_position(loc) tick.set_label1(label) tick.set_label2(label) - minor_locs = self.minor.locator() - minor_ticks = self.get_minor_ticks(len(minor_locs)) + minor_locs = self.get_minorticklocs() minor_labels = self.minor.formatter.format_ticks(minor_locs) + minor_ticks = self.get_minor_ticks(len(minor_locs)) + self.minor.formatter.set_locs(minor_locs) for tick, loc, label in zip(minor_ticks, minor_locs, minor_labels): tick.update_position(loc) tick.set_label1(label) tick.set_label2(label) ticks = [*major_ticks, *minor_ticks] + # mark the ticks that we will not be using as not visible + for t in (self.minorTicks[len(minor_locs):] + + self.majorTicks[len(major_locs):]): + t.set_visible(False) + view_low, view_high = self.get_view_interval() if view_low > view_high: view_low, view_high = view_high, view_low @@ -1322,9 +1341,10 @@ def get_minorticklocs(self): # Use the transformed view limits as scale. 1e-5 is the default rtol # for np.isclose. tol = (hi - lo) * 1e-5 - minor_locs = [ - loc for loc, tr_loc in zip(minor_locs, tr_minor_locs) - if not np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()] + if self.remove_overlapping_locs: + minor_locs = [ + loc for loc, tr_loc in zip(minor_locs, tr_minor_locs) + if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()] return minor_locs def get_ticklocs(self, minor=False): @@ -1390,7 +1410,7 @@ def get_minor_formatter(self): def get_major_ticks(self, numticks=None): 'Get the tick instances; grow as necessary.' if numticks is None: - numticks = len(self.get_major_locator()()) + numticks = len(self.get_majorticklocs()) while len(self.majorTicks) < numticks: # Update the new tick label properties from the old. @@ -1404,7 +1424,7 @@ def get_major_ticks(self, numticks=None): def get_minor_ticks(self, numticks=None): 'Get the minor tick instances; grow as necessary.' if numticks is None: - numticks = len(self.get_minor_locator()()) + numticks = len(self.get_minorticklocs()) while len(self.minorTicks) < numticks: # Update the new tick label properties from the old. diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 4e759ef27857..4f26330d69f3 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -925,3 +925,49 @@ def minorticksubplot(xminor, yminor, i): minorticksubplot(True, False, 2) minorticksubplot(False, True, 3) minorticksubplot(True, True, 4) + + +@pytest.mark.parametrize('remove_overlapping_locs, expected_num', + ((True, 6), + (None, 6), # this tests the default + (False, 9))) +def test_remove_overlap(remove_overlapping_locs, expected_num): + import numpy as np + import matplotlib.dates as mdates + + t = np.arange("2018-11-03", "2018-11-06", dtype="datetime64") + x = np.ones(len(t)) + + fig, ax = plt.subplots() + ax.plot(t, x) + + ax.xaxis.set_major_locator(mdates.DayLocator()) + ax.xaxis.set_major_formatter(mdates.DateFormatter('\n%a')) + + ax.xaxis.set_minor_locator(mdates.HourLocator((0, 6, 12, 18))) + ax.xaxis.set_minor_formatter(mdates.DateFormatter('%H:%M')) + # force there to be extra ticks + ax.xaxis.get_minor_ticks(15) + if remove_overlapping_locs is not None: + ax.xaxis.remove_overlapping_locs = remove_overlapping_locs + + # check that getter/setter exists + current = ax.xaxis.remove_overlapping_locs + assert (current == ax.xaxis.get_remove_overlapping_locs()) + plt.setp(ax.xaxis, remove_overlapping_locs=current) + new = ax.xaxis.remove_overlapping_locs + assert (new == ax.xaxis.remove_overlapping_locs) + + # check that the accessors filter correctly + # this is the method that does the actual filtering + assert len(ax.xaxis.get_minorticklocs()) == expected_num + # these three are derivative + assert len(ax.xaxis.get_minor_ticks()) == expected_num + assert len(ax.xaxis.get_minorticklabels()) == expected_num + assert len(ax.xaxis.get_minorticklines()) == expected_num*2 + + # force a draw to call _update_ticks under the hood + fig.canvas.draw() + # check that the correct number of ticks report them selves as + # visible + assert sum(t.get_visible() for t in ax.xaxis.minorTicks) == expected_num
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: