Skip to content

Commit 16e8a7a

Browse files
authored
Merge pull request #23331 from meeseeksmachine/auto-backport-of-pr-22835-on-v3.5.x
Backport PR #22835 on branch v3.5.x (Fix BoundaryNorm cursor data output)
2 parents c4cae98 + 5992285 commit 16e8a7a

File tree

2 files changed

+178
-4
lines changed

2 files changed

+178
-4
lines changed

lib/matplotlib/artist.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import matplotlib as mpl
1414
from . import _api, cbook
15+
from .colors import BoundaryNorm
1516
from .cm import ScalarMappable
1617
from .path import Path
1718
from .transforms import (Bbox, IdentityTransform, Transform, TransformedBbox,
@@ -1280,10 +1281,20 @@ def format_cursor_data(self, data):
12801281
return "[]"
12811282
normed = self.norm(data)
12821283
if np.isfinite(normed):
1283-
# Midpoints of neighboring color intervals.
1284-
neighbors = self.norm.inverse(
1285-
(int(self.norm(data) * n) + np.array([0, 1])) / n)
1286-
delta = abs(neighbors - data).max()
1284+
if isinstance(self.norm, BoundaryNorm):
1285+
# not an invertible normalization mapping
1286+
cur_idx = np.argmin(np.abs(self.norm.boundaries - data))
1287+
neigh_idx = max(0, cur_idx - 1)
1288+
# use max diff to prevent delta == 0
1289+
delta = np.diff(
1290+
self.norm.boundaries[neigh_idx:cur_idx + 2]
1291+
).max()
1292+
1293+
else:
1294+
# Midpoints of neighboring color intervals.
1295+
neighbors = self.norm.inverse(
1296+
(int(normed * n) + np.array([0, 1])) / n)
1297+
delta = abs(neighbors - data).max()
12871298
g_sig_digits = cbook._g_sig_digits(data, delta)
12881299
else:
12891300
g_sig_digits = 3 # Consistent with default below.

lib/matplotlib/tests/test_artist.py

+163
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import pytest
77

8+
from matplotlib import cm
9+
import matplotlib.colors as mcolors
810
import matplotlib.pyplot as plt
911
import matplotlib.patches as mpatches
1012
import matplotlib.lines as mlines
@@ -374,3 +376,164 @@ class MyArtist4(MyArtist3):
374376
pass
375377

376378
assert MyArtist4.set is MyArtist3.set
379+
380+
381+
def test_format_cursor_data_BoundaryNorm():
382+
"""Test if cursor data is correct when using BoundaryNorm."""
383+
X = np.empty((3, 3))
384+
X[0, 0] = 0.9
385+
X[0, 1] = 0.99
386+
X[0, 2] = 0.999
387+
X[1, 0] = -1
388+
X[1, 1] = 0
389+
X[1, 2] = 1
390+
X[2, 0] = 0.09
391+
X[2, 1] = 0.009
392+
X[2, 2] = 0.0009
393+
394+
# map range -1..1 to 0..256 in 0.1 steps
395+
fig, ax = plt.subplots()
396+
fig.suptitle("-1..1 to 0..256 in 0.1")
397+
norm = mcolors.BoundaryNorm(np.linspace(-1, 1, 20), 256)
398+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
399+
400+
labels_list = [
401+
"[0.9]",
402+
"[1.]",
403+
"[1.]",
404+
"[-1.0]",
405+
"[0.0]",
406+
"[1.0]",
407+
"[0.09]",
408+
"[0.009]",
409+
"[0.0009]",
410+
]
411+
for v, label in zip(X.flat, labels_list):
412+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.1))
413+
assert img.format_cursor_data(v) == label
414+
415+
plt.close()
416+
417+
# map range -1..1 to 0..256 in 0.01 steps
418+
fig, ax = plt.subplots()
419+
fig.suptitle("-1..1 to 0..256 in 0.01")
420+
cmap = cm.get_cmap('RdBu_r', 200)
421+
norm = mcolors.BoundaryNorm(np.linspace(-1, 1, 200), 200)
422+
img = ax.imshow(X, cmap=cmap, norm=norm)
423+
424+
labels_list = [
425+
"[0.90]",
426+
"[0.99]",
427+
"[1.0]",
428+
"[-1.00]",
429+
"[0.00]",
430+
"[1.00]",
431+
"[0.09]",
432+
"[0.009]",
433+
"[0.0009]",
434+
]
435+
for v, label in zip(X.flat, labels_list):
436+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.01))
437+
assert img.format_cursor_data(v) == label
438+
439+
plt.close()
440+
441+
# map range -1..1 to 0..256 in 0.01 steps
442+
fig, ax = plt.subplots()
443+
fig.suptitle("-1..1 to 0..256 in 0.001")
444+
cmap = cm.get_cmap('RdBu_r', 2000)
445+
norm = mcolors.BoundaryNorm(np.linspace(-1, 1, 2000), 2000)
446+
img = ax.imshow(X, cmap=cmap, norm=norm)
447+
448+
labels_list = [
449+
"[0.900]",
450+
"[0.990]",
451+
"[0.999]",
452+
"[-1.000]",
453+
"[0.000]",
454+
"[1.000]",
455+
"[0.090]",
456+
"[0.009]",
457+
"[0.0009]",
458+
]
459+
for v, label in zip(X.flat, labels_list):
460+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.001))
461+
assert img.format_cursor_data(v) == label
462+
463+
plt.close()
464+
465+
# different testing data set with
466+
# out of bounds values for 0..1 range
467+
X = np.empty((7, 1))
468+
X[0] = -1.0
469+
X[1] = 0.0
470+
X[2] = 0.1
471+
X[3] = 0.5
472+
X[4] = 0.9
473+
X[5] = 1.0
474+
X[6] = 2.0
475+
476+
labels_list = [
477+
"[-1.0]",
478+
"[0.0]",
479+
"[0.1]",
480+
"[0.5]",
481+
"[0.9]",
482+
"[1.0]",
483+
"[2.0]",
484+
]
485+
486+
fig, ax = plt.subplots()
487+
fig.suptitle("noclip, neither")
488+
norm = mcolors.BoundaryNorm(
489+
np.linspace(0, 1, 4, endpoint=True), 256, clip=False, extend='neither')
490+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
491+
for v, label in zip(X.flat, labels_list):
492+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.33))
493+
assert img.format_cursor_data(v) == label
494+
495+
plt.close()
496+
497+
fig, ax = plt.subplots()
498+
fig.suptitle("noclip, min")
499+
norm = mcolors.BoundaryNorm(
500+
np.linspace(0, 1, 4, endpoint=True), 256, clip=False, extend='min')
501+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
502+
for v, label in zip(X.flat, labels_list):
503+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.33))
504+
assert img.format_cursor_data(v) == label
505+
506+
plt.close()
507+
508+
fig, ax = plt.subplots()
509+
fig.suptitle("noclip, max")
510+
norm = mcolors.BoundaryNorm(
511+
np.linspace(0, 1, 4, endpoint=True), 256, clip=False, extend='max')
512+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
513+
for v, label in zip(X.flat, labels_list):
514+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.33))
515+
assert img.format_cursor_data(v) == label
516+
517+
plt.close()
518+
519+
fig, ax = plt.subplots()
520+
fig.suptitle("noclip, both")
521+
norm = mcolors.BoundaryNorm(
522+
np.linspace(0, 1, 4, endpoint=True), 256, clip=False, extend='both')
523+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
524+
for v, label in zip(X.flat, labels_list):
525+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.33))
526+
assert img.format_cursor_data(v) == label
527+
528+
plt.close()
529+
530+
fig, ax = plt.subplots()
531+
fig.suptitle("clip, neither")
532+
norm = mcolors.BoundaryNorm(
533+
np.linspace(0, 1, 4, endpoint=True), 256, clip=True, extend='neither')
534+
img = ax.imshow(X, cmap='RdBu_r', norm=norm)
535+
for v, label in zip(X.flat, labels_list):
536+
# label = "[{:-#.{}g}]".format(v, cbook._g_sig_digits(v, 0.33))
537+
assert img.format_cursor_data(v) == label
538+
539+
plt.close()

0 commit comments

Comments
 (0)