1919 On top of `.ColorbarBase` this connects the colorbar with a
2020 `.ScalarMappable` such as an image or contour plot.
2121
22- :class:`ColorbarPatch`
23- A specialized `.Colorbar` to support hatched contour plots.
24-
2522:func:`make_axes`
2623 Create an `~.axes.Axes` suitable for a colorbar. This functions can be
2724 used with figures containing a single axes or with freely placed axes.
@@ -472,6 +469,7 @@ def __init__(self, ax, cmap=None,
472469 self .extendfrac = extendfrac
473470 self .extendrect = extendrect
474471 self .solids = None
472+ self .solids_patches = []
475473 self .lines = []
476474
477475 for spine in ax .spines .values ():
@@ -531,14 +529,13 @@ def draw_all(self):
531529 # Set self.vmin and self.vmax to first and last boundary, excluding
532530 # extensions.
533531 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.
535533 X , Y = self ._mesh ()
536534 # Extract bounding polygon (the last entry's value (X[0, 1]) doesn't
537535 # matter, it just matches the CLOSEPOLY code).
538536 x = np .concatenate ([X [[0 , 1 , - 2 , - 1 ], 0 ], X [[- 1 , - 2 , 1 , 0 , 0 ], 1 ]])
539537 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 ])
542539 # Configure axes limits, patch, and outline.
543540 xmin , ymin = xy .min (axis = 0 )
544541 xmax , ymax = xy .max (axis = 0 )
@@ -798,42 +795,52 @@ def set_label(self, label, *, loc=None, **kwargs):
798795
799796 def _edges (self , X , Y ):
800797 """Return the separator line segments; helper for _add_solids."""
801- N = X .shape [0 ]
802798 # Using the non-array form of these line segments is much
803799 # 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 )]
808801
809802 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.
826805 if self .solids is not None :
827806 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 )
832815 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 :
834825 if len (self ._y ) >= self .n_rasterize :
835826 self .solids .set_rasterized (True )
836827
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+
837844 def add_lines (self , levels , colors , linewidths , erase = True ):
838845 """
839846 Draw lines on the colorbar.
@@ -1082,11 +1089,10 @@ def _proportional_y(self):
10821089
10831090 def _mesh (self ):
10841091 """
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.
10881093
1089- These are scaled between vmin and vmax.
1094+ These are scaled between vmin and vmax, and already handle colorbar
1095+ orientation.
10901096 """
10911097 # copy the norm and change the vmin and vmax to the vmin and
10921098 # vmax of the colorbar, not the norm. This allows the situation
@@ -1119,7 +1125,7 @@ def _mesh(self):
11191125 X [0 , :] = xmid
11201126 if self ._extend_upper () and not self .extendrect :
11211127 X [- 1 , :] = xmid
1122- return X , Y
1128+ return ( X , Y ) if self . orientation == 'vertical' else ( Y , X )
11231129
11241130 def _locate (self , x ):
11251131 """
@@ -1213,6 +1219,10 @@ def __init__(self, ax, mappable, **kwargs):
12131219 _add_disjoint_kwargs (kwargs , alpha = mappable .get_alpha ())
12141220 super ().__init__ (ax , ** kwargs )
12151221
1222+ mappable .colorbar = self
1223+ mappable .colorbar_cid = mappable .callbacksSM .connect (
1224+ 'changed' , self .update_normal )
1225+
12161226 @cbook .deprecated ("3.3" , alternative = "update_normal" )
12171227 def on_mappable_changed (self , mappable ):
12181228 """
@@ -1548,61 +1558,12 @@ def make_axes_gridspec(parent, *, location=None, orientation=None,
15481558 return cax , kw
15491559
15501560
1561+ @cbook .deprecated ("3.4" , alternative = "Colorbar" )
15511562class 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
16041564
16051565
1566+ @cbook .deprecated ("3.4" , alternative = "Colorbar" )
16061567def colorbar_factory (cax , mappable , ** kwargs ):
16071568 """
16081569 Create a colorbar on the given axes for the given mappable.
@@ -1624,20 +1585,7 @@ def colorbar_factory(cax, mappable, **kwargs):
16241585
16251586 Returns
16261587 -------
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.
16301590 """
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