[39] | 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 |
---|