Question: I am trying to create a custom colorbar with discrete intervals using this resource (https://matplotlib.org/3.1.1/tutorials/colors/colorbar_only.html), but I am running into this error which references my 'cb2' line in my code:  Error: "AttributeError: 'GeoContourSet' object has no attribute 'set'"  import xarray as xr from sklearn.linear_model import LinearRegression import proplot as pplt import cartopy as ct import matplotlib as mpl import matplotlib.pyplot as plt import colormaps as cmaps import matplotlib.colors as colors fig, axs = pplt.subplots(ncols = 2, nrows = 1, axwidth = 7, proj='pcarree') ax1, ax2 = axs a = ax1.contourf(era_cape['lon'], era_cape['lat'], regression, add_colorbar = False, extend = 'both') axs.format(coast=True, latlim = (20,51), lonlim = (234,293), innerborders = True) axs.add_feature(ct.feature.OCEAN, zorder=100, edgecolor='k', color = 'white') axs.add_feature(ct.feature.COASTLINE, zorder=100, edgecolor='k') cmap1 = mpl.colors.ListedColormap(['purple','navy','slateblue','blue','skyblue','lightblue','aliceblue','yellow','gold','orange','orangered','red','firebrick','darksalmon']) bounds = [-600, -480, -320, -160, -80, -40, -20, 0, 20, 40, 80, 160, 320, 480, 600] norm = mpl.colors.BoundaryNorm(bounds, cmap1.N, extend = 'neither') cb2 = mpl.colorbar.ColorbarBase(a, cmap = cmap1, norm = norm, ticks = bounds, spacing = 'uniform')  In this example, I am relatively new to coding and I am not sure if there is anything I need to do to my data to get it to 'fit' into my custom colorbar. If I I changed the cb2 line to read: cb2 = fig.colorbar(a, cmap = cmap, norm = norm, ticks = bounds)  This gives me a colorbar but it is set to an old, cmap (RdBu) rather than the new one I have created. The ticks are visible but only go up about 1/4 way on the colorbar. If I change the line back to cb2 = mpl.colorbar.ColorbarBase()  I get the attribute error mentioned above again. In this example, era_cape['lat'] and era_cape['lon'] is 2D data over the United States with data spanning from roughly -600 to 600. {'coords': {'lon': {'dims': ('lon',), 'attrs': {'units': 'degrees_east', 'short_name': 'lon', 'long_name': 'longitude'}, 'data': [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.25, 4.5, 4.75, 5.0, 5.25, 5.5, 5.75, 6.0, 6.25, 6.5, 6.75, 7.0, 7.25, 7.5, 7.75, 8.0, 8.25, 8.5, 8.75, 9.0, 9.25, 9.5, 9.75]}, 'lat': {'dims': ('lat',), 'attrs': {'qualifier': 'Gaussian', 'units': 'degrees_north', 'short_name': 'lat', 'long_name': 'latitude'}, 'data': [90.0, 89.75, 89.5, 89.25, 89.0, 88.75, 88.5, 88.25, 88.0, 87.75, 87.5, 87.25, 87.0, 86.75, 86.5, 86.25, 86.0, 85.75, 85.5, 85.25, 85.0, 84.75, 84.5, 84.25, 84.0, 83.75, 83.5, 83.25, 83.0, 82.75, 82.5, 82.25, 82.0, 81.75, 81.5, 81.25, 81.0, 80.75, 80.5, 80.25]}}, 'attrs': {'title': 'p_cal_daily2monthly_era5.ncl', 'program' 

Answer: There's a lot going on in your sample code, and without data to replicate the error it requires some guessing. So perhaps you can simplify it a little and use toy data (or publicly available otherwise)? I suspect the error comes from the fact that you pass the result from ax.contourf to mpl.colorbar.ColorBase. The latter expects an axes object as the argument, and that should be a dedicated axes for the colorbar, not the axes of you actual plot. But instead of an axes, you provide the result of contourf (a GeoContourSet), which is not appropriate and I'm guessing triggers the error you experienced. Often more high-level functions are used to avoid having to create a colorbar axes (cax) explicitly (see below). I'm not familiar with proplot myself, so in the example below I have removed it for simplicity. I don't think that matters in the end and you could probably add it back in for the functionality that you need. A simplified example, focusing on the colorbar specifically, is shown below: import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np # generate toy data, example data from: # https://matplotlib.org/stable/gallery/images_contours_and_fields/contourf_demo.html x = y = np.arange(-3.0, 3.01, 0.025) X, Y = np.meshgrid(x, y) Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) Z = (Z1 - Z2) * 2 # scale toy example data to global lon = x * 180/3 lat = y * 90/3 regression = Z * 300 # define the colormap and scaling cmap = mpl.colors.ListedColormap([ 'purple','navy','slateblue','blue','skyblue','lightblue', 'aliceblue', 'yellow','gold','orange','orangered','red','firebrick','darksalmon', ]) bounds = [-600, -480, -320, -160, -80, -40, -20, 0, 20, 40, 80, 160, 320, 480, 600] norm = mpl.colors.BoundaryNorm(bounds, cmap.N) # create the figure and axes fig, ax = plt.subplots( 1,1, figsize=(8,5), facecolor="w", layout="compressed", subplot_kw=dict(projection=ccrs.PlateCarree()), ) # plot the contours, note that "cf" is also a mappable that could # be used instead of "im" below cf = ax.contourf( lon, lat, regression, cmap=cmap, norm=norm, transform=ccrs.PlateCarree(), ) # create the colorbar im = mpl.cm.ScalarMappable(cmap=cmap, norm=norm) cb = fig.colorbar( im, ax=ax, ticks=bounds, spacing="uniform", orientation="horizontal", shrink=0.8, ) ax.add_feature(cfeature.OCEAN, ec="k", fc="w", zorder=100) ax.add_feature(cfeature.COASTLINE, ec="k", zorder=100)  Which results in:  The use of fig.colorbar allows you to pass your main axes (for plotting), and makes Matplotlib automatically create a reasonable colorbar axes for you. You can also provide that axes yourself (cax=cax), but that's mainly useful if you need more control on the specific location for example, like placing it in/over your main axes.