# cptcmap

## PURPOSE

CPTCMAP Apply a .cpt file as colormap to an axis

## SYNOPSIS

function varargout = cptcmap(varargin)

## DESCRIPTION

    CPTCMAP Apply a .cpt file as colormap to an axis

     cptcmap(name);
     cptcmap(name, ax);
     cptcmap(... param, val, ...);
     [cmap, lims, ticks, bfncol, ctable] = cptcmap(...)

     This function creates and applies a colormap defined in a color palette
     table (.cpt file).  For a full description of the cpt file format, see
     the Generic Mapping Tools documentation (http://gmt.soest.hawaii.edu/).
     Color palette files provide more flexible colormapping than Matlab's
     default schemes, including both discrete and continuous gradients, as
     well as easier direct color mapping.

     Limitations: X11 color names not supported, patterns not supported, CMYK
     not supported yet

     Input variables:

       name:       .cpt file name.  You may either specify either the full
                   path, or just the file name.  In the latter case, the
                   function will look for the file in the folder specified by
                   the cptpath variable in the first line of code; by default
                   this folder is located in the same location as cptcmap.m
                   and is called cptfiles.

       ax:         handle of axis or axes where colormap should be applied
                   (colormaps will effect the entire figure(s), but axis clim
                   adjustments for direct scaling will only affect the
                   specified axes).  If no axis is specified and no output
                   variables are supplied, colormap will be applied to the
                   current axis.  If no axis is specified and output variables
                   are supplied, the colormap will not be applied to any axes.

       'showall':  When this option is used, a figure is created displaying
                   colorbars for all colormaps contained in the .cpt folder.
                   Color limits of each colormap are listed along with the
                   names of each.  A small tick mark indicates the location of
                   0, where applicable.  NOTE: the number of columns to use
                   for display is hard-coded.  As you start collecting more
                   color palettes, the figure may get too cluttered and you
                   may have to adjust this (variable ncol is the plotcmaps
                   subfunction).

     Optional input variables (passed as parameter/value pairs):

       'mapping':  'scaled' or 'direct'.  Scaled mapping spreads the colormap
                   to cover the color limits of the figure.  Direct mapping
                   resets the color limits of the axes so that colors are
                   mapped to the levels specified by the .cpt file. ['scaled']

       'ncol':     number of colors in final colormap. If not included or NaN,
                   this function will try to choose the fewest number of
                   blocks needed to display the colormap as accurately as
                   possible. I have arbitrarily chosen that it will not try to
                   create more than 256 colors in the final colormap when
                   using this automatic scheme.  However, you can manually set
                   ncol higher if necessary to resolve all sharp breaks and
                   gradients in the colormap.

       'flip':     if true, reverse the colormap order [false]

     Output variables:

       cmap:       ncol x 3 colormap array

       lims:       1 x 2 array holding minimum and maximum values for which
                   the colormap is defined.

       ticks:      vector of tick values specifying where colors were defined
                   in the original file

       bfncol:     3 x 3 colormap array specifying the colors defined for
                   background (values lower than lowest color limit),
                   foreground (values higher than highest color limit), and
                   NaN values.  These do not affect the resulting colormap,
                   but can be applied by the user to replicate the behavior
                   seen in GMT.

       ctable:     n x 8 color palette table, translated to Matlab color
                   space. Column 1 holds the lower limit of each color cell,
                   columns 2-4 the RGB values corresponding to the lower
                   limit, column 5 the upper limit of the color cell, and
                   columns 6-8 the RGB values of the upper limit.  When the
                   lower and upper colors are the same, this defines a
                   solid-colored cell; when they are different, colors are
                   linearly interpolated between the endpoints.

     Example:

       [lat, lon, z] = satbath(10);
       pcolor(lon, lat, z);
       shading flat;
       cptcmap('GMT_globe', 'mapping', 'direct');
       colorbar;

## CROSS-REFERENCE INFORMATION

This function calls:

- cptcmap CPTCMAP Apply a .cpt file as colormap to an axis

This function is called by:

- VMT_GraphicsControl --- THE VELOCITY MAPPING TOOLBOX ---
- cptcmap CPTCMAP Apply a .cpt file as colormap to an axis
- quiverc Modified version of Quiver to plots velocity vectors as arrows

## SUBFUNCTIONS

- function [cmap, lims, ticks, bfncol, ctable] = cpt2cmap(file, ncol)
- function plotcmaps(folder)
- function [x,y,c] = ctable2patch(ctable)

## SOURCE CODE

    0001 function varargout = cptcmap(varargin)
    0002 %CPTCMAP Apply a .cpt file as colormap to an axis
    0003 %
    0004 % cptcmap(name);
    0005 % cptcmap(name, ax);
    0006 % cptcmap(... param, val, ...);
    0007 % [cmap, lims, ticks, bfncol, ctable] = cptcmap(...)
    0008 %
    0009 % This function creates and applies a colormap defined in a color palette
    0010 % table (.cpt file).  For a full description of the cpt file format, see
    0011 % the Generic Mapping Tools documentation (http://gmt.soest.hawaii.edu/).
    0012 % Color palette files provide more flexible colormapping than Matlab's
    0013 % default schemes, including both discrete and continuous gradients, as
    0014 % well as easier direct color mapping.
    0015 %
    0016 % Limitations: X11 color names not supported, patterns not supported, CMYK
    0017 % not supported yet
    0018 %
    0019 % Input variables:
    0020 %
    0021 %   name:       .cpt file name.  You may either specify either the full
    0022 %               path, or just the file name.  In the latter case, the
    0023 %               function will look for the file in the folder specified by
    0024 %               the cptpath variable in the first line of code; by default
    0025 %               this folder is located in the same location as cptcmap.m
    0026 %               and is called cptfiles.
    0027 %
    0028 %   ax:         handle of axis or axes where colormap should be applied
    0029 %               (colormaps will effect the entire figure(s), but axis clim
    0030 %               adjustments for direct scaling will only affect the
    0031 %               specified axes).  If no axis is specified and no output
    0032 %               variables are supplied, colormap will be applied to the
    0033 %               current axis.  If no axis is specified and output variables
    0034 %               are supplied, the colormap will not be applied to any axes.
    0035 %
    0036 %   'showall':  When this option is used, a figure is created displaying
    0037 %               colorbars for all colormaps contained in the .cpt folder.
    0038 %               Color limits of each colormap are listed along with the
    0039 %               names of each.  A small tick mark indicates the location of
    0040 %               0, where applicable.  NOTE: the number of columns to use
    0041 %               for display is hard-coded.  As you start collecting more
    0042 %               color palettes, the figure may get too cluttered and you
    0043 %               may have to adjust this (variable ncol is the plotcmaps
    0044 %               subfunction).
    0045 %
    0046 % Optional input variables (passed as parameter/value pairs):
    0047 %
    0048 %   'mapping':  'scaled' or 'direct'.  Scaled mapping spreads the colormap
    0049 %               to cover the color limits of the figure.  Direct mapping
    0050 %               resets the color limits of the axes so that colors are
    0051 %               mapped to the levels specified by the .cpt file. ['scaled']
    0052 %
    0053 %   'ncol':     number of colors in final colormap. If not included or NaN,
    0054 %               this function will try to choose the fewest number of
    0055 %               blocks needed to display the colormap as accurately as
    0056 %               possible. I have arbitrarily chosen that it will not try to
    0057 %               create more than 256 colors in the final colormap when
    0058 %               using this automatic scheme.  However, you can manually set
    0059 %               ncol higher if necessary to resolve all sharp breaks and
    0060 %               gradients in the colormap.
    0061 %
    0062 %   'flip':     if true, reverse the colormap order [false]
    0063 %
    0064 % Output variables:
    0065 %
    0066 %   cmap:       ncol x 3 colormap array
    0067 %
    0068 %   lims:       1 x 2 array holding minimum and maximum values for which
    0069 %               the colormap is defined.
    0070 %
    0071 %   ticks:      vector of tick values specifying where colors were defined
    0072 %               in the original file
    0073 %
    0074 %   bfncol:     3 x 3 colormap array specifying the colors defined for
    0075 %               background (values lower than lowest color limit),
    0076 %               foreground (values higher than highest color limit), and
    0077 %               NaN values.  These do not affect the resulting colormap,
    0078 %               but can be applied by the user to replicate the behavior
    0079 %               seen in GMT.
    0080 %
    0081 %   ctable:     n x 8 color palette table, translated to Matlab color
    0082 %               space. Column 1 holds the lower limit of each color cell,
    0083 %               columns 2-4 the RGB values corresponding to the lower
    0084 %               limit, column 5 the upper limit of the color cell, and
    0085 %               columns 6-8 the RGB values of the upper limit.  When the
    0086 %               lower and upper colors are the same, this defines a
    0087 %               solid-colored cell; when they are different, colors are
    0088 %               linearly interpolated between the endpoints.
    0089 %
    0090 % Example:
    0091 %
    0092 %   [lat, lon, z] = satbath(10);
    0093 %   pcolor(lon, lat, z);
    0094 %   shading flat;
    0095 %   cptcmap('GMT_globe', 'mapping', 'direct');
    0096 %   colorbar;
    0097
    0098 % Copyright 2011 Kelly Kearney
    0099 % File Exchange update: 4/12/2011
    0100
    0101 %------------------------------
    0102 % Parse input
    0103 %------------------------------
    0104
    0105 % The .cpt file folder.  By default, the cptfiles folder is located in
    0106 % the same place as the cptcmap.m file.  If you change this on your
    0107 % computer, change the cptpath definition to reflect the new location.
    0108
    0109 % cptpath = [pwd '\utils\colormaps\'];%fullfile(fileparts(which('cptcmap')), 'cptfiles');
    0110 % if ~exist(cptpath, 'dir')
    0111 %     error('You have moved the cptfiles directory.  Please modify the cptpath variable in this code to point to the directory where your.cpt files are stored');
    0112 % end
    0113 %
    0114 % % Check for 'showall' option first
    0115 %
    0116 % if nargin == 1 & strcmp(varargin{1}, 'showall')
    0117 %     plotcmaps(cptpath);
    0118 %     return;
    0119 % end
    0120
    0121 % Name of file
    0122
    0123 [blah, blah, ext] = fileparts(varargin{1});
    0124 if isempty(ext)
    0125     varargin{1} = [varargin{1} '.cpt'];
    0126 end
    0127
    0128 if exist(varargin{1}, 'file')   % full filename and path given
    0129     filename = varargin{1};
    0130 else                            % only file name given
    0131     [blah,blah,ext] = fileparts(varargin{1});
    0132     if ~isempty(ext)            % with extension
    0133         filename = fullfile(cptpath, varargin{1});
    0134     else                        % without extension
    0135         filename = fullfile(cptpath, [varargin{1} '.cpt']);
    0136     end
    0137     if ~exist(filename, 'file')
    0138         error('Specified .cpt file not found');
    0139     end
    0140 end
    0141
    0142 % Axes to which colormap will be applied
    0143
    0144 if nargin > 1 && isnumeric(varargin{2}) && all(ishandle(varargin{2}(:)))
    0145     ax = varargin{2};
    0146     pv = varargin(3:end);
    0147     applycmap = true;
    0148 elseif nargout == 0
    0149     ax = gca;
    0150     pv = varargin(2:end);
    0151     applycmap = true;
    0152 else
    0153     pv = varargin(2:end);
    0154     applycmap = false;
    0155 end
    0156
    0157 % Optional paramter/value pairs
    0158
    0159 p = inputParser;
    0160 p.addParamValue('mapping', 'scaled', @(x) any(strcmpi(x, {'scaled', 'direct'})));
    0161 p.addParamValue('ncol', NaN, @(x) isscalar(x) && isnumeric(x));
    0162 p.addParamValue('flip', false, @(x) isscalar(x) && islogical(x));
    0163
    0164 p.parse(pv{:});
    0165 Opt = p.Results;
    0166
    0167 %------------------------------
    0168 % Calculate colormap and apply
    0169 %------------------------------
    0170
    0171 [cmap, lims,ticks,bfncol,ctable] = cpt2cmap(filename, Opt.ncol);
    0172 if Opt.flip
    0173     if strcmp(Opt.mapping, 'direct')
    0174         warning('Flipping colormap with direct mapping may lead to odd color breaks');
    0175     end
    0176     cmap = flipud(cmap);
    0177 end
    0178
    0179 if applycmap
    0180     for iax = 1:numel(ax)
    0181         axes(ax(iax));
    0182         if strcmp(Opt.mapping, 'direct')
    0183             set(ax(iax), 'clim', lims);
    0184         end
    0185         colormap(cmap);
    0186     end
    0187 end
    0188
    0189 %------------------------------
    0190 % Output
    0191 %------------------------------
    0192
    0193 allout = {cmap, lims, ticks, bfncol, ctable};
    0194 varargout(1:nargout) = allout(1:nargout);
    0195
    0196
    0197 %------------------------------
    0198 % Subfunction: Read colormap
    0199 % from file
    0200 %------------------------------
    0201
    0202 function [cmap, lims, ticks, bfncol, ctable] = cpt2cmap(file, ncol)
    0203
    0204 % Read file
    0205
    0206 fid = fopen(file);
    0207 txt = textscan(fid, '%s', 'delimiter', '\n');
    0208 txt = txt{1};
    0209 fclose(fid);
    0210
    0211 isheader = strncmp(txt, '#', 1);
    0212 isfooter = strncmp(txt, 'B', 1) | strncmp(txt, 'F', 1) | strncmp(txt, 'N', 1);
    0213
    0214 % Extract color data, ignore labels (errors if other text found)
    0215
    0216 ctabletxt = txt(~isheader & ~isfooter);
    0217 ctable = str2num(strvcat(txt(~isheader & ~isfooter)));
    0218 if isempty(ctable)
    0219     nr = size(ctabletxt,1);
    0220     ctable = cell(nr,1);
    0221     for ir = 1:nr
    0222         ctable{ir} = str2num(strvcat(regexp(ctabletxt{ir}, '[\d\.-]*', 'match')))';
    0223     end
    0224     try
    0225         ctable = cell2mat(ctable);
    0226     catch
    0227         error('Cannot parse this format .cpt file yet');
    0228     end
    0229 end
    0230
    0231 % Determine which color model is used (RGB, HSV, CMYK, names, patterns,
    0232 % mixed)
    0233
    0234 [nr, nc] = size(ctable);
    0235 iscolmodline = cellfun(@(x) ~isempty(x), regexp(txt, 'COLOR_MODEL'));
    0236 if any(iscolmodline)
    0237     colmodel = regexprep(txt{iscolmodline}, 'COLOR_MODEL', '');
    0238     colmodel = strtrim(lower(regexprep(colmodel, '[#=]', '')));
    0239 else
    0240     if nc == 8
    0241         colmodel = 'rgb';
    0242     elseif nc == 10
    0243         colmodel = 'cmyk';
    0244     else
    0245         error('Cannot parse this format .cpt file yet');
    0246     end
    0247 end
    0248 %     try
    0249 %         temp = str2num(strvcat(txt(~isheader & ~isfooter)));
    0250 %         if size(temp,2) == 8
    0251 %             colmodel = 'rgb';
    0252 %         elseif size(temp,2) == 10
    0253 %             colmodel = 'cmyk';
    0254 %         else % grayscale, maybe others
    0255 %             error('Cannot parse this format .cpt file yet');
    0256 %         end
    0257 %     catch % color names, mixed formats, dash placeholders
    0258 %         error('Cannot parse this format .cpt file yet');
    0259 %     end
    0260 % end
    0261 %
    0262
    0263 %
    0264 % iscmod = strncmp(txt, '# COLOR_MODEL', 13);
    0265 %
    0266 %
    0267 % if ~any(iscmod)
    0268 %     isrgb = true;
    0269 % else
    0270 %     cmodel = strtrim(regexprep(txt{iscmod}, '# COLOR_MODEL =', ''));
    0271 %     if strcmp(cmodel, 'RGB')
    0272 %         isrgb = true;
    0273 %     elseif strcmp(cmodel, 'HSV')
    0274 %         isrgb = false;
    0275 %     else
    0276 %         error('Unknown color model: %s', cmodel);
    0277 %     end
    0278 % end
    0279
    0280 % Reformat color table into one column of colors
    0281
    0282 cpt = zeros(nr*2, 4);
    0283 cpt(1:2:end,:) = ctable(:,1:4);
    0284 cpt(2:2:end,:) = ctable(:,5:8);
    0285
    0286 % Ticks
    0287
    0288 ticks = unique(cpt(:,1));
    0289
    0290 % Choose number of colors for output
    0291
    0292 if isnan(ncol)
    0293
    0294     endpoints = unique(cpt(:,1));
    0295
    0296     % For gradient-ed blocks, ensure at least 4 steps between endpoints
    0297
    0298     issolid = all(ctable(:,2:4) == ctable(:,6:8), 2);
    0299
    0300     for ie = 1:length(issolid)
    0301         if ~issolid(ie)
    0302             temp = linspace(endpoints(ie), endpoints(ie+1), 11)';
    0303             endpoints = [endpoints; temp(2:end-1)];
    0304         end
    0305     end
    0306
    0307     endpoints = sort(endpoints);
    0308
    0309     % Determine largest step size that resolves all endpoints
    0310
    0311     space = diff(endpoints);
    0312     space = unique(space);
    0313     space = roundn(space, -3); % To avoid floating point issues when converting to integers
    0314
    0315     nspace = length(space);
    0316     if ~isscalar(space)
    0317
    0318         fac = 1;
    0319         tol = .001;
    0320         while 1
    0321             if all(space >= 1 & (space - round(space)) < tol)
    0322                 space = round(space);
    0323                 break;
    0324             else
    0325                 space = space * 10;
    0326                 fac = fac * 10;
    0327             end
    0328         end
    0329
    0330         pairs = nchoosek(space, 2);
    0331         np = size(pairs,1);
    0332         commonsp = zeros(np,1);
    0333         for ip = 1:np
    0334             commonsp(ip) = gcd(pairs(ip,1), pairs(ip,2));
    0335         end
    0336
    0337         space = min(commonsp);
    0338         space = space/fac;
    0339     end
    0340
    0341     ncol = (max(endpoints) - min(endpoints))./space;
    0342     ncol = min(ncol, 256);
    0343
    0344 end
    0345
    0346 % Remove replicates and mimic sharp breaks
    0347
    0348 isrep =  [false; ~any(diff(cpt),2)];
    0349 cpt = cpt(~isrep,:);
    0350
    0351 difc = diff(cpt(:,1));
    0352 minspace = min(difc(difc > 0));
    0353 isbreak = [false; difc == 0];
    0354 cpt(isbreak,1) = cpt(isbreak,1) + .01*minspace;
    0355
    0356 % Parse background, foreground, and nan colors
    0357
    0358 footer = txt(isfooter);
    0359 bfncol = nan(3,3);
    0360 for iline = 1:length(footer)
    0361     if strcmp(footer{iline}(1), 'B')
    0362         bfncol(1,:) = str2num(regexprep(footer{iline}, 'B', ''));
    0363     elseif strcmp(footer{iline}(1), 'F')
    0364         bfncol(2,:) = str2num(regexprep(footer{iline}, 'F', ''));
    0365     elseif strcmp(footer{iline}(1), 'N')
    0366         bfncol(3,:) = str2num(regexprep(footer{iline}, 'N', ''));
    0367     end
    0368 end
    0369
    0370 % Convert to Matlab-format colormap and color limits
    0371
    0372 lims = [min(cpt(:,1)) max(cpt(:,1))];
    0373 endpoints = linspace(lims(1), lims(2), ncol+1);
    0374 midpoints = (endpoints(1:end-1) + endpoints(2:end))/2;
    0375
    0376 cmap = interp1(cpt(:,1), cpt(:,2:4), midpoints);
    0377
    0378 switch colmodel
    0379     case 'rgb'
    0380         cmap = cmap ./ 255;
    0381         bfncol = bfncol ./ 255;
    0382         ctable(:,[2:4 6:8]) = ctable(:,[2:4 6:8]) ./ 255;
    0383
    0384     case 'hsv'
    0385         cmap(:,1) = cmap(:,1)./300;
    0386         cmap = hsv2rgb(cmap);
    0387
    0388         bfncol(:,1) = bfncol(:,1)./300;
    0389         bfncol = hsv2rgb(bfncol);
    0390
    0391         ctable(:,2) = ctable(:,2)./300;
    0392         ctable(:,6) = ctable(:,6)./300;
    0393
    0394         ctable(:,2:4) = hsv2rgb(ctable(:,2:4));
    0395         ctable(:,6:8) = hsv2rgb(ctable(:,6:8));
    0396
    0397     case 'cmyk'
    0398         error('CMYK color conversion not yet supported');
    0399 end
    0400
    0401 %------------------------------
    0402 % Subfunction: Display all
    0403 % colormaps
    0404 %------------------------------
    0405
    0406 function plotcmaps(folder)
    0407
    0408 Files = dir(fullfile(folder, '*.cpt'));
    0409 nfile = length(Files);
    0410 ncol = 3;
    0411 nr = ceil(nfile/ncol);
    0412 width = (1 - .05*2)/ncol;
    0413 height = (1-.05*2)/nr;
    0414 left = .05 + (0:ncol-1)*width;
    0415 bot = .05 + (0:nr-1)*height;
    0416
    0417 [l, b] = meshgrid(left, bot);
    0418 w = width * .8;
    0419 h = height * .4;
    0420
    0421 figure('color','w');
    0422 ax = axes('position', [0 0 1 1]);
    0423 hold on;
    0424
    0425 for ifile = 1:nfile
    0426     [cmap,blah,blah,blah,ctable] = cptcmap(Files(ifile).name);
    0427     [x,y,c] = ctable2patch(ctable);
    0428
    0429     xtick = unique(x);
    0430     dx = max(x(:)) - min(x(:));
    0431
    0432     xsc = ((x-min(xtick))./dx).*w + l(ifile);
    0433     ysc = y.*h + b(ifile);
    0434
    0435     xrect = [0 1 1 0 0] .*w + l(ifile);
    0436     yrect = [1 1 0 0 1] .*h + b(ifile);
    0437
    0438     xticksc = ((xtick-min(xtick))./dx).*w + l(ifile);
    0439     x0 = interp1(xtick, xticksc, 0);
    0440     y0 = b(ifile) + [0 .2*h NaN .8*h h];
    0441     x0 = ones(size(y0))*x0;
    0442
    0443     lbl = sprintf('%s [%g, %g]', regexprep(Files(ifile).name,'\.cpt',''), min(x(:)), max(x(:)));
    0444
    0445     patch(xsc, ysc, c, 'edgecolor', 'none');
    0446     line(xrect, yrect, 'color', 'k');
    0447     line(x0, y0, 'color', 'k');
    0448     text(l(ifile), b(ifile)+h, lbl, 'interpreter', 'none', 'fontsize', 8, 'verticalalignment', 'bottom', 'horizontalalignment', 'left');
    0449
    0450 end
    0451
    0452 set(ax, 'ylim', [0 1], 'xlim', [0 1], 'visible', 'off');
    0453
    0454 % Determine patch coordinates
    0455
    0456 function [x,y,c] = ctable2patch(ctable)
    0457
    0458 np = size(ctable,1);
    0459
    0460 x = zeros(4, np);
    0461 y = zeros(4, np);
    0462 c = zeros(4, np, 3);
    0463
    0464 y(1:2,:) = 1;
    0465
    0466 for ip = 1:np
    0467     x(:,ip) = [ctable(ip,1) ctable(ip,5) ctable(ip,5) ctable(ip,1)];
    0468     c(:,ip,:) = [ctable(ip,2:4); ctable(ip,6:8); ctable(ip,6:8); ctable(ip,2:4)];
    0469 end
    0470
    0471