From 8d4cee5019df3f05f3e2e10dfc26177527bd5b42 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 31 Mar 2019 23:09:38 -0400 Subject: [PATCH 1/7] FIX: restore de-confliction logic for minor ticks In #13363 when `iter_ticks` was deprecated the in-lined logic did not account for the updates from #13314. --- lib/matplotlib/axis.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 3569187e0fcf..5e9391f21271 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1064,17 +1064,18 @@ 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) From e3a83931ea42980a05e93741a44b1c7c6481da5f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 8 Apr 2019 22:16:34 -0400 Subject: [PATCH 2/7] ENH: add flag to disable locator 'de-confliction' If Axis.remove_overlapping_locs is set to False the `Axis` object will not attempt to remove locations in the minor ticker that overlap with locations in the major ticker. This is a follow up to #13314 which un-conditionally removed the overlap. closes #13618 --- lib/matplotlib/axis.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 5e9391f21271..1cd860613c58 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. @@ -1323,9 +1336,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): From 964e8cd74df9faca3694332684478ce0402a4686 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 8 Apr 2019 22:27:26 -0400 Subject: [PATCH 3/7] TST: add a test of not removing overlapping locations --- lib/matplotlib/tests/test_ticker.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index b35a7fd45df8..1368e71f31f7 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -923,3 +923,24 @@ def minorticksubplot(xminor, yminor, i): minorticksubplot(True, False, 2) minorticksubplot(False, True, 3) minorticksubplot(True, True, 4) + + +def test_remove_overlap(): + 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')) + + assert len(ax.xaxis.get_minorticklocs()) == 6 + ax.xaxis.remove_overlapping_locs = False + assert len(ax.xaxis.get_minorticklocs()) == 9 From 24de3e90c960b1f4bfdf40ba30f8cc512f8cf255 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 10 Apr 2019 08:45:48 -0400 Subject: [PATCH 4/7] ENH: have `get_*_ticks` respect overlap filtering by default This will make sure extra ticks (that will not be used) will not be returned. The change to `get_major_ticks` is a no-op, but done for symmetry. --- lib/matplotlib/axis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 1cd860613c58..4aed0f242235 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1405,7 +1405,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. @@ -1419,7 +1419,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. From e908efe37131da0697bd8e9c62444d37b4940ad3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 10 Apr 2019 08:47:24 -0400 Subject: [PATCH 5/7] ENH: set non-used ticks to not visible This is to prevent user confusion if they are reaching directly into `Axis.minorTicks` and `Axis.majorTicks` and see ticks report they are visible but are not actually drawn. --- lib/matplotlib/axis.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 4aed0f242235..1855af151f27 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1095,6 +1095,11 @@ def _update_ticks(self): 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 From 0a6f575e9d82cba79557f123e939ff8301ae9fda Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 10 Apr 2019 08:56:58 -0400 Subject: [PATCH 6/7] TST: extend tests of remove_overlapping_locs --- lib/matplotlib/tests/test_ticker.py | 35 ++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 1368e71f31f7..8c52fc9dde9b 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -925,7 +925,11 @@ def minorticksubplot(xminor, yminor, i): minorticksubplot(True, True, 4) -def test_remove_overlap(): +@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 @@ -940,7 +944,28 @@ def test_remove_overlap(): ax.xaxis.set_minor_locator(mdates.HourLocator((0, 6, 12, 18))) ax.xaxis.set_minor_formatter(mdates.DateFormatter('%H:%M')) - - assert len(ax.xaxis.get_minorticklocs()) == 6 - ax.xaxis.remove_overlapping_locs = False - assert len(ax.xaxis.get_minorticklocs()) == 9 + # 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 From 00fbd77ba94ad9586183eafb4947500ee63b4100 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 11 Apr 2019 23:04:19 -0400 Subject: [PATCH 7/7] DOC: add remove_overlapping_locs to API page --- doc/api/axis_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ----------