; docformat = 'IDL preformatted'
;+
; NAME:
; MULTIPLOT
; PURPOSE:
; Create multiple plots with shared axes.
; EXPLANATION:
; This procedure makes a matrix of plots with *SHARED AXES*, either using
; parameters passed to multiplot or !p.multi in a non-standard way.
; It is good for data with one or two shared axes and retains all the
; versatility of the plot commands (e.g. all keywords and log scaling).
; The plots are connected with the shared axes, which saves space by
; omitting redundant ticklabels and titles. Multiplot does this by
; setting !p.position, !x.tickname and !y.tickname automatically.
; A call (multiplot,/reset) restores original values.
;
; Note: This method may be superseded by future improvements in !p.multi
; by RSI. For now, it's a good way to gang plots together.
; CALLING SEQUENCE:
; multiplot[pmulti][,/help][,/initialize][,/reset][,/rowmajor]
; EXAMPLES:
; multiplot,/help ; print this header.
; ; Then copy & paste, from your xterm, the following lines to test:
;
; x = findgen(100) ; MULTIPLOT
; t=exp(-(x-50)^2/300) ; -------------------------
; erase ; | | |
; u=exp(-x/30) ; | | |
; y = sin(x) ; | UL plot | UR plot |
; r = reverse(y*u) ; | | |
; !p.multi=[0,2,2,0,0] ; | | |
; multiplot ; y-------------------------
; plot,x,y*u,title='MULTIPLOT' ; l| | |
; multiplot & plot,x,r ; a| | |
; multiplot ; b| LL plot | LR plot |
; plot,x,y*t,ytit='ylabels' ; e| | |
; multiplot ; l| | |
; plot,x,y*t,xtit='xlabels' ; s-------------------------
; multiplot,/reset ; xlabels
;
; wait,2 & erase ; TEST
; multiplot,[1,3] ; H------------------------
; plot,x,y*u,title='TEST' ; E| plot #1 |
; multiplot ; I------------------------
; plot,x,y*t,ytit='HEIGHT' ; G| plot #2 |
; multiplot ; H------------------------
; plot,x,r,xtit='PHASE' ; T| plot #3 |
; multiplot,/reset ; ------------------------
; ; PHASE
;
; multiplot,[1,1],/init,/verbose ; one way to return to single plot
; % MULTIPLOT: Initialized for 1x1, plotted across then down (column major).
; OPTIONAL INPUTS:
; pmulti = 2-element or 5-element vector giving number of plots, e.g.,
; multiplot,[1,6] ; 6 plots vertically
; multiplot,[0,4,2,0,0] ; 4 plots along x and 2 along y
; multiplot,[0,4,2,0,1] ; ditto, except rowmajor (down 1st)
; multiplot,[4,2],/rowmajor ; identical to previous line
; OPTIONAL KEYWORDS:
; help = flag to print header
; initialize = flag to begin only---no plotting, just setup,
; e.g., multiplot,[4,2],/init,/verbose & multiplot & plot,x,y
; reset = flag to reset system variables to values prior to /init
; default = flag to restore IDL's default value for system variables
; rowmajor = flag to number plots down column first (D=columnmajor)
; verbose = flag to output informational messages
; Outputs:
; !p.position = 4-element vector to place a plot
; !x.tickname = either '' or else 30 ' ' to suppress ticknames
; !y.tickname = either '' or else 30 ' ' to suppress ticknames
; !p.noerase = 1
; Common blocks:
; multiplot---to hold saved variables and plot counter. See code.
; Side Effects:
; Multiplot sets a number of system variables: !p.position, !p.multi,
; !x.tickname, !y.tickname, !P.noerase---but all can be reset with
; the call: multiplot,/reset
; RESTRICTIONS:
; 1. If you use !p.multi as the method of telling how many plots
; are present, you have to set !p.multi at the beginning each time you
; use multiplot or call multiplot with the /reset keyword.
; 2. There's no way to make an xtitle or ytitle span more than one plot,
; except by adding spaces to shift it or to add it manually with xyouts.
; 3. There is no way to make plots of different sizes; each plot
; covers the same area on the screen or paper.
; PROCEDURE:
; This routine makes a matrix of plots with common axes, as opposed to
; the method of !p.multi where axes are separated to allow labels.
; Here the plots are joined and labels are suppressed, except at the
; left edge and the bottom. You tell multiplot how many plots to make
; using either !p.multi (which is then reset) or the parameter pmulti.
; However, multiplot keeps track of the position by itself because
; !p.multi interacts poorly with !p.position.
; MODIFICATION HISTORY:
; write, 21-23 Mar 94, Fred Knight (knight@ll.mit.edu)
; alter plot command that sets !x.window, etc. per suggestion of
; Mark Hadfield (hadfield@storm.greta.cri.nz), 7 Apr 94, FKK
; add a /default keyword restore IDL's default values of system vars,
; 7 Apr 94, FKK
; modify two more sys vars !x(y).tickformat to suppress user-formatted
; ticknames, per suggestion of Mark Hadfield (qv), 8 Apr 94, FKK
; Converted to IDL V5.0 W. Landsman September 1997
; Changed the margin calculation to incorporate !x.omargin
; and !y.omargin. P. Penteado (pp.penteado@gmail.com) Apr 2010
; Changed the lines that disable ticks to set x|y.tickname to all ' ',
; instead of replicating ' ' a set 30 times. P. Penteado (pp.penteado@gmail.com) Apr 2011
;-
pro multiplot_pp,help=help,pmulti $
,initialize=initialize,reset=reset,default=default $
,rowmajor=rowmajor,verbose=verbose
;
; =====>> COMMON
;
common multiplot_pp $
,nplots $ ; [# of plots along x, # of plots along y]
,nleft $ ; # of plots remaining---like the first element of !p.multi
,pdotmulti $ ; saved value of !p.multi
,margins $ ; calculated margins based on !p.multi or pmulti
,pposition $ ; saved value of !p.position
,colmajor $ ; flag for column major order
,noerase $ ; saved value of !p.noerase
,xtickname $ ; Original value
,ytickname $ ; Original value
,xtickformat $; Original value
,ytickformat,$ ; Original value
ontop,onright ;Whether the current plot will be on the bottom line or the left column
;
; =====>> HELP
;
;on_error,2
if keyword_set(help) then begin & doc_library,'multiplot_pp' & return & endif
;
; =====>> RESTORE IDL's DEFAULT VALUES (kill multiplot's influence)
;
if keyword_set(default) then begin
!p.position = 0
!x.tickname = ''
!y.tickname = ''
!x.tickformat = ''
!y.tickformat = ''
!p.multi = 0
!p.noerase = 0
nleft = 0
nplots = [1,1]
pdotmulti = !p.multi
margins = 0
pposition = !p.position
noerase = !p.noerase
xtickname = !x.tickname
ytickname = !y.tickname
xtickformat = !x.tickformat
ytickformat = !y.tickformat
if keyword_set(verbose) then begin
message,/inform,'Restore IDL''s defaults for affected system variables.'
message,/inform,'Reset multiplot''s common to IDL''s defaults.'
endif
return
endif
;
; =====>> RESTORE SAVED SYSTEM VARIABLES
;
if keyword_set(reset) then begin
if n_elements(pposition) gt 0 then begin
!p.position = pposition
!x.tickname = xtickname
!y.tickname = ytickname
!x.tickformat = xtickformat
!y.tickformat = ytickformat
!p.multi = pdotmulti
!p.noerase = noerase
endif
nleft = 0
if keyword_set(verbose) then begin
coords = '['+string(!p.position,form='(3(f4.2,","),f4.2)')+']'
multi = '['+string(!p.multi,form='(4(i2,","),i2)')+']'
message,/inform,'Reset. !p.position='+coords+', !p.multi='+multi
endif
return
endif
;
; =====>> SETUP: nplots, MARGINS, & SAVED SYSTEM VARIABLES
;
if n_elements(nleft) eq 1 then init = (nleft eq 0) else init = 1
if (n_elements(pmulti) eq 2) or (n_elements(pmulti) eq 5) then init = 1
if (n_elements(!p.multi) eq 5) then begin
if (!p.multi[1] gt 0) and (!p.multi[2] gt 0) then init = (!p.multi[0] eq 0)
endif
if init or keyword_set(initialize) then begin
case n_elements(pmulti) of
0:begin
if n_elements(!p.multi) eq 1 then return ; NOTHING TO SET
if n_elements(!p.multi) ne 5 then message,'Bogus !p.multi; aborting.'
nplots = !p.multi[1:2] > 1
if keyword_set(rowmajor) then colmajor = 0 else colmajor = !p.multi[4] eq 0
end
2:begin
nplots = pmulti
colmajor = not keyword_set(rowmajor) ; D=colmajor: left to rt 1st
end
5:begin
nplots = pmulti[1:2]
if keyword_set(rowmajor) then colmajor = 0 else colmajor = pmulti[4] eq 0
end
else: message,'pmulti can only have 0, 2, or 5 elements.'
endcase
pposition = !p.position ; save sysvar to be altered
xtickname = !x.tickname
ytickname = !y.tickname
xtickformat = !x.tickformat
ytickformat = !y.tickformat
pdotmulti = !p.multi
nleft = nplots[0]*nplots[1] ; total # of plots
!p.position = 0 ; reset
!p.multi = 0
plot,/nodata,xstyle=4,ystyle=4,!x.range,!y.range,/noerase ; set window & region
margins = [min(!x.window)-min(!x.region)+!d.x_ch_size*!x.omargin[0]/double(!d.x_vsize) $ ; in normalized coordinates
,min(!y.window)-min(!y.region)+!d.y_ch_size*!y.omargin[0]/double(!d.y_vsize) $
,max(!x.region)-max(!x.window)+!d.x_ch_size*!x.omargin[1]/double(!d.x_vsize) $
,max(!y.region)-max(!y.window)+!d.y_ch_size*!y.omargin[1]/double(!d.y_vsize)]
noerase = !p.noerase
!p.noerase = 1 ; !p.multi does the same
if keyword_set(verbose) then begin
major = ['across then down (column major).','down then across (row major).']
if colmajor then index = 0 else index = 1
message,/inform,'Initialized for '+strtrim(nplots[0],2) $
+'x'+strtrim(nplots[1],2)+', plotted '+major[index]
endif
; print,margins,'=margins'
if keyword_set(initialize) then return
endif
;
; =====>> Define the plot region without using !p.multi.
;
cols = nplots[0] ; for convenience
rows = nplots[1]
nleft = nleft - 1 ; decrement plots remaining
cur = cols*rows - nleft ; current plot #: 1 to cols*rows
idx = [(1.-margins[0]-margins[2])/cols $
,(1.-margins[1]-margins[3])/rows] ; normalized coords per plot
if colmajor then begin ; location in matrix of plots
col = cur mod cols
if col eq 0 then col = cols
row = (cur-1)/cols + 1
endif else begin ; here (1,2) is 1st col, 2nd row
row = cur mod rows
if row eq 0 then row = rows
col = (cur-1)/rows + 1
endelse
pos = [(col-1)*idx[0],(rows-row)*idx[1],col*idx[0],(rows-row+1)*idx[1]] $
+ [margins[0],margins[1],margins[0],margins[1]]
;print,row,col,rows,cols,pos
;
; =====>> Finally set the system variables; user shouldn't change them.
;
!p.position = pos
onbottom = (row eq rows) or (rows eq 1)
onleft = (col eq 1) or (cols eq 1)
ontop = (row eq 1) or (rows eq 1)
onright = (col eq cols) or (cols eq 1)
if onbottom then !x.tickname = xtickname else !x.tickname = ' ';replicate(' ',30)
if onleft then !y.tickname = ytickname else !y.tickname = ' ';replicate(' ',30)
if onbottom then !x.tickformat = xtickformat else !x.tickformat = ''
if onleft then !y.tickformat = ytickformat else !y.tickformat = ''
if keyword_set(verbose) then begin
coords = '['+string(pos,form='(3(f4.2,","),f4.2)')+']'
plotno = 'Setup for plot ['+strtrim(col,2)+','+strtrim(row,2)+'] of ' $
+strtrim(cols,2)+'x'+strtrim(rows,2)
message,/inform,plotno+' at '+coords
endif
;stop
return
end