KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > faceless > graph2 > DateAxis


1 // $Id: DateAxis.java,v 1.11 2005/03/31 10:01:53 mike Exp $
2

3 package org.faceless.graph2;
4
5 import java.text.DateFormat JavaDoc;
6 import java.text.SimpleDateFormat JavaDoc;
7 import java.util.*;
8
9 /**
10  * A subclass of Axis to be used for plotting date values. Dates are stored
11  * internally as doubles, and the two can be converted back and forth using
12  * the {@link #toDouble toDouble} and {@link #toDate toDate} methods.
13  * For example, to plot a number of dates on a {@link LineSeries} you could use
14  * code like the following:
15  *
16  * <pre class=example>
17  * AxesGraph graph = new AxesGraph();
18  * LineSeries series = new LineSeries("Dates");
19  * series.set(DateAxis.toDouble(date1), value1);
20  * series.set(DateAxis.toDouble(date2), value2);
21  * graph.setAxis(Axis.BOTTOM, new DateAxis());
22  * </pre>
23  * (here <i>date1</i> and <i>date2</i> are {@link Date} objects).
24  * Bars can also be plotted against a DateAxis - see the {@link GeneralBarSeries} API documentation for more info.
25  */

26 public class DateAxis extends Axis
27 {
28     private static final long MIDPOINT = 788940000; // 25 years
29
private static final int SECDAYS=60*60*24; // number of seconds in a day
30
private static final double mul=1000; // used to convert doubles to days and vice versa.
31
// Needed because of rounding error
32

33     final DateFormat JavaDoc f;
34     final boolean hasseconds, hashours, hasminutes, hasdays, hasmonths;
35     private final float mdensity;
36     private boolean barsatnoon;
37
38     /**
39      * Create a new DateAxis with a format of "dd-MMM-yyyy"
40      */

41     public DateAxis()
42     {
43         this(new SimpleDateFormat JavaDoc("dd-MMM-yyyy"));
44     }
45
46     /**
47      * Create a new DateAxis with the specified format
48      */

49     public DateAxis(DateFormat JavaDoc format)
50     {
51         this(format, DENSITY_NORMAL);
52     }
53
54     /**
55      * Create a new DateAxis with the specified format and density
56      *
57      * @param format how each date should be formatted
58      * @param density the density of the values on the axis - one of {@link Axis#DENSITY_NORMAL}, {@link Axis#DENSITY_SPARSE} or {@link Axis#DENSITY_MINIMAL}
59      */

60     public DateAxis(DateFormat JavaDoc format, int density)
61     {
62         f = format;
63     long l = 1012820522000l; // A random date that has hours, mins, seconds all non-zero.
64
String JavaDoc s = f.format(new Date(l));
65     hasseconds = !s.equals(f.format(new Date(l+1000l)));
66     hasminutes = !s.equals(f.format(new Date(l+60000l)));
67     hashours = !s.equals(f.format(new Date(l+3700000l)));
68     hasdays = !s.equals(f.format(new Date(l+90100000l)));
69     hasmonths = !s.equals(f.format(new Date(l+2678400000l)));
70     this.mdensity = density/10f;
71     }
72
73     public String JavaDoc format(double in)
74     {
75     return f.format(toDate(in));
76     }
77
78     /**
79      * Convert a date to a double, so it can be stored internally.
80      * For example, to plot todays date on a Line Graph, you would do
81      * something like this:
82      * <pre class=example>
83      * LineSeries data = new LineSeries("Dates");
84      * Date today = new Date();
85      * data.set(DateAxis.toDouble(today), 123.45);
86      * </pre>
87      * @see #toDate
88      */

89     public static final double toDouble(Date in)
90     {
91      return (double)(in.getTime()/mul)-MIDPOINT;
92     }
93
94     /**
95      * Convert a double to a Date. The opposite of {@link #toDouble}. Use this
96      * to convert a double passed in to the {@link #format} method back to
97      * a Date.
98      */

99     public static final Date toDate(double in)
100     {
101         return new Date((long)((in+MIDPOINT)*mul));
102     }
103
104     public double[] steps(double min, double max)
105     {
106         Date start = toDate(min);
107         Date end = toDate(max);
108     long diff = (long)(max-min); // Difference in seconds
109
int step;
110
111     if (hasseconds && diff<1800) {
112         step = 1;
113     } else if (hasminutes && diff<43200) {
114         step = 60;
115     } else if (hashours && diff<SECDAYS*7) {
116         step = 3600;
117     } else {
118         step = SECDAYS;
119     }
120
121     List a = new ArrayList();
122
123     String JavaDoc oldt=null, t=null;
124     for (long i=((int)(min+step)/step)*step;i<=(int)(max+(step/2));i+=step)
125     {
126         Calendar c = new GregorianCalendar();
127         c.setTime(toDate((double)i));
128         if (step==SECDAYS && c.get(c.HOUR_OF_DAY)!=0) {
129             int h = c.get(c.HOUR_OF_DAY);
130         if (h>=22) h=24-h;
131         i-=(h*60*60);
132         c.setTime(toDate((double)i));
133         }
134
135         boolean add=false;
136         if (hasseconds && diff<=15*mdensity) { // 15 seconds - every seconds, tot<=15
137
add=true;
138         } else if (hasseconds && diff<=60*mdensity) { // 1 minutes - every 5 seconds, tot<=12
139
if (c.get(c.SECOND)%5==0) add=true;
140         } else if (hasseconds && diff<=180*mdensity) { // 3 minutes - every 15 seconds, tot<=12
141
if (c.get(c.SECOND)%15==0) add=true;
142         } else if (hasseconds && diff<=300*mdensity) { // 5 minutes - every 30 seconds, tot<=10
143
if (c.get(c.SECOND)%30==0) add=true;
144         } else if (hasminutes && diff<=900*mdensity) { // 15 minutes - every minute, tot<=15
145
if (c.get(c.SECOND)==0) add=true;
146         } else if (hasminutes && diff<=3600*mdensity) { // 1 hour - every 5 minutes, tot<=12
147
if (c.get(c.MINUTE)%5==0 && c.get(c.SECOND)==0) add=true;
148         } else if (hasminutes && diff<=7200*mdensity) { // 2 hours - every 10 minutes, tot<=12
149
if (c.get(c.MINUTE)%10==0 && c.get(c.SECOND)==0) add=true;
150         } else if (hasminutes && diff<=10800*mdensity) { // 3 hours - every 15 minutes, tot<=12
151
if (c.get(c.MINUTE)%15==0 && c.get(c.SECOND)==0) add=true;
152         } else if (hasminutes && diff<=21600*mdensity) { // 6 hours - every 30 minutes, tot<=12
153
if (c.get(c.MINUTE)%30==0 && c.get(c.SECOND)==0) add=true;
154         } else if (hashours && diff<=SECDAYS*mdensity) { // 24 hours - every 2 hours, tot<=12
155
if (c.get(c.HOUR_OF_DAY)%2==0 && c.get(c.MINUTE)==0) add=true;
156         } else if (hashours && diff<4*SECDAYS*mdensity) { // 96 hours (4 days) - every 6 hours, tot<15
157
if (c.get(c.HOUR_OF_DAY)%6==0 && c.get(c.MINUTE)==0) add=true;
158         } else if (hashours && diff<=6*SECDAYS*mdensity) { // 144 hours (6 days) - ev. 12 hours, tot<=12
159
if (c.get(c.HOUR_OF_DAY)%12==0 && c.get(c.MINUTE)==0) add=true;
160         } else if (diff<15*SECDAYS*mdensity) { // 2 weeks - every day, tot<=14
161
if (step>3600 || c.get(c.HOUR_OF_DAY)==0) add=true;
162         } else if (diff<32*SECDAYS*mdensity) { // 1 month - every 2 days, tot<=15
163
if (c.get(c.DAY_OF_MONTH)%2==1 && c.get(c.HOUR_OF_DAY)==0) add=true;
164         } else if (diff<100*SECDAYS*mdensity) { // 3 months - every sunday, tot<=13
165
if (c.get(c.DAY_OF_WEEK)==1 && c.get(c.HOUR_OF_DAY)==0) add=true;
166         } else if (diff<200*SECDAYS*mdensity) { // 6 months - every 1st and 15th, tot<=12
167
if ((c.get(c.DAY_OF_MONTH)==1 || c.get(c.DAY_OF_MONTH)==15) && c.get(c.HOUR_OF_DAY)==0) add=true;
168         } else if (diff<367*SECDAYS*mdensity) { // 1 year - every 1st, tot<=12
169
if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.HOUR_OF_DAY)==0) add=true;
170         } else if (diff<730*SECDAYS*mdensity) { // 2 years - 1st of every 2nd month, tot<=12
171
if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.MONTH)%2==0 && c.get(c.HOUR_OF_DAY)==0) add=true;
172         } else if (diff<2190*SECDAYS*mdensity) { // 3 years - 1st of every quarter, tot<=12
173
if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.MONTH)%3==0 && c.get(c.HOUR_OF_DAY)==0) add=true;
174         } else if (diff<4400*SECDAYS*mdensity) { // 6 years - 1st of jan/jul, tot<=12
175
if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.MONTH)%6==0 && c.get(c.HOUR_OF_DAY)==0) add=true;
176         } else if (diff<5478*SECDAYS*mdensity) { // 15 years - 1st of jan, tot<=15
177
if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.MONTH)==0 && c.get(c.HOUR_OF_DAY)==0) add=true;
178         } else { // Over 15 years - Jan 1st every n years
179
int y = (int)(diff/(5478*SECDAYS*mdensity))+1;
180             if (c.get(c.DAY_OF_MONTH)==1 && c.get(c.MONTH)==0 && (c.get(c.YEAR)%y)==0 && c.get(c.HOUR_OF_DAY)==0) add=true;
181         }
182         if (add) {
183         oldt=t;
184             t = format((double)i);
185         if (!t.equals(oldt)) {
186             a.add(new Double JavaDoc(i));
187         }
188         }
189     }
190
191     // Add the first and last days of the specified period. If they're
192
// extremely close to calculated values from the block above,
193
// replace them (instead of adding new values).
194
//
195
if (a.size()<2) {
196         a.clear();
197         a.add(new Double JavaDoc(min));
198         a.add(new Double JavaDoc(max));
199     } else {
200         double first = ((Double JavaDoc)a.get(0)).doubleValue();
201         double last = ((Double JavaDoc)a.get(a.size()-1)).doubleValue();
202         if (first!=min) {
203             t = format(min);
204         String JavaDoc t2 = format(first);
205         if (!t.equals(t2)) {
206             if ((first-min)/(max-min)>0.05/mdensity) {
207             a.add(0, new Double JavaDoc(min));
208 // throw new Error("first="+first+" min="+min+" max="+max+" first-min="+(first-min)+" max-min="+(max-min)+" f/m="+((first-min)/(max-min))+" den="+mdensity);
209
} else {
210             a.set(0, new Double JavaDoc(min));
211             }
212         } else {
213             a.set(0, new Double JavaDoc(first<min?first:min));
214         }
215         }
216         if (last!=max) {
217             t = format(max);
218         String JavaDoc t2 = format(last);
219         if (!t.equals(t2)) {
220             if (((max-last)/(max-min))>0.05/mdensity) {
221             a.add(new Double JavaDoc(max));
222             } else {
223             a.set(a.size()-1, new Double JavaDoc(max));
224             }
225         } else {
226             a.set(a.size()-1, new Double JavaDoc(last>max?last:max));
227         }
228         }
229     }
230
231     // Return the list as a double[]
232
//
233
double[] out = new double[a.size()];
234     for (int i=0;i<out.length;i++) {
235         out[i] = ((Double JavaDoc)a.get(i)).doubleValue();
236     }
237     return out;
238     }
239
240     /**
241      * When plotting a {@link GeneralBarSeries} against a DateAxis, whether
242      * to center the bars on the tick, ie. midnight (false) or midway between the tick and
243      * the next tick, ie. noon (true). Typically you will want to set this method to
244      * true when plotting bars against lines that represent hour values, not just day values.
245      * When not plotting Bars, this method has no effect
246      *
247      * @param noon whether to center bars plotted against this axis at midday
248      */

249     public void setBarsAtNoon(boolean noon)
250     {
251         this.barsatnoon = noon;
252     }
253
254     boolean getBarsAtNoon()
255     {
256         return barsatnoon;
257     }
258 }
259
Popular Tags