19
19
On top of `.ColorbarBase` this connects the colorbar with a
20
20
`.ScalarMappable` such as an image or contour plot.
21
21
22
- :class:`ColorbarPatch`
23
- A specialized `.Colorbar` to support hatched contour plots.
24
-
25
22
:func:`make_axes`
26
23
Create an `~.axes.Axes` suitable for a colorbar. This functions can be
27
24
used with figures containing a single axes or with freely placed axes.
@@ -472,6 +469,7 @@ def __init__(self, ax, cmap=None,
472
469
self .extendfrac = extendfrac
473
470
self .extendrect = extendrect
474
471
self .solids = None
472
+ self .solids_patches = []
475
473
self .lines = []
476
474
477
475
for spine in ax .spines .values ():
@@ -531,14 +529,13 @@ def draw_all(self):
531
529
# Set self.vmin and self.vmax to first and last boundary, excluding
532
530
# extensions.
533
531
self .vmin , self .vmax = self ._boundaries [self ._inside ][[0 , - 1 ]]
534
- # Compute the X/Y mesh, assuming vertical orientation .
532
+ # Compute the X/Y mesh.
535
533
X , Y = self ._mesh ()
536
534
# Extract bounding polygon (the last entry's value (X[0, 1]) doesn't
537
535
# matter, it just matches the CLOSEPOLY code).
538
536
x = np .concatenate ([X [[0 , 1 , - 2 , - 1 ], 0 ], X [[- 1 , - 2 , 1 , 0 , 0 ], 1 ]])
539
537
y = np .concatenate ([Y [[0 , 1 , - 2 , - 1 ], 0 ], Y [[- 1 , - 2 , 1 , 0 , 0 ], 1 ]])
540
- xy = (np .column_stack ([x , y ]) if self .orientation == 'vertical' else
541
- np .column_stack ([y , x ])) # Apply orientation.
538
+ xy = np .column_stack ([x , y ])
542
539
# Configure axes limits, patch, and outline.
543
540
xmin , ymin = xy .min (axis = 0 )
544
541
xmax , ymax = xy .max (axis = 0 )
@@ -798,42 +795,52 @@ def set_label(self, label, *, loc=None, **kwargs):
798
795
799
796
def _edges (self , X , Y ):
800
797
"""Return the separator line segments; helper for _add_solids."""
801
- N = X .shape [0 ]
802
798
# Using the non-array form of these line segments is much
803
799
# simpler than making them into arrays.
804
- if self .orientation == 'vertical' :
805
- return [list (zip (X [i ], Y [i ])) for i in range (1 , N - 1 )]
806
- else :
807
- return [list (zip (Y [i ], X [i ])) for i in range (1 , N - 1 )]
800
+ return [list (zip (X [i ], Y [i ])) for i in range (1 , len (X ) - 1 )]
808
801
809
802
def _add_solids (self , X , Y , C ):
810
- """
811
- Draw the colors using `~.axes.Axes.pcolormesh`;
812
- optionally add separators.
813
- """
814
- if self .orientation == 'vertical' :
815
- args = (X , Y , C )
816
- else :
817
- args = (np .transpose (Y ), np .transpose (X ), np .transpose (C ))
818
- kw = dict (cmap = self .cmap ,
819
- norm = self .norm ,
820
- alpha = self .alpha ,
821
- edgecolors = 'None' )
822
- _log .debug ('Setting pcolormesh' )
823
- col = self .ax .pcolormesh (* args , ** kw , shading = 'flat' )
824
- # self.add_observer(col) # We should observe, not be observed...
825
-
803
+ """Draw the colors; optionally add separators."""
804
+ # Cleanup previously set artists.
826
805
if self .solids is not None :
827
806
self .solids .remove ()
828
- self .solids = col
829
-
830
- if self .drawedges :
831
- self .dividers .set_segments (self ._edges (X , Y ))
807
+ for solid in self .solids_patches :
808
+ solid .remove ()
809
+ # Add new artist(s), based on mappable type. Use individual patches if
810
+ # hatching is needed, pcolormesh otherwise.
811
+ mappable = getattr (self , 'mappable' , None )
812
+ if (isinstance (mappable , contour .ContourSet )
813
+ and any (hatch is not None for hatch in mappable .hatches )):
814
+ self ._add_solids_patches (X , Y , C , mappable )
832
815
else :
833
- self .dividers .set_segments ([])
816
+ self ._add_solids_pcolormesh (X , Y , C )
817
+ self .dividers .set_segments (self ._edges (X , Y ) if self .drawedges else [])
818
+
819
+ def _add_solids_pcolormesh (self , X , Y , C ):
820
+ _log .debug ('Setting pcolormesh' )
821
+ self .solids = self .ax .pcolormesh (
822
+ X , Y , C , cmap = self .cmap , norm = self .norm , alpha = self .alpha ,
823
+ edgecolors = 'none' , shading = 'flat' )
824
+ if not self .drawedges :
834
825
if len (self ._y ) >= self .n_rasterize :
835
826
self .solids .set_rasterized (True )
836
827
828
+ def _add_solids_patches (self , X , Y , C , mappable ):
829
+ hatches = mappable .hatches * len (C ) # Have enough hatches.
830
+ patches = []
831
+ for i in range (len (X ) - 1 ):
832
+ xy = np .array ([[X [i , 0 ], Y [i , 0 ]],
833
+ [X [i , 1 ], Y [i , 0 ]],
834
+ [X [i + 1 , 1 ], Y [i + 1 , 0 ]],
835
+ [X [i + 1 , 0 ], Y [i + 1 , 1 ]]])
836
+ patch = mpatches .PathPatch (mpath .Path (xy ),
837
+ facecolor = self .cmap (self .norm (C [i ][0 ])),
838
+ hatch = hatches [i ], linewidth = 0 ,
839
+ antialiased = False , alpha = self .alpha )
840
+ self .ax .add_patch (patch )
841
+ patches .append (patch )
842
+ self .solids_patches = patches
843
+
837
844
def add_lines (self , levels , colors , linewidths , erase = True ):
838
845
"""
839
846
Draw lines on the colorbar.
@@ -1082,11 +1089,10 @@ def _proportional_y(self):
1082
1089
1083
1090
def _mesh (self ):
1084
1091
"""
1085
- Return ``(X, Y)``, the coordinate arrays for the colorbar pcolormesh.
1086
- These are suitable for a vertical colorbar; swapping and transposition
1087
- for a horizontal colorbar are done outside this function.
1092
+ Return the coordinate arrays for the colorbar pcolormesh/patches.
1088
1093
1089
- These are scaled between vmin and vmax.
1094
+ These are scaled between vmin and vmax, and already handle colorbar
1095
+ orientation.
1090
1096
"""
1091
1097
# copy the norm and change the vmin and vmax to the vmin and
1092
1098
# vmax of the colorbar, not the norm. This allows the situation
@@ -1119,7 +1125,7 @@ def _mesh(self):
1119
1125
X [0 , :] = xmid
1120
1126
if self ._extend_upper () and not self .extendrect :
1121
1127
X [- 1 , :] = xmid
1122
- return X , Y
1128
+ return ( X , Y ) if self . orientation == 'vertical' else ( Y , X )
1123
1129
1124
1130
def _locate (self , x ):
1125
1131
"""
@@ -1213,6 +1219,10 @@ def __init__(self, ax, mappable, **kwargs):
1213
1219
_add_disjoint_kwargs (kwargs , alpha = mappable .get_alpha ())
1214
1220
super ().__init__ (ax , ** kwargs )
1215
1221
1222
+ mappable .colorbar = self
1223
+ mappable .colorbar_cid = mappable .callbacksSM .connect (
1224
+ 'changed' , self .update_normal )
1225
+
1216
1226
@cbook .deprecated ("3.3" , alternative = "update_normal" )
1217
1227
def on_mappable_changed (self , mappable ):
1218
1228
"""
@@ -1548,61 +1558,12 @@ def make_axes_gridspec(parent, *, location=None, orientation=None,
1548
1558
return cax , kw
1549
1559
1550
1560
1561
+ @cbook .deprecated ("3.4" , alternative = "Colorbar" )
1551
1562
class ColorbarPatch (Colorbar ):
1552
- """
1553
- A Colorbar that uses a list of `~.patches.Patch` instances rather than the
1554
- default `~.collections.PatchCollection` created by `~.axes.Axes.pcolor`,
1555
- because the latter does not allow the hatch pattern to vary among the
1556
- members of the collection.
1557
- """
1558
-
1559
- def __init__ (self , ax , mappable , ** kw ):
1560
- # we do not want to override the behaviour of solids
1561
- # so add a new attribute which will be a list of the
1562
- # colored patches in the colorbar
1563
- self .solids_patches = []
1564
- super ().__init__ (ax , mappable , ** kw )
1565
-
1566
- def _add_solids (self , X , Y , C ):
1567
- """
1568
- Draw the colors using `~matplotlib.patches.Patch`;
1569
- optionally add separators.
1570
- """
1571
- n_segments = len (C )
1572
-
1573
- # ensure there are sufficient hatches
1574
- hatches = self .mappable .hatches * n_segments
1575
-
1576
- patches = []
1577
- for i in range (len (X ) - 1 ):
1578
- val = C [i ][0 ]
1579
- hatch = hatches [i ]
1580
-
1581
- xy = np .array ([[X [i ][0 ], Y [i ][0 ]],
1582
- [X [i ][1 ], Y [i ][0 ]],
1583
- [X [i + 1 ][1 ], Y [i + 1 ][0 ]],
1584
- [X [i + 1 ][0 ], Y [i + 1 ][1 ]]])
1585
-
1586
- if self .orientation == 'horizontal' :
1587
- # if horizontal swap the xs and ys
1588
- xy = xy [..., ::- 1 ]
1589
-
1590
- patch = mpatches .PathPatch (mpath .Path (xy ),
1591
- facecolor = self .cmap (self .norm (val )),
1592
- hatch = hatch , linewidth = 0 ,
1593
- antialiased = False , alpha = self .alpha )
1594
- self .ax .add_patch (patch )
1595
- patches .append (patch )
1596
-
1597
- if self .solids_patches :
1598
- for solid in self .solids_patches :
1599
- solid .remove ()
1600
-
1601
- self .solids_patches = patches
1602
-
1603
- self .dividers .set_segments (self ._edges (X , Y ) if self .drawedges else [])
1563
+ pass
1604
1564
1605
1565
1566
+ @cbook .deprecated ("3.4" , alternative = "Colorbar" )
1606
1567
def colorbar_factory (cax , mappable , ** kwargs ):
1607
1568
"""
1608
1569
Create a colorbar on the given axes for the given mappable.
@@ -1624,20 +1585,7 @@ def colorbar_factory(cax, mappable, **kwargs):
1624
1585
1625
1586
Returns
1626
1587
-------
1627
- `.Colorbar` or `.ColorbarPatch`
1628
- The created colorbar instance. `.ColorbarPatch` is only used if
1629
- *mappable* is a `.ContourSet` with hatches.
1588
+ `.Colorbar`
1589
+ The created colorbar instance.
1630
1590
"""
1631
- # if the given mappable is a contourset with any hatching, use
1632
- # ColorbarPatch else use Colorbar
1633
- if (isinstance (mappable , contour .ContourSet )
1634
- and any (hatch is not None for hatch in mappable .hatches )):
1635
- cb = ColorbarPatch (cax , mappable , ** kwargs )
1636
- else :
1637
- cb = Colorbar (cax , mappable , ** kwargs )
1638
-
1639
- cid = mappable .callbacksSM .connect ('changed' , cb .update_normal )
1640
- mappable .colorbar = cb
1641
- mappable .colorbar_cid = cid
1642
-
1643
- return cb
1591
+ return Colorbar (cax , mappable , ** kwargs )
0 commit comments