17
17
import matplotlib .font_manager as font_manager
18
18
import matplotlib .text as text
19
19
import matplotlib .cbook as cbook
20
- import matplotlib .mathtext as mathtext
21
20
import matplotlib .patches as mpatches
22
- import matplotlib .texmanager as texmanager
23
21
import matplotlib .transforms as mtransforms
24
22
25
23
# Import needed for adding manual selection capability to clabel
@@ -51,7 +49,7 @@ class ContourLabeler:
51
49
"""Mixin to provide labelling capability to `.ContourSet`."""
52
50
53
51
def clabel (self , levels = None , * ,
54
- fontsize = None , inline = True , inline_spacing = 5 , fmt = '%1.3f' ,
52
+ fontsize = None , inline = True , inline_spacing = 5 , fmt = None ,
55
53
colors = None , use_clabeltext = False , manual = False ,
56
54
rightside_up = True , zorder = None ):
57
55
"""
@@ -92,14 +90,17 @@ def clabel(self, levels=None, *,
92
90
This spacing will be exact for labels at locations where the
93
91
contour is straight, less so for labels on curved contours.
94
92
95
- fmt : str or dict, default: '%1.3f'
96
- A format string for the label.
93
+ fmt : `.Formatter` or str or callable or dict, optional
94
+ How the levels are formatted:
97
95
98
- Alternatively, this can be a dictionary matching contour levels
99
- with arbitrary strings to use for each contour level (i.e.,
100
- fmt[level]=string), or it can be any callable, such as a
101
- `.Formatter` instance, that returns a string when called with a
102
- numeric contour level.
96
+ - If a `.Formatter`, it is used to format all levels at once, using
97
+ its `.Formatter.format_ticks` method.
98
+ - If a str, it is interpreted as a %-style format string.
99
+ - If a callable, it is called with one level at a time and should
100
+ return the corresponding label.
101
+ - If a dict, it should directly map levels to labels.
102
+
103
+ The default is to use a standard `.ScalarFormatter`.
103
104
104
105
manual : bool or iterable, default: False
105
106
If ``True``, contour labels will be placed manually using
@@ -144,6 +145,9 @@ def clabel(self, levels=None, *,
144
145
# labels method (case of automatic label placement) or
145
146
# `BlockingContourLabeler` (case of manual label placement).
146
147
148
+ if fmt is None :
149
+ fmt = ticker .ScalarFormatter (useOffset = False )
150
+ fmt .create_dummy_axis ()
147
151
self .labelFmt = fmt
148
152
self ._use_clabeltext = use_clabeltext
149
153
# Detect if manual selection is desired and remove from argument list.
@@ -243,20 +247,12 @@ def get_label_width(self, lev, fmt, fsize):
243
247
"""
244
248
if not isinstance (lev , str ):
245
249
lev = self .get_text (lev , fmt )
246
- lev , ismath = text .Text ()._preprocess_math (lev )
247
- if ismath == 'TeX' :
248
- lw , _ , _ = (texmanager .TexManager ()
249
- .get_text_width_height_descent (lev , fsize ))
250
- elif ismath :
251
- if not hasattr (self , '_mathtext_parser' ):
252
- self ._mathtext_parser = mathtext .MathTextParser ('agg' )
253
- _ , _ , _ , _ , _ , img , _ = self ._mathtext_parser .parse (
254
- lev , dpi = 72 , prop = self .labelFontProps )
255
- _ , lw = np .shape (img ) # at dpi=72, the units are PostScript points
256
- else :
257
- # width is much less than "font size"
258
- lw = len (lev ) * fsize * 0.6
259
- return lw
250
+ fig = self .axes .figure
251
+ width = (text .Text (0 , 0 , lev , figure = fig ,
252
+ size = fsize , fontproperties = self .labelFontProps )
253
+ .get_window_extent (fig .canvas .get_renderer ()).width )
254
+ width *= 72 / fig .dpi
255
+ return width
260
256
261
257
def set_label_props (self , label , text , color ):
262
258
"""Set the label properties - color, fontsize, text."""
@@ -269,13 +265,14 @@ def get_text(self, lev, fmt):
269
265
"""Get the text of the label."""
270
266
if isinstance (lev , str ):
271
267
return lev
268
+ elif isinstance (fmt , dict ):
269
+ return fmt .get (lev , '%1.3f' )
270
+ elif callable (getattr (fmt , "format_ticks" , None )):
271
+ return fmt .format_ticks ([* self .labelLevelList , lev ])[- 1 ]
272
+ elif callable (fmt ):
273
+ return fmt (lev )
272
274
else :
273
- if isinstance (fmt , dict ):
274
- return fmt .get (lev , '%1.3f' )
275
- elif callable (fmt ):
276
- return fmt (lev )
277
- else :
278
- return fmt % lev
275
+ return fmt % lev
279
276
280
277
def locate_label (self , linecontour , labelwidth ):
281
278
"""
@@ -564,23 +561,14 @@ def labels(self, inline, inline_spacing):
564
561
paths = con .get_paths ()
565
562
for segNum , linepath in enumerate (paths ):
566
563
lc = linepath .vertices # Line contour
567
- slc0 = trans .transform (lc ) # Line contour in screen coords
568
-
569
- # For closed polygons, add extra point to avoid division by
570
- # zero in print_label and locate_label. Other than these
571
- # functions, this is not necessary and should probably be
572
- # eventually removed.
573
- if _is_closed_polygon (lc ):
574
- slc = np .row_stack ([slc0 , slc0 [1 :2 ]])
575
- else :
576
- slc = slc0
564
+ slc = trans .transform (lc ) # Line contour in screen coords
577
565
578
566
# Check if long enough for a label
579
567
if self .print_label (slc , lw ):
580
568
x , y , ind = self .locate_label (slc , lw )
581
569
582
570
rotation , new = self .calc_label_rot_and_inline (
583
- slc0 , ind , lw , lc if inline else None , inline_spacing )
571
+ slc , ind , lw , lc if inline else None , inline_spacing )
584
572
585
573
# Actually add the label
586
574
add_label (x , y , rotation , lev , cvalue )
0 commit comments