cptcmap.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. function varargout = cptcmap(varargin)
  2. %CPTCMAP Apply a .cpt file as colormap to an axis
  3. %
  4. % cptcmap(name);
  5. % cptcmap(name, ax);
  6. % cptcmap(... param, val, ...);
  7. % [cmap, lims, ticks, bfncol, ctable] = cptcmap(...)
  8. %
  9. % This function creates and applies a colormap defined in a color palette
  10. % table (.cpt file). For a full description of the cpt file format, see
  11. % the Generic Mapping Tools documentation (http://gmt.soest.hawaii.edu/).
  12. % Color palette files provide more flexible colormapping than Matlab's
  13. % default schemes, including both discrete and continuous gradients, as
  14. % well as easier direct color mapping.
  15. %
  16. % Limitations: X11 color names not supported, patterns not supported, CMYK
  17. % not supported yet
  18. %
  19. % Input variables:
  20. %
  21. % name: .cpt file name. You may either specify either the full
  22. % path, or just the file name. In the latter case, the
  23. % function will look for the file in the folder specified by
  24. % the cptpath variable in the first line of code; by default
  25. % this folder is located in the same location as cptcmap.m
  26. % and is called cptfiles.
  27. %
  28. % ax: handle of axis or axes where colormap should be applied
  29. % (for pre-2014b versions, colormaps will effect the entire
  30. % figure(s), but axis clim adjustments for direct scaling
  31. % will only affect the specified axes; for 2014b+, colormap
  32. % will only be applied to the specified axes). If no axis is
  33. % specified and no output variables are supplied, colormap
  34. % will be applied to the current axis. If no axis is
  35. % specified and output variables are supplied, the colormap
  36. % will not be applied to any axes.
  37. %
  38. % 'showall': When this option is used, a figure is created displaying
  39. % colorbars for all colormaps contained in the .cpt folder.
  40. % Color limits of each colormap are listed along with the
  41. % names of each. A small tick mark indicates the location of
  42. % 0, where applicable. NOTE: the number of columns to use
  43. % for display is hard-coded. As you start collecting more
  44. % color palettes, the figure may get too cluttered and you
  45. % may have to adjust this (variable ncol in the plotcmaps
  46. % subfunction).
  47. %
  48. % Optional input variables (passed as parameter/value pairs):
  49. %
  50. % 'mapping': 'scaled' or 'direct'. Scaled mapping spreads the colormap
  51. % to cover the color limits of the figure. Direct mapping
  52. % resets the color limits of the axes so that colors are
  53. % mapped to the levels specified by the .cpt file. ['scaled']
  54. %
  55. % 'ncol': number of colors in final colormap. If not included or NaN,
  56. % this function will try to choose the fewest number of
  57. % blocks needed to display the colormap as accurately as
  58. % possible. I have arbitrarily chosen that it will not try to
  59. % create more than 256 colors in the final colormap when
  60. % using this automatic scheme. However, you can manually set
  61. % ncol higher if necessary to resolve all sharp breaks and
  62. % gradients in the colormap.
  63. %
  64. % 'flip': if true, reverse the colormap order [false]
  65. %
  66. % Output variables:
  67. %
  68. % cmap: ncol x 3 colormap array
  69. %
  70. % lims: 1 x 2 array holding minimum and maximum values for which
  71. % the colormap is defined.
  72. %
  73. % ticks: vector of tick values specifying where colors were defined
  74. % in the original file
  75. %
  76. % bfncol: 3 x 3 colormap array specifying the colors defined for
  77. % background (values lower than lowest color limit),
  78. % foreground (values higher than highest color limit), and
  79. % NaN values. These do not affect the resulting colormap,
  80. % but can be applied by the user to replicate the behavior
  81. % seen in GMT.
  82. %
  83. % ctable: n x 8 color palette table, translated to Matlab color
  84. % space. Column 1 holds the lower limit of each color cell,
  85. % columns 2-4 the RGB values corresponding to the lower
  86. % limit, column 5 the upper limit of the color cell, and
  87. % columns 6-8 the RGB values of the upper limit. When the
  88. % lower and upper colors are the same, this defines a
  89. % solid-colored cell; when they are different, colors are
  90. % linearly interpolated between the endpoints.
  91. %
  92. % Example:
  93. %
  94. % [lat, lon, z] = satbath(10);
  95. % pcolor(lon, lat, z);
  96. % shading flat;
  97. % cptcmap('GMT_globe', 'mapping', 'direct');
  98. % colorbar;
  99. % Copyright 2011 Kelly Kearney
  100. % File Exchange update: 4/12/2011
  101. %------------------------------
  102. % Parse input
  103. %------------------------------
  104. % The .cpt file folder. By default, the cptfiles folder is located in
  105. % the same place as the cptcmap.m file. If you change this on your
  106. % computer, change the cptpath definition to reflect the new location.
  107. cptpath = fullfile(fileparts(which('cptcmap')), 'cptfiles');
  108. if ~exist(cptpath, 'dir')
  109. 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');
  110. end
  111. if nargin < 1
  112. error('You must provide a colormap name');
  113. end
  114. % Check for 'showall' option first
  115. if nargin == 1 && strcmp(varargin{1}, 'showall')
  116. plotcmaps(cptpath);
  117. return;
  118. end
  119. % Name of file
  120. [blah, blah, ext] = fileparts(varargin{1});
  121. if isempty(ext)
  122. varargin{1} = [varargin{1} '.cpt'];
  123. end
  124. if exist(varargin{1}, 'file') % full filename and path given
  125. filename = varargin{1};
  126. else % only file name given
  127. [blah,blah,ext] = fileparts(varargin{1});
  128. if ~isempty(ext) % with extension
  129. filename = fullfile(cptpath, varargin{1});
  130. else % without extension
  131. filename = fullfile(cptpath, [varargin{1} '.cpt']);
  132. end
  133. if ~exist(filename, 'file')
  134. error('Specified .cpt file not found');
  135. end
  136. end
  137. % Axes to which colormap will be applied
  138. if nargin > 1 && all(ishandle(varargin{2}(:)))
  139. ax = varargin{2};
  140. pv = varargin(3:end);
  141. applycmap = true;
  142. elseif nargout == 0
  143. ax = gca;
  144. pv = varargin(2:end);
  145. applycmap = true;
  146. else
  147. pv = varargin(2:end);
  148. applycmap = false;
  149. end
  150. % Optional paramter/value pairs
  151. p = inputParser;
  152. p.addParamValue('mapping', 'scaled', @(x) any(strcmpi(x, {'scaled', 'direct'})));
  153. p.addParamValue('ncol', NaN, @(x) isscalar(x) && isnumeric(x));
  154. p.addParamValue('flip', false, @(x) isscalar(x) && islogical(x));
  155. p.parse(pv{:});
  156. Opt = p.Results;
  157. %------------------------------
  158. % Calculate colormap and apply
  159. %------------------------------
  160. [cmap, lims,ticks,bfncol,ctable] = cpt2cmap(filename, Opt.ncol);
  161. if Opt.flip
  162. if strcmp(Opt.mapping, 'direct')
  163. warning('Flipping colormap with direct mapping may lead to odd color breaks');
  164. end
  165. cmap = flipud(cmap);
  166. end
  167. if applycmap
  168. for iax = 1:numel(ax)
  169. if strcmp(Opt.mapping, 'direct')
  170. set(ax(iax), 'clim', lims);
  171. end
  172. colormap(ax(iax), cmap);
  173. end
  174. end
  175. %------------------------------
  176. % Output
  177. %------------------------------
  178. allout = {cmap, lims, ticks, bfncol, ctable};
  179. varargout(1:nargout) = allout(1:nargout);
  180. %------------------------------
  181. % Subfunction: Read colormap
  182. % from file
  183. %------------------------------
  184. function [cmap, lims, ticks, bfncol, ctable] = cpt2cmap(file, ncol)
  185. % Read file
  186. fid = fopen(file);
  187. txt = textscan(fid, '%s', 'delimiter', '\n');
  188. txt = txt{1};
  189. fclose(fid);
  190. isheader = strncmp(txt, '#', 1);
  191. isfooter = strncmp(txt, 'B', 1) | strncmp(txt, 'F', 1) | strncmp(txt, 'N', 1);
  192. % Extract color data, ignore labels (errors if other text found)
  193. ctabletxt = txt(~isheader & ~isfooter);
  194. ctable = str2num(strvcat(txt(~isheader & ~isfooter)));
  195. if isempty(ctable)
  196. nr = size(ctabletxt,1);
  197. ctable = cell(nr,1);
  198. for ir = 1:nr
  199. ctable{ir} = str2num(strvcat(regexp(ctabletxt{ir}, '[\d\.-]*', 'match')))';
  200. end
  201. try
  202. ctable = cell2mat(ctable);
  203. catch
  204. error('Cannot parse this format .cpt file yet');
  205. end
  206. end
  207. % Determine which color model is used (RGB, HSV, CMYK, names, patterns,
  208. % mixed)
  209. [nr, nc] = size(ctable);
  210. iscolmodline = cellfun(@(x) ~isempty(x), regexp(txt, 'COLOR_MODEL'));
  211. if any(iscolmodline)
  212. colmodel = regexprep(txt{iscolmodline}, 'COLOR_MODEL', '');
  213. colmodel = strtrim(lower(regexprep(colmodel, '[#=]', '')));
  214. else
  215. if nc == 8
  216. colmodel = 'rgb';
  217. elseif nc == 10
  218. colmodel = 'cmyk';
  219. else
  220. error('Cannot parse this format .cpt file yet');
  221. end
  222. end
  223. % try
  224. % temp = str2num(strvcat(txt(~isheader & ~isfooter)));
  225. % if size(temp,2) == 8
  226. % colmodel = 'rgb';
  227. % elseif size(temp,2) == 10
  228. % colmodel = 'cmyk';
  229. % else % grayscale, maybe others
  230. % error('Cannot parse this format .cpt file yet');
  231. % end
  232. % catch % color names, mixed formats, dash placeholders
  233. % error('Cannot parse this format .cpt file yet');
  234. % end
  235. % end
  236. %
  237. %
  238. % iscmod = strncmp(txt, '# COLOR_MODEL', 13);
  239. %
  240. %
  241. % if ~any(iscmod)
  242. % isrgb = true;
  243. % else
  244. % cmodel = strtrim(regexprep(txt{iscmod}, '# COLOR_MODEL =', ''));
  245. % if strcmp(cmodel, 'RGB')
  246. % isrgb = true;
  247. % elseif strcmp(cmodel, 'HSV')
  248. % isrgb = false;
  249. % else
  250. % error('Unknown color model: %s', cmodel);
  251. % end
  252. % end
  253. % Reformat color table into one column of colors
  254. cpt = zeros(nr*2, 4);
  255. cpt(1:2:end,:) = ctable(:,1:4);
  256. cpt(2:2:end,:) = ctable(:,5:8);
  257. % Ticks
  258. ticks = unique(cpt(:,1));
  259. % Choose number of colors for output
  260. if isnan(ncol)
  261. endpoints = unique(cpt(:,1));
  262. % For gradient-ed blocks, ensure at least 4 steps between endpoints
  263. issolid = all(ctable(:,2:4) == ctable(:,6:8), 2);
  264. for ie = 1:length(issolid)
  265. if ~issolid(ie)
  266. temp = linspace(endpoints(ie), endpoints(ie+1), 11)';
  267. endpoints = [endpoints; temp(2:end-1)];
  268. end
  269. end
  270. endpoints = sort(endpoints);
  271. % Determine largest step size that resolves all endpoints
  272. space = diff(endpoints);
  273. space = unique(space);
  274. % space = roundn(space, -3); % To avoid floating point issues when converting to integers
  275. space = round(space*1e3)/1e3;
  276. nspace = length(space);
  277. if ~isscalar(space)
  278. fac = 1;
  279. tol = .001;
  280. while 1
  281. if all(space >= 1 & (abs(space - round(space))) < tol)
  282. space = round(space);
  283. break;
  284. else
  285. space = space * 10;
  286. fac = fac * 10;
  287. end
  288. end
  289. pairs = nchoosek(space, 2);
  290. np = size(pairs,1);
  291. commonsp = zeros(np,1);
  292. for ip = 1:np
  293. commonsp(ip) = gcd(pairs(ip,1), pairs(ip,2));
  294. end
  295. space = min(commonsp);
  296. space = space/fac;
  297. end
  298. ncol = (max(endpoints) - min(endpoints))./space;
  299. ncol = min(ncol, 256);
  300. end
  301. % Remove replicates and mimic sharp breaks
  302. isrep = [false; ~any(diff(cpt),2)];
  303. cpt = cpt(~isrep,:);
  304. difc = diff(cpt(:,1));
  305. minspace = min(difc(difc > 0));
  306. isbreak = [false; difc == 0];
  307. cpt(isbreak,1) = cpt(isbreak,1) + .01*minspace;
  308. % Parse background, foreground, and nan colors
  309. footer = txt(isfooter);
  310. bfncol = nan(3,3);
  311. for iline = 1:length(footer)
  312. if strcmp(footer{iline}(1), 'B')
  313. bfncol(1,:) = str2num(regexprep(footer{iline}, 'B', ''));
  314. elseif strcmp(footer{iline}(1), 'F')
  315. bfncol(2,:) = str2num(regexprep(footer{iline}, 'F', ''));
  316. elseif strcmp(footer{iline}(1), 'N')
  317. bfncol(3,:) = str2num(regexprep(footer{iline}, 'N', ''));
  318. end
  319. end
  320. % Convert to Matlab-format colormap and color limits
  321. lims = [min(cpt(:,1)) max(cpt(:,1))];
  322. endpoints = linspace(lims(1), lims(2), ncol+1);
  323. midpoints = (endpoints(1:end-1) + endpoints(2:end))/2;
  324. cmap = interp1(cpt(:,1), cpt(:,2:4), midpoints);
  325. switch colmodel
  326. case 'rgb'
  327. cmap = cmap ./ 255;
  328. bfncol = bfncol ./ 255;
  329. ctable(:,[2:4 6:8]) = ctable(:,[2:4 6:8]) ./ 255;
  330. case 'hsv'
  331. cmap(:,1) = cmap(:,1)./360;
  332. cmap = hsv2rgb(cmap);
  333. bfncol(:,1) = bfncol(:,1)./360;
  334. bfncol = hsv2rgb(bfncol);
  335. ctable(:,2) = ctable(:,2)./360;
  336. ctable(:,6) = ctable(:,6)./360;
  337. ctable(:,2:4) = hsv2rgb(ctable(:,2:4));
  338. ctable(:,6:8) = hsv2rgb(ctable(:,6:8));
  339. case 'cmyk'
  340. error('CMYK color conversion not yet supported');
  341. end
  342. % Rouding issues: occasionally, the above calculations lead to values just
  343. % above 1, which colormap doesn't like at all. This is a bit kludgy, but
  344. % should solve those issues
  345. isnear1 = cmap > 1 & (abs(cmap-1) < 2*eps);
  346. cmap(isnear1) = 1;
  347. %------------------------------
  348. % Subfunction: Display all
  349. % colormaps
  350. %------------------------------
  351. function plotcmaps(folder)
  352. Files = dir(fullfile(folder, '*.cpt'));
  353. nfile = length(Files);
  354. ncol = 3;
  355. nr = ceil(nfile/ncol);
  356. width = (1 - .05*2)/ncol;
  357. height = (1-.05*2)/nr;
  358. left = .05 + (0:ncol-1)*width;
  359. bot = .05 + (0:nr-1)*height;
  360. [l, b] = meshgrid(left, bot);
  361. w = width * .8;
  362. h = height * .4;
  363. figure('color','w');
  364. ax = axes('position', [0 0 1 1]);
  365. hold on;
  366. for ifile = 1:nfile
  367. [cmap,blah,blah,blah,ctable] = cptcmap(Files(ifile).name);
  368. [x,y,c] = ctable2patch(ctable);
  369. xtick = unique(x);
  370. dx = max(x(:)) - min(x(:));
  371. xsc = ((x-min(xtick))./dx).*w + l(ifile);
  372. ysc = y.*h + b(ifile);
  373. xrect = [0 1 1 0 0] .*w + l(ifile);
  374. yrect = [1 1 0 0 1] .*h + b(ifile);
  375. xticksc = ((xtick-min(xtick))./dx).*w + l(ifile);
  376. x0 = interp1(xtick, xticksc, 0);
  377. y0 = b(ifile) + [0 .2*h NaN .8*h h];
  378. x0 = ones(size(y0))*x0;
  379. lbl = sprintf('%s [%g, %g]', regexprep(Files(ifile).name,'\.cpt',''), min(x(:)), max(x(:)));
  380. patch(xsc, ysc, c, 'edgecolor', 'none');
  381. line(xrect, yrect, 'color', 'k');
  382. line(x0, y0, 'color', 'k');
  383. text(l(ifile), b(ifile)+h, lbl, 'interpreter', 'none', 'fontsize', 10, 'verticalalignment', 'bottom', 'horizontalalignment', 'left');
  384. end
  385. set(ax, 'ylim', [0 1], 'xlim', [0 1], 'visible', 'off');
  386. % Determine patch coordinates
  387. function [x,y,c] = ctable2patch(ctable)
  388. np = size(ctable,1);
  389. x = zeros(4, np);
  390. y = zeros(4, np);
  391. c = zeros(4, np, 3);
  392. y(1:2,:) = 1;
  393. for ip = 1:np
  394. x(:,ip) = [ctable(ip,1) ctable(ip,5) ctable(ip,5) ctable(ip,1)];
  395. c(:,ip,:) = [ctable(ip,2:4); ctable(ip,6:8); ctable(ip,6:8); ctable(ip,2:4)];
  396. end