]> Creatis software - CreaPhase.git/blob - octave_packages/m/time/datetick.m
update packages
[CreaPhase.git] / octave_packages / m / time / datetick.m
1 ## Copyright (C) 2008-2012 David Bateman
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING.  If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn  {Function File} {} datetick ()
21 ## @deftypefnx {Function File} {} datetick (@var{form})
22 ## @deftypefnx {Function File} {} datetick (@var{axis}, @var{form})
23 ## @deftypefnx {Function File} {} datetick (@dots{}, "keeplimits")
24 ## @deftypefnx {Function File} {} datetick (@dots{}, "keepticks")
25 ## @deftypefnx {Function File} {} datetick (@dots{ax}, @dots{})
26 ## Add date formatted tick labels to an axis.  The axis the apply the
27 ## ticks to is determined by @var{axis} that can take the values "x",
28 ## "y" or "z".  The default value is "x".  The formatting of the labels is
29 ## determined by the variable @var{form}, that can either be a string in
30 ## the format needed by @code{dateform}, or a positive integer that can
31 ## be accepted by @code{datestr}.
32 ## @seealso{datenum, datestr}
33 ## @end deftypefn
34
35 function datetick (varargin)
36
37   [h, varargin, nargin] = __plt_get_axis_arg__ ("datetick", varargin{:});
38
39   oldh = gca ();
40   unwind_protect
41     axes (h);
42     __datetick__ (varargin{:});
43   unwind_protect_cleanup
44     axes (oldh);
45   end_unwind_protect
46
47 endfunction
48
49 %!demo
50 %! yr = 1900:10:2000;
51 %! pop = [76.094, 92.407, 106.461, 123.077 131.954, 151.868, 179.979, ...
52 %!        203.984, 227.225, 249.623, 282.224];
53 %! plot (datenum (yr, 1, 1), pop);
54 %! title ("US population (millions)");
55 %! xlabel ("Year");
56 %! datetick ("x", "YYYY");
57
58 %!demo
59 %! yr =1988:2:2002;
60 %! yr =datenum(yr,1,1);
61 %! pr = [12.1 13.3 12.6 13.1 13.3 14.1 14.4 15.2];
62 %! plot(yr,pr);
63 %! xlabel('year')
64 %! ylabel('average price')
65 %! ax=gca;
66 %! set(ax,'xtick',datenum(1990:5:2005,1,1))
67 %! datetick(2,'keepticks')
68 %! set(ax,'ytick',12:16)
69
70 ## Remove from test statistics.  No real tests possible.
71 %!assert (1)
72
73 function __datetick__ (varargin)
74
75   keeplimits = false;
76   keepticks = false;
77   idx = [];
78   for i = 1 : nargin
79     arg = varargin {i};
80     if (ischar (arg))
81       if (strcmpi (arg, "keeplimits"))
82         keeplimits = true;
83         idx = [idx, i];
84       elseif (strcmpi (arg, "keepticks"))
85         keepticks = true;
86         idx = [idx, i];
87       endif
88     endif
89   endfor
90
91   varargin(idx) = [];
92   nargin = length (varargin);
93   form = [];
94   ax = "x";
95
96   if (nargin != 0)
97     arg = varargin{1};
98     if (ischar (arg) && (strcmp (arg, "x") || strcmp (arg, "y")
99                          || strcmp (arg, "z")))
100       ax = arg;
101       if (nargin > 1)
102         form = varargin{2};
103         varargin(1:2) = [];
104       else
105         varargin(1) = [];
106       endif
107     else
108       form = arg;
109       varargin(1) = [];
110     endif
111   endif
112
113   ## Don't publish the existence of this variable for use with dateaxis
114   if (length (varargin) > 0)
115     startdate = varargin{1};
116   else
117     startdate = [];
118   endif
119
120   if (! isempty (form))
121     if (isnumeric (form))
122       if (! isscalar (form) || floor (form) != form || form < 0)
123         error ("datetick: expecting FORM argument to be a positive integer");
124       endif
125     elseif (! ischar (form))
126       error ("datetick: expecting valid date format string");
127     endif
128   endif
129
130   if (keepticks)
131     ticks = get (gca (), strcat (ax, "tick"));
132   else
133     ## Need to do our own axis tick position calculation as
134     ## year, etc, don't fallback on nice datenum values.
135     objs = findall (gca());
136     xmax = NaN;
137     xmin = NaN;
138     for i = 1 : length (objs)
139       fld = get (objs (i));
140       if (isfield (fld, strcat (ax, "data")))
141         xdata = getfield (fld, strcat (ax, "data"))(:);
142         xmin = min (xmin, min (xdata));
143         xmax = max (xmax, max (xdata));
144       endif
145     endfor
146
147     if (isnan (xmin) || isnan (xmax))
148       xmin = 0;
149       xmax = 1;
150     elseif (xmin == xmax)
151       xmax = xmin + 1;
152     endif
153
154     N = 3;
155     if (xmax - xmin < N)
156       ## Day scale or less
157       if (xmax - xmin < N / 24 / 60 / 60)
158         scl = 1 / 24 / 60 / 60;
159       elseif (xmax - xmin < N / 24 / 6)
160         scl = 1 / 24 / 60;
161       else
162         scl = 1 / 24;
163       endif
164       sep = __calc_tick_sep__ (xmin / scl , xmax / scl);
165       xmin = sep * floor (xmin / scl / sep);
166       xmax = sep * ceil (xmax / scl / sep);
167       nticks = (xmax - xmin) / sep + 1;
168       xmin *= scl;
169       xmax *= scl;
170     else
171       [ymin, mmin, dmin] = datevec (xmin);
172       [ymax, mmax, dmax] = datevec (xmax);
173       minyear = ymin + (mmin - 1) / 12 + (dmin - 1) / 12 / 30;
174       maxyear = ymax + (mmax - 1) / 12 + (dmax - 1) / 12 / 30;
175       minmonth = mmin + (dmin - 1) / 30;
176       maxmonth = (ymax  - ymin) * 12 + mmax + (dmax - 1) / 30;
177
178       if (maxmonth - minmonth < N)
179         sep = __calc_tick_sep__ (xmin, xmax);
180         xmin = sep * floor (xmin / sep);
181         xmax = sep * ceil (xmax / sep);
182         nticks = (xmax - xmin) / sep + 1;
183       elseif (maxyear - minyear < N)
184         sep = __calc_tick_sep__ (minmonth , maxmonth);
185         xmin = datenum (ymin, sep * floor (minmonth / sep), 1);
186         xmax = datenum (ymin, sep * ceil (maxmonth / sep), 1);
187         nticks = ceil (maxmonth / sep) - floor (minmonth / sep) + 1;
188       else
189         sep = __calc_tick_sep__ (minyear , maxyear);
190         xmin = datenum (sep * floor (minyear / sep), 1, 1);
191         xmax = datenum (sep * ceil (maxyear / sep), 1, 1);
192         nticks = ceil (maxyear / sep) - floor (minyear / sep) + 1;
193       endif
194     endif
195     ticks = xmin + [0 : nticks - 1] / (nticks - 1) * (xmax - xmin);
196   endif
197
198   if (isempty (form))
199     r = max(ticks) - min(ticks);
200     if r < 10/60/24
201       ## minutes and seconds
202       form = 13;
203     elseif r < 2
204       ## hours
205       form = 15;
206     elseif r < 15
207       ## days
208       form = 8;
209     elseif r < 365
210       ## FIXME -- FORM should be 19 for European users who use dd/mm
211       ## instead of mm/dd.  How can that be determined automatically?
212       ## months
213       form = 6;
214     elseif r < 90*12
215       ## quarters
216       form = 27;
217     else
218       ## years
219       form = 10;
220     endif
221   endif
222
223   if (length (ticks) == 6)
224     ## Careful that its not treated as a datevec
225     if (! isempty (startdate))
226       sticks = strvcat (datestr (ticks(1:end-1) - ticks(1) + startdate, form),
227       datestr (ticks(end) - ticks(1) + startdate, form));
228     else
229       sticks = strvcat (datestr (ticks(1:end-1), form),
230       datestr (ticks(end), form));
231     endif
232   else
233     if (! isempty (startdate))
234       sticks = datestr (ticks - ticks(1) + startdate, form);
235     else
236       sticks = datestr (ticks, form);
237     endif
238   endif
239
240   sticks = mat2cell (sticks, ones (rows (sticks), 1), columns (sticks));
241
242   if (keepticks)
243     if (keeplimits)
244       set (gca(), strcat (ax, "ticklabel"), sticks);
245     else
246       set (gca(), strcat (ax, "ticklabel"), sticks, strcat (ax, "lim"),
247       [min(ticks), max(ticks)]);
248     endif
249   else
250     if (keeplimits)
251       set (gca(), strcat (ax, "tick"), ticks, strcat (ax, "ticklabel"), sticks);
252     else
253       set (gca(), strcat (ax, "tick"), ticks, strcat (ax, "ticklabel"), sticks,
254       strcat (ax, "lim"), [min(ticks), max(ticks)]);
255     endif
256   endif
257 endfunction
258
259 function [a, b] = __magform__ (x)
260   if (x == 0)
261     a = 0;
262     b = 0;
263   else
264     l = log10 (abs (x));
265     r = fmod (l, 1);
266     a = 10 .^ r;
267     b = fix (l - r);
268     if (a < 1)
269       a *= 10;
270       b -= 1;
271     endif
272     if (x < 0)
273       a = -a;
274     endif
275   endif
276 endfunction
277
278 ## A translation from Tom Holoryd's python code at
279 ## http://kurage.nimh.nih.gov/tomh/tics.py
280 function sep = __calc_tick_sep__ (lo, hi)
281   persistent sqrt_2  = sqrt (2.0);
282   persistent sqrt_10 = sqrt (10.0);
283   persistent sqrt_50 = sqrt (50.0);
284
285   ticint = 5;
286
287   ## Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
288   ## SCALE3 for Determination of Scales on Computer Generated
289   ## Plots", Communications of the ACM, 10 (1973), 639-640.
290   ## Also cited as ACM Algorithm 463.
291
292   [a, b] = __magform__ ((hi - lo) / ticint);
293
294   if (a < sqrt_2)
295     x = 1;
296   elseif (a < sqrt_10)
297     x = 2;
298   elseif (a < sqrt_50)
299     x = 5;
300   else
301     x = 10;
302   endif
303   sep = x * 10 .^ b;
304 endfunction
305