; $Id: davedat2d__rebin.pro,v 1.4 2005/02/08 17:17:34 azuah Exp $
;###############################################################################
;
; CLASS NAME:
;   DaveDat2D
;
; PURPOSE:
;   This class encapsulates a DAVE formated dataset containing 2D
;   data.
;
; CATEGORY:
;   Data Browser
;
; SUPERCLASSES:
;   DaveDat
;
; PUBLIC METHODS:
;   Clone
;   Get
;   Print
;   Rebin
;   ResetData
;   Restore
;   Set
;   Transpose
;   XSpec
;
; AUTHOR:
;   Richard Tumanjong Azuah
;   NIST Center for Neutron Research
;   100 Bureau Drive, Gaithersburg, MD 20899
;   United States
;   azuah@nist.gov; (301) 9755604
;   Oct, 2002
;
; LICENSE:
;  The software in this file is written by an employee of 
;  National Institute of Standards and Technology 
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used or if the code in this file is
;  included in another product.
;
;###############################################################################



;===============================================================================
; DaveDat2D::rebin
;
; PURPOSE:
;   Rebin the current object's data along the specified axis.
;
; PARAMETERS:
;   axis [in] - (string) Is the axis (either 'x' or 'y') along which
;               the rebinning should be performed.
;
;   lolim [in] - the lower limit of the ind-axis data after
;                rebinning. lolim may be an array if multiple ranges
;                are required; in this case, the array size of lolim,
;                uplim and binw should be the same.
;
;   uplim [in] - the upper limit of the ind-axis data after
;                rebinning. uplim must be greater than lolim. uplim
;                may be  an array if multiple ranges are required; in
;                this case, the array size of lolim, uplim and binw
;                should be the same
;
;   binw [in] - the bin width of the ind-axis data after
;               rebinning. binw may be an array if multiple ranges are
;               required; in this case, the array size of lolim, uplim
;               and binw should be the same. 
;
; KEYWORDS:
;
;   notruncate [in] - (byte: 0 or 1) If not set (the default), then
;                     after rebinning the data will be truncated to 
;                     that specified by lolim and uplim otherwise,
;                     any data in unspecified ranges will be
;                     retained/copied to the rebinned result.
;
; RETURN VALUE
;     0 => failure
;     1 => success
;
function DaveDat2D::rebin, axis, lolim, uplim, binw, notruncate=notruncate

; reset !error_state structure, ie clear all errors
message,/reset

retVal = 1


; ensure (required) number of parameters are present
if (n_params() ne 4) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: Inappropriate number of parameters"
    return, 0
endif

; ensure axis is a string and set to 'x' or 'y'
if (vartype(axis) eq 'STRING') then begin
    if ((strupcase(axis) ne 'X') and (strupcase(axis) ne 'Y')) then begin
        !error_state.code = -1
        !error_state.msg = "DaveDat2D::rebin: first parameter should be either 'X' or 'Y'"
        return, 0
    endif
endif else begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: first parameter (axis) should be a string ('X' or 'Y')"
    return, 0
endelse

; ensure lolim, hilim and binw have numeric type
if ((vartype(lolim) ne 'FLOAT') and $
    (vartype(lolim) ne 'DOUBLE') and $
    (vartype(lolim) ne 'INT') and $
    (vartype(lolim) ne 'LONG') ) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: second parameter (lolim) should be a number [or vector of numbers]"
    return, 0
endif
if ((vartype(uplim) ne 'FLOAT') and $
    (vartype(uplim) ne 'DOUBLE') and $
    (vartype(uplim) ne 'INT') and $
    (vartype(uplim) ne 'LONG') ) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: third parameter (uplim) should be a number [or vector of numbers]"
    return, 0
endif
if ((vartype(binw) ne 'FLOAT') and $
    (vartype(binw) ne 'DOUBLE') and $
    (vartype(binw) ne 'INT') and $
    (vartype(binw) ne 'LONG') ) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: fourth parameter (binw) should be a number [or vector of numbers]"
    return, 0
endif
; lolim, uplim and binw should all have the same dimension and size
lolim = float(lolim)
uplim = float(uplim)
binw = float(binw)
res1 = where((size(lolim))[0:1] ne (size(uplim))[0:1], cnt1)
res2 = where((size(lolim))[0:1] ne (size(binw))[0:1], cnt2)
if ((cnt1 ne 0) or (cnt2 ne 0)) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: second, third and fourth parameters should all have the same size"
    return, 0
endif
; uplim must be greater than lolim
if ((where(uplim le lolim))[0] ge 0) then begin
    !error_state.code = -1
    !error_state.msg = "DaveDat2D::rebin: third (uplim) parameter must be > the second (lolim)"
    return, 0
endif

; the rebinning regions must not overlap
nranges = n_elements(lolim)
if (nranges gt 1) then begin
    for i=1,nranges-1 do begin
        if (lolim[i] lt uplim[i-1]) then begin
            !error_state.code = -1
            !error_state.msg = "DaveDat2D::rebin: there should be no overlap of rebin ranges"
            return, 0
        endif
    endfor 
endif  

; Get the data to be rebinned - depends on the axis 
switch strupcase(axis) of 
    'X': begin
        if (not self->get(xtype=type,xval=vin, zval=zin, zerr=zein, treatment=trmt)) then return, 0
        break
    end
    
    'Y': begin
        if (not self->get(ytype=type,yval=vin, zval=zin, zerr=zein, treatment=trmt)) then return, 0
        zin = transpose(zin)    ; transpose data so that y-axis data become the rows to be rebinned
        zein = transpose(zein)  ;
        break
    end
endswitch

; modify treatment info accordingly
line = '=================================================='
trmt = [trmt, $
        line, $
        'Perform a rebin along '+axis+'-axis']


; switch between single/multiple set limits
switch (size(lolim))[0] of
    0: begin                    ; a single scaler lolim value
        ;; Make sure rebin limits are within the data input range
        nin = n_elements(vin)
        lolim_mod = (lolim lt vin[0])? vin[0] : lolim
        uplim_mod = (uplim gt vin[nin-1])? vin[nin-1] : uplim 

        ;; determine output bins
        nregions = 1
        nout = fix((uplim_mod - lolim_mod)/binw) + 1
        vout = lolim_mod + findgen(nout)*binw
        ;; do the rebinning
        case strupcase(strmid(type,0,4)) of ; switch between histogram or points data rebinning
            'HIST': drebin,vin,zin,zein,vout,zout,zeout,/hist,/to_hist,err=err,emsg=emsg
            else: drebin,vin,zin,zein,vout,zout,zeout,/points,/to_points,err=err,emsg=emsg
        endcase
        if (err ne 0) then begin
            !error_state.code = err
            !error_state.msg = emsg
            return, 0
        endif
        ;; update treatment information
        trmt = [trmt, $
        'From '+axis+' = '+ $
        strtrim(string(lolim_mod),2)+' to '+strtrim(string(uplim_mod),2)+ $
        ' in steps of '+strtrim(string(binw),2)]

        ;; if the data outside the rebinning range is not to be
        ;; truncated then this data needs to be included in the output
        if (keyword_set(notruncate)) then begin
            ;; get any data below lolim
            res = where(vin lt lolim_mod, cnt)
            if (cnt gt 0) then begin
                vout = [vin[res],vout]
                zout = [zin[res,*],zout]
                zeout = [zein[res,*],zeout]
            endif

            ;; get any data above uplim
            res = where(vin gt uplim_mod, cnt)
            if (cnt gt 0) then begin
                vout = [vout,vin[res]]
                zout = [zout,zin[res,*]]
                zeout = [zeout,zein[res,*]]
            endif
        endif
        
        break
    end

    1: begin                    ; lolim is a vector specifying multiple lower limits

        nregions = n_elements(lolim)
        nin = n_elements(vin)
        lolim_mod = fltarr(nregions)
        uplim_mod = fltarr(nregions)
        ;; Make sure rebin limits are within the data input range
        for i = 0,nregions-1 do begin
            lolim_mod[i] = (lolim[i] lt vin[0])? vin[0] : lolim[i]
            uplim_mod[i] = (uplim[i] gt vin[nin-1])? vin[nin-1] : uplim[i]
        endfor 
        for i = 0,nregions-1 do begin

            ;; if upper limit is <= lower limit, don't do anything
            if (uplim_mod[i] le lolim_mod[i]) then continue
            ;; determine output bins
            nout = fix((uplim_mod[i] - lolim_mod[i])/binw[i]) + 1
            vout = lolim_mod[i] + findgen(nout)*binw[i]
            ;; do the rebinning
            case strupcase(strmid(type,0,4)) of ; switch between histogram or points data rebinning
                'HIST': drebin,vin,zin,zein,vout,zout,zeout,/hist,/to_hist,err=err,emsg=emsg
                else: drebin,vin,zin,zein,vout,zout,zeout,/points,/to_points,err=err,emsg=emsg
            endcase
            if (err ne 0) then begin
                !error_state.code = err
                !error_state.msg = emsg
                return, 0
            endif
            ;; update treatment information
            trmt = [trmt, $
                    'From '+axis+' = '+ $
                    strtrim(string(lolim_mod[i]),2)+' to '+strtrim(string(uplim_mod[i]),2)+ $
                    ' in steps of '+strtrim(string(binw[i]),2)]
            
            ;; if the data outside the rebinning range is not to be
            ;; truncated then this data needs to be included in the output
            if (keyword_set(notruncate)) then begin
                ;; get any data below lolim
                res = (i eq 0)? where(vin lt lolim_mod[i], cnt) : $
                                where((vin lt lolim_mod[i]) and (vin gt uplim_mod[i-1]), cnt)
                if (cnt gt 0) then begin
                    vout = [vin[res],vout]
                    zout = [zin[res,*],zout]
                    zeout = [zein[res,*],zeout]
                endif
                
                ;; if this is the last rebin region, 
                ;;then also get any data above uplim
                if (i eq (nregions-1)) then begin
                    res = where(vin gt uplim_mod[i], cnt)
                    if (cnt gt 0) then begin
                        vout = [vout,vin[res]]
                        zout = [zout,zin[res,*]]
                        zeout = [zeout,zein[res,*]]
                    endif 
                endif
            endif

            ;; accumulate the rebinned data so far
            if (n_elements(vtot) gt 0) then begin
                vtot = [vtot,vout]
                ztot = [ztot,zout] 
                zetot = [zetot,zeout]
            endif else begin
                vtot = vout
                ztot = zout
                zetot = zeout
            endelse
        endfor
        vout = vtot
        zout = ztot
        zeout = zetot
        break
    end
    
    else: begin
        !error_state.code = -1
        !error_state.msg = "DaveDat2D::rebin: second, third and fourth parameters should be scaler or vector"
        return, 0
        break
    end
endswitch

;; returned rebinned data to object
switch strupcase(axis) of 
    'X': begin
        if (not self->set(xval=vout, zval=zout, zerr=zeout,treatment=trmt)) then return, 0
        break
    end
    
    'Y': begin
        zout = transpose(zout)
        zeout = transpose(zeout)
        if (not self->set(yval=vout, zval=zout, zerr=zeout,treatment=trmt)) then return, 0
        break
    end
endswitch

return, retVal
end
;_______________________________________________________________________________
