8000 Merge pull request #13956 from meeseeksmachine/auto-backport-of-pr-13… · matplotlib/matplotlib@0d0d87c · GitHub
[go: up one dir, main page]

Skip to content

Commit 0d0d87c

Browse files
authored
Merge pull request #13956 from meeseeksmachine/auto-backport-of-pr-13908-on-v3.1.x
Backport PR #13908 on branch v3.1.x (Enh control tick deconflict2)
2 parents 2cf4c8b + d35fc5b commit 0d0d87c

File tree

3 files changed

+77
-11
lines changed

3 files changed

+77
-11
lines changed

doc/api/axis_api.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Formatters and Locators
6060
Axis.set_major_locator
6161
Axis.set_minor_formatter
6262
Axis.set_minor_locator
63-
63+
Axis.remove_overlapping_locs
6464

6565
Axis Label
6666
----------

lib/matplotlib/axis.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,8 @@ def __init__(self, axes, pickradius=15):
723723
`.Axis.contains`.
724724
"""
725725
martist.Artist.__init__(self)
726+
self._remove_overlapping_locs = True
727+
726728
self.set_figure(axes.figure)
727729

728730
self.isDefault_label = True
@@ -754,6 +756,17 @@ def __init__(self, axes, pickradius=15):
754756
majorTicks = _LazyTickList(major=True)
755757
minorTicks = _LazyTickList(major=False)
756758

759+
def get_remove_overlapping_locs(self):
760+
return self._remove_overlapping_locs
761+
762+
def set_remove_overlapping_locs(self, val):
763+
self._remove_overlapping_locs = bool(val)
764+
765+
remove_overlapping_locs = property(
766+
get_remove_overlapping_locs, set_remove_overlapping_locs,
767+
doc=('If minor ticker locations that overlap with major '
768+
'ticker locations should be trimmed.'))
769+
757770
def set_label_coords(self, x, y, transform=None):
758771
"""
759772
Set the coordinates of the label.
@@ -1064,23 +1077,29 @@ def _update_ticks(self):
10641077
Update ticks (position and labels) using the current data interval of
10651078
the axes. Return the list of ticks that will be drawn.
10661079
"""
1067-
1068-
major_locs = self.major.locator()
1069-
major_ticks = self.get_major_ticks(len(major_locs))
1080+
major_locs = self.get_majorticklocs()
10701081
major_labels = self.major.formatter.format_ticks(major_locs)
1082+
major_ticks = self.get_major_ticks(len(major_locs))
1083+
self.major.formatter.set_locs(major_locs)
10711084
for tick, loc, label in zip(major_ticks, major_locs, major_labels):
10721085
tick.update_position(loc)
10731086
tick.set_label1(label)
10741087
tick.set_label2(label)
1075-
minor_locs = self.minor.locator()
1076-
minor_ticks = self.get_minor_ticks(len(minor_locs))
1088+
minor_locs = self.get_minorticklocs()
10771089
minor_labels = self.minor.formatter.format_ticks(minor_locs)
1090+
minor_ticks = self.get_minor_ticks(len(minor_locs))
1091+
self.minor.formatter.set_locs(minor_locs)
10781092
for tick, loc, label in zip(minor_ticks, minor_locs, minor_labels):
10791093
tick.update_position(loc)
10801094
tick.set_label1(label)
10811095
tick.set_label2(label)
10821096
ticks = [*major_ticks, *minor_ticks]
10831097

1098+
# mark the ticks that we will not be using as not visible
1099+
for t in (self.minorTicks[len(minor_locs):] +
1100+
self.majorTicks[len(major_locs):]):
1101+
t.set_visible(False)
1102+
10841103
view_low, view_high = self.get_view_interval()
10851104
if view_low > view_high:
10861105
view_low, view_high = view_high, view_low
@@ -1322,9 +1341,10 @@ def get_minorticklocs(self):
13221341
# Use the transformed view limits as scale. 1e-5 is the default rtol
13231342
# for np.isclose.
13241343
tol = (hi - lo) * 1e-5
1325-
minor_locs = [
1326-
loc for loc, tr_loc in zip(minor_locs, tr_minor_locs)
1327-
if not np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()]
1344+
if self.remove_overlapping_locs:
1345+
minor_locs = [
1346+
loc for loc, tr_loc in zip(minor_locs, tr_minor_locs)
1347+
if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()]
13281348
return minor_locs
13291349

13301350
def get_ticklocs(self, minor=False):
@@ -1390,7 +1410,7 @@ def get_minor_formatter(self):
13901410
def get_major_ticks(self, numticks=None):
13911411
'Get the tick instances; grow as necessary.'
13921412
if numticks is None:
1393-
numticks = len(self.get_major_locator()())
1413+
numticks = len(self.get_majorticklocs())
13941414

13951415
while len(self.majorTicks) < numticks:
13961416
# Update the new tick label properties from the old.
@@ -1404,7 +1424,7 @@ def get_major_ticks(self, numticks=None):
14041424
def get_minor_ticks(self, numticks=None):
14051425
'Get the minor tick instances; grow as necessary.'
14061426
if numticks is None:
1407-
numticks = len(self.get_minor_locator()())
1427+
numticks = len(self.get_minorticklocs())
14081428

14091429
while len(self.minorTicks) < numticks:
14101430
# Update the new tick label properties from the old.

lib/matplotlib/tests/test_ticker.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,3 +925,49 @@ def minorticksubplot(xminor, yminor, i):
925925
minorticksubplot(True, False, 2)
926926
minorticksubplot(False, True, 3)
927927
minorticksubplot(True, True, 4)
928+
929+
930+
@pytest.mark.parametrize('remove_overlapping_locs, expected_num',
931+
((True, 6),
932+
(None, 6), # this tests the default
933+
(False, 9)))
934+
def test_remove_overlap(remove_overlapping_locs, expected_num):
935+
import numpy as np
936+
import matplotlib.dates as mdates
937+
938+
t = np.arange("2018-11-03", "2018-11-06", dtype="datetime64")
939+
x = np.ones(len(t))
940+
941+
fig, ax = plt.subplots()
942+
ax.plot(t, x)
943+
944+
ax.xaxis.set_major_locator(mdates.DayLocator())
945+
ax.xaxis.set_major_formatter(mdates.DateFormatter('\n%a'))
946+
947+
ax.xaxis.set_minor_locator(mdates.HourLocator((0, 6, 12, 18)))
948+
ax.xaxis.set_minor_formatter(mdates.DateFormatter('%H:%M'))
949+
# force there to be extra ticks
950+
ax.xaxis.get_minor_ticks(15)
951+
if remove_overlapping_locs is not None:
952+
ax.xaxis.remove_overlapping_locs = remove_overlapping_locs
953+
954+
# check that getter/setter exists
955+
current = ax.xaxis.remove_overlapping_locs
956+
assert (current == ax.xaxis.get_remove_overlapping_locs())
957+
plt.setp(ax.xaxis, remove_overlapping_locs=current)
958+
new = ax.xaxis.remove_overlapping_locs
959+
assert (new == ax.xaxis.remove_overlapping_locs)
960+
961+
# check that the accessors filter correctly
962+
# this is the method that does the actual filtering
963+
assert len(ax.xaxis.get_minorticklocs()) == expected_num
964+
# these three are derivative
965+
assert len(ax.xaxis.get_minor_ticks()) == expected_num
966+
assert len(ax.xaxis.get_minorticklabels()) == expected_num
967+
assert len(ax.xaxis.get_minorticklines()) == expected_num*2
968+
969+
# force a draw to call _update_ticks under the hood
970+
fig.canvas.draw()
971+
# check that the correct number of ticks report them selves as
972+
# visible
973+
assert sum(t.get_visible() for t in ax.xaxis.minorTicks) == expected_num

0 commit comments

Comments
 (0)
0