;+
; ZUNWRAP
;
; "Unwrap" a solar image, converting X to angle and Y to radius. 
; Use zra2xy to do the transformation.  
;
; Scanning is done widdershins (CCW) over a specified angular range
; in the original image plane; the widdershins scan is converted
; to a horizontal one in the resulting image plane (ie moving
; in the +X direction on the final image plane moves in the CCW
; direction on the original image plane).  Radius is mapped to the
; Y axis.  This preserves angular sign conventions but mirror-reverses
; the image that is being unwrapped.
;
;;
; CALLING SEQUENCE:
; zunwrap,in=in,ihdr=ihdr,out=out,ohdr=ohdr,[optional keywords]
;
; REQUIRED KEYWORDS
;
; in, ihdr - the fits image and header to unwrap about the origin
; out,ohdr - the destination image and header
;
; OPTIONAL KEYWORDS
;
; RMIN - the minimum radius, in solar radii, of the unwrapped image
; RMAX - the maximum radius, in solar radii, of the unwrapped image
; RSTEP - the radial step, in pixels(!) of the input image
; ARANGE - A vector specifying the range (in degrees clockwise from North)
;          of the start and end of the scan.  Default is [0,360].
; ASTEPS - the number of steps around the specified 'arange'.
;
; SMOOTH - binary keyword.  If set, a slower algorithm is used that averages
; 	pixel values where relevant.  Doesn't work on conformal unwrapping
;
; LERP - Binary keyword.  If set, use bilinear interpolation on the original
;     image plane (rather than sampling) to get the destination pixel values.
;
; CONFORMAL - Binary keyword.  If set, make the transformation conformal
;   (by putting 'r' on a log scale).
;
; RADIUS - floating point.  Used with conformal keyword, specifies 
; (in output image plane pixel widths) the size of the smoothing kernel
; being applied to the input image.  (The smoothing kernel size is
; constant in the output image plane, meaning that it is variable in the
; input image plane).  Specify 0.0 for sampling, 1.0 for quick-n-dirty
; anti-moire averaging, and higher to smooth the output image more.
;
; FAST - Binary keyword - Sets "radius" to zero.
;
; MISSING - If set, contains the value which should signal missing datapoints.
;
; GRID - Binary keyword.  If set, the conformal unwrapper engine 
; 	adds a grid indicating 10s of degrees of azimuth and individual
;	solar radii on the output image.  See also GRMASK.  If this is 
;	DEFINED, then a GRMASK is produces; if it is SET, then the grid is
;	superposed on the output image. 
;
; GRMASK - output keyword. A binary mask containing 255 wherever a gridpoint
;	goes.
;
; EXAMPLE:
;	zunwrap,in=a,ihdr=ahdr,out=b,ohdr=bhdr,/conformal,rmin=1.0,rmax=6.0,$
;		asteps=600,arange=[-180,180]
;
; HISTORY:
; 	Written by Craig DeForest, early 1993.
;
; 	29-Jan-97 - Fixed up the bilinear interpolation in the conformal
; 	mapping code (Was fixing the zra2xy output *then* doing bilinear.
; 	Bad.  Definitely bad.)
;
; 	12-Jan-97 - Added conformal mapping stuff; this is slow but very cool.
; 	It really ought to be vectorized in the opposite direction.
;       Added "arange" option:  you can specify a range of angles 
;	to cover (eg "arange=[90,270]" to get the South pole).
;       Also added "rot" option; "rot" lets you specify the 
;	location of the splice, in degrees.  (Default is zero).  This must be 
;	in the arange.
;
;	20-Aug-97 - Upgraded to use structure, rather than string-array, 
;	headers.
; 	
;	3-Jan-97 - Added differential smoothing option, rather than "mere" 
;	sampling. If you set the /smooth flag, then pixels in the source 
;	plane are averaged over regions roughly the size of the relevant 
;	destination-plane pixels.
;	
;	13-Mar-98 - Fixed up to use CRPIX correctly (off-by-1)
;	16-Jun-98 - Hacked on conformal headers; added 'missing' keyword.
;	26-Mar-99 - Removed references to deprecated "reflect" keyword.
;	20-Apr-99 - Improved header docs slightly; modified rmin default for linear
;			case.
;-

pro zunwrap,in=in,ihdr=iihdr,out=out,ohdr=ohdr,rmin=rmin,rmax=rmax,rstep=rstep $
	,asteps=asteps,smooth=smooth,monitor=monitor,monout=monout,lerp=lerp $
	,foo_out=foo_out,conformal=conformal,radius=smoothing,arange=arange $
	,rot=rot,grid=grid,grmask=grmask,fast=fast,missing=missing

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Cheezy fake-vectorization recursion goes here:  If we get more than
;; one image, we call ourselves multiple times.  This could be cleaned
;; up, but what the heck -- it works for now, and the overhead isn't
;; too humongous...
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if(nlm(iihdr) gt 1) then begin
	ifoohdr = iihdr(0)
	zunwrap,in=in(*,*,0),ihdr=ifoohdr,out=foo,ohdr=foohdr,rmin=rmin,rmax=rmax,rstep=rstep,asteps=asteps,smooth=smooth,monitor=monitor,monout=monout,lerp=lerp,foo_out=foo_out,conformal=conformal,radius=smoothing,arange=arange,rot=rot,grid=grid,grmask=grmask,fast=fast
	out = replicate(foo(0),nlm(foo(*,0)),nlm(foo(0,*)),nlm(iihdr))
	out(*,*,0) = foo
	ohdr = replicate(foohdr,nlm(iihdr))

	for i=1,nlm(iihdr)-1 do begin
		ifoohdr = iihdr(i)
		zunwrap,in=in(*,*,i),ihdr=ifoohdr,out=foo,ohdr=foohdr,rmin=rmin,rmax=rmax,rstep=rstep,asteps=asteps,smooth=smooth,monitor=monitor,monout=monout,lerp=lerp,foo_out=foo_out,conformal=conformal,radius=smoothing,arange=arange,rot=rot,grid=grid,grmask=grmask,fast=fast
		out(*,*,i) = foo
		ohdr(i) = foohdr
	end
	return
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ihdr = iihdr				; Copy ihdr so as not to mess it up
v = keyword_set(verbose) 
if(v) then openu,44,'/dev/tty',error=err
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Initial setup ... check and generate defaults for the various keywords
;;

if ((not isvalid(in)) or (not isvalid(ihdr))) then begin
	print,"You must specify a valid input image and header!"
	return
end

w = ihdr.NAXIS1
h = ihdr.NAXIS2

xy0 = [(ihdr.CRPIX1-1), (ihdr.CRPIX2-1)]

zhdrunit,ihdr,'solar-radii'
pix2radii = ihdr.CDELT1
radii2pix = 1.0 / pix2radii

if (not isvalid(arange)) then arange = [0.0,360.0] else arange = float(arange)
if (not isvalid(asteps)) then asteps=720
if (not isvalid(rmin)) then begin
	if keyword_set(conformal) then rmin = 0.5 else rmin = 0.0
end

;;;;;;  Default rmax is the largest radius in the image plane
if (not isvalid(rmax)) then begin
	xys =  transpose([ [0,0,w,w] , $
	 	 [0,h,0,h] ])
	corner_rs = fltarr(4)
	for i=0,3 do corner_rs(i) = cartdist(xy0,xys(*,i))

	rmax = max(corner_rs) * pix2radii
end

;;;;;  rstep defaults to one pixel in the input plane...
;;;;;  rpix is rstep in pixels on the input plane.
if (not isvalid(rstep)) then rstep=pix2radii * 1.0
rpix = fix(0.5 + rstep*radii2pix)

if (not isvalid(smoothing)) then smoothing = (1.0+sqrt(2))/2.0
if keyword_set(fast) then smoothing = 0

if(keyword_set(monitor)) then begin
	monout = fltarr(ihdr.naxis1,ihdr.naxis2)
	wmatch,15,monout

	foosiz = 500
	foo_out = fltarr(foosiz,foosiz)
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; conformal case:  we do some fancy footwork...
; To make the transformation conformal, we have to put the radial scale
; on a log scale.  
;
; In this case, we almost certainly want to do some averaging over larger
; and larger areas, to take advantage of the image degradation we're doing
; toward the edges of the image.  Since the mapping is conformal, we 
; know that small shapes on the final image will be the same shapes (but
; rotated) on the initial image.  We sample over small circles, which have
; the advantage of rotational symmetry -- so that we don't have to rotate
; (as well as scale) the sampling zone.
;

if(keyword_set(conformal)) then begin
	; For conformal mapping, 'rstep' is the step in log-r space.  We
	; adjust it to match the "asteps" argument.  At r=1, a=0, then
	; dr/dy = 1 and da/dx = 1/2pi.  So rstep should be 2pi * astep
	; there.   (Note that 'astep' is the (sort-of) reciprocal of 'asteps',
	; corrected for the arc length of the "arange".)
	rstep =  (arange(1) - arange(0)) / !radeg / asteps

	if(rmin le 0) then rmin = rstep   ; Make sure we got no singularity...

	ow = asteps
	oh = fix(alog(rmax/rmin)/rstep) + 1

	print,"Output image:  width = ",fug(asteps),"; height = ",fug(oh)

	ohdr = ihdr
	ohdr.CTYPE1=	'degrees'
	ohdr.CDELT1=	(arange(1)-arange(0)) / asteps
	ohdr.CTYPE2=	'alog solar-radii'
	ohdr.CDELT2=	rstep
	ohdr.CRPIX1=	(-arange(0) * asteps / (arange(1) - arange(0))) + 1
	ohdr.CRPIX2=	1
	ohdr.NAXIS1=	ow
	ohdr.NAXIS2=	oh
	ohdr.CRVAL1 = 0
	ohdr.CRVAL2 = alog(rmin)

	out = fltarr(ohdr.naxis1,ohdr.naxis2)
	if isvalid(missing) then out(*) = missing

	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;; Coordinate arrays.  "as" will be filled with copies of
	;; the current angle for each loop iteration; "rs" and 
	;; "sizes" hold the 'r' location of each pixel in the 
	;; source image plane (in solar radii) and size of each 
	;; region to be snarfed (in pixels in the source image
	;; plane).
	;;
	;; "Weights" holds the weighting information for boundary
	;; pixels, in squares with the lower left corner at (0,0) in
	;; each layer of the cube.
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	sizes = fltarr(oh)
	rs = exp(findgen(oh)*rstep)*rmin
	sizes(1:oh-2) =	(((rs(2:oh-1) - rs(0:oh-3))/4.0) * radii2pix * smoothing) > 0 
	sizes(0) = 0
        sizes(oh-1) = sizes(oh-2)

	if(keyword_set(monitor)) then begin
		window,14,xsiz=400,ysiz=300
		plot,sizes
		wset,15
	end

	msiz = fix(max(sizes)+1)
	if(max(fix(sizes)) eq 0) then msiz = 0

;	sz = fix(sizes)+1

	; 
	; On a higher-resolution grid, draw a circle in single bits.
	; Then rebin the high-resolution grid back down to weighted
	; pixel averages.
	;
	wn = 5

	foo = 2*msiz+1
	weights = fltarr(foo,foo,oh)
	weights2 = fltarr(foo,foo,oh)

	sketchpad = bytarr(wn*foo,wn*foo)
	skxy = [ transpose(indgen(n_elements(sketchpad)) /   (wn*foo)) ,$ 
	         transpose(indgen(n_elements(sketchpad)) mod (wn*foo)) ]
	skc = skxy
	
	if(v) then writeu,44,"Calculating kernels..."
	
	sizes = sizes > 1.0
	weights(msiz,msiz,*) = 1
	weights2(msiz,msiz,*) = 1

	for i=max(where(sizes eq 1.0)),oh-1 do begin
		skc(*) = msiz*wn+fix(wn/2)
		sketchpad(skxy(0,*),skxy(1,*)) = 100 * (cartdist(skxy,skc) le sizes(i)*wn)
		weights2(*,*,i) = rebin(sketchpad,foo,foo)/100.0
		weights (*,*,i) = weights2(*,*,i) / total(weights2(*,*,i))

		if keyword_set(monitor) then begin
			tvscl,rebin(weights(*,*,i),5*wn*foo,5*wn*foo,/sample)
		end
	end
	if(v) then writeu,44,"done -- kernel size is ",string(foo,format="(I3.3)"),byte(13),byte(10)

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;; Begin of actual transformation...  we make it bilinear
	;; by pixel.  The innermost loop is over the "weights" array.
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	xs = intarr(oh)
	ys = intarr(oh)
	msk = bytarr(oh)

;	in00 = in(0,0)
;	in(0,0) = 0

	for i=0,asteps - 1 do begin

		;; Calculate the angle we're doing now, and then convert to
		;; (x,y) coordinates.
		a = ( arange(0) + i * (arange(1) - arange(0)) / asteps )
		xys = zra2xy(rs,a,ihdr) - msiz

		for xx = 0,foo-1 do for yy = 0,foo-1 do begin
				 
		    n = where ( (weights(xx,yy,*) gt 0) and  $
				((xys(*,0) ge 0) and         $
			         (xys(*,0) lt w) and         $
			         (xys(*,1) ge 0) and         $
		 	         (xys(*,1) lt h)             $
				) )
		    ni = n_elements(n)-1
		    if(ni ge 1) then $
			out(i,n(*)) = out(i,n(*)) + weights(xx,yy,n(*))*interpolate(in,xys(n,0)+xx,xys(n,1)+yy)
		
		end

		if(i mod 10 eq 0 or i eq asteps-1) then begin
			if(v) then writeu,44,byte(13),fug(fix(100*(i/(asteps - 1.0)))),"%, ",fug(i)," rasters completed ..."
			if(keyword_set(monitor)) then begin
				wset,15
				tvscl,monout<10
				wset,14
				tvscl,out
			end
		end
	end		

	if(isvalid(grid)) then begin
		if(where((size(grmask))(0:2) ne (size(out))(0:2)))(0) ne -1 then $
			grmask = bytarr(nlm(out(*,0)),nlm(out(0,*)))

		for i=fix(rmin)+1,fix(rmax) do begin
			y0 = alog(float(i)/rmin)/rstep
			for j=0,ow/10-1 do grmask(j*10,y0) = 1
		end

		for i=0,fix(arange(1) - arange(0))/10-1 do begin
			x0 = (i*10)*float(asteps)/(arange(1) - arange(0))
			for j=0,fix(oh/10)-1 do grmask(x0,j*10) = 1
		end

		if keyword_set(grid) then begin
			m = max(out)+(256.0/255.0)*(max(out) - min(out))
			out(where(grmask)) = m
		end
	end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  End of conformal map section
;;  Beginning of linear-radial map section
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

end else begin
	out = fltarr(asteps,((rmax - rmin)/rstep))
	if isvalid(missing) then out(*)=missing

	ohdr = ihdr
	ohdr.CTYPE1 = 'degrees'
	ohdr.CDELT1 = (arange(1)-arange(0)) / asteps
	ohdr.CDELT2 = rstep	; (ohdr.CTYPE2 is already 'solar-radii')
	ohdr.CRPIX1 = (-arange(0) * asteps / (arange(1) - arange(0))) + 1
	ohdr.CRPIX2 = -(rmin/rstep) + 1
	ohdr.NAXIS1 = asteps
	ohdr.NAXIS2 = fix((rmax-rmin)/rstep)
	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	if(keyword_set(smooth)) then begin
	;
	; Smoothing case:  We unwrap at greater than full resolution, then 
	; average down to scale...
	;
		circumference = rmaxpix * 3.1415926 * 2
		multiplier = fix(circumference / asteps) + 1
		zunwrap,in=in,ihdr=ihdr,out=foo,ohdr=foohdr,rstep = rstep / 2.0, asteps=asteps * multiplier,monitor=monitor,monout=monout,smooth=0,rmin=rmin,rmax=rmax
		hgt = (size(foo))(2)
		hgt2 = hgt - (hgt mod 2)
		out = rebin(foo(*,0:(hgt2 - 1)),asteps,hgt2/2)
		ohdr = foohdr
		ohdr.naxis1 = asteps
		ohdr.cdelt1 = ohdr.cdelt1 * multiplier
		ohdr.naxis2 = (size(foo))(2)/2.0
		ohdr.cdelt2 = ohdr.cdelt2 * 2.0
	
	end else begin
	;
	; Non-smoothing case:  Just do the sampling.  If the interpolation
	; flag is set, we do a bilinear.
	;
	
		; Sampling unwrap algorithm
		rs = float(indgen((rmax-rmin)/rstep) * rstep) + rmin
		as = float(rs)
		
		for i=0,asteps-1 do begin
			as(*) = ( arange(0) + i * (arange(1) - arange(0)) / asteps )
			xys = zra2xy([transpose(rs),transpose(as)],ihdr);
		
			foo = where(((xys(*,0) ge 0) and $
				  	 (xys(*,0) lt w-1) and  $
			  	  	 (xys(*,1) ge 0) and $
				  	 (xys(*,1) lt h-1) ))
			if(foo(0) ge 0) then begin
		;;;;;;;;;;;;;;;;;;;;;;;;;
				if(keyword_set(lerp)) then $
					out(i,foo) = interpolate(in,xys(foo,0),xys(foo,1)) $
				else $
					out(i,foo) = in(xys(foo,0),xys(foo,1))
		;;;;;;;;;;;;;;;;;;;;;;
		
				if(i mod 10 eq 0 or i eq asteps-1) then begin
					if(v) then writeu,44,byte(13),fug(fix(100*(i/(asteps - 1.0)))),"%..."
					if(keyword_set(monitor)) then tvscl,monout<4
				end
			end
	
		end
		if(v) then printf,44,byte(13),"done."
		if(v) then close,44
	
		if(keyword_set(monitor)) then begin
			wmatch,16,foo_out
			tvscl,foo_out
		end
	end
end

;sxaddpar,ohdr,'HISTORY','Unwrapped into radial coords'
;sxaddpar,ohdr,'HISTORY','  - rstep='+fug(sxpar(ohdr,'CDELT2')*zunits(sxpar(ohdr,'CTYPE2'),'solar-radii'))+' solar radii; '+fug(asteps)+' angular steps'

end