Repository: matplotlib/matplotlib
File: galleries/users_explain/colors/colormapnorms.py (Python)

Code before:
terrain_map = colors.LinearSegmentedColormap.from_list(
    'terrain_map', all_colors)

# make the norm:  Note the center is offset so that the land has more
# dynamic range:
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                    cmap=terrain_map, shading='auto')
# Simple geographic plot, set aspect ratio because distance between lines of
# longitude depends on latitude.
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
ax.set_title('TwoSlopeNorm(x)')
cb = fig.colorbar(pcm, shrink=0.6)
cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000])
plt.show()

# %%
# Colorbar scale for norms with a scale
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# By default, the colorbar for norms with a ``scale`` will use that scale
# for the colorbar as well. For `.TwoSlopeNorm`, this means the colorbar
# will be centered at the midpoint visually. You can override this to get
# linear spacing by calling ``cb.ax.set_yscale('linear')`` after creating
# the colorbar.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))

# Left plot: Default scaled colorbar (centered at midpoint)
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)
pcm1 = ax1.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                      cmap=terrain_map, shading='auto')
ax1.set_aspect(1 / np.cos(np.deg2rad(49)))
ax1.set_title('Default: Scaled colorbar')
cb1 = fig.colorbar(pcm1, ax=ax1, shrink=0.6)
cb1.set_ticks([-500, 0, 1000, 2000, 3000, 4000])

# Right plot: Linear colorbar spacing
pcm2 = ax2.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                      cmap=terrain_map, shading='auto')
ax2.set_aspect(1 / np.cos(np.deg2rad(49)))
ax2.set_title('Linear colorbar spacing')
cb2 = fig.colorbar(pcm2, ax=ax2, shrink=0.6)
cb2.ax.set_yscale('linear')  # Set linear scale for colorbar
cb2.set_ticks([-500, 0, 1000, 2000, 3000, 4000])

plt.show()

# %%
# FuncNorm: Arbitrary function normalization

Review comment (suggestion):
While this is technically applicable to any "norm with a scale". I would phrase it specifically to TwoSlopeNorm only for the following reasons:
- AFAICS "norm with a scale" is not a public concept but an implementation detail. - We do not document what that means and there is no public API to find out whether a Norm has a scale.
- For most norms, it is the canonically expected behavior that the colorbar reflects the nonlinear scale (e.g. for log scales it is the very purpose to redistribute data density). I would say, TwoSlopeNorm is an exception here where both linear and nonlinear scaling can be reasonable.

```suggestion
# %%
# Using a linear scale on the colormap
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# By default, the colorbar for a `.TwoSlopeNorm` is divided into two equal
# parts for the two branches. As a result, the scaling in the two segments
# is different, i.e. the screen-space per data range. You can override this
# to get linear scaling by calling ``cb.ax.set_yscale('linear')``. This
# redistributes the colors and values on the colorbar, but leaves the
# color-to-value mapping unchanged.
```


Code after:
terrain_map = colors.LinearSegmentedColormap.from_list(
    'terrain_map', all_colors)

# make the norm:  Note the center is offset so that the land has more
# dynamic range:
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                    cmap=terrain_map, shading='auto')
# Simple geographic plot, set aspect ratio because distance between lines of
# longitude depends on latitude.
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
ax.set_title('TwoSlopeNorm(x)')
cb = fig.colorbar(pcm, shrink=0.6)
cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000])
plt.show()

# %%
# Using a linear scale on the colormap
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# By default, colorbars adopt the same axis scaling as their associated norm.
# For example, for a `.TwoSlopeNorm`, colormap segments are distributed
# linearly and the colorbar ticks positions are spaced non-linearly (as above,
# and the left-hand colorbar below). To make the tick spacing linear instead,
# you can change the scale by calling ``cb.ax.set_yscale('linear')``, as shown
# in the right-hand colorbar below. The ticks will then be evenly spaced, the
# colormap will appear compressed in the smaller of the two slope regions.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)

for ax, title in zip([ax1, ax2],
                     ['Default: Scaled colorbar', 'Linear colorbar spacing']):
    pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                        cmap=terrain_map, shading='auto')
    ax.set_aspect(1 / np.cos(np.deg2rad(49)))
    ax.set_title(title)
    cb = fig.colorbar(pcm, ax=ax, shrink=0.6)
    cb.set_ticks(np.arange(-500, 4001, 500))

# Set linear scale for the right colorbar
cb.ax.set_yscale('linear')

# %%
# FuncNorm: Arbitrary function normalization
# ------------------------------------------
#
# If the above norms do not provide the normalization you want, you can use
# `~.colors.FuncNorm` to define your own.  Note that this example is the same
# as `~.colors.PowerNorm` with a power of 0.5: