; docformat = 'rst rst'
;+
;
; pp_multiplot objects hold a grid of plots (from the plot function, of IDL
; Graphics, not the plot procedure from direct graphics) with no empty space between lines
; and columns, to use a single vertical/horizontal axis for each line/column
; of plots. This was inspired by the functionality provided by `multiplot` from IDLAstro
; (`http://idlastro.gsfc.nasa.gov/ftp/pro/plot/multiplot.pro <http://idlastro.gsfc.nasa.gov/ftp/pro/plot/multiplot.pro>`)
; for direct graphics.
;
; But contrary to multiplot, this system works for Graphics (instead of direct
; graphics), allows for lines/columns of different heights/widths,
; provides global margins and axes titles, and provides a synchronization
; mechanism to keep the plots in the same line/column (or all) with the same
; axes ranges and properties. Also, like in `pp_plot`, there is the option
; of suppressing the first/last tick marks of the axes, to avoid their overlap
; with the neighboring plots (enabled by default, depending on the plot's
; location on the grid).
;
; A pp_multiplot object makes a window to hold the grid, and will contain all the
; plots in the grid (made with the plot method provided here). The individual
; plot objects can be retrieved (for instance, to set/get their properties),
; and axes properties can be set (and synchronized) for entire lines/columns
; (or all plots in the grid) at a time. The init/get/set methods pass any
; extra properties to the window that contains the plot - the window class is
; used here, not inherited.
;
; See the documentation on the public methods
; `pp_multiplot::init`, `pp_multiplot::plot`, `pp_multiplot::image`,
; `pp_multiplot::contour`, `pp_multiplot::getproperty`,
; `pp_multiplot::setproperty`, `pp_multiplot::sync_axes`, ,`pp_multiplot::close`,
; `pp_multiplot::save`, and `pp_multiplot::getposition`, for details on their use.
;
; :Examples:
; Make a simple 2x2 grid with equal sizes::
;
; m=pp_multiplot(multi_layout=[2,2],global_xtitle='Test X axis title',global_ytitle='Test Y axis title')
;
; Now populate the grid with plots using plot()'s test data::
;
; ;Since multi_index was not provided, this will occupy the first free location in the grid:
; p0=m.plot(/test,color='red',thick=2.)
; p1=m.plot(/test,color='blue',linestyle=1) ;Second location, since multi_index was omitted
; p2=m.plot(/test,multi_index=2,symbol='circle') ;Third location, explicitly set with multi_index
;
; The result should look like
;
; .. image:: pp_multiplot_ex1.png
;
; Now, the x and y ranges on the second plot will be changed::
;
; p1.xrange=[50,100]
; p1.yrange=[0,1]
; m.sync_axes,1 ;Update the axes on the plot, taking the second one as reference.
;
; Now the plot will have a different x axis on the second column, and a different
; y axis on the first line: .. image:: pp_multiplot_ex2.png
;
; These new ranges will automatically be used when a new plot is put on that
; line/column::
;
; p3=m.plot(/test,multi_index=3) ;Fourth location, explicitly set with multi_index
;
; Now, save the output to a vector pdf, and close the window in the usual Graphics way::
;
; m.save,'pp_multiplot_ex3.pdf'
; m.close
;
; The file would look like
;
; .. image:: pp_multiplot_ex3.png
;
; A more complicated example, with a 2x3 grid with variable widths and heights::
;
; m=pp_multiplot(multi_layout=[2,3],lineheights=[100,200,100],columnwidths=[100,200],/absolute_dims)
; p0=m.plot(/test,color='red')
; p1=m.plot(/test,color='blue',xrange=[50,100])
; p2=m.plot(/test,color='green',ycolor='magenta',propagate=2) ;Make ycolor extend only to the second line
; p3=m.plot(/test,linestyle='dotted')
; p4=m.plot(/test,symbol='square')
; p5=m.plot(/test,symbol='circle')
;
; Which would look like
;
; .. image:: pp_multiplot_ex4.png
;
; Now to retrieve some y properties of each line::
;
; print,m.yranges
; ; -0.82532734 0.93990618
; ; -0.82532734 0.93990618
; ; -0.82532734 0.93990618
; print,m.ycolor
; ;!NULL
; ;magenta
; ;!NULL
;
; Now to make the second column have red x axes::
;
; xcolor=m.xcolor
; print,xcolor
; ;!NULL
; ;!NULL
; xcolor[1]='red'
; m.xcolor=xcolor
;
; Now to make all plots have larger y tick marks::
;
; m.yticklen=0.2
;
; The final result would be
;
; .. image:: pp_multiplot_ex5.png
;
; Save this window to a low resolution bitmap (same used in the documentation)
; and close it::
;
; m.save,'pp_multiplot_ex5.png',resolution=100
; m.close
;
; A simple example, with plots made with a set of default properties, so that
; it is not necessary to repeat them for each plot::
;
; props={color:'red',symbol:'square',sym_filled:1}
; m=pp_multiplot(multi_layout=[1,3],graphproperties=props)
; p1=m.plot(/test)
; p2=m.plot(/test)
; p3=m.plot(/test,color='blue')
;
; The result would be
;
; .. image:: pp_multiplot_ex6.png
;
; Now, we will change the yrange in one of the panels. This will cause the
; axis tick labels to get recalculated, resulting in overlapping labels. This
; would happen even if the new yrange was identical to the current range, beacuse
; setting the range triggers a recalculation of the ticks::
;
; p2.yrange=[-1,2]
;
; Which looks like
;
; .. image:: pp_multiplot_ex7.png
;
; The fix to that is a call to the `updateranges` method::
;
; m.updateranges
;
; Which will make the plot look like
;
; .. image:: pp_multiplot_ex8.png
;
; :Todo: Document how to use internal objects, like global_xtitle
;
; :Version: 20101027
;
; :Requires: IDL 8.0
;
; :Author: Paulo Penteado (pp.penteado@gmail.com), Sep/2010
;-
;+
; :Description:
; Creates an object to hold a grid of plots (from the plot function, of IDL
; Graphics, not the plot procedure from direct graphics) with no empty space between lines
; and columns, to use a single vertical/horizontal axis for each line/column
; of plots. See the description of the class `pp_multiplot` for details
; and examples.
;
; :Keywords:
;
; multi_layout: in, required
; The layout of the grid to create, as a 2-element array [ncolumns, nlines],
; with the number of columns and the number of lines for the grid.
; title: in, optional
; The title for the entire plot (passed on to the window object).
; global_xtitle: in, optional
; The common title for all the x axes, so that a same title does not have to
; be shown repeatedly below every column.
; global_ytitle: in, optional
; The common title for all the y axes, so that a same title does not have to
; be shown repeatedly on the left of every line.
; global_margin: in, optional
; The margins, in normalized units (range 0 to 1) to be given around the
; grid. Provided as a 4-element array, for [left, bottom, right, top] margins.
; Defaults to [0.125d0,0.15d0,0.005d0,0.1d0].
; columnwidths: in, optional
; If set to an array with the same number of elements as the number of grid
; columns, specifies the width to use for each plot column. If not provided,
; equal widths are used. The units are arbitrary, as only their relative values
; is used, unless `absolute_dims` is set, in which case the units are the same
; as window()'s dimensions.
; lineheights: in, optional
; If set to an array with the same number of elements as the number of grid
; lines, specifies the height to use for each plot column. If not provided,
; equal heights are used. The units are arbitrary, as only their relative values
; is used, unless `absolute_dims` is set, in which case the units are the same
; as window()'s dimensions.
; absolute_dims: in, optional, default=0
; If set, causes `columnwidths` and `lineheights` to be interpreted as absolute,
; rather than relative values. In that case, their units are the same as those
; used by window()'s dimensions.
; _REF_EXTRA: in, out, optional
; Any extra properties are just passed to window() (the init method of the
; window class). The most common to be used is probably going to be dimensions.
; See the help on window() for more information.
; graphproperties: in, optional
; Use this keyword to provide a set of graphic keywords to be passed to all
; individual graphs by default. It should be in the form of a structure, with
; each field containing the value for the keyword of corresponding name.
; If an individual graph's creation specifies a value for a keyword given in
; graphproperties, it will take precendece over the graphproperties value.
; See examples above.
;
;-
function pp_multiplot::init,_REF_EXTRA=ex,$
multi_layout=mlayout,title=gtitle,global_xtitle=gxtitle,global_ytitle=gytitle,$
global_margin=gmargin,columnwidths=cwidths,lineheights=lheights,absolute_dims=absolute,$
xgap=xgap,ygap=ygap,xsupressdivision=xsupressdivision,ysupressdivision=ysupressdivision,$
graphproperties=graphproperties,xtickratio=xtickratio,ytickratio=ytickratio
compile_opt idl2, logical_predicate
if (n_elements(mlayout) ne 2) then begin
print,'multi_layout must be a 2-element integer array'
return,0
endif
;Defaults
;Are the widths and heigths provided to be interpreted as absolute or relative dimensions?
absolute=(n_elements(absolute) eq 1) ? absolute : 0
;Grid shape
self.mlayout=round(mlayout)
ncolumns=self.mlayout[0]
nlines=self.mlayout[1]
self.ncolumns=ncolumns
self.nlines=nlines
;Initialize lists
self.oplots=list(length=self.mlayout[0]*self.mlayout[1])
;Set equal widths and heights by default
if (n_elements(cwidths) eq ncolumns) then self.cwidths=list(cwidths/total(cwidths),/extract) $
else self.cwidths=list(replicate(1d0/ncolumns,ncolumns),/extract)
if (n_elements(lheights) eq nlines) then self.lheights=list(lheights/total(lheights),/extract) $
else self.lheights=list(replicate(1d0/nlines,nlines),/extract)
self.xranges=list(length=ncolumns)
self.yranges=list(length=nlines)
keynames=['COLOR','GRIDSTYLE','LOG','MAJOR','MINOR','SUBTICKLEN','TEXT_COLOR',$
'TEXT_POS','THICK','TICKDIR','TICKFONT_NAME','TICKFONT_SIZE','TICKFONT_STYLE',$
'TICKFORMAT','TICKINTERVAL','TICKLAYOUT','TICKLEN','TICKNAME','TICKUNITS','TICKVALUES',$
'TITLE','TRANSPARENCY']
self.xproperties=list()
for i=0,ncolumns-1 do self.xproperties.add,hash('X'+keynames)
self.yproperties=list()
for i=0,nlines-1 do self.yproperties.add,hash('Y'+keynames)
self.xendticks=list(length=nlines*ncolumns)
self.yendticks=list(length=nlines*ncolumns)
;Process optional parameters
;Default margins
self.global_margin=[0.125d0,0.15d0,0.005d0,0.1d0]
if (n_elements(gmargin) eq 4) then self.global_margin=gmargin
if (n_elements(xranges) ne 0) then self.setproperty,xranges=xranges
if (n_elements(yranges) ne 0) then self.setproperty,yranges=yranges
if (n_elements(ygap) ne 0) then self.ygap=ygap
if (n_elements(xgap) ne 0) then self.xgap=xgap
;Create the window to contain the graphics
if absolute then begin
if (n_elements(dimensions) ne 2) then begin
;Compute window dimensions to give the proper margins in addition to the line/column dimensions
totalw=total(cwidths)/(1d0-total((self.global_margin)[[0,2]]))
totalh=total(lheights)/(1d0-total((self.global_margin)[[1,3]]))
dimensions=[totalw,totalh]
;Or take the absolute dimensions given for the window and ignore relative margins
endif else self.global_margin=[0d0,0d0,0d0,0d0]
self.owindow=window(_extra=ex,title=gtitle,dimensions=dimensions)
endif else self.owindow=window(_extra=ex,title=gtitle)
;Parameters that depend on the window's existence
if (n_elements(gxtitle) eq 1) then self.setproperty,global_xtitle=gxtitle
if (n_elements(gytitle) eq 1) then self.setproperty,global_ytitle=gytitle
if (n_elements(gtitle) ne 0) then self.setproperty,title=gtitle
if (n_elements(xsupressdivision) ne 0) then self.xsupressdivision=xsupressdivision
if (n_elements(ysupressdivision) ne 0) then self.ysupressdivision=ysupressdivision
self.xtickratio=n_elements(xtickratio) ? xtickratio : 0.2d0
self.ytickratio=n_elements(ytickratio) ? ytickratio : 0.2d0
self.graphproperties=n_elements(graphproperties) ? hash(graphproperties) : hash()
return,isa(self.owindow,'graphicswin') || isa(self.owindow,'graphicsbuffer')
end
;+
; :Description:
; A wrapper for plot(), which creates the plot in the proper place and with
; the right properties (particularly x/y ranges) in the multiplot grid, adding
; the object to the list of plots contained by the pp_multiplot object.
;
; :Returns:
; The plot object created in the multiplot grid. This is a regular object of
; IDL's plot class, and can be manipulated in the usual way. The method
; `pp_multiplot::sync_axes` can be used to synchronize axes properties across
; a line/column or all plots, after one plot has been changed (programmatically
; or interactively).
;
; :Params:
; arg1: in, optional
; The first argument to be passed on to plot(). See the help on plot()
; for details.
; arg2: in, optional
; The second argument to be passed on to plot(). See the help on plot()
; for details.
; arg3: in, optional
; The third argument to be passed on to plot(). See the help on plot()
; for details.
;
; :Keywords:
; TEST: in, optional, default=0
; Passed on to plot(), to make plot() used the test data instead.
; multi_index: in, optional
; The index of the position to place the plot in the grid. Count starts at
; 0, on the top left, proceeding left-to-right, then top-to-bottom, up to
; nlines*ncolumns-1.
; xrange: in, optional
; A 2-element array with the minimum and maximum to use for the x axis. If
; not provided, the xrange set for the current column is used, or, if no range
; was set for the column, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the column
; The other plots in the column have their ranges updated, if necessary.
; yrange: in, optional
; A 2-element array with the minimum and maximum to use for the y axis. If
; not provided, the xrange set for the current line is used, or, if no range
; was set for the line, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the line
; The other plots in the line have their ranges updated, if necessary.
; propagate: in, optional, default=1
; Determines the mode of propagating the axes properties (range and endticks
; not included) of this plot. If 0, no propagation of properties is done.
; If 1, properties are propagated to all plots in the grid.
; If 2, properties are propagated to all plots in the same line/column.
; xendticks: in, optional
; The mode set for suppressing the first/last x tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (xendticks=1), except for those that fall on the rightmost
; column (xendticks=3).
; yendticks: in, optional
; The mode set for suppressing the first/last y tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (yendticks=1), except for those that fall on the top
; line (yendticks=3).
; _EXTRA: in, optional
; Any keywords not handled by this method are passed on to plot(). See the
; help on plot() for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y) with the keyword `propagate`. See the help on plot
; objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
function pp_multiplot::plot,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks
compile_opt idl2, logical_predicate
graphic_to_do='plot'
case n_params() of
0: ret=self.do_graphic(graphic_to_do=graphic_to_do, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
1: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
2: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
3: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
endcase
return,ret
end
;+
; :Description:
; A wrapper for contour(), which creates the image in the proper place and with
; the right properties (particularly x/y ranges) in the multiplot grid, adding
; the object to the list of plots contained by the pp_multiplot object.
;
; :Returns:
; The contour object created in the multiplot grid. This is a regular object of
; IDL's image class, and can be manipulated in the usual way. The method
; `pp_multiplot::sync_axes` can be used to synchronize axes properties across
; a line/column or all plots, after one plot has been changed (programmatically
; or interactively).
;
; :Params:
; arg1: in, optional
; The first argument to be passed on to plot(). See the help on plot()
; for details.
; arg2: in, optional
; The second argument to be passed on to plot(). See the help on plot()
; for details.
; arg3: in, optional
; The third argument to be passed on to plot(). See the help on plot()
; for details.
;
; :Keywords:
; TEST: in, optional, default=0
; Passed on to plot(), to make plot() used the test data instead.
; multi_index: in, optional
; The index of the position to place the plot in the grid. Count starts at
; 0, on the top left, proceeding left-to-right, then top-to-bottom, up to
; nlines*ncolumns-1.
; xrange: in, optional
; A 2-element array with the minimum and maximum to use for the x axis. If
; not provided, the xrange set for the current column is used, or, if no range
; was set for the column, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the column
; The other plots in the column have their ranges updated, if necessary.
; yrange: in, optional
; A 2-element array with the minimum and maximum to use for the y axis. If
; not provided, the xrange set for the current line is used, or, if no range
; was set for the line, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the line
; The other plots in the line have their ranges updated, if necessary.
; propagate: in, optional, default=1
; Determines the mode of propagating the axes properties (range and endticks
; not included) of this plot. If 0, no propagation of properties is done.
; If 1, properties are propagated to all plots in the grid.
; If 2, properties are propagated to all plots in the same line/column.
; xendticks: in, optional
; The mode set for suppressing the first/last x tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (xendticks=1), except for those that fall on the rightmost
; column (xendticks=3).
; yendticks: in, optional
; The mode set for suppressing the first/last y tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (yendticks=1), except for those that fall on the top
; line (yendticks=3).
; _EXTRA: in, optional
; Any keywords not handled by this method are passed on to plot(). See the
; help on plot() for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y) with the keyword `propagate`. See the help on plot
; objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
function pp_multiplot::contour,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks
compile_opt idl2, logical_predicate
graphic_to_do='contour'
case n_params() of
0: ret=self.do_graphic(graphic_to_do=graphic_to_do, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
1: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
2: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
3: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks)
endcase
return,ret
end
;+
; :Description:
; A wrapper for image(), which creates the image in the proper place and with
; the right properties (particularly x/y ranges) in the multiplot grid, adding
; the object to the list of plots contained by the pp_multiplot object.
;
; :Returns:
; The image object created in the multiplot grid. This is a regular object of
; IDL's image class, and can be manipulated in the usual way. The method
; `pp_multiplot::sync_axes` can be used to synchronize axes properties across
; a line/column or all plots, after one plot has been changed (programmatically
; or interactively).
;
; :Params:
; arg1: in, optional
; The first argument to be passed on to plot(). See the help on plot()
; for details.
; arg2: in, optional
; The second argument to be passed on to plot(). See the help on plot()
; for details.
; arg3: in, optional
; The third argument to be passed on to plot(). See the help on plot()
; for details.
;
; :Keywords:
; TEST: in, optional, default=0
; Passed on to plot(), to make plot() used the test data instead.
; multi_index: in, optional
; The index of the position to place the plot in the grid. Count starts at
; 0, on the top left, proceeding left-to-right, then top-to-bottom, up to
; nlines*ncolumns-1.
; xrange: in, optional
; A 2-element array with the minimum and maximum to use for the x axis. If
; not provided, the xrange set for the current column is used, or, if no range
; was set for the column, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the column
; The other plots in the column have their ranges updated, if necessary.
; yrange: in, optional
; A 2-element array with the minimum and maximum to use for the y axis. If
; not provided, the xrange set for the current line is used, or, if no range
; was set for the line, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the line
; The other plots in the line have their ranges updated, if necessary.
; propagate: in, optional, default=1
; Determines the mode of propagating the axes properties (range and endticks
; not included) of this plot. If 0, no propagation of properties is done.
; If 1, properties are propagated to all plots in the grid.
; If 2, properties are propagated to all plots in the same line/column.
; xendticks: in, optional
; The mode set for suppressing the first/last x tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (xendticks=1), except for those that fall on the rightmost
; column (xendticks=3).
; yendticks: in, optional
; The mode set for suppressing the first/last y tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (yendticks=1), except for those that fall on the top
; line (yendticks=3).
; _EXTRA: in, optional
; Any keywords not handled by this method are passed on to plot(). See the
; help on plot() for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y) with the keyword `propagate`. See the help on plot
; objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
function pp_multiplot::image,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks,axis_style=axs,aspect_ratio=ar
compile_opt idl2, logical_predicate
graphic_to_do='image'
axs=n_elements(axs) ? axs : 2
ar=n_elements(ar) ? ar : 0
case n_params() of
0: ret=self.do_graphic(graphic_to_do=graphic_to_do, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks,axis_style=axs,aspect_ratio=ar)
1: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks,axis_style=axs,aspect_ratio=ar)
2: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks,axis_style=axs,aspect_ratio=ar)
3: ret=self.do_graphic(graphic_to_do=graphic_to_do,arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks,axis_style=axs,aspect_ratio=ar)
endcase
return,ret
end
;+
; :Description:
; A wrapper for plot(), image() and contour(), which creates the plot in the
; proper place and with the right properties (particularly x/y ranges) in the
; multiplot grid, adding the object to the list of plots contained by the
; pp_multiplot object.
;
; :Returns:
; The plot object created in the multiplot grid. This is a regular object of
; IDL's plot class, and can be manipulated in the usual way. The method
; `pp_multiplot::sync_axes` can be used to synchronize axes properties across
; a line/column or all plots, after one plot has been changed (programmatically
; or interactively).
;
; :Params:
; arg1: in, optional
; The first argument to be passed on to plot(). See the help on plot()
; for details.
; arg2: in, optional
; The second argument to be passed on to plot(). See the help on plot()
; for details.
; arg3: in, optional
; The third argument to be passed on to plot(). See the help on plot()
; for details.
;
; :Keywords:
; TEST: in, optional, default=0
; Passed on to plot(), to make plot() used the test data instead.
; multi_index: in, optional
; The index of the position to place the plot in the grid. Count starts at
; 0, on the top left, proceeding left-to-right, then top-to-bottom, up to
; nlines*ncolumns-1.
; xrange: in, optional
; A 2-element array with the minimum and maximum to use for the x axis. If
; not provided, the xrange set for the current column is used, or, if no range
; was set for the column, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the column
; The other plots in the column have their ranges updated, if necessary.
; yrange: in, optional
; A 2-element array with the minimum and maximum to use for the y axis. If
; not provided, the xrange set for the current line is used, or, if no range
; was set for the line, the plot is created with the default range
; (determined by plot()), and that range is subsequently set for the line
; The other plots in the line have their ranges updated, if necessary.
; propagate: in, optional, default=1
; Determines the mode of propagating the axes properties (range and endticks
; not included) of this plot. If 0, no propagation of properties is done.
; If 1, properties are propagated to all plots in the grid.
; If 2, properties are propagated to all plots in the same line/column.
; xendticks: in, optional
; The mode set for suppressing the first/last x tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (xendticks=1), except for those that fall on the rightmost
; column (xendticks=3).
; yendticks: in, optional
; The mode set for suppressing the first/last y tick labels of the plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (yendticks=1), except for those that fall on the top
; line (yendticks=3).
; _EXTRA: in, optional
; Any keywords not handled by this method are passed on to plot(). See the
; help on plot() for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y) with the keyword `propagate`. See the help on plot
; objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
function pp_multiplot::do_graphic,graphic_to_do=graphic_to_do,$
arg1, arg2, arg3, TEST=test, _EXTRA=ex,$
multi_index=mindex,xrange=xrange,yrange=yrange,propagate=propagate,$
xendticks=xendticks,yendticks=yendticks
compile_opt idl2, logical_predicate
;Defaults
mindex=n_elements(mindex) eq 1 ? 0>mindex<(self.mlayout[0]*self.mlayout[1]-1) : self.mindex
self.mindex=mindex ;The index of the current location in the multiplot matrix
;Propagate [xy]properties over nothing (0), all (1), line/column (2)
propagate=(n_elements(propagate) eq 1) ? propagate : 1
;Determine the location for the plot
position=self.getposition(mindex,bottom=bottom,left=left,top=top,right=right,column=column,line=line)
;By default, supress end x/y ticks in plots that are in the middle of the columns/lines
if n_elements(xendticks) ne 1 then begin
xendticks=right ? 3 : 1
fixxticks=right ? 0 : 1
endif else fixxticks=0
if n_elements(yendticks) ne 1 then begin
yendticks=top ? 3 : 1
fixyticks=top ? 0 : 1
endif else fixyticks=0
;xendticks=n_elements(xendticks) eq 1 ? xendticks : right ? 3 : 1
;yendticks=n_elements(yendticks) eq 1 ? yendticks : top ? 3 : 1
;Update the [xy]range, if provided
if (n_elements(xrange) eq 2) then begin
newxranges=(self.xranges)[*]
newxranges[column]=xrange
self.setproperty,xranges=newxranges
endif
if (n_elements(yrange) eq 2) then begin
newyranges=(self.yranges)[*]
newyranges[line]=yrange
self.setproperty,yranges=newyranges
endif
;Process the axes properties in the extra parameters
ex=(self.process_extras_plot(hash(ex),mindex,propagate)).tostruct()
;Combine extras with default properties
if self.graphproperties then begin
exh=hash(ex)
exh=self.graphproperties+exh
ex=exh.tostruct()
endif
;Create the plot object
self.owindow.select,/clear ;Just to make this window the current one
if !version.release ge '8.2' then begin
case n_params() of
0:ret=call_function(graphic_to_do,TEST=test, _EXTRA=ex,position=position,/current,$
xshowtext=bottom*1,yshowtext=left*1,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
1:ret=call_function(graphic_to_do,arg1,TEST=test, _EXTRA=ex,position=position,/current,$
xshowtext=bottom*1,yshowtext=left*1,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
2:ret=call_function(graphic_to_do,arg1,arg2,TEST=test, _EXTRA=ex,position=position,/current,$
xshowtext=bottom*1,yshowtext=left*1,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
3:ret=call_function(graphic_to_do,arg1,arg2,arg3,TEST=test, _EXTRA=ex,position=position,/current,$
xshowtext=bottom*1,yshowtext=left*1,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
endcase
axes=ret.axes
axes[2].showtext=0
axes[3].showtext=0
endif else begin
case n_params() of
0:ret=call_function(graphic_to_do,TEST=test, _EXTRA=ex,position=position,/current,$
xtickfont_size=bottom*12,ytickfont_size=left*12,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
1:ret=call_function(graphic_to_do,arg1,TEST=test, _EXTRA=ex,position=position,/current,$
xtickfont_size=bottom*12,ytickfont_size=left*12,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
2:ret=call_function(graphic_to_do,arg1,arg2,TEST=test, _EXTRA=ex,position=position,/current,$
xtickfont_size=bottom*12,ytickfont_size=left*12,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
3:ret=call_function(graphic_to_do,arg1,arg2,arg3,TEST=test, _EXTRA=ex,position=position,/current,$
xtickfont_size=bottom*12,ytickfont_size=left*12,xrange=(self.xranges)[column],yrange=(self.yranges)[line])
endcase
endelse
;Set the [xy]range, if necessary
if (~isa((self.xranges)[column])) then (self.xranges)[column]=ret.xrange
if (~isa((self.yranges)[line])) then (self.yranges)[line]=ret.yrange
if ~array_equal((self.xranges)[column],ret.xrange) then (self.xranges)[column]=ret.xrange
if ~array_equal((self.yranges)[line],ret.yrange) then (self.yranges)[line]=ret.yrange
;Remove the first/last labels, if necessary
self.setendticks,xendticks,ret,'x'
self.setendticks,yendticks,ret,'y'
(self.oplots)[mindex]=ret
(self.xendticks)[mindex]=xendticks
(self.yendticks)[mindex]=yendticks
;Change the default grid index to the next one
if mindex lt (self.mlayout[0]*self.mlayout[1]-1) then self.mindex++
if self.xsupressdivision then begin
if ~left then ret['axis1'].hide=1
if ~right then ret['axis3'].hide=1
endif
if self.ysupressdivision then begin
if ~bottom then ret['axis0'].hide=1
if ~top then ret['axis2'].hide=1
endif
return,ret
end
;+
; :Description:
; Processes the extra parameters passed to `pp_multiplot::plot`, to pick those
; that specify axes properties. Axes properties are given a special treatment,
; as they might be kept synchronized across lines/column/all plots.
;
; :Hidden: This routine is to be used only by `pp_multiplot::plot`.
;
; :Private: This routine is to be used only by `pp_multiplot::plot`.
;
; :Params:
; extras: in, required
; Extra parameters passed to `pp_multiplot::plot`, converted to a hash, to be processed here.
; mindex: in, required
; The index of the plot being processed in the multiplot grid (0 to nlines*ncolumns-1).
; propagate: in, required
; If 0, no propagation of properties is done. If 1, properties are propagated
; to all plots. If 2, properties are propagated to all
; plots in the same line/column.
;
; :Returns:
; A hash with the extra parameters that should be passed to plot(), which
; includes those that were not altered here, and those that were processed
; based on the x/y properties for that position in the grid.
;
;-
function pp_multiplot::process_extras_plot,extras,mindex,propagate
compile_opt idl2, logical_predicate,hidden
if (n_elements(extras) eq 0) then return,extras ;Get out now if nothing has to be done
column=mindex mod self.ncolumns
line=mindex/self.ncolumns
;Get the hashes of the currently set properties
currentx=((self.xproperties)[column])[*]
currenty=((self.yproperties)[line])[*]
to_update=hash() ;Indices of the plots that will have to be updated
foreach el,extras,i do begin ;Parse each extra argument, if it is an x/y property
if currentx.haskey(i) then begin ;If it is one of the x properties to process
if isa(el,'LIST') then foreach props,self.xproperties,j do ((self.xproperties)[j])[i]=el[j] else begin
case propagate of
;Do not propagate property
0 : currentx[i]=el
;Propagate property to all plots
1 : begin
foreach props,self.xproperties,j do ((self.xproperties)[j])[i]=el
to_update=hash(indgen(self.nlines*self.ncolumns),self.oplots)
end
;Propagate property over the plot's column
2 : begin
((self.xproperties)[column])[i]=el
to_update[column+indgen(self.nlines)*self.ncolumns]=(self.oplots)[column+indgen(self.nlines)*self.ncolumns]
end
endcase
endelse
endif
if currenty.haskey(i) then begin ;If it is one of the y properties to process
if isa(el,'LIST') then foreach props,self.yproperties,j do ((self.yproperties)[j])[i]=el[j] else begin
case propagate of
;Do not propagate property
0 : currenty[i]=el
;Propagate property to all plots
1 : begin
foreach props,self.yproperties,j do ((self.yproperties)[j])[i]=el
to_update=hash(indgen(self.nlines*self.ncolumns),self.oplots)
end
;Propagate property over the plot's line
2 : begin
((self.yproperties)[line])[i]=el
to_update[line*self.ncolumns+indgen(self.ncolumns)]=(self.oplots)[line*self.ncolumns:(line+1)*self.ncolumns-1]
end
endcase
endelse
endif
endforeach
;Update the properties in the plot objects where it is necessary
foreach el,to_update,i do if isa(el) then $
el.setproperty,_extra=(((self.xproperties)[i mod self.ncolumns])+((self.yproperties)[i/self.ncolumns])).tostruct()
;Prepare the new extras, which includes the x/y properties for that plot, and the other (untouched) properties
ret=extras[*]
foreach el,(self.xproperties)[column],i do if isa(el) then ret[i]=el
foreach el,(self.yproperties)[line],i do if isa(el) then ret[i]=el
return,ret
end
;+
; :Description:
; Processes the extra parameters passed to `pp_multiplot::getproperty` and
; `pp_multiplot::setproperty`, to pick those that specify axes properties.
; Axes properties are given a special treatment, as they might be kept
; synchronized across lines/column/all plots.
;
; :Hidden: This routine is to be used only by `pp_multiplot::getproperty` and
; `pp_multiplot::setproperty`.
;
; :Private: This routine is to be used only by `pp_multiplot::getproperty` and
; `pp_multiplot::setproperty`.
;
; :Params:
; extras: in, required
; Extra parameters that were passed to the caller, converted to a hash, to be
; processed here.
;
; :Keywords:
; get: out, optional
; If provided, a variable where the values of the x/y properties that were
; processed here are returned.
; set: out, optional
; This informs whether this routine encountered and processed any x/y properties,
; so that `pp_multiplot::setproperty` can know that an update on plot properties
; might be needed.
;
; The other keywords present here are the x/y properties that this routine processes,
; and which must be present in the argument list so that their values can be passed
; back trough `pp_multiplot::getproperty`, due to the way _ref_extra works.
;
; :Returns:
; A hash of the properties that were not processed here, which should be passed
; on to the call of window's get/set property.
;
;-
function pp_multiplot::process_extras_properties,extras,get=get,set=set,$
XCOLOR=xcolor,XGRIDSTYLE=xgridstyle,XLOG=xlog,XMAJOR=xmajor,XMINOR=xminor,XSUBTICKLEN=xsubticklen,$
XTEXT_COLOR=xtext_color,XTEXT_POS=xtext_pos,XTHICK=xthick,XTICKDIR=xtickdir,XTICKFONT_NAME=xtickfont_NAME,$
XTICKFONT_SIZE=xtickfont_size,XTICKFONT_STYLE=xtickfont_style,XTICKFORMAT=xtickformat,$
XTICKINTERVAL=xtickinterval,XTICKLAYOUT=xticklayout,XTICKLEN=xticklen,XTICKNAME=xtickname,$
XTICKUNITS=xtickunits,XTICKVALUES=xtickvalues,XTITLE=xtitle,XTRANSPARENCY=xtransparency,$
YCOLOR=ycolor,YGRIDSTYLE=ygridstyle,YLOG=ylog,YMAJOR=ymajor,YMINOR=yminor,YSUBTICKLEN=ysubticklen,$
YTEXT_COLOR=ytext_color,YTEXT_POS=ytext_pos,YTHICK=ythick,YTICKDIR=ytickdir,YTICKFONT_NAME=ytickfont_name,$
YTICKFONT_SIZE=ytickfont_size,YTICKFONT_STYLE=ytickfont_style,YTICKFORMAT=ytickformat,$
YTICKINTERVAL=ytickinterval,YTICKLAYOUT=yticklayout,YTICKLEN=yticklen,YTICKNAME=ytickname,$
YTICKUNITS=ytickunits,YTICKVALUES=ytickvalues,YTITLE=ytitle,YTRANSPARENCY=ytransparency
compile_opt idl2, logical_predicate,hidden
retr=arg_present(get)
if retr then get=hash() else begin
oldx=list()
foreach column,self.xproperties,i do oldx.add,column[*]
oldy=list()
foreach column,self.yproperties,i do oldy.add,column[*]
endelse
set=0
if (n_elements(extras) eq 0) then return,hash() ;Get out if there is nothing to process
foreach value,extras,key do begin
if ((self.xproperties)[0]).haskey(key) then begin
set=1
extras.remove,key
if retr then begin;If properties are being retrieved
get[key]=list()
foreach column,self.xproperties do (get[key]).add,column[key]
endif else begin ;If properties are being set
if (n_elements(value) eq self.ncolumns) && isa(value,'LIST') then $
foreach el,value,i do ((self.xproperties)[i])[key]=el else $ ;If a list was provided
foreach el,self.xproperties do el[key]=value ;If a single value must be replicated for all columns
endelse
endif
if ((self.yproperties)[0]).haskey(key) then begin
set=1
extras.remove,key
if retr then begin;If properties are being retrieved
get[key]=list()
foreach column,self.yproperties do (get[key]).add,column[key]
endif else begin ;If properties are being set
if (n_elements(value) eq self.nlines) && isa(value,'LIST') then $
foreach el,value,i do ((self.yproperties)[i])[key]=el else $ ;If a list was provided
foreach el,self.yproperties do el[key]=value ;If a single value must be replicated for all lines
endelse
endif
endforeach
;Update any properties that have changed
if ~retr then begin
foreach el,self.xproperties,column do if (el ne oldx[column]) then $
foreach opl,(self.oplots)[indgen(self.nlines)*self.ncolumns+column] do $
opl.setproperty,_extra=el.tostruct()
foreach el,self.yproperties,line do if (el ne oldy[line]) then $
foreach opl,(self.oplots)[indgen(self.ncolumns)+(self.ncolumns*line)] do $
opl.setproperty,_extra=el.tostruct()
endif
;Send the retrieved properties back
if retr then begin
if arg_present(xcolor) then xcolor=get['XCOLOR']
if arg_present(xgridstyle) then xgridstyle=get['XGRIDSTYLE']
if arg_present(xlog) then xlog=get['XLOG']
if arg_present(xmajor) then xmajor=get['XMAJOR']
if arg_present(xminor) then xminor=get['XMINOR']
if arg_present(xsubticklen) then xsubticklen=get['XSUBTICKLEN']
if arg_present(xtext_color) then xtext_color=get['XTEXT_COLOR']
if arg_present(xtext_pos) then xtext_pos=get['XTEXT_POS']
if arg_present(xthick) then xthick=get['XTHICK']
if arg_present(xtickdir) then xtickdir=get['XTICKDIR']
if arg_present(xtickfont_name) then xtickfont_name=get['XTICKFONT_NAME']
if arg_present(xtickfont_size) then xtickfont_size=get['XTICKFONT_SIZE']
if arg_present(xtickfont_style) then xtickfont_style=get['XTICKFONT_STYLE']
if arg_present(xtickformat) then xtickformat=get['XTICKFORMAT']
if arg_present(xtickinterval) then xticklayout=get['XTICKLAYOUT']
if arg_present(xticklayout) then xticklayout=get['XTICKLAYOUT']
if arg_present(xticklen) then xticklen=get['XTICKLEN']
if arg_present(xtickname) then xtickname=get['XTICKNAME']
if arg_present(xtickunits) then xtickunits=get['XTICKUNITS']
if arg_present(xtickvalues) then xtickvalues=get['XTICKVALUES']
if arg_present(xtitle) then xtitle=get['XTITLE']
if arg_present(xtransparency) then xtransparency=get['XTRANSPARENCY']
if arg_present(ycolor) then ycolor=get['YCOLOR']
if arg_present(ygridstyle) then ygridstyle=get['YGRIDSTYLE']
if arg_present(ylog) then ylog=get['YLOG']
if arg_present(ymajor) then ymajor=get['YMAJOR']
if arg_present(yminor) then yminor=get['YMINOR']
if arg_present(ysubticklen) then ysubticklen=get['YSUBTICKLEN']
if arg_present(ytext_color) then ytext_color=get['YTEXT_COLOR']
if arg_present(ytext_pos) then ytext_pos=get['YTEXT_POS']
if arg_present(ythick) then ythick=get['YTHICK']
if arg_present(ytickdir) then ytickdir=get['YTICKDIR']
if arg_present(ytickfont_name) then ytickfont_name=get['YTICKFONT_NAME']
if arg_present(ytickfont_size) then ytickfont_size=get['YTICKFONT_SIZE']
if arg_present(ytickfont_style) then ytickfont_style=get['YTICKFONT_STYLE']
if arg_present(ytickformat) then ytickformat=get['YTICKFORMAT']
if arg_present(ytickinterval) then yticklayout=get['YTICKLAYOUT']
if arg_present(yticklayout) then yticklayout=get['YTICKLAYOUT']
if arg_present(yticklen) then yticklen=get['YTICKLEN']
if arg_present(ytickname) then ytickname=get['YTICKNAME']
if arg_present(ytickunits) then ytickunits=get['YTICKUNITS']
if arg_present(ytickvalues) then ytickvalues=get['YTICKVALUES']
if arg_present(ytitle) then ytitle=get['YTITLE']
if arg_present(ytransparency) then ytransparency=get['YTRANSPARENCY']
endif
ret=extras[*]
return,ret
end
;+
; :Description:
; Updates the tick labels in the given plot object, to suppress the first, last
; or both tick labels in the plot.
;
; :Hidden: This routine is to be used only by methods of `pp_multiplot`.
;
; :Private: This routine is to be used only by methods of `pp_multiplot`.
;
; :Params:
; endticks: in, required
; The mode to use for tick suppression. Like in `pp_plot`, 1 suppresses only
; the last tick, 2 suppresses only the first tick, and 0 suppresses both.
; opl: in, required
; The plot object to act upon when updating the tick labels.
; ax: in, required
; The axis to act upon in the given plot object: 'x' or 'y'
;
;-
pro pp_multiplot::setendticks,endticks,opl,ax
compile_opt idl2,logical_predicate,hidden
;If necessary, revise x/yendticks
if (ax eq 'x') && (endticks eq 1) then begin
xr=opl.xrange
xtv=opl.xtickv
nt=n_elements(xtv)
ti=abs(xtv[-1]-xtv[-2])
if ((abs(xr[1]-xtv[-1]))<(abs(xr[1]-xtv[0]))) gt self.xtickratio*ti*(nt/3d0) then endticks=3
endif
if (ax eq 'y') && (endticks eq 1) then begin
xr=opl.yrange
xtv=opl.ytickv
nt=n_elements(xtv)
ti=abs(xtv[-1]-xtv[-2])
if ((abs(xr[1]-xtv[-1]))<(abs(xr[1]-xtv[0]))) gt self.ytickratio*ti*(nt/3d0) then endticks=3
endif
if (endticks ne 3) then begin
axes=opl[ax+'axis']
if axes eq obj_new() then axes=[]
foreach el, axes do if isa(el.tickname) then begin
tn=el.tickname
range=ax eq 'x' ? el.xrange : el.yrange
if range[1] ge range[0] then begin
r0=0 & r1=-1
endif else begin
r0=-1 & r1=0
endelse
if ((endticks eq 0)||(endticks eq 2)) then tn[r0]=''
if ((endticks eq 0)||(endticks eq 1)) then tn[r1]=''
el.tickname=tn
endif
endif
end
;+
; :Description:
; Calculates the position parameters for a plot in the grid, given its index,
; and the parameters set in the fields of self. For use of plot objects, use
; `pp_multiplot::plot` directly, which already uses this method to compute the
; location in the grid. This method can be useful if one wants to add another
; type of Graphic (not a plot object) into the grid, such as an image or a contour.
;
; :Params:
; mindex: in, required
; The grid index of the plot being created, from 0 to ncolumns*nlines-1.
;
; :Keywords:
; bottom: out
; Indicates whether this plot is in the bottom line of the grid.
; left: out
; Indicates whether this plot is in the left column of the grid.
; top: out
; Indicates whether this plot is in the top line of the grid.
; right: out
; Indicates whether this plot is in the right column of the grid.
; column: out
; The column where this plot lies on the grid (starting from 0).
; line: out
; The line where this plot lies on the grid (starting from 0).
;
; :Returns:
; The position array to be passed on to plot, to create this plot on the proper
; place in the multiplot window. Contains [x0,y0,x1,y1], where the first two
; refer to the lower-left corner, and the last two refer to the top-right corner,
; with x counting from the left, and y counting from the bottom.
;-
function pp_multiplot::getposition,mindex,bottom=bottom,left=left,top=top,right=right,$
column=column,line=line
compile_opt idl2,logical_predicate
;Determine the location for the plot
column=mindex mod self.ncolumns
line=mindex/self.ncolumns
;Parameters that determine if the axes labels are going to be drawn
left=column eq 0 ;Is this plot on the left column?
right=column eq (self.ncolumns-1) ;Is this plot on the right column?
bottom=line eq (self.nlines-1) ;Is this plot on the bottom line?
top=line eq 0 ;Is this plot on the bottom line?
fullwidth=1d0-self.global_margin[0]-self.global_margin[2]
fullheight=1d0-self.global_margin[1]-self.global_margin[3]
shiftsx=self.global_margin[0]+([0d0,total(self.cwidths.toarray(),/cumulative)])*fullwidth
shiftsy=self.global_margin[1]+(1d0-[0d0,total(self.lheights.toarray(),/cumulative)])*fullheight
return,[shiftsx[column]+(left ? 0d0 : self.xgap/2d0),shiftsy[line+1]+(bottom ? 0d0 : self.ygap/2d0),$
shiftsx[column+1]-(right ? 0d0 : self.xgap/2d0),shiftsy[line]-(top ? 0d0 : self.ygap/2d0)]
end
;+
; :Description:
; Retrieves properties from pp_multiplot objects. Since pp_multiplot inherits
; from IDL_Object, these properties can be accessed with the dot (.) operator.
; The extra properties not handled here are passed on to the getproperty method
; of the window object (which contains the plots).
;
; :Keywords:
; multi_layout: out, optional
; The layout of the grid, as a 2-element array with the number of
; columns and the number of lines for the grid.
; global_xtitle: out, optional
; The common title for all the x axes.
; global_ytitle: out, optional
; The common title for all the y axes.
; global_margin: out, optional
; The margins, in normalized units (range 0 to 1) around the grid. Returned
; as a 4-element array, for [left, bottom, right, top] margins.
; window: out, optional
; The window object, which contains the plots.
; title: out, optional
; The title for the entire plot (which resides in the window object).
; xranges: out, optional
; A list, with one element for each column, each being a 2-element array
; with the minimum and maximum of the x axes for the plots on each column.
; yranges: out,optional
; A list, with one element for each line, each being a 2-element array
; with the minimum and maximum of the y axes for the plots on each line.
; xproperties: out,optional
; A list, with one element for each column, each being a hash with the x
; axes' properties that have been set for the plots on each column. On columns
; with no properties set, the hash is empty.
; yproperties: out,optional
; A list, with one element for each line, each being a hash with the y
; axes' properties that have been set for the plots on each line. On lines
; with no properties set, the hash is empty.
; xendticks: out, optional
; A list, with one element for each column, each being the mode set for
; suppressing the first/last x tick labels of the corresponding plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (xendticks=1), except for those that fall on the rightmost
; column (xendticks=3).
; yendticks: out, optional
; A list, with one element for each column, each being the mode set for
; suppressing the first/last y tick labels of the corresponding plot: 0
; means that both the first and last labels are suppressed, 1 means that only
; the last label is suppressed, 2 means that only the first label is suppressed,
; and 3 means that neither is suppressed. The default for each plot depends
; on its location on the multiplot grid (its mindex): Plots have their last
; label suppressed (yendticks=1), except for those that fall on the top
; line (yendticks=3).
; _ref_extra: out, optional
; Any keywords not handled by this method are passed on to the getproperty
; method of the window object. See the help on window objects for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y). They are from the plot objects, and are returned
; as a list, with one element for each column (x) or line (y). See the help
; on plot objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
pro pp_multiplot::getproperty,_ref_extra=ex,$
multi_layout=mlayout,global_xtitle=gxtitle,global_ytitle=gytitle,$
global_margin=gmargin,window=owindow,$
title=otitle,xranges=xranges,yranges=yranges,$
xproperties=xproperties,yproperties=yproperties,xendticks=xendticks,yendticks=yendticks,$
xgap=xgap,ygap=ygap
compile_opt idl2, logical_predicate
if arg_present(gxtitle) then gxtitle=self.global_xtitle
if arg_present(gytitle) then gytitle=self.global_ytitle
if arg_present(gmargin) then gmargin=self.global_margin
if arg_present(mlayout) then mlayout=self.mlayout
if arg_present(owindow) then owindow=self.owindow
if arg_present(xranges) then xranges=(self.xranges)[*]
if arg_present(yranges) then yranges=(self.yranges)[*]
if arg_present(xendticks) then xendticks=(self.xendticks)[*]
if arg_present(yendticks) then yendticks=(self.yendticks)[*]
;Return only the x/y properties that have been set
if arg_present(xproperties) then xproperties=(self.xproperties)[where(self.xproperties ne !null)]
if arg_present(yproperties) then yproperties=(self.yproperties)[where(self.yproperties ne !null)]
if arg_present(xgap) then xgap=self.xgap
if arg_present(ygap) then ygap=self.ygap
;Filter and process the [x/y]properties from the extras
extras=hash(ex)
ex=self.process_extras_properties(extras,get=props,_extra=ex)
ex=(ex.keys()).toarray() ;Extras to pass on
;Get all other properties from the window object
;This block is to provide the right title object, due to a bug in IDL 8.0's igetid that breaks title in window objects.
if arg_present(otitle) then begin
tool=self.owindow.gettool()
id=tool.findidentifiers('*TITLE')
otitle=tool.getbyidentifier(id)
endif
if (n_elements(ex) ne 0) then self.owindow.getproperty,_extra=ex
end
;+
; :Description:
; Sets properties for pp_multiplot objects. Since pp_multiplot inherits
; from IDL_Object, these properties can be accessed with the dot (.) operator.
; The extra properties not handled here are passed on to the setproperty method
; of the window object (which contains the plots).
;
; :Keywords:
; global_xtitle: in, optional
; The common title for all the x axes.
; global_ytitle: in, optional
; The common title for all the y axes.
; title: in, optional
; The title for the entire plot (which resides in the window object).
; xranges: in, optional
; The minimum and maximum of the x axes for the plots on each column. Provided
; as either a list, with one element for each column, each being a 2-element
; array, or a 2-element array, to be applied for all columns, or as a [2,ncolumns]
; array, with the minimum and maximum for each column.
; yranges: in,optional
; The minimum and maximum of the y axes for the plots on each line. Provided
; as either a list, with one element for each line, each being a 2-element
; array, or a 2-element array, to be applied for all lines, or as a [2,nlines]
; array, with the minimum and maximum for each line.
; xproperties: in,optional
; The x axes' properties that are set for the plots on each column. Provided
; as either a list, with one element for each column, each being a hash with
; the properties/values as keys/values, or a single hash, to be applied for
; all columns.
; yproperties: in,optional
; The y axes' properties that are set for the plots on each line. Provided
; as either a list, with one element for each line, each being a hash with
; the properties/values as keys/values, or a single hash, to be applied for
; all lines.
; xendticks: in, optional
; The modes for suppressing the first/last x tick labels of the corresponding
; plots: 0 means that both the first and last labels are suppressed, 1 means
; that only the last label is suppressed, 2 means that only the first label
; is suppressed, and 3 means that neither is suppressed. Provided as either
; a nlines*ncolumns array, with one element for each plot, or as a scalar, to
; be applied the same for all plots.
; yendticks: in, optional
; The modes for suppressing the first/last y tick labels of the corresponding
; plots: 0 means that both the first and last labels are suppressed, 1 means
; that only the last label is suppressed, 2 means that only the first label
; is suppressed, and 3 means that neither is suppressed. Provided as either
; a nlines*ncolumns array, with one element for each plot, or as a scalar, to
; be applied the same for all plots.
; _extra: in, optional
; Any keywords not handled by this method are passed on to the getproperty
; method of the window object. See the help on window objects for details.
;
; The following properties are axes properties, that can be made common across
; columns (x) or lines (y). They are for the plot objects, and are set
; as either a list, with one element for each column (x) or line (y), or a
; single property, to be replicated for all plots in the column/line. See
; the help on plot objects for details:
;
; [XY]COLOR, [XY]GRIDSTYLE, [XY]LOG, [XY]MAJOR, [XY]MINOR, [XY]SUBTICKLEN, [XY]TEXT_COLOR,
; [XY]TEXT_POS, [XY]THICK, [XY]TICKDIR, [XY]TICKFONT_NAME, [XY]TICKFONT_SIZE, [XY]TICKFONT_STYLE,
; [XY]TICKFORMAT, [XY]TICKINTERVAL, [XY]TICKLAYOUT, [XY]TICKLEN, [XY]TICKNAME, [XY]TICKUNITS,
; [XY]TICKVALUES, [XY]TITLE, [XY]TRANSPARENCY
;
;-
pro pp_multiplot::setproperty,_extra=ex,$
global_xtitle=gxtitle,global_ytitle=gytitle,$
title=title,xranges=xranges,yranges=yranges,$
xproperties=xproperties,yproperties=yproperties,xendticks=xendticks,yendticks=yendticks,$
xgap=xgap,ygap=ygap
compile_opt idl2, logical_predicate
if (n_elements(gxtitle) ne 0) then begin ;Set the global x title
if obj_valid(self.global_xtitle) then self.global_xtitle.string=gxtitle else begin
self.owindow.select,/clear ;Just to make this window the current one
self.global_xtitle=text(0.5,0.025,gxtitle,alignment=0.5)
endelse
endif
if (n_elements(gytitle) ne 0) then begin ;Set the global y title
if obj_valid(self.global_ytitle) then self.global_ytitle.string=gytitle else begin
self.owindow.select,/clear ;Just to make this window the current one
self.global_ytitle=text(0.05,0.5,gytitle,alignment=0.5,baseline=[0.,1.0,0.],updir=[-1.,0.,0.])
endelse
endif
;Set [xy]ranges
if ((n_elements(xranges) eq self.ncolumns) && isa(xranges,'list')) then self.xranges=xranges[*]
if (n_elements(xranges) eq 2*self.ncolumns) then for i=0,self.ncolumns-1 do (self.xranges)[i]=xranges[*,i]
if ((n_elements(xranges) eq 2) && (~isa(xranges,'list'))) then for i=0,self.ncolumns-1 do (self.xranges)[i]=xranges
;Update ranges if necessary
if (n_elements(xranges) ne 0) then foreach el,self.oplots,i do if isa(el) then begin
column=i mod self.ncolumns
if ~array_equal(el.xrange,xranges[column]) then begin
el.xrange=(self.xranges)[column]
self.setendticks,(self.xendticks)[i],el,'x'
self.setendticks,(self.yendticks)[i],el,'y'
endif
endif
if ((n_elements(yranges) eq self.nlines) && isa(yranges,'list')) then self.yranges=yranges[*]
if (n_elements(yranges) eq 2*self.nlines) then for i=0,self.nlines-1 do (self.yranges)[i]=yranges[*,i]
if ((n_elements(yranges) eq 2) && (~isa(yranges,'list'))) then for i=0,self.nlines-1 do self.yranges[i]=yranges
if (n_elements(yranges) ne 0) then foreach el,self.oplots,i do if isa(el) then el.yrange=(self.yranges)[i/self.ncolumns]
;Update ranges if necessary
if (n_elements(yranges) ne 0) then foreach el,self.oplots,i do if isa(el) then begin
line=i/self.ncolumns
if ~array_equal(el.yrange,yranges[line]) then begin
el.yrange=(self.yranges)[line]
self.setendticks,(self.xendticks)[i],el,'x'
self.setendticks,(self.yendticks)[i],el,'y'
endif
endif
;Set [xy]endticks
oldxendticks=(self.xendticks)[*]
if (n_elements(xendticks) eq self.ncolumns*self.nlines) then (self.xendticks)[*]=xendticks[*]
if ((n_elements(xendticks) eq 1) && (~isa(xendticks,'list'))) then (self.xendticks)[*]=xendticks
;Update ranges if necessary
if (n_elements(xendticks) ne 0) then begin
w=where(self.xendticks ne oldxendticks,/null)
foreach el,(self.oplots)[w],i do if isa(el) then begin
self.setendticks,(self.xendticks)[i],el,'x'
endif
endif
oldyendticks=(self.yendticks)[*]
if (n_elements(yendticks) eq self.ncolumns*self.nlines) then (self.yendticks)[*]=yendticks[*]
if ((n_elements(yendticks) eq 1) && (~isa(yendticks,'list'))) then (self.yendticks)[*]=yendticks
;Update ranges if necessary
if (n_elements(yendticks) ne 0) then begin
w=where(self.yendticks ne oldyendticks,/null)
foreach el,(self.oplots)[w],i do if isa(el) then begin
self.setendticks,(self.yendticks)[i],el,'y'
endif
endif
;Set [xy]properties
oldx=list()
foreach column,self.xproperties,i do oldx.add,column[*]
oldy=list()
foreach column,self.yproperties,i do oldy.add,column[*]
;Filter and process the [x/y]properties from the extras
;Get around bug in IDL 8.0's hash init()
extras=hash() & if (n_elements(ex) ne 0) then begin
tn=tag_names(ex)
for i=0,n_tags(ex)-1 do extras[tn[i]]=ex.(i)
endif
ex=self.process_extras_properties(extras,set=set)
ex=ex.tostruct() ;Extras to pass on
;If the properties were provided in a list (one element per column/line)
if ((n_elements(xproperties) eq self.ncolumns) && isa(xproperties,'list')) then $
foreach el,xproperties,i do (self.xproperties)[i]=el
if ((n_elements(yproperties) eq self.nlines) && isa(yproperties,'list')) then $
foreach el,yproperties,i do (self.yproperties)[i]=el
;If the properties were provided in a hash (same properties for all columns/lines)
if (n_elements(xproperties) eq 1) && isa(xproperties,'hash') then foreach el,self.xproperties do el+=xproperties
if (n_elements(yproperties) eq 1) && isa(yproperties,'hash') then foreach el,self.yproperties do el+=yproperties
;Update properties if necessary
if (n_elements(xproperties) ne 0)||(n_elements(yproperties) ne 0)||set then $
foreach el,self.oplots,i do if isa(el) then begin
column=i mod self.ncolumns
line=i/self.ncolumns
if ((self.xproperties)[column] ne oldx[column]) or ((self.yproperties)[line] ne oldy[line]) then $
el.setproperty,_extra=((self.xproperties)[column]+(self.yproperties)[line]).tostruct()
end
;Pass on all other properties to the window object
;The following block is to avoid window's title property, which is broken in IDL 8.0
if (n_elements(title) ne 0) then begin
self.getproperty,title=otitle
if ~isa(oTitle) then $ ;Make a title if there is none
itext,title,target=self.owindow->getfullidentifier(),/title else $
otitle.setproperty,string=title ;Or just change the title's string if there a title already
otitle.updatescene
endif
if (n_elements(ex) ne 0) then self.owindow.setproperty,_extra=ex
end
;+
; :Description:
; Synchronizes the axes, across a line and column, or over the whole grid,
; so that they have the same x/y ranges, and, optionally, the same set
; x/y axes properties.
;
; :Params:
; mindex: in, optional
; If provided, uses the plot at position mindex (count starts from 0) as the
; reference for the axes. If not provided, the currently selected plot is used.
; If the index is not provided and none are selected, the first valid plot
; is used.
;
; :Keywords:
; layout: in, optional, default=0
; If set, not only the ranges are synchronized, but also the x/y axes layouts,
; from those properties that have been set (in xproperties and xyproperties).
; all: in, optional, default=0
; If set, synchronization is carried out on all plots in the grid, instead of
; only to those at the same line and same column as the plot used as reference.
;
; :Examples:
; See the documentation on the method `pp_multiplot::plot` for examples.
;
;-
pro pp_multiplot::sync_axes,mindex,layout=layout,all=all
compile_opt idl2, logical_predicate
;Defaults
layout=n_elements(layout) eq 1 ? layout : 0
all=n_elements(all) eq 1 ? all : 0
if (n_elements(mindex) ne 1) then begin ;Get the ranges from the current plot
sel=self.owindow.getselecteditems()
if isa(sel) then begin
sel.getproperty,xrange=xr,yrange=yr
dss=(sel.getdataspace()).getfullidentifier()+'/'
foreach el,self.oplots,i do if isa(el) then begin
ds=stregex(el.getfullidentifier(),'.*(/DATA SPACE).*/',/extract)
if dss eq ds then break
endif
mindex=i
endif
endif
if (n_elements(mindex) eq 0) then mindex=0
;Get the ranges from the given index
mindex=0>mindex<(self.ncolumns*self.nlines-1)
column=mindex mod self.ncolumns
line=mindex/self.ncolumns
print,'sync_axes: Updating axes to those of index '+strtrim(mindex,2)
if (n_elements(mindex) eq 1) then begin
if isa((self.oplots)[mindex]) then begin
xr=((self.oplots)[mindex]).xrange
yr=((self.oplots)[mindex]).yrange
self.setendticks,(self.xendticks)[mindex],(self.oplots)[mindex],'x'
self.setendticks,(self.yendticks)[mindex],(self.oplots)[mindex],'y'
endif else begin
print,'sync_axes: Selected index does not yet contain a plot; doing nothing'
return
endelse
endif
;Update the ranges
newxranges=(self.xranges)[*]
newyranges=(self.yranges)[*]
if all then begin ;Sync all plots
newxranges[*]=xr
newyranges[*]=yr
endif else begin ;Sync only plots in the current line and column
newxranges[column]=xr
newyranges[line]=yr
endelse
self.setproperty,xranges=newxranges,yranges=newyranges
;Update the other properties, if this option was set
if layout then begin
props=(self.xproperties)[column]+(self.yproperties)[line]
props=(props[where(props ne !null)]).tostruct()
if (~all) then begin ;Sync only plots in the current line and column
to_update=hash(indgen(self.ncolumns)+self.ncolumns*line)
to_update+=hash(indgen(self.nlines)*self.ncolumns+self.ncolumns*line)
foreach el,(self.oplots)[(to_update.keys()).toarray()] do el.setproperty,_extra=props
;Sync all plots
endif else foreach el,self.oplots do if isa(el) then el.setproperty,_extra=props
endif
end
pro pp_multiplot::decideintervals,dprange,dticks,dint,decide=decide,newrange=newrange,dminor=dminor,newdticks=newdticks;New algorithm
compile_opt idl2,logical_predicate,hidden
intervals=[2d0,1d0,1d0,0.5d0];,0.25d0] ;Possible interval multipliers to use
intrats=[0.2d0,0.1d0,1d0,0.5d0];,0.25d0] ;Possible normalized interval multipliers
minors=[4,10,10,5]
nintervals=n_elements(intervals)
ints=[6,5,4,3] ;Possible number of intervals
nints=n_elements(ints)
mindiff=!values.d_infinity
for i=0,nints-1 do begin ;Try every number of intervals, to find out which one gives nicer intervals
dints=double(dprange[1]-dprange[0])/ints[i]
al=alog10(dints)
dints=al ge 0d0 ? 1d1^((al mod 1)-1d0) : 1d1^(al mod 1)
tmp=min(abs(dints-intrats),minloc)
if ((tmp-mindiff) lt -(machar(/double)).eps) then begin
mindiff=tmp
minint=intervals[minloc]
sints=ints[i]
dint=intrats[minloc]*1d1^ceil(al)
dminor=minors[minloc]
endif
;print,ints[i],minint,tmp,sints,dint
endfor
newrange=dint*[floor(dprange[0]/dint),ceil(dprange[1]/dint)]
dticks=sints+1d0
newdticks=(newrange[1]-newrange[0])/dint
end
;+
; :Description:
; This method should be called after one or more plots in the multiplot had
; its axes changed in a way that caused its ticks to be recomputed (setting the
; range, for instance), so that the end ticks get fixed.
;
;-
pro pp_multiplot::updateranges,x=x,y=y
compile_opt idl2,logical_predicate
if keyword_set(x) then begin
dint=0d0
dticks=0
xranges=(self.xranges)[*]
foreach xr,xranges,ix do begin
self.decideintervals,xr,dtick,dint,newrange=nr
xranges[ix]=nr
endforeach
self.setproperty,xranges=xranges
endif
if keyword_set(x) then begin
dint=0d0
dticks=0
xranges=(self.xranges)[*]
foreach xr,xranges,ix do begin
self.decideintervals,xr,dtick,dint,newrange=nr
xranges[ix]=nr
endforeach
self.setproperty,yranges=yranges
endif
foreach el,self.oplots,iel do if isa(el) then begin
el['yaxis'].tickname=''
el['xaxis'].tickname=''
self.setendticks,self.xendticks[iel],el,'x'
self.setendticks,self.yendticks[iel],el,'y'
endif
end
;+
; :Description:
; Simple wrapper for window::close, to make pp_multiplot objects
; look almost like they inherited the window class they use. For more details,
; see the help on the close method of IDL's Graphics.
;-
pro pp_multiplot::close
compile_opt idl2,logical_predicate
self.owindow.close
end
;+
; :Description:
; Simple wrapper for window::save, to make pp_multiplot objects
; look almost like they inherited the window class they use. For more details,
; see the help on the save method of IDL's Graphics.
;
; :Params:
; filename, in, required
; Passed on to window::save, the name for the file to create. Its extension
; determines the type of file to be produced. See the help window::save for
; more details.
;
; :Keywords:
; _REF_EXTRA, in, out, optional
; Any keywords are passed on, unaltered, to window::save. See the
; help on the window::save for details.
;
; :Author: Paulo Penteado (pp.penteado@gmail.com), 2010
;-
pro pp_multiplot::save,filename, _REF_EXTRA=_extra
compile_opt idl2,logical_predicate
self.owindow.save,filename, _strict_extra=_extra
end
;+
; :Description:
; Class definition for `pp_multiplot`.
;-
pro pp_multiplot__define
compile_opt idl2, logical_predicate
!null={pp_multiplot, inherits idl_object, $
owindow:obj_new(),$ ;The window is contained, instead of inherited, because of the convoluted way window objects are initialized.
oplots:list(),$ ;Where the plots will be contained
cwidths:list(),lheights:list(),mlayout:intarr(2),ncolumns:0,nlines:0,$ ;Specify the grid shape
;Global formatting
global_title:'', global_xtitle:obj_new(), global_ytitle:obj_new(),$
global_margin:dblarr(4),$
mindex:0,$ ;Current position in the grid (0 to nlines*ncolumns)
xranges:list(),yranges:list(),$ ;x/y ranges for each column/line in the grid
xproperties:list(),yproperties:list(),$ ;x/y axes roperties for each column/line in the grid
xendticks:list(),yendticks:list(),$ ;x/y endticks for each plot in the grid
xgap:0d0,ygap:0d0,$ ;x/y gap between plots
xsupressdivision:0B,ysupressdivision:0B,$ ;Supress the lines between plots in x/y
graphproperties:obj_new(),$ ;Default properties for individual graphs
xtickratio:0d0,ytickratio:0d0};Parameter to decide if the last tick on an axis should not be auto suppressed
end