pgfplots with nonlinearly scaled color maps

This here is a question which came on the pgfplots mailing list; I answer it here since this allows an answer of higher quality.

I have a picture that uses a diverging color map.

In this case, minimum and maximum don’t have the same absolute value (but -0.2 and +0.5 instead)

I want to be able to do a "centered color map", where “0” is the middle color, all points >0 use the upper half of the map, and all <0 the lower half.

The color bar should be skewed according to the real values (i.e. the lower half of the map should take up 2/7 of the bar,a nd the upper half the remaining 5/7)

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.9}

\begin{document}
\begin{tikzpicture}

\begin{axis}[
enlargelimits=false,
% I want the color to be distributed in a nonlinear way, not like this
% I want the tick labels to reflect the centered colorbar
colorbar,
]
\end{axis}
\end{tikzpicture}

\end{document}


maybe a point meta center=[auto,] key, with auto meaning a calculated value of (point meta max + point meta min) ÷ 2

It is possible to scale the point meta. Naturally, this will also scale the colorbar and its axis descriptions. But since a colorbar is actually nothing but a normal axis, we can defined custom transformations to "undo" the effect.

The following code defines a new style nonlinear colormap around 0={<min>}{<max>} which rescales the point meta (assuming that it would have been the y coordinate by default). It also rescales the colorbar in a non-linear way to restore the correct descriptions:

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.9}

\pgfplotsset{
% this transformation ensures that every input argument is
% transformed from -0.2 : 0.5 -> -0.5,0.5
% and every tick label is transformed back:
nonlinear colormap trafo/.code 2 args={
\def\nonlinearscalefactor{((#2)/(#1))}%
\pgfkeysalso{%
y coord trafo/.code={%
\pgfmathparse{##1 < 0 ? -1*##1*\nonlinearscalefactor : ##1}%
},
y coord inv trafo/.code={%
\pgfmathparse{##1 < 0 ? -1*##1/\nonlinearscalefactor : ##1}%
},
}%
},
nonlinear colormap around 0/.code 2 args={
\def\nonlinearscalefactor{((#2)/(#1))}%
\pgfkeysalso{
colorbar style={
nonlinear colormap trafo={#1}{#2},
%
% OVERRIDE this here. The value is *only* used to
% generate a nice axis, it does not affect the data.
% Note that these values will be mapped through the
% colormap trafo as defined above.
point meta min={#1},
point meta max={#2},
},
%
% this here is how point meta is computed for the plot.
% It means that a point meta of -0.2 will actually become -0.5
% Thus, the *real* point meta min is -0.5... but we
% override it above.
point meta={y < 0 ? -y*\nonlinearscalefactor : y},
}%
},
}

\begin{document}
\begin{tikzpicture}

\begin{axis}[
enlargelimits=false,
colorbar,
%
% activate the nonlinear colormap:
nonlinear colormap around 0={-0.2}{0.5},
%
% reconfigure it - the default yticks are typically unsuitable
% (because they are chosen in a linear way)
colorbar style={
ytick={-0.2,-0.1,0,0.25,0.5},
},
]