;+ ; ;PROCEDURE: MPLOT_SYMLOG ; ;PURPOSE: Draws a tplot variable with a bi-symmetric log y-axis ; that allows to display both positive and negative ; values in log scale. ; ; In general, it is assumed to be only used as follows: ; ; IDL> options, 'tplot name', tplot_routine='mplot_symlog' ; ; In the current version, it can only draw multi-line plots. ; ;INPUTS: Tplot data and settings same to 'mplot'. ; ;KEYWORDS: ; ; DATA: A structure that contains the elements 'x', 'y', and ['dy']. ; (used by 'tplot'). ; ; LIMITS: A structure that contains any combination of the following elements: ; All PLOT/OPLOT keywords (i.e., psym, symsize, linestyle, colors, etc.) ; ;CREATED BY: Takuya Hara on 2016-09-27. ; ;LAST MODIFICATION: ; $LastChangedBy: egrimes $ ; $LastChangedDate: 2019-10-08 11:11:53 -0700 (Tue, 08 Oct 2019) $ ; $LastChangedRevision: 27831 $ ; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/tags/spedas_4_1/general/tplot/mplot_symlog.pro $ ; ;- FUNCTION mplot_symlog_ytick_p, axis, index, number times = 'x' ; A special case. IF number EQ 0 THEN RETURN, '0' ; Assuming multiples of 10 with format. ex = STRING(number, '(e8.0)') pt = STRPOS(ex, '.') first = STRMID(ex, 0, pt) sign = STRMID(ex, pt+2, 1) thisExponent = STRMID(ex, pt+3) ; Shave off leading zero in exponent WHILE STRMID(thisExponent, 0, 1) EQ '0' DO thisExponent = STRMID(thisExponent, 1) ; Fix for sign and missing zero problem. IF (LONG(thisExponent) EQ 0) THEN BEGIN sign = '' thisExponent = '0' ENDIF IF (first EQ ' 1') OR (first EQ ' 1') THEN BEGIN first = '' times = '' ENDIF ; Make the exponent a superscript. IF sign EQ '-' THEN BEGIN RETURN, first + times + '10!U' + sign + thisExponent + '!N' ENDIF ELSE BEGIN RETURN, first + times + '10!U' + thisExponent + '!N' ENDELSE END FUNCTION mplot_symlog_ytick_n, axis, index, number times = 'x' ; A special case. IF number EQ 0 THEN RETURN, '0' ; Assuming multiples of 10 with format. ex = STRING(number, '(e8.0)') pt = STRPOS(ex, '.') first = STRMID(ex, 0, pt) sign = STRMID(ex, pt+2, 1) thisExponent = STRMID(ex, pt+3) ; Shave off leading zero in exponent WHILE STRMID(thisExponent, 0, 1) EQ '0' DO thisExponent = STRMID(thisExponent, 1) ; Fix for sign and missing zero problem. IF (LONG(thisExponent) EQ 0) THEN BEGIN sign = '' thisExponent = '0' ENDIF IF (first EQ ' 1') OR (first EQ ' 1') THEN BEGIN first = '' times = '' ENDIF ; Make the exponent a superscript. IF sign EQ '-' THEN BEGIN RETURN, '-' + first + times + '10!U' + sign + thisExponent + '!N' ENDIF ELSE BEGIN RETURN, '-' + first + times + '10!U' + thisExponent + '!N' ENDELSE END PRO mplot_symlog, data=data, limits=lim IF SIZE(data.y, /type) EQ 5 THEN nan = !values.d_nan ELSE nan = !values.f_nan str_element, lim, 'overplot', value=overplot str_element, lim, 'labflag', value=labflag str_element, lim, 'labels', value=labels str_element, lim, 'all_labels', value=all_labels ;pseudo vars only str_element, lim, 'label_index', value=label_index ;pseudo vars only str_element, lim, 'labpos', value=labpos str_element, lim, 'labsize', value=lbsize str_element, lim, 'ytitle', value=ytitle str_element, lim, 'ysubtitle', value=ysubtitle str_element, lim, 'charsize', value=charsize str_element, lim, 'colors', value=colors str_element, lim, 'xtickname', value=xtickname str_element, lim, 'yrange', value=yrange str_element, lim, 'verbose', value=verbose IF SIZE(charsize, /type) EQ 0 THEN charsize = !p.charsize IF charsize EQ 0 THEN charsize = 1. str_element, lim, 'labflag', /delete str_element, lim, 'labels', /delete str_element, lim, 'all_labels', /delete str_element, lim, 'labpos', /delete str_element, lim, 'labsize', /delete extract_tags, alim, lim, /axis str_element, lim, 'ytitle', /delete str_element, lim, 'ysubtitle', /delete dlim = lim pdat = data plim = lim w = WHERE(pdat.y LT 0., nw) IF nw GT 0 THEN pdat.y[w] = nan ndat = data nlim = lim w = WHERE(ndat.y GT 0., nw) IF nw GT 0 THEN ndat.y[w] = nan ndat.y = ABS(ndat.y) pos = lim.position xrange = lim.xrange dy = pos[3] - pos[1] ycenter = pos[1] + .5*dy str_element, plim, 'position', [pos[0], ycenter, pos[2:*]], /add_replace str_element, nlim, 'position', [pos[0:2], ycenter], /add_replace w = WHERE(data.x GE xrange[0] AND data.x LE xrange[1], nw) IF nw GT 0 THEN BEGIN ymax = MAX(ABS(data.y[w, *]))*1.05 pmax = REFORM(pdat.y[w[nw-1], *]) nmax = REFORM(ndat.y[w[nw-1], *]) tmax = pdat.x[w[nw-1]] ENDIF ELSE BEGIN ymax = MAX(ABS(data.y))*1.05 pmax = REFORM(pdat.y[N_ELEMENTS(data.x)-1, *]) nmax = REFORM(ndat.y[N_ELEMENTS(data.x)-1, *]) tmax = pdat.x[N_ELEMENTS(data.x)-1] ENDELSE IF SIZE(yrange, /type) NE 0 THEN BEGIN ymax = MAX(ABS(yrange)) ymin = MIN(yrange) IF ymin LE 0.0 THEN BEGIN dprint, dlevel=1, verbose=verbose, 'Minimum yrange value is negative or zero!' ymin = Max(abs(yrange))/1.0e3 ENDIF ENDIF IF SIZE(ymin, /type) EQ 0 THEN BEGIN ;set ymin based on data, jmm, 2019-03-05 ymin = ymax/1.0e3 ENDIF str_element, plim, 'xtickname', REPLICATE(' ', N_ELEMENTS(xtickname)), /add_replace str_element, plim, 'yrange', [ymin, ymax], /add_replace str_element, plim, 'ylog', 1, /add_replace str_element, plim, 'ystyle', 1, /add_replace str_element, plim, 'ytickformat', 'mplot_symlog_ytick_p', /add_replace str_element, nlim, 'yrange', [ymax, ymin], /add_replace str_element, nlim, 'ylog', 1, /add_replace str_element, nlim, 'ystyle', 1, /add_replace str_element, nlim, 'ytickformat', 'mplot_symlog_ytick_n', /add_replace IF KEYWORD_SET(labels) THEN nlab = N_ELEMENTS(labels) ELSE nlab = 1 ;# of labels for variable xlast = REPLICATE(tmax, nlab) IF (overplot) THEN BEGIN extract_tags, plim, {xstyle: 5, ystyle: 5, overplot: 0} extract_tags, nlim, {xstyle: 5, ystyle: 5, overplot: 0} str_element, dlim, 'overplot', 0, /add_replace ENDIF ytn = !y.tickname !y.tickname[*] = ' ' extract_tags, dlim, {xstyle: 5, ystyle: 5} extract_tags, alim, {xstyle: 5, yticks: 1, yminor: 1, yticklayout: 1} box, dlim axis, yaxis=0, _extra=alim !y.tickname[*] = ytn ;handle possible colors in pseudo variable, jmm, 2019-03-05 ;but only if n_colors = n_labels IF KEYWORD_SET(all_labels) && $ ;Cannot just use keyword_set for colors, index (keyword_set(label_index) || (n_elements(label_index) Gt 0 && label_index[0] Ne -1)) && $ (keyword_set(colors) || (n_elements(colors) Gt 0 && colors[0] Ne -1)) THEN BEGIN nc = n_elements(colors) IF NC EQ N_ELEMENTS(all_labels) THEN BEGIN ;one color for each label str_element, plim, 'colors', get_colors(colors[label_index]), /add_replace str_element, nlim, 'colors', get_colors(colors[label_index]), /add_replace pseudo_colors = 1b ;save a flag for labels ENDIF ELSE pseudo_colors = 0b ENDIF ELSE pseudo_colors = 0b mplot, data=pdat, limits=plim plast = CONVERT_COORD(xlast, pmax, /data, /to_norm) plast = REFORM(plast[1, *]) mplot, data=ndat, limits=nlim nlast = CONVERT_COORD(xlast, nmax, /data, /to_norm) nlast = REFORM(nlast[1, *]) IF KEYWORD_SET(colors) THEN col = get_colors(colors) labbins = REPLICATE(1, dimen2(data.y)) IF KEYWORD_SET(labels) THEN BEGIN ylast = plast ylast[*] = 0. p = WHERE(FINITE(plast), np, complement=m, ncomplement=nm) IF np GT 0 THEN ylast[p] = plast[p] IF nm GT 0 THEN ylast[m] = nlast[m] undefine, p, m, np, nm ;# used for calculating label size and placement ;should include total number in case of pseudo var nlabtot = KEYWORD_SET(all_labels) ? N_ELEMENTS(all_labels):nlab IF ~KEYWORD_SET(all_labels) && nlab NE (dimen2(data.y)) THEN $ dprint, dlevel=2, 'Incorrect number of labels', /no_check_events yw = [pos[1], pos[3]] xw = [pos[0], pos[2]] IF NOT KEYWORD_SET(lbsize) THEN $ lbsize = charsize < (yw[1] - yw[0])/(nlabtot+1) * !d.y_size/!d.y_ch_size $ ELSE lbsize = lbsize * charsize IF N_ELEMENTS(labflag) EQ 0 THEN BEGIN IF KEYWORD_SET(labpos) THEN labflag = 3 ELSE labflag = 2 ENDIF IF N_ELEMENTS(labflag) EQ 0 THEN labflag = 0 ; no labels IF (labflag EQ 1) OR (labflag EQ -1) THEN BEGIN ; evenly spaced labels nlabpos = (FINDGEN(nlabtot) + .5) * (yw[1] - yw[0]) / nlabtot + yw[0] IF labflag EQ -1 THEN nlabpos = REVERSE(nlabpos) ENDIF IF labflag EQ 3 THEN BEGIN ; specified label position IF KEYWORD_SET(labpos) THEN BEGIN foo = CONVERT_COORD(/data, /to_norm, FINDGEN(N_ELEMENTS(labpos)), labpos) nlabpos = foo[1, *] ENDIF ELSE dprint, dlevel=2, 'Custom label position not set, please set LABPOS option.' , verbose=verbose ENDIF IF KEYWORD_SET(all_labels) THEN BEGIN ; pseudo var labels and colors lidx = WHERE(label_index LE N_ELEMENTS(all_labels)-1, nl) IF nl GT 0 THEN BEGIN ; get correct labels and placement for this variable (set in tplot) labels = all_labels[label_index[lidx]] IF KEYWORD_SET(nlabpos) THEN nlabpos = nlabpos[label_index[lidx]] ENDIF ELSE labflag = 0 nlab = nl ;jmm, 2019-03-05, avoid crash at line below: ypos = nlabpos[n] ENDIF labbins = REPLICATE(1, nlab) xpos = xw[1] ENDIF ELSE labflag = 0 IF KEYWORD_SET(labels) AND KEYWORD_SET(labflag) THEN BEGIN FOR n=0, nlab-1 DO BEGIN ypos = 0. IF KEYWORD_SET(nlabpos) THEN BEGIN ; evenly spaced labels ypos = nlabpos[n] ENDIF ELSE BEGIN ;labels at end of trace ; fixes crash when the variable contains too many labels IF n GE N_ELEMENTS(ylast) THEN CONTINUE ypos = ylast[n] ;IF WHERE(FINITE(pmax[n])) THEN ylast = pmax[n] ELSE ylast = nmax[n] ;ypos = (CONVERT_COORD(trange[1], ylast, /data, /to_norm))[1] ;fooind = where( foo[0,*] le xw[1],count) ;if count ne 0 then mx = max(foo[0,fooind],ms) ;if count ne 0 then ypos = foo[1,fooind[ms]] ENDELSE IF ypos LE yw[1] AND ypos GE yw[0] THEN BEGIN IF ~undefined(col) THEN BEGIN ; if colors aren't defined, default to black, egrimes, 2019-08-27 IF pseudo_colors THEN col_n = col[label_index] $ ;jmm, 2019-03-05 ELSE col_n = col[n] ENDIF ELSE col_n = 0 XYOUTS, xpos, ypos, ' ' + labels[n], color=col_n, /norm, charsize=lbsize ENDIF ENDFOR ENDIF RETURN END