1 | ; $Id: label_date.pro,v 1.10 2001/01/15 22:28:06 scottm Exp $ |
---|
2 | ; |
---|
3 | ; Copyright (c) 1993-2001, Research Systems, Inc. All rights reserved. |
---|
4 | ; Unauthorized reproduction prohibited. |
---|
5 | |
---|
6 | ;+ |
---|
7 | ; NAME: |
---|
8 | ; LABEL_DATE |
---|
9 | ; |
---|
10 | ; PURPOSE: |
---|
11 | ; This function labels axes with dates and times. |
---|
12 | ; |
---|
13 | ; CATEGORY: |
---|
14 | ; Plotting. |
---|
15 | ; |
---|
16 | ; CALLING SEQUENCE: |
---|
17 | ; To set up: |
---|
18 | ; dummy = LABEL_DATE(DATE_FORMAT='string') |
---|
19 | ; To use: |
---|
20 | ; PLOT, x, y, XTICKFORMAT='LABEL_DATE' |
---|
21 | ; |
---|
22 | ; INPUTS: |
---|
23 | ; No explicit user defined inputs. When called from the plotting |
---|
24 | ; routines, the input parameters are (Axis, Index, Value [, Level]) |
---|
25 | ; |
---|
26 | ; KEYWORD PARAMETERS: |
---|
27 | ; DATE_FORMAT: a format string which may contain the following: |
---|
28 | ; %M for month |
---|
29 | ; %N for month (2 digit abbr) |
---|
30 | ; %D for day of month, |
---|
31 | ; %Y for 4 digit year. |
---|
32 | ; %Z for last two digits of year. |
---|
33 | ; %W for day of week |
---|
34 | ; For time: |
---|
35 | ; %A for AM or PM |
---|
36 | ; %H for Hours, 2 digits. |
---|
37 | ; %I for mInutes, 2 digits. |
---|
38 | ; %S for Seconds, 2 digits. |
---|
39 | ; %0--%9 following %S, indicates digits after decimal point. |
---|
40 | ; %% is %. |
---|
41 | ; |
---|
42 | ; If a time format string is specified, the time of day |
---|
43 | ; will be rounded to the nearest least significant time format |
---|
44 | ; specified. E.g. if the format '%H:%I' is specified |
---|
45 | ; (hours:minutes) the time is rounded to the nearest minute. |
---|
46 | ; |
---|
47 | ; Other characters are passed directly thru. |
---|
48 | ; For example, '%M %D, %Y' prints DEC 11, 1993 |
---|
49 | ; '%M %2Y' yields DEC 93 |
---|
50 | ; '%D-%M' yields 11-DEC |
---|
51 | ; '%D/%N/%Y' yields 11/12/1993 |
---|
52 | ; '%M!C%Y' yields DEC on the top line, 1993 on |
---|
53 | ; the bottom (!C is the new line graphic command). |
---|
54 | ; |
---|
55 | ; AM_PM: The names for AM or PM. The default is ["am","pm"] |
---|
56 | ; |
---|
57 | ; DAYS_OF_WEEK: The names of the days, a seven-element string array. |
---|
58 | ; The default is [Sun, Mon, Tue, Wed, Thu, Fri, Sat]. |
---|
59 | ; |
---|
60 | ; MONTHS: The names of the months, a twelve element string array. |
---|
61 | ; The default is [Jan, Feb,..., Dec]. |
---|
62 | ; |
---|
63 | ; OFFSET: Set this keyword to a value representing the offset to be added |
---|
64 | ; to each tick value before conversion to a label. This keyword |
---|
65 | ; is usually used when your axis values are measured relative to |
---|
66 | ; a certain starting time. In this case, OFFSET should be set to |
---|
67 | ; the Julian date of the starting time. |
---|
68 | ; |
---|
69 | ; ROUND_UP: Set this keyword to force times to be rounded up to the |
---|
70 | ; smallest time unit that is present in the DATE_FORMAT string. |
---|
71 | ; The default is for times to be truncated to the |
---|
72 | ; smallest time unit. |
---|
73 | ; |
---|
74 | ; OUTPUTS: |
---|
75 | ; The date string to be plotted. |
---|
76 | ; |
---|
77 | ; COMMON BLOCKS: |
---|
78 | ; LABEL_DATE_COM. |
---|
79 | ; |
---|
80 | ; RESTRICTIONS: |
---|
81 | ; Uses a common block, so only one date axis may be simultaneously active. |
---|
82 | ; |
---|
83 | ; PROCEDURE: |
---|
84 | ; Straightforward. |
---|
85 | ; |
---|
86 | ; For an alternative way to label a plot axis with dates, refer to |
---|
87 | ; the C() format code accepted within format strings (applicable via |
---|
88 | ; the [XYZ]TICKFORMAT keywords). This format code was |
---|
89 | ; introduced in IDL 5.2. |
---|
90 | ; |
---|
91 | ; EXAMPLE: |
---|
92 | ; For example, to plot from Jan 1, 1993, to July 12, 1994: |
---|
93 | ; Start_date = julday(1, 1, 1993) |
---|
94 | ; End_date = julday(7, 12, 1994) |
---|
95 | ; Dummy = LABEL_DATE(DATE_FORMAT='%N/%D') ;Simple mm/dd |
---|
96 | ; x = findgen(end_date+1 - start_date) + start_date ;Time axis |
---|
97 | ; PLOT, x, sqrt(x), XTICKFORMAT = 'LABEL_DATE', XSTYLE=1 |
---|
98 | ; (Plot with X axis style set to exact.) |
---|
99 | ; |
---|
100 | ; Example with times: |
---|
101 | ; For example, to plot from 3PM, Jan 1, 1993, to 5AM, Jan 3, |
---|
102 | ; 1993: |
---|
103 | ; Start_date = Julday(1,1,1993) ;Also starting offset |
---|
104 | ; Start_time = (3+12)/24. ;Starting_time less offset |
---|
105 | ; End_time = (Julday(1,3,1993) - Start_date) + 5./24. ;Ending |
---|
106 | ; ;date/time - offset, note that the order of operations is |
---|
107 | ; ; important to avoid loss of precision. |
---|
108 | ; Dummy = LABEL_DATE(DATE_FORMAT='%D %M!C%H:%I', $ |
---|
109 | ; offset=Start_date) ;MMM NN <new line> HH:MM format |
---|
110 | ; x = findgen(20) * (End_time - Start_time) / 19 + start_time ;Time axis |
---|
111 | ; PLOT, x, sqrt(x), XTICKFORMAT = 'LABEL_DATE', XSTYLE=1 |
---|
112 | ; |
---|
113 | ; MODIFICATION HISTORY: |
---|
114 | ; DMS, RSI. April, 1993. Written. |
---|
115 | ; DMS, RSI. March, 1997. Added Time format. |
---|
116 | ; DMS, RSI. Jan, 1999. Rounded least significant time unit |
---|
117 | ; CT, RSI. May 2000. Completely rewrote to use calendar format codes. |
---|
118 | ; Added Level argument for new date/time TICKUNITS. |
---|
119 | ; Added AM_PM and DAYS_OF_WEEK keywords, '%A' and '%W' codes. |
---|
120 | ;- |
---|
121 | |
---|
122 | |
---|
123 | ;-------------------------------------------------- LABEL_DATE_CONVERT_FORMAT |
---|
124 | ; Given a LABEL_DATE input string, convert codes to Calendar format codes. |
---|
125 | FUNCTION label_date_convert_format, format, cMonths, cAmpm, cDaysWeek |
---|
126 | |
---|
127 | COMPILE_OPT idl2, hidden |
---|
128 | |
---|
129 | ON_ERROR, 2 |
---|
130 | |
---|
131 | n = strlen(format) |
---|
132 | newFormat = '' |
---|
133 | for i=0L, n-1 do begin ;Each format character... |
---|
134 | add = strmid(format, i, 1) ;The character. |
---|
135 | if add eq '%' then begin |
---|
136 | i = i + 1 ; skip to next character |
---|
137 | c = STRUPCASE(strmid(format, i, 1)) |
---|
138 | ; if format character, then convert to calendar format code |
---|
139 | case c of |
---|
140 | ; if user specified months, then width=zero to use "natural" |
---|
141 | ; month length, otherwise use the default month length (3) |
---|
142 | 'M': add = (N_ELEMENTS(cMonths) EQ 12) ? 'CMoA0' : 'CMoA' |
---|
143 | 'N': add = 'CMOI2.2' |
---|
144 | 'D': add = 'CDI2.2' |
---|
145 | 'Y': add = 'CYI4' |
---|
146 | 'Z': add = 'CYI2.2' |
---|
147 | 'H': add = 'CHI2.2' |
---|
148 | 'I': add = 'CMI2.2' |
---|
149 | 'S': BEGIN |
---|
150 | add = 'CSI2.2' ; default format for seconds |
---|
151 | ; check for additional %n code |
---|
152 | IF (STRMID(format,i+1,1) EQ '%') THEN BEGIN |
---|
153 | numberChar = STRMID(format,i+2,1) |
---|
154 | number = STRPOS('0123456789',numberChar) |
---|
155 | IF (number GE 0) THEN BEGIN |
---|
156 | i = i + 2 ; skip format characters |
---|
157 | width = STRTRIM(3+number,2) ;tens+ones+decimal |
---|
158 | add = 'CSF' + width + '.' + numberChar |
---|
159 | ; add TLn,CSI2.2 to include leading zero |
---|
160 | add = add + ',TL' + width + ',CSI2.2' |
---|
161 | ENDIF |
---|
162 | ENDIF |
---|
163 | END |
---|
164 | 'W' : add = (N_ELEMENTS(cDaysWeek) EQ 7) ? 'CDwA0' : 'CDwA' |
---|
165 | 'A' : add = (N_ELEMENTS(cAmpm) EQ 2) ? 'CapA0' : 'CapA' |
---|
166 | '%' : add = '"%"' |
---|
167 | ELSE: add = '""' ; if unknown, add null string |
---|
168 | endcase |
---|
169 | endif else add = '"' + add + '"' ; just output the character |
---|
170 | newFormat = (newFormat EQ '') ? add : newFormat + ',' + add |
---|
171 | endfor |
---|
172 | IF (STRPOS(newFormat,'CapA') GE 0) THEN BEGIN |
---|
173 | hourPos = STRPOS(newFormat,'CHI') |
---|
174 | IF (hourPos GE 0) THEN newFormat = STRMID(newFormat,0,hourPos+1) + $ |
---|
175 | 'hI' + STRMID(newFormat,hourPos+6) |
---|
176 | ENDIF |
---|
177 | RETURN, '(C(' + newFormat + '))' |
---|
178 | END |
---|
179 | |
---|
180 | |
---|
181 | ;----------------------------------------------------------------- LABEL_DATE |
---|
182 | FUNCTION LABEL_DATE, axisIn, indexIn, valueIn, levelIn, $ |
---|
183 | AM_PM = am_pm, $ |
---|
184 | DATE_FORMAT = dateFormat, $ |
---|
185 | DAYS_OF_WEEK = days_of_week, $ |
---|
186 | MONTHS = months, $ |
---|
187 | OFFSET = offs, $ |
---|
188 | ROUND_UP = round_up |
---|
189 | |
---|
190 | COMPILE_OPT idl2 |
---|
191 | COMMON label_date_com, cFormatArray, cMonths, cOffset, $ |
---|
192 | cRoundup, cAmpm, cDaysWeek |
---|
193 | ON_ERROR, 2 |
---|
194 | |
---|
195 | |
---|
196 | IF (N_PARAMS() LT 3) THEN $ ; use default for no inputs |
---|
197 | IF NOT KEYWORD_SET(dateFormat) THEN dateFormat='' |
---|
198 | |
---|
199 | |
---|
200 | ; process a new months vector? |
---|
201 | ; if months is undefined, then make cMonths undefined |
---|
202 | IF ARG_PRESENT(months) OR (N_ELEMENTS(months) GT 0) THEN BEGIN |
---|
203 | cMonths = -1 |
---|
204 | dummy = TEMPORARY(cMonths) |
---|
205 | ENDIF |
---|
206 | ; if months has 12 elements, then copy it to cMonths |
---|
207 | if (N_ELEMENTS(months) EQ 12) then cMonths = months |
---|
208 | |
---|
209 | |
---|
210 | ; process a new days_of_week vector? |
---|
211 | ; if days_of_week is undefined, then make cDaysWeek undefined |
---|
212 | IF ARG_PRESENT(days_of_week) OR (N_ELEMENTS(days_of_week) GT 0) THEN BEGIN |
---|
213 | cDaysWeek = -1 |
---|
214 | dummy = TEMPORARY(cDaysWeek) |
---|
215 | ENDIF |
---|
216 | ; if days_of_week has 2 elements, then copy it to cDaysWeek |
---|
217 | if (N_ELEMENTS(days_of_week) EQ 7) then cDaysWeek = days_of_week |
---|
218 | |
---|
219 | |
---|
220 | ; process a new AM_PM vector? |
---|
221 | ; if AM_PM is undefined, then make cAmpm undefined |
---|
222 | IF ARG_PRESENT(am_pm) OR (N_ELEMENTS(am_pm) GT 0) THEN BEGIN |
---|
223 | cAmpm = -1 |
---|
224 | dummy = TEMPORARY(cAmpm) |
---|
225 | ENDIF |
---|
226 | ; if AM_PM has 2 elements, then copy it to cAmpm |
---|
227 | if (N_ELEMENTS(am_pm) EQ 2) then cAmpm = am_pm |
---|
228 | |
---|
229 | |
---|
230 | ; process a new cOffset? |
---|
231 | IF ARG_PRESENT(offs) THEN cOffset = 0 |
---|
232 | IF (N_ELEMENTS(offs) GT 0) THEN cOffset = offs[0] |
---|
233 | ; make sure we have an cOffset |
---|
234 | IF (N_ELEMENTS(cOffset) EQ 0) THEN cOffset = 0d |
---|
235 | |
---|
236 | |
---|
237 | ; process a new cRoundup? |
---|
238 | IF (N_ELEMENTS(round_up) GT 0) THEN cRoundup = KEYWORD_SET(round_up) |
---|
239 | |
---|
240 | |
---|
241 | ; process a new date_format string? |
---|
242 | nNewFormat = N_ELEMENTS(dateFormat) |
---|
243 | IF (nNewFormat GT 0) THEN BEGIN |
---|
244 | cFormatArray = STRARR(nNewFormat) |
---|
245 | FOR a=0L,nNewFormat-1 DO cFormatArray[a] = $ |
---|
246 | LABEL_DATE_CONVERT_FORMAT(dateFormat[a],cMonths,cAmpm,cDaysWeek) |
---|
247 | ENDIF |
---|
248 | IF (N_ELEMENTS(cFormatArray) EQ 0) THEN cFormatArray = '(C())' |
---|
249 | |
---|
250 | |
---|
251 | IF (N_PARAMS() LT 3) THEN RETURN, 0 |
---|
252 | |
---|
253 | |
---|
254 | ;------------------------------------------------------ Process an axis value |
---|
255 | value1 = valueIn + cOffset ; convert to Julian |
---|
256 | date = LONG(value1) ; Julian date |
---|
257 | time = value1 - date ; Julian time |
---|
258 | |
---|
259 | |
---|
260 | IF (N_ELEMENTS(levelIn) LT 1) THEN levelIn = 0 |
---|
261 | nFormat = N_ELEMENTS(cFormatArray) |
---|
262 | formatLevel = cFormatArray[levelIn MOD nFormat] ; repeat cyclically |
---|
263 | |
---|
264 | |
---|
265 | ; Round subseconds to the desired precision |
---|
266 | secPos = STRPOS(formatLevel, 'CSF') |
---|
267 | ; for fractional seconds, need to manually round because the "float" |
---|
268 | ; format codes do rounding rather than truncation. |
---|
269 | IF (secPos GE 0) THEN BEGIN |
---|
270 | decimalPoint = STRPOS(formatLevel,'.',secPos) |
---|
271 | sigdigits = 10d^STRMID(formatLevel, decimalPoint + 1, 1) |
---|
272 | time = (ROUND(time*86400d*sigdigits,/L64)+0.1d)/(86400d*sigdigits) |
---|
273 | value1 = date + time |
---|
274 | ENDIF |
---|
275 | |
---|
276 | |
---|
277 | ; Round fractional time to the least significant format specified. |
---|
278 | IF KEYWORD_SET(cRoundup) THEN BEGIN |
---|
279 | ; Because the "integer" format codes automatically truncate, |
---|
280 | ; just add 1/2 of smallest desired time increment to do rounding. |
---|
281 | ; The CASE selections must be in order from smallest units to largest, |
---|
282 | ; so that rounding occurs to the least-significant unit. |
---|
283 | CASE 1 OF |
---|
284 | (secPos GE 0): ; should have already done rounding |
---|
285 | (STRPOS(formatLevel, 'CSI') GE 0): time = time + 0.5d/86400d |
---|
286 | (STRPOS(formatLevel, 'CMI') GE 0): time = time + 0.5d/1440d |
---|
287 | (STRPOS(formatLevel, 'CHI') GE 0): time = time + 0.5/24d |
---|
288 | ELSE: |
---|
289 | ENDCASE |
---|
290 | value1 = date + time |
---|
291 | ENDIF |
---|
292 | |
---|
293 | |
---|
294 | ; check for negative (B.C.E.) years |
---|
295 | jan1_1ad = 1721424L ; Julian date for 1 Jan, 1 C.E. |
---|
296 | IF (date LT jan1_1ad) THEN BEGIN |
---|
297 | yearPos = STRPOS(formatLevel,'CYI') |
---|
298 | ; if there is a year code, then increase width by one for minus sign |
---|
299 | IF (yearPos GE 0) THEN BEGIN |
---|
300 | yearPos = yearPos + 3 |
---|
301 | width = STRTRIM(FIX(STRMID(formatLevel,yearPos,1))+1,2) |
---|
302 | formatLevel = STRMID(formatLevel, 0, yearPos) + $ |
---|
303 | width + STRMID(formatLevel, yearPos+1) |
---|
304 | ENDIF |
---|
305 | ENDIF |
---|
306 | |
---|
307 | RETURN, STRING(value1,FORMAT=formatLevel, $ |
---|
308 | MONTHS=cMonths,AM_PM=cAmpm,DAYS_OF_WEEK=cDaysWeek) |
---|
309 | END |
---|