Code covered by the BSD License  

Highlights from
Thermometer plot

image thumbnail

Thermometer plot

by

 

03 Oct 2006 (Updated )

Draw a thermometer style plot

thermometer(varargin)
function h = thermometer(varargin)
% Create a Thermometer plot.
% THERMOMETER(DATA, MAX) - Create a thermometer 
%    where DATA is a set of 1D data to plot in the thermometer.
%    Each entry in DATA adds onto the previous number, thus:
%    [ 3 4 5 ] will show the '3' between 0 and 3, and '4' between 3
%    and 7, and '5' between 7 and 12.
% THERMOMETER(DATA, MIN, MAX) 
%    MIN and MAX represent the minimum and maximum thermometer
%    values.
%    Values in DATA add onto MIN. Thus, if MIN is 10, and DATA is
%    [ 3 4 ] then the '3' is shown between 10 and 13, and the '4'
%    between 13 and 17.
% THERMOMETER(..., { 'name1', 'name2' }) 
%    Area labels.
% THERMOMETER(..., { GOAL 'label' })
%    At a specific GOAL, add LABEL.
%    The GOAL value is absolute, and does not change based on MIN
%    or MAX.
%
% The thermometer code has been tweeked to look good printed from
% painters.  Using Zbuffer or OpenGL will make it looks strange.
%
% To adjust the tick labels, modify GCA's yticks.
%
% To make the zone labels alternate sides, uncomment the section
% that contains the word 'left'.
%
% Example:
% thermometer([ 3 4 ], 10, { 'tasks' 'bugs' }, { 8 'Almost There' })

% History:
%  Version 1.2: Update doc to better describe data.
%
%  Version 1.1:  Fixed problem using color-order for shading
%  elements so more than 7 entries can be used.
%
%
% Eric Ludlam
% Copyright (c) 2006 The MathWorks Inc.

  data = varargin{1};

  if any(data < 0)
      error('DATA Values for thermometer must be > 0.');
  end
  
  if nargin >= 3 && isnumeric(varargin{3})
    inputy = [ varargin{2} varargin{3} ];
    base = 4;
  else
    inputy = [ 0 varargin{2} ];
    base = 3;
  end
  
  names = {};
  goals = {};
  
  while nargin >= base

    nexta = varargin{base};
    
    if ~iscell(nexta)
      error('Arguments after MAX must be cell arrays');
    end
    
    if ischar(nexta{1})
    
      names = cell(size(data,1),1);
      names(1:length(varargin{base})) = varargin{base};
      
    elseif isnumeric(nexta{1})
      
      goals = nexta;
      
    else
      
      error('Unknown cell array format for thermometer.', nexta); %#ok
      
    end
    
    
    base = base + 1;
  end
  
  lw = 2;
  ax = newplot;
  
  set(ax,'color','white');
  total = inputy(2) - inputy(1);
  yl = [ inputy(1)-(total/15) inputy(2) ];
  set(ax,'ylim', yl)
  set(ax,'xlim', [ 0 1 ])
  set(ax,'xtick', [] )
  set(ax,'plotboxaspectratio',[ 1 20 1 ]);
  set(ax,'box','on');
  set(ax,'tickdir','out');
  set(ax,'ygrid','on');
  set(ax,'gridlinestyle','-');
  set(ax,'box','off');

  co = get(gca,'colororder');  
  
  l = get(gca,'loose');
  l(1) = l(end);
  set(gca,'loose',l);
  
  % Draw a circle at bottom of thermometer
  
  rectangle('position',[-.5 inputy(1)-(total/15*1.5) 2 total/15*1.6 ],...
            'curvature',[ 1 1 ],...
            'facecolor',co(1,:),'edgecolor','k',...
            'clipping','off',...
            'linewidth',3);

  % Data Patchs
  
  % One number being plotted
    
  surface('xdata', [ 0 1 ] , 'ydata', [ yl(1) data(1)+inputy(1) ], ...
          'zdata', [ 1 1 ; 1 1 ],  ...
          'facecolor', co(1,:) ,'edgecolor','none');

  if ~isempty(names)
    yzonelabel_t(inputy(1), data(1)+inputy(1), names{1}, 'right');
  end
  
  bottom = data(1)+inputy(1);

  % A stacked data plot
  for i = 2:length(data)

    cidx = i-1;
    color = co(mod(cidx,size(co,1))+1,:);
      
    surface('xdata', [ 0 1 ] , 'ydata', [ bottom bottom+data(i) ], ...
            'zdata', [ 0 0 ; 0 0 ],  ...
            'facecolor', color ,'edgecolor','none');      
    %if ~mod(i,2)
    %  lr = 'left';
    %else
    lr = 'right';
    %end

    if ~isempty(names) && length(names) >= i
      yzonelabel_t(bottom, bottom+data(i), names{i}, lr);
    end
  
    bottom = bottom+data(i);
    
  end

  while ~isempty(goals)
    
    pointlabel(goals{1}, goals{2}, false);
    
    goals = goals(3:end);
    
  end
  
    % Put a new box onto the axes.
  line('xdata',[0 0 1 1],'ydata', [ inputy(1) yl(2) yl(2) inputy(1) ],...
       'zdata',[1 1 1 1],...
       'color','k','linewidth',lw,'clipping','off');
  
  if nargout > 0
      h = ax;
  end

end
  
function pointlabel(yval, str, left)
% Create a fancy label at yval
  
  if left
    xdata = [ 0 -.7 ];
    txtpos = -.8;
    ha = 'right';
  else
    xdata = [ 1 1.7 ];
    txtpos = 1.8;
    ha = 'left';
  end
  ydata = [ yval yval ];
  
  line(xdata,ydata,'color','k','clipping','off');
  line(1,yval,'color','k',...
       'markersize',6,...
       'marker','diamond','markerfacecolor','k');
  line(0,yval,'color','k',...
       'markersize',6,...
       'marker','diamond','markerfacecolor','k');
  line([0 1],ydata,'color','k','linestyle',':');
  text(txtpos,yval,str,'horizontalalign',ha,...
       'verticalalign','middle');
  
end

function yzonelabel_t(start, fin, str, side)
% Create a small label thingy between START and END
% STR is an optional string to add to the label.  The text
% representing the total Y value encompassed is also included.
% SIDE is a string, either 'left', or 'right'.

  xl = xlim;
  xr = xl(2);
  xl = xl(1);
  factor = abs(xl-xr);
  
  switch side
   case 'left'
    in = xl-1.2*factor;
    out = xl-2*factor;
    xdata = [ in out out in ];
    txtpos = xl-2.1*factor;
    rot = 90;
   case 'right'
    in = xr + .5*factor;
    out = xr + .7*factor;
    xdata = [ in out out in ];
    txtpos = xr + .8*factor;
    rot = -90;
  end
  ydata = [ start start fin fin ];
  
  mid = (start + fin) / 2;
  
  numstr = num2str(fin-start);
  
  if isempty(str)
    newstr = numstr;
  else
    newstr = [ numstr char(10) str ];
  end
  
  l = line(xdata,ydata,'color','k');
  t = text(txtpos, mid, newstr,'rotation',rot,...
           'fontsize',10,...
           'horizontalalign','center',...
           'verticalalign','bottom');
  set([ l t ], 'xliminclude','off',...
               'clipping','off');
  
end

Contact us