% -----------------------------------------------
function dateNtick(ax,dateform)
%
%SPECIALIZED VERSION OF DATETICK
%   ADDS ANOTHER OPTION (#19) ALLOWS DATES TO BE DISPLAYED
%   ON TWO LINES
%   TESTED ON MATLAB 5.3
%
%DATETICK Date formatted tick labels. 
%   DATETICK(TICKAXIS,DATEFORM) annotates the specified tick axis with
%   date formatted tick labels. TICKAXIS must be one of the strings
%   'x','y', or 'z'. The default is 'x'.  The labels are formatted
%   according to the format number or string DATEFORM (see table
%   below).  If no DATEFORM argument is entered, DATETICK makes a
%   guess based on the data for the objects within the specified axis.
%   To produce correct results, the data for the specified axis must
%   be serial date numbers (as produced by DATENUM).
%
%   DATEFORM number   DATEFORM string         Example
%      0             'dd-mmm-yyyy HH:MM:SS'   01-Mar-1995 15:45:17 
%      1             'dd-mmm-yyyy'            01-Mar-1995  
%      2             'mm/dd/yy'               03/01/95     
%      3             'mmm'                    Mar          
%      4             'm'                      M            
%      5             'mm'                     3            
%      6             'mm/dd'                  03/01        
%      7             'dd'                     1            
%      8             'ddd'                    Wed          
%      9             'd'                      W            
%     10             'yyyy'                   1995         
%     11             'yy'                     95           
%     12             'mmmyy'                  Mar95        
%     13             'HH:MM:SS'               15:45:17     
%     14             'HH:MM:SS PM'             3:45:17 PM  
%     15             'HH:MM'                  15:45        
%     16             'HH:MM PM'                3:45 PM     
%     17             'QQ-YY'                  Q1-96        
%     18             'QQ'                     Q1
%     19             'mm/dd over HH:MM'       03/01
%                                             15:45  
%
%       
%
%   If the HOLD is ON then DATETICK, will change the tick labels into
%   date-based labels without changing their locations.  When the HOLD
%   is OFF, DATETICK changes the locations of the ticks as well.
%
%Here is an example of how to use dateNtick
%
%yr = 1999;
%M = 10;
%MI = 0;
%sec = 0;
%
%X = [];
%
%for day = 3:4,
%   for  hr = 0:.2:23.5,
%      X = [X; datenum(yr,M,day,hr,MI,sec)]; 
%    end
%end
%
%Y = rand(length(X),1);
%
%plot(X,Y); grid
%
%hold
%dateNtick('x',19);
%hold off
%
%   DATETICK relies on DATESTR to convert date numbers to date strings.
%
%   Example (based on the 1990 U.S. census):
%      t = (1900:10:1990)'; % Time interval
%      p = [75.995 91.972 105.711 123.203 131.669 ...
%          150.697 179.323 203.212 226.505 249.633]';  % Population
%      plot(datenum(t,1,1),p) % Convert years to date numbers and plot
%      datetick('x','yyyy') % Replace x-axis ticks with 4 digit year labels.
%    
%   See also DATESTR, DATENUM.
%
%		Author(s): C.F. Garvin, 4-03-95, Clay M. Thompson 1-29-96
%   Copyright (c) 1984-98 by The MathWorks, Inc.
%
%
%   $Revision: 1.1 $  $Date: 2005/04/13 20:38:50 $
%     added #19 to do 2 lines (date/time) at each tick.         
%     By Arthur Newhall
%			anewhall@whoi.edu


labels = [];


if nargin == 0
  ax = 'x';
end


if nargin==1 & ~isstr(ax),
  error('The axis must be ''x'',''y'', or ''z''.');
end


% Compute data limits.  If the limits are manual, use them instead of
% looking at the data to determine vmin and vmax.
if strcmp(get(gca,[ax 'limMode']),'manual')
  lim = get(gca,[ax 'lim']);
  vmin = lim(1);
  vmax = lim(2);
else
  h = get(gca,'children');
  vmin = inf; vmax = -inf;
  for i=1:length(h),
    t = get(h(i),'type');
    if strcmp(t,'surface') | strcmp(t,'patch') | ...
       strcmp(t,'line') | strcmp(t,'image') 
      vdata = get(h(i),[ax,'data']);
      vmin = min(vmin,min(vdata(:)));
      vmax = max(vmax,max(vdata(:)));
    elseif strcmp(t,'text')
      pos = get(h(i),'position');
      switch ax
      case 'x'
        vdata = pos(1);
      case 'y'
        vdata = pos(2);
      case 'z'
        vdata = pos(3);
      end
      vmin = min(vmin,min(vdata(:)));
      vmax = max(vmax,max(vdata(:)));
    end
  end
end


if nargin==2 & isstr(dateform), % Determine dateformat from string.
  switch dateform
  case 'dd-mmm-yyyy HH:MM:SS', dateform = 0;
  case 'dd-mmm-yyyy', dateform = 1;
  case 'mm/dd/yy', dateform = 2;
  case 'mmm', dateform = 3;
  case 'm', dateform = 4;
  case 'mm', dateform = 5;
  case 'mm/dd', dateform = 6;
  case 'dd', dateform = 7;
  case 'ddd', dateform = 8;
  case 'd', dateform = 9;
  case 'yyyy', dateform = 10;
  case 'yy', dateform = 11;
  case 'mmmyy', dateform = 12;
  case 'HH:MM:SS', dateform = 13;
  case 'HH:MM:SS PM', dateform = 14;
  case 'HH:MM', dateform = 15;
  case 'HH:MM PM', dateform = 16;
  case 'QQ-YY', dateform = 17;
  case 'QQ', dateform = 18;
  case 'mm/dd-HH:MM', dateform = 19;
  otherwise
    error(sprintf('Unknown date format: %s',dateform))
  end
end


% If hold is on don't change the axis tick locations
if ~ishold,
    if nargin==2,
      switch dateform
      case 0, dateChoice = 'yqmwdHMS';
      case 1, dateChoice = 'yqmwd';
      case 2, dateChoice = 'yqmwd';
      case 3, dateChoice = 'yqm';
      case 4, dateChoice = 'yqm';
      case 5, dateChoice = 'yqm';
      case 6, dateChoice = 'yqmwd';
      case 7, dateChoice = 'yqmwd';
      case 8, dateChoice = 'yqmwd';
      case 9, dateChoice = 'yqmwd';
      case 10, dateChoice = 'y';
      case 11, dateChoice = 'y';
      case 12, dateChoice = 'yqm';
      case 13, dateChoice = 'yqmwdHMS';
      case 14, dateChoice = 'yqmwdHMS';
      case 15, dateChoice = 'yqmwdHMS';
      case 16, dateChoice = 'yqmwdHMS';
      case 17, dateChoice = 'yq';
      case 18, dateChoice = 'yq';
      case 19, dateChoice = 'yqmwd';            % same as 6  Newhall
      otherwise
        error('Date format number must be between 0 and 18.');
      end
      ticks = bestscale(vmin,vmax,dateChoice);
    else
      [ticks,dateform] = bestscale(vmin,vmax);
    end
else
    ticks = get(gca,[ax,'tick']);
end


% ..............................................
% Set axis tick labels      newhall
if dateform == 19, 
     if ax ~= 'x',
         fprintf('Option 19 can only be used with x axis... sorry\n');
         return
     end
     dateform = 6; 
     another = 1;
end
% ..............................................


labels = datestr(ticks,dateform);
set(gca,[ax,'tick'],ticks,[ax,'ticklabel'],labels, ...
        [ax,'lim'],[min(ticks) max(ticks)])


% ..............................................
% newhall
if another == 1,


        xt = get(gca,'XTick');
        yt = get(gca,'YTick');


        set(gca,'units','inches');
        yax = get(gca,'position'); 
        yaxlinch = yax(4);
        ylims = get(gca,'ylim');
        
        offset =  .25 * (ylims(2)-ylims(1)) / yaxlinch
        yy = ylims(1) - offset;


        xlen = ( xt(length(xt)) - xt(1) ) * .027;
        ypos = yy * ones(length(xt),1);
 
        labels = datestr(xt,15);
        text(xt-xlen,ypos,labels);


        set(gca,'units','normal');
end   % 19



aa = get(get(gca,'xlabel'),'position');
offset = ylims(1) - ( .4 * (ylims(2)-ylims(1)) / yaxlinch)
set(get(gca,'xlabel'),'position',[aa(1) offset 0])


%.................................................




%--------------------------------------------------
function [labels,format] = bestscale(xmin,xmax,dateChoice)
%BESTSCALE Returns ticks for "best" scale.
%   [TICKS,FORMAT] = BESTSCALE(XMIN,XMAX) returns the tick
%   locations in the vector TICKS that span the interval (XMIN,XMAX) 
%   with "nice" tick spacing.  The dateform FORMAT is also returned.


if nargin<3, dateChoice = 'yqmwdHMS'; end
penalty = 0.03;


% Compute xmin, xmax if matrices passed.
if length(xmin) > 1, xmin = min(xmin(:)); end
if length(xmax) > 1, xmax = max(xmax(:)); end


% "Good" spacing between dates
if xmin==xmax, 
    xmin = xmin-1;
    xmax = xmax+1;
end
yearDelta = 10.^(max(0,round(log10(xmax-xmin)-3)))* ...
                 [ .1 .2 .25 .5 1 2 2.5 5 10 20 25 50];
yearDelta(yearDelta<1)= []; % Make sure we use integer years.
quarterDelta = [3];
monthDelta = [1];
weekDelta = 1;
dayDelta = [1 2];
hourDelta = [1 3 6];
minuteDelta = [1 5 10 15 30 60];
secondDelta = min(1,10.^(round(log10(xmax-xmin)-1))* ...
                  [ .1 .2 .25 .5 1 2 2.5 5 10 20 25 50 ]);
secondDelta = [secondDelta 1 5 10 15 30 60];
    
x = [xmin xmax];
[y,m,d] = datevec(x);


% Compute continuous variables for the various time scales.
year = y + (m-1)/12 + (d-1)/12/32;
qtr = (y-y(1))*12 + m + d/32 - 1;
mon = (y-y(1))*12 + m + d/32; 
day = x;
week = (x-2)/7;
hour = (x-floor(x(1)))*24;
minute = (x-floor(x(1)))*24*60;
second = (x-floor(x(1)))*24*3600;


% Compute possible low, high and ticks
if any(dateChoice=='y')
    yearHigh = yearDelta.*ceil(year(2)./yearDelta);
    yearLow = yearDelta.*floor(year(1)./yearDelta);
    yrTicks = round((yearHigh-yearLow)./yearDelta);
    yrHigh = datenum(yearHigh,1,1);
    yrLow = datenum(yearLow,1,1);
    % Encode location of year tick locations in format
    yrFormat = 10 + (1:length(yearDelta))/10;
else
    yrHigh=[]; yrLow=[]; yrTicks=[]; yrFormat = 10;
end 


if any(dateChoice=='q'),
    quarterHigh = quarterDelta.*ceil(qtr(2)./quarterDelta);
    quarterLow = quarterDelta.*floor(qtr(1)./quarterDelta);
    qtrTicks = round((quarterHigh-quarterLow)./quarterDelta);
    qtrHigh = datenum(y(1),quarterHigh+1,1);
    qtrLow = datenum(y(1),quarterLow+1,1);
    % Encode location of qtr tick locations in format
    qtrFormat = 17 + (1:length(quarterDelta))/10;
else
    qtrHigh=[]; qtrLow=[]; qtrTicks=[]; qtrFormat = [];
end


if any(dateChoice=='m'),
    monthHigh = monthDelta.*ceil(mon(2)./monthDelta);
    monthLow = monthDelta.*floor(mon(1)./monthDelta);
    monTicks = round((monthHigh-monthLow)./monthDelta);
    monHigh = datenum(y(1),monthHigh,1);
    monLow = datenum(y(1),monthLow,1);
    % Encode location of month tick locations in format
    monFormat = 3 + (1:length(monthDelta))/10;
else
    monHigh=[]; monLow=[]; monTicks=[]; monFormat = [];
end


if any(dateChoice=='w')
    weekHigh = weekDelta.*ceil(week(2)./weekDelta);
    weekLow = weekDelta.*floor(week(1)./weekDelta);
    weekTicks = round((weekHigh-weekLow)./weekDelta);
    weekHigh = weekHigh*7+2;
    weekLow = weekLow*7+2;
    weekFormat = 6*ones(size(weekDelta));
else
    weekHigh=[]; weekLow=[]; weekTicks=[]; weekFormat=[];
end


if any(dateChoice=='d'),
    dayHigh = dayDelta.*ceil(day(2)./dayDelta);
    dayLow = dayDelta.*floor(day(1)./dayDelta);
    dayTicks = round((dayHigh-dayLow)./dayDelta);
    dayFormat = 6*ones(size(dayDelta));
else
    dayHigh=[]; dayLow=[]; dayTicks=[]; dayFormat = [];
end


if any(dateChoice=='H'),
    hourHigh = hourDelta.*ceil(hour(2)./hourDelta);
    hourLow = hourDelta.*floor(hour(1)./hourDelta);
    hourTicks = round((hourHigh-hourLow)./hourDelta);
    hourHigh = datenum(y(1),m(1),d(1),hourHigh,0,0);
    hourLow = datenum(y(1),m(1),d(1),hourLow,0,0);
    hourFormat = 15*ones(size(hourDelta));
else
    hourHigh=[]; hourLow=[]; hourTicks=[]; hourFormat=[];
end


if any(dateChoice=='M')
    minHigh = minuteDelta.*ceil(minute(2)./minuteDelta);
    minLow = minuteDelta.*floor(minute(1)./minuteDelta);
    minTicks = round((minHigh-minLow)./minuteDelta);
    minHigh = datenum(y(1),m(1),d(1),0,minHigh,0);
    minLow = datenum(y(1),m(1),d(1),0,minLow,0);
    minFormat = 15*ones(size(minuteDelta));
else
    minHigh=[]; minLow=[]; minTicks=[]; minFormat=[];
end


if any(dateChoice=='S'),
    secHigh = secondDelta.*ceil(second(2)./secondDelta);
    secLow = secondDelta.*floor(second(1)./secondDelta);
    secTicks = round((secHigh-secLow)./secondDelta);
    secHigh = datenum(y(1),m(1),d(1),0,0,secHigh);
    secLow = datenum(y(1),m(1),d(1),0,0,secLow);
    secFormat = 13*ones(size(secondDelta));
else
    secHigh=[]; secLow=[]; secTicks=[]; secFormat=[];
end


% Concatenate all the date formats together to determine
% the best spacing.
high =  [yrHigh   qtrHigh   monHigh   dayHigh   weekHigh   hourHigh   minHigh   secHigh];
low =   [yrLow    qtrLow    monLow    dayLow    weekLow    hourLow    minLow    secLow];
ticks = [yrTicks  qtrTicks  monTicks  dayTicks  weekTicks  hourTicks  minTicks  secTicks];
format =[yrFormat qtrFormat monFormat dayFormat weekFormat hourFormat minFormat secFormat];


% sort the formats by number of ticks.
[ticks,ndx] = sort(ticks);
high = high(ndx);
low = low(ndx);
format = format(ndx);


% Chose the best fit
fit = (abs(xmin-low) + abs(high-xmax))./(high-low) + penalty*((ticks-5).^2);
i = find(fit == min(fit)); i=i(1);
low = low(i); high = high(i); ticks = ticks(i); format = format(i);


if floor(format) == 3, % Month format
  i = round(rem(format,1)*10); % Retrieve encoded value
  labels = datenum(y(1),linspace(monthLow(i),monthHigh(i),ticks+1),1);
  format = floor(format);
elseif floor(format) == 17, % Quarter format
  i = round(rem(format,1)*10); % Retrieve encoded value
  labels = datenum(y(1),linspace(quarterLow(i)+1,quarterHigh(i)+1,ticks+1),1);
  format = floor(format);
elseif floor(format) == 10, % Year format
  i = round(rem(format,1)*10); % Retrieve encoded value
  labels = datenum(linspace(yearLow(i),yearHigh(i),ticks+1),1,1);
  format = floor(format);
else
  labels = linspace(low,high,ticks+1);
end 