;+
;PROCEDURE specplot,x,y,z
;NAME:
;   specplot
;PURPOSE:
;   Creates a spectrogram plot.
;   All plot limits and plot positions are handled by the keyword LIMITS.
;INPUT:
;   x:  xaxis values:  dimension N.
;   y:  yaxis values:  dimension M.  (Future update will allow (N,M))
;   Z:  color axis values:  dimension (N,M).
;
;   All options are passed in through a single structure.
;KEYWORDS:
;   LIMITS:  A structure that may contain any combination of the following
;       elements:
;       X_NO_INTERP:   Prevents interpolation along the x-axis.
;       Y_NO_INTERP:   Prevents interpolation along the y-axis.
;       NO_INTERP:     Prevents interpolation along either axis
;       NO_COLOR_SCALE: Prevents drawing of color bar scale.
;       BOTTOM, TOP:   Sets the bottom and top colors for byte-scaling
;       ALL plot keywords such as:
;       XLOG,   YLOG,   ZLOG,
;       XRANGE, YRANGE, ZRANGE,
;       XTITLE, YTITLE,
;       TITLE, POSITION, REGION  etc. (see IDL documentation for a description)
;         The following elements can be included in LIMITS to effect DRAW_COLOR_SCALE:
;       ZTICKS, ZRANGE, ZTITLE, ZPOSITION, ZOFFSET
;   DATA:  A structure that provides an alternate means of supplying
;       the data and options.  This is the method used by "TPLOT".
;   X_NO_INTERP:   Prevents interpolation along the x-axis.
;   Y_NO_INTERP:   Prevents interpolation along the y-axis.
;   OVERPLOT:      If non-zero then data is plotted over last plot.
;   OVERLAY:       If non-zero then data is plotted on top of data from last
;        last plot.
;   PS_RESOLUTION: Post Script resolution.  Default is 150.
;   NO_INTERP:     If set, do no x or y interpolation.
;   IGNORE_NAN:    If nonzero, ignore data points that are not finite.
;   DX_GAP_SIZE = Maximum time gap over which to interpolate the plot. Use this
;     keyword when overlaying spectra plots, allowing the underlying spectra to
;     be shown in the data gaps of the overlying spectra.  Overrides value set
;     by DATAGAP in dlimits.  Note: if either DX_GAP_SIZE or DATAGAP is set to
;     less than zero, then the 20 times the smallest delta x is used.
;
;Notes:
;  - The arrays x and y MUST be monotonic!  (increasing or decreasing)
;  - The default is to interpolate in both the x and y dimensions.
;  - Data gaps can be included by setting the z values to NAN  (!values.f_nan).
;  - If ZLOG is set then non-positive zvalues are treated as missing data.
;
;See Also:  "XLIM", "YLIM", "ZLIM",  "OPTIONS",  "TPLOT", "DRAW_COLOR_SCALE"
;Author:  Davin Larson,  Space Sciences Lab
; $LastChangedBy: bckerr $
; $LastChangedDate: 2009-06-16 16:46:31 -0700 (Tue, 16 Jun 2009) $
; $LastChangedRevision: 6230 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/ssl_general/trunk/tplot/specplot.pro $
;-
pro specplot,x,y,z,limits=lim,data=data,overplot=overplot,overlay=overlay,$
    ps_resolution=ps_res,x_no_interp=x_no_interp,y_no_interp=y_no_interp, $
        no_interp=no_interp, ignore_nan=ignore_nan, $
        dx_gap_size=dx_gap_size

opt = {xrange:[0.,0.],yrange:[0.,0.],zrange:[1.,1.]}

if keyword_set(dx_gap_size) then dg=dx_gap_size else str_element,lim,'datagap',dg

if keyword_set(data) then begin
  x = struct_value(data,'x')
  y = struct_value(data,'v')
  z = struct_value(data,'y')
  if not keyword_set( y ) then begin
       y = struct_value(data,'v2')    ;bp
       z = total(z,2)
  endif
  extract_tags,opt,data,except=['x','y','v']
  if keyword_set(dx_gap_size) then dg=dx_gap_size else str_element,lim,'datagap',dg
  ;str_element,lim,'datagap',dg ; old way
  ;if keyword_set(dg) then makegap,dg,x,z,v=y   ;bpif keyword_set(dg)
endif

if keyword_set(no_interp) then begin
   x_no_interp=1
   y_no_interp=1
endif

; Find where gaps are
if keyword_set(dg) then begin

   ;dg = max_gap_interp
   ;dt = median(x[1:*]-x)
   tdif = [x[1:*]-x[0:n_elements(x)-2]]
   
   ; set minimum gap interp to twice median sampling rate in current trange
   ;if dg lt 2*dt then dg = 2*dt
   
   ; set dg to 20 times the smallest dx if datagap and/or dx_gap_size is negative
   if dg lt 0 then begin
      posindx = where(tdif gt 0,poscnt)
      dg = 20d*min(tdif(posindx))
   endif
   
   dprint,verbose=verbose,'No plot interpolation for data gaps longer than ', $
          strcompress(dg,/remove_all),' seconds.'
   
   gapindx = where(tdif gt dg, gapcnt)
   
   if gapcnt gt 0 then begin
      ; create separate vars
      seg0 = lonarr(gapcnt+1) ; index numbers of start of each data segment 
      seg1 = seg0             ; index numbers of end of each data segment
      seg1[gapcnt] =  n_elements(x)-1
      for i=0L,gapcnt-1 do begin
      ;TODO: Need to account for "consecutive gaps" to reduce # of segments 
      ;      (gapcnt) and speed up the main loop for time windows with lots of
      ;      data with sample intervals greater than DATAGAP flag and/or
      ;      DX_GAP_SIZE.
         seg0[i+1] = gapindx[i]+1
         seg1[i] = gapindx[i] 
      endfor
 
   endif else begin
      ; prepare for only single iteration in for loop
      seg0 = 0L
      seg1 = n_elements(x)-1
   endelse
   
endif else begin
   ; prepare for only single iteration in for loop
   gapcnt = 0
   seg0 = 0L
   seg1 = n_elements(x)-1
endelse

;copy to temp variable
xtemp = x
ytemp = y
ztemp = z

ydim = size(y,/n_dim)

for j=0L,gapcnt do begin

   x = xtemp[seg0[j]:seg1[j]]
   if ydim eq 1 then y=ytemp else y=ytemp[seg0[j]:seg1[j],*]
   z = ztemp[seg0[j]:seg1[j],*]

   if n_params() eq 1 then begin
     dim = dimen(x)
     specplot,findgen(dim(0)),findgen(dim(1)),x,limits=lim,overplot=overplot,$
          overlay=overlay,ps_resolution=ps_res, $
          x_no_interp=x_no_interp,y_no_interp=y_no_interp
     return
   endif
   
   extract_tags,opt,lim
   
   if opt.xrange(0) eq opt.xrange(1) then opt.xrange = minmax(x)
   if opt.yrange(0) eq opt.yrange(1) then opt.yrange = minmax(y)
   
   ;str_element,opt,'ytype',value=ylog   ; obsolete keywords
   ;str_element,opt,'xtype',value=xlog
   ;str_element,opt,'ztype',value=zlog
   
   str_element,opt,'xlog',value=xlog
   str_element,opt,'ylog',value=ylog
   str_element,opt,'zlog',value=zlog
   
   str_element,opt,'gifplot',value=gifplot
   if keyword_set(gifplot) then begin
     x_no_interp = 1
     y_no_interp = 1
     no_color_scale = 1
   endif
   
   str_element,opt,'x_no_interp',value=x_no_interp
   str_element,opt,'y_no_interp',value=y_no_interp
   str_element,opt,'no_interp',value=no_interp
   if keyword_set(no_interp) then begin
      x_no_interp=1
      y_no_interp=1
   endif
   
   
   str_element,opt,'max_value',value=mx
   str_element,opt,'min_value',value=mn
   
   ;if keyword_set(mx) then print,'max_value= ', mx
   
   str_element,opt,'ztitle',value=ztitle
   str_element,opt,'bottom',value=bottom
   str_element,opt,'top',   value=top
   
   if not keyword_set(overplot) then box,opt     ; Sets plot parameters.
   
   
   zrange = opt.zrange
   y1 = y
   if keyword_set(ylog) then begin
     bad = where( finite(y1) eq 0, c)
     if c ne 0 then y1(bad) = 0.
     bad = where(y1 le 0,c)
     if c ne 0 then y1(bad) = !values.f_nan
     y1 = alog10(y1)
   endif
   
   if keyword_set(xlog) then x1 = alog10(x) else x1 = x
   
   str_element,opt,'minzlog',value=minzlog
   z1 = z
   if keyword_set(zlog) then begin
      bad = where( finite(z1) eq 0, cbad)
      if cbad ne 0 then z1[bad] = !values.f_nan
      neg = where(z1 le 0,cneg)
      if keyword_set(minzlog) then begin
          posrange = minmax(z1,/pos)
          negvals = posrange[0]/10.
      endif else negvals = !values.f_nan
      if cneg ne 0 then z1[neg] = negvals
      z1 = alog10(z1)
      zrange = alog10(zrange)
      if keyword_set(mn) then mn = alog10(mn)
      if keyword_set(mx) then mx = alog10(mx)
   endif
   
   xwindow=!x.window
   ywindow=!y.window
   xcrange=!x.crange
   ycrange=!y.crange
   
   ;str_element,opt,'overlay',value=overlay
   overlay = struct_value(opt,'overlay',default=1)
   
   ; need to be in overlay mode if stitching multiple segments together
   if gapcnt gt 0 then overlay=1
   
   if keyword_set(overlay) then begin
      winpos = convert_coord(minmax(x),minmax(y),/data,/to_norm)
      xwr = minmax(winpos(0,*))
      ywr = minmax(winpos(1,*))
   ;   xwindow(0) = xwindow(0) > xwr(0)
   ;   xwindow(1) = xwindow(1) < xwr(1)
      xwindow = xwindow > xwr[0]
      xwindow = xwindow < xwr[1]
      ywindow(0) = ywindow(0) > ywr(0)
      ywindow(1) = ywindow(1) < ywr(1)
      datpos = convert_coord(xwindow,ywindow,/norm,/to_data)
      xcrange = reform(datpos(0,*))
      ycrange = reform(datpos(1,*))
      if !x.type then xcrange = alog10(xcrange)
      if !y.type then ycrange = alog10(ycrange)
   endif
   
   
   pixpos = round(convert_coord(xwindow,ywindow,/norm,/to_device))
   npx = pixpos(0,1)-pixpos(0,0)+1
   npy = pixpos(1,1)-pixpos(1,0)+1
   xposition = pixpos(0,0)
   yposition = pixpos(1,0)
   
   if npx gt 0 and npy gt 0 then begin
   
      str_element,opt,'ignore_nan',ignore_nan
      if keyword_set(ignore_nan) then begin
         wg = where(finite(total(z1,2)),c)
         if c gt 0 then begin
           z1 = z1[wg,*]
           y1 = y1[wg,*]
           x1 = x1[wg]
         endif
      endif
      
      if !d.flags and 1 then begin   ; scalable pixels (postscript)
         if keyword_set(ps_res) then ps_resolution=ps_res else  ps_resolution = 150.  ; Postscript defaults to 150 dpi
         str_element,opt,'ps_resolution',value=ps_resolution
         dprint,dlevel=4,ps_resolution
         scale = ps_resolution/!d.x_px_cm/2.54
      endif else scale = 1.
      
      yd = ndimen(y1)
      if yd eq 1 then begin            ; Typical, y does not vary with time
        nypix = round(scale*npy)
        ny = n_elements(y1)
        yp = findgen(nypix)*(ycrange(1)-ycrange(0))/(nypix-1) + ycrange(0)
        ys = interp(findgen(ny),y1,yp)
        if keyword_set(y_no_interp) then  ys = round(ys)
      
        nxpix = round(scale*npx)
        if nxpix eq 0 then begin
           dprint, verbose=verbose, dlevel=2,'WARNING: Data segment ',strcompress(j,/remove_all),' is too small along the  x-axis',string(13B),$
           '   for the given time window or is not within the given window.  Nothing will be',string(13B),$
           '   plotted.  Try making the x-axis window smaller, or if creating a postscript',string(13B),$
           "   file, try increasing the 'ps_resolution' value using the OPTIONS command."
           no_color_scale=1
           continue
        endif
        nx = n_elements(x1)
        xp = findgen(nxpix)*(xcrange(1)-xcrange(0))/(nxpix-1) + xcrange(0)
        xs = interp(findgen(nx),x1,xp )
        if keyword_set(x_no_interp) then  xs = round(xs)
        image = interpolate(float(z1),xs,ys,missing = !values.f_nan,/grid)  ; using float( ) to fix IDL bug.
      
      ;  str_element,opt,'roi',roi
      ;  if keyword_set(roi) then begin
      ;     xp_ = xp # replicate(1.,nypix)
      ;     yp_ = replicate(1.,nxpix) # yp
      ;     roi_x = keyword_set(xlog) ? alog10(roi[*,0]) : roi[*,0]
      ;     roi_y = keyword_set(ylog) ? alog10(roi[*,1]) : roi[*,1]
      ;     dummy = enclosed(xp_,yp_,roi_x,roi_y,ncircs=ncirc)
      ;     image[where(ncirc eq 0)] = !values.f_nan
      ;  endif
      
      endif else begin
      ;  starttime = systime(1)
      ;  message,'y is 2 dimensional.  Please be patient...',/info
      
        nypix = round(scale*npy)
        ny = dimen2(y1)
        yp = findgen(nypix)*(ycrange(1)-ycrange(0))/(nypix-1) + ycrange(0)
        nxpix = round(scale*npx)
        if nxpix eq 0 then begin
           dprint, verbose=verbose, dlevel=2,'WARNING: Data segment ',strcompress(j,/remove_all),' is too small along the  x-axis',string(13B),$
           '   for the given time window or is not within the given window.  Nothing will be',string(13B),$
           '   plotted.  Try making the x-axis window smaller, or if creating a postscript',string(13B),$
           "   file, try increasing the 'ps_resolution' value using the OPTIONS command."
           no_color_scale=1
           continue
        endif
        nx = n_elements(x1)
        xp = findgen(nxpix)*(xcrange(1)-xcrange(0))/(nxpix-1) + xcrange(0)
        xs = interp(findgen(nx),x1,xp)
        xs = xs # replicate(1.,nypix)
        bad = where(finite(xs) eq 0,c)
        if c ne 0 then xs(bad)=-1
        if keyword_set(x_no_interp) then  xs = round(xs)
      
        ys = replicate(-1.,nxpix,nypix)
        ny1 = dimen1(y1)
        y_ind = findgen(ny)
        xi = round(xs)
        for i=0l,nxpix-1 do begin
          m = (xi(i) > 0) < (ny1-1)
          yt1 = reform(y1(m,*))
          ys(i,*) = interp(y_ind,yt1,yp)
        endfor
      ;dtime = systime(1)-starttime
      ;message,string(dtime)+' seconds.',/info
      
        bad = where(finite(ys) eq 0,c)
        if c ne 0 then ys(bad)=-1
        if keyword_set(y_no_interp) then  ys = round(ys)
        image = interpolate(float(z1),xs,ys,missing = !values.f_nan)
      
      endelse
      
      
      if not keyword_set(gifplot) then begin
      
        if zrange(0) eq zrange(1) then $
           zrange = minmax(image,max=mx,min=mn)
        image = bytescale(image,bottom=bottom,top=top,range=zrange)
      
      endif
      
      ;printdat,image,xposition,yposition
      if xposition ge 0 and yposition ge 0 and xposition lt !d.x_size and yposition lt !d.y_size then $
          tv,image,xposition,yposition,xsize=npx,ysize=npy
      
      
      ;redraw the axes
      str_element,/add,opt,'noerase',1
      str_element,/add,opt,'overplot',/delete
      str_element,/add,opt,'ytitle',/delete
      str_element,/add,opt,'position',reform(transpose([[!x.window],[!y.window]]),4)
      ;help,opt,/st
      box,opt

;      ; keep track of min/max zrange for all data segments
;      if j eq 0 then begin
;        zrange_min = zrange[0]
;        zrange_max = zrange[1]
;      endif else begin
;        zrange_min = min(zrange[0], zrange_min)
;        zrange_max = max(zrange[1], zrange_max)   
;      endelse
   
   endif else dprint,dlevel=0,'Out of range error'

endfor ; loop over data segments

;zrange = [zrange_min, zrange_max]

if keyword_set(zlog) then zrange = 10.^zrange

charsize=!p.charsize
str_element,opt,'charsize',value=charsize
if not keyword_set(charsize) then charsize = 1.

str_element,opt,'no_color_scale',value=no_color_scale
str_element,opt,'zticks',zticks
str_element,opt,'zposition',zposition
str_element,opt,'zoffset',zoffset
if not keyword_set(no_color_scale) then $
  draw_color_scale,brange=[bottom,top],range=zrange,log=zlog,title=ztitle, $
    charsize=charsize,yticks=zticks,position=zposition,offset=zoffset

;copy from temp variable back to input variables
x = xtemp
y = ytemp
z = ztemp

end
