SUBROUTINE BLOT C++ C C BLOT.F Version 0.6 "Reverse drizzling" C C It is based on ideas of Andy Fruchter and was coded by Richard Hook. C C Version 0.6 is compatible and tested with Drizzle V1.2. C C This task uses coefficients held in a text file to define C geometrical distortions. C C This code uses the F77/VOS for IRAF i/o. C It may be compiled and linked using a command like: C C xc -p stsdas (-z) blot.f drutil.f x_blot.x libiraf77.a libiminterp.a C C Note that the optional -z switch (which tells IRAF to create a static C executable which doesn't use the IRAF sharable library) may be C necessary to avoid a virtual memory use limitation of around 167MB in C IRAF versions up to 2.10.4. C C History: C C Created first try (V0.1) from DRIZZLE.F V0.97, 9th July 1996 C Modified for new origin of coordinates etc (V0.2), 17th July 1996 C Modified to match up with DRIZZLE V1.05, 14th November 1996 C (rotfir parameter added, few bug fixes, change coordinate origin) C Match up to Drizzle 1.08 - V0.4 of BLOT, 6th December 1996 C Correct shift units bug, V0.41, 16th December 1996 C Add ALIGN option, V0.42, 16th January 1997 C Use "drutil.f" utilities, no -z, 4th February 1997 C Prepared V0.5 for release, matches V1.097 of Drizzle, 17th March 1997 C C First public release of Blot, 12th May 1997 C C Added in_un/out_un switches for compatibility with V1.2 of Drizzle. C This was for the first release of drizzle/blot in the dither package C within STSDAS. 25th February 1998. C C Added automatic output image size selection, 27th February 1998 C-- IMPLICIT NONE C Iraf global storage (real in this program) REAL MEMR(1) !I COMMON /MEM/MEMR !I C Local variables INTEGER ISTAT,I,LUN INTEGER NDIMS,DNX,DNY,ONX,ONY,DDIMS(7),ODIMS(7),DATTYP INTEGER IDD,IDND INTEGER PDATA,PNDAT INTEGER INTYPE,IC REAL XCO(10),YCO(10),XSH,YSH,LAM,MISVAL REAL SCALE,ROT,EF,EXPIN,EXPOUT REAL LASTPF,LASTSC,LASTOX,LASTOY DOUBLE PRECISION WCS(8) CHARACTER*80 DATA,OUTDAT,CHARS CHARACTER*80 COEFFS CHARACTER*8 CTYPE1,CTYPE2,SHFTFR,SHFTUN,ALIGN CHARACTER*8 EXPKEY,INUN,OUTUN,LASTUN CHARACTER*80 EXPSTR CHARACTER*45 VERS CHARACTER*7 INTERP C Logical flags LOGICAL VERBOSE LOGICAL NOCO LOGICAL GOTWCS LOGICAL ROTFIR LOGICAL HCOEFF LOGICAL INCPS LOGICAL OUTCPS C-- Start of executable code C First announce the version VERS='BLOT Version 0.6 (February 1998)' CALL UMSPUT('+ '//VERS,1,0,ISTAT) VERBOSE=.TRUE. C Get reference frame for shifts - specifying whether shifts are done before C or after rotation CALL UCLGST('shft_fr',SHFTFR,ISTAT) IF(SHFTFR.EQ.'output') THEN ROTFIR=.FALSE. ELSE ROTFIR=.TRUE. ENDIF C Get the alignment parameter CALL UCLGST('align',ALIGN,ISTAT) C Get the exposure related parameters CALL UCLGST('expkey',EXPKEY,ISTAT) CALL UCLGST('expout',EXPSTR,ISTAT) C Get the switch value for whether to read in CPS or C counts (latter is the default) CALL UCLGST('in_un',INUN,ISTAT) IF(INUN.EQ.'cps') THEN INCPS=.TRUE. ELSE INCPS=.FALSE. ENDIF C Get the switch value for whether to write out in CPS or C counts (latter is the default) CALL UCLGST('out_un',OUTUN,ISTAT) IF(OUTUN.EQ.'cps') THEN OUTCPS=.TRUE. ELSE OUTCPS=.FALSE. ENDIF C Get the name of the file containing the coefficients CALL UCLGST('coeffs',COEFFS,ISTAT) C Get the scale and rotation for the final image CALL UCLGSR('scale',SCALE,ISTAT) CALL UCLGSR('rot',ROT,ISTAT) C Check for invalid scale IF(SCALE.EQ.0.0) THEN CALL UMSPUT('! Invalid scale',1,0,ISTAT) GO TO 99 ENDIF C Convert the rot to radians ROT=ROT*3.1415926/180.0 C Get the X,Y final shift CALL UCLGSR('xsh',XSH,ISTAT) CALL UCLGSR('ysh',YSH,ISTAT) C Also get the binary flag for whether units are input or C output pixels CALL UCLGST('shft_un',SHFTUN,ISTAT) IF(SHFTUN.EQ.'input') THEN XSH=XSH*SCALE YSH=YSH*SCALE ENDIF C Note that the shifts are in INPUT pixel units C Get the missing value flag CALL UCLGSR('fillval',MISVAL,ISTAT) C Get the name of the input data image CALL UCLGST('data',DATA,ISTAT) C Try to access the input data image CALL UIMOPN(DATA,1,IDD,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT('! Unable to open data image', : 1,0,ISTAT) GO TO 99 ELSE IF(VERBOSE) CALL UMSPUT( : '-Opening input data image: '//DATA, : 1,0,ISTAT) ENDIF C Get the array size and shape CALL UIMGID(IDD,DATTYP,NDIMS,DDIMS,ISTAT) IF(NDIMS.NE.2) THEN CALL UMSPUT('! Input image is not two dimensional', : 1,0,ISTAT) GO TO 99 ELSE DNX=DDIMS(1) DNY=DDIMS(2) ENDIF C We now get some of the previous drizzle information from C the header of the current image and check for funny C combinations CALL GOLDH(IDD,IC,LASTPF,LASTSC, : LASTUN,LASTOX,LASTOY,ISTAT) C Check for incompatibilities IF(INCPS) THEN IF(LASTUN.EQ.'counts' .OR. : LASTUN.EQ.'COUNTS') THEN CALL UMSPUT('! Warning input image header gives units', : 1,0,ISTAT) CALL UMSPUT('! as counts NOT cps! Assuming cps', : 1,0,ISTAT) ENDIF ELSE IF(LASTUN.EQ.'cps' .OR. : LASTUN.EQ.'CPS') THEN CALL UMSPUT('! Warning input image header gives units', : 1,0,ISTAT) CALL UMSPUT('! as cps NOT counts! Assuming counts', : 1,0,ISTAT) ENDIF ENDIF C Interpret the coefficients file name and act appropriately NOCO=.FALSE. IF(COEFFS.EQ.' ') NOCO=.TRUE. C Now check for the special string "header" IF(COEFFS.EQ.'header') THEN HCOEFF=.TRUE. C and try to read these from the input header CALL GTCOEF(IDD,COEFFS,LAM,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Unable to get necessary header information', : 1,0,ISTAT) GO TO 99 ENDIF ELSE HCOEFF=.FALSE. ENDIF C if there is no coeffients file then set the coeffs to defaults IF(NOCO) THEN DO I=1,10 XCO(I)=0.0 YCO(I)=0.0 ENDDO ELSE C Open the coefficients file CALL UFOPEN(COEFFS,1,LUN,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT('! Unable to open coefficients file',1,0, : ISTAT) GO TO 99 ELSE IF(VERBOSE) CALL UMSPUT( : '-Opening coefficients file: '//COEFFS, : 1,0,ISTAT) ENDIF C Get the wavelength (nm). This is only used for "trauger" C style coefficients sets, if we are using the header we C will already have this IF(.NOT.HCOEFF) CALL UCLGSR('lambda',LAM,ISTAT) C Read in the coefficients from the file CALL GETCO(LUN,LAM,XCO,YCO,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT('! Unable to read coefficients', : 1,0,ISTAT) CALL UFCLOS(LUN,ISTAT) GO TO 99 ENDIF C Close the coefficients file CALL UFCLOS(LUN,ISTAT) ENDIF C We get the name of the output image here CALL UCLGST('outdata',OUTDAT,ISTAT) C Check that a name is specified IF(OUTDAT.EQ.' ') THEN CALL UMSPUT( : '! No output data array name specified', : 1,0,ISTAT) GO TO 99 ENDIF C We have to create the output data image C so we need to find out their dimension from the CL CALL UCLGSI('outnx',ONX,ISTAT) CALL UCLGSI('outny',ONY,ISTAT) ODIMS(1)=ONX ODIMS(2)=ONY C Now we need to get the name for the output image CALL UCLGST('outdata',OUTDAT,ISTAT) CALL UIMCRE(OUTDAT,6,2,ODIMS,IDND,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Unable to create output data image', : 1,0,ISTAT) GO TO 99 ENDIF C Now we allocate space for all the arrays we will need, note that they C are all real CALL UDMGET(DNX*DNY,6,PDATA,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Unable to allocate memory for data array', : 1,0,ISTAT) GO TO 99 ENDIF CALL UDMGET(ONX*ONY,6,PNDAT,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Unable to allocate memory for output data array', : 1,0,ISTAT) GO TO 99 ENDIF C Now we can actually read the data into the arrays C Note that we read a line at a time to try to avoid C excessive memory use DO I=1,DNY CALL UIGS2R(IDD,1,DNX,I,I,MEMR(PDATA+(I-1)*DNX),ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Failed to read input data image', : 1,0,ISTAT) GO TO 99 ENDIF ENDDO C Before we close the data array we copy the header to C the output, this also transfers the exposure time information CALL UHDCPY(IDD,IDND,ISTAT) C Also try to get the header items for the WCS, note that C this is not the full WCS but just 8 header items commonly used C for HST images plus CTYPE1 & CTYPE2 CALL GETWCS(IDD,WCS,CTYPE1,CTYPE2,ISTAT) IF(ISTAT.EQ.0) THEN GOTWCS=.TRUE. ELSE CALL UMSPUT( : '! Warning, unable to read WCS information from header', : 1,0,ISTAT) ENDIF C Here we set the WCS matrix to something sensible anyway IF(.NOT.GOTWCS) THEN CTYPE1='PIXELS' CTYPE2='PIXELS' DO I=1,8 WCS(I)=0.0D0 ENDDO ENDIF C Get the exposure time from the header, if possible CALL UHDGSR(IDD,EXPKEY,EXPIN,ISTAT) IF(ISTAT.NE.0) THEN CALL UMSPUT( : '! Warning, failed to get exposure time keyword value', : 1,0,ISTAT) GO TO 99 ENDIF C Check for the "input" value for the output exposure, C this is the standard behaviour IF(EXPSTR.EQ.'INPUT' .OR. EXPSTR.EQ.'input') THEN EXPOUT=EXPIN ELSE READ(EXPSTR,*,IOSTAT=ISTAT) EXPOUT IF(ISTAT.NE.0) THEN CALL UMSPUT('! Invalid output exposure value', : 1,0,ISTAT) GO TO 99 ENDIF ENDIF C How we re-scale depends on the units, there are four cases IF(INCPS) THEN IF(OUTCPS) THEN EF=1.0 ELSE EF=EXPOUT ENDIF ELSE IF(OUTCPS) THEN EF=1.0/EXPIN ELSE EF=EXPOUT/EXPIN ENDIF ENDIF CALL UHDPSR(IDND,EXPKEY,EXPOUT,ISTAT) WRITE(CHARS, : '(''- Input exposure '',F10.3,'' Output exposure: '''// : 'F10.3)') EXPIN,EXPOUT CALL UMSPUT(CHARS,1,0,ISTAT) C Close the data array now we have it CALL UIMCLO(IDD,ISTAT) C The final case is where the output is empty. C Before we start blotting we need to drain out the output arrays C if there are no old arrays CALL SETIM(MEMR(PNDAT),ONX,ONY,0.0) C Before we start also get the interpolation type and convert C to a numerical code CALL UCLGST('interpol',INTERP,ISTAT) IF(INTERP.EQ.'nearest') THEN INTYPE=1 ELSE IF(INTERP.EQ.'linear') THEN INTYPE=2 ELSE IF(INTERP.EQ.'poly3') THEN INTYPE=3 ELSE IF(INTERP.EQ.'poly5') THEN INTYPE=4 ELSE IF(INTERP.EQ.'spline3') THEN INTYPE=5 ELSE CALL UMSPUT('! Invalid interpolant specified', : 1,0,ISTAT) GO TO 99 ENDIF C Now do the actual combination using interpolation CALL DOBLOT(MEMR(PDATA),MEMR(PNDAT),INTYPE,MISVAL, : DNX,DNY,ROTFIR,EF,ALIGN, : ONX,ONY,XCO,YCO,SCALE,ROT,XSH,YSH,WCS) C Write out the new image IF(VERBOSE) : CALL UMSPUT('-Writing output blotted image: '// : OUTDAT,1,0,ISTAT) CALL UIPS2R(IDND,1,ONX,1,ONY,MEMR(PNDAT),ISTAT) C Update the header CALL BUHEAD(IDND,VERS,DATA,OUTDAT,COEFFS,SHFTUN,MISVAL, : EXPKEY,EXPSTR,ALIGN,INUN,OUTUN, : SHFTFR,INTERP,LAM,SCALE,ROT,XSH,YSH,ONX,ONY) IF(GOTWCS) THEN CALL UWCS(IDND,WCS,CTYPE1,CTYPE2) ENDIF CALL UIMCLO(IDND,ISTAT) CALL UDMFRE(PNDAT,6,ISTAT) C Close down everything and exit CALL UDMFRE(PDATA,6,ISTAT) 99 CONTINUE END SUBROUTINE BUHEAD(ID,VERS,DATA,OUTDAT,COEFFS,SHFTUN,MISVAL, : EXPKEY,EXPSTR,ALIGN,INUN,OUTUN, : SHFTFR,INTERP,LAM,SCALE,ROT,XSH,YSH,OUTNX,OUTNY) C C Update the header of the output image with all the C parameters of the current BLOT run. C IMPLICIT NONE INTEGER ID,OUTNX,OUTNY,ISTAT CHARACTER*80 DATA CHARACTER*80 OUTDAT,COEFFS CHARACTER*7 INTERP CHARACTER*8 SHFTFR,SHFTUN,EXPKEY,ALIGN,INUN,OUTUN CHARACTER*80 EXPSTR CHARACTER*(*) VERS REAL LAM,SCALE,ROT REAL DROT,XSH,YSH,XSHI,YSHI,MISVAL CALL UHDAST(ID,'BLOTVER',VERS, : 'Blot, task version',0,ISTAT) CALL UHDAST(ID,'BLOTDATA',DATA, : 'Blot, input data image',0,ISTAT) CALL UHDAST(ID,'BLOTOUTDA',OUTDAT, : 'Blot, output data image',0,ISTAT) CALL UHDAST(ID,'BLOTCOEFF',COEFFS, : 'Blot, coefficients file name ',0,ISTAT) CALL UHDASR(ID,'BLOTLAM',LAM, : 'Blot, wavelength applied for transformation (nm)', : 0,ISTAT) CALL UHDASR(ID,'BLOTSCALE',SCALE, : 'Blot, scale (pixel size) of input image',0,ISTAT) C Convert the rotation angle back to degrees DROT=ROT/3.1415926*180.0 CALL UHDASR(ID,'BLOTROT',DROT, : 'Blot, rotation angle, degrees clockwise',0,ISTAT) C Check the SCALE and units IF(SHFTUN.EQ.'input') THEN XSHI=XSH/SCALE YSHI=YSH/SCALE ELSE XSHI=XSH YSHI=YSH ENDIF CALL UHDASR(ID,'BLOTXSH',XSHI, : 'Blot, X shift applied',0,ISTAT) CALL UHDASR(ID,'BLOTYSH',YSHI, : 'Blot, Y shift applied',0,ISTAT) CALL UHDAST(ID,'BLOTSHFU',SHFTUN, : 'Blot, units used for shifts (output or input pixels)', : 0,ISTAT) CALL UHDAST(ID,'BLOTINUN',INUN, : 'Blot, units of input image (counts or cps)', : 0,ISTAT) CALL UHDAST(ID,'BLOTOUUN',OUTUN, : 'Blot, units for output image (counts or cps)', : 0,ISTAT) CALL UHDAST(ID,'BLOTSHFF',SHFTFR, : 'Blot, frame in which shifts were applied',0,ISTAT) CALL UHDASI(ID,'BLOTONX',OUTNX, : 'Blot, X size of output image',0,ISTAT) CALL UHDASI(ID,'BLOTONY',OUTNY, : 'Blot, Y size of output image',0,ISTAT) CALL UHDAST(ID,'BLOTINT',INTERP, : 'Blot, interpolation method used',0,ISTAT) CALL UHDASR(ID,'BLOTFILVA',MISVAL, : 'Blot, value assigned to invalid pixels',0,ISTAT) CALL UHDAST(ID,'BLOTEXKY',EXPKEY, : 'Blot, exposure time keyword in input header',0,ISTAT) CALL UHDAST(ID,'BLOTEXOU',EXPSTR, : 'Blot, exposure time for output image',0,ISTAT) RETURN END SUBROUTINE DOBLOT(DATA,NDAT,INTYPE,MISVAL,DNX,DNY, : ROTFIR,EF,ALIGN,ONX,ONY,XCO,YCO,SCALE,ROT,XSH,YSH,WCS) C C This routine does the interpolation of the input array C IMPLICIT NONE INTEGER DNX,DNY,ONX,ONY INTEGER I,J,NMISS,ISTAT INTEGER INTYPE REAL DATA(DNX,DNY),V REAL NDAT(ONX,ONY) REAL XCO(10),YCO(10) DOUBLE PRECISION WCS(8) REAL X,Y,XOUT,YOUT,XSH,YSH,XIN,YIN,XV,YV REAL EVALCU,SINTH,COSTH,ROT,XF,YF,XOFF,YOFF,SCALE REAL XS,XC,YS,YC,XT,YT,S2,XDC,YDC REAL XCEN,YCEN,XP,YP,MISVAL REAL MRIEVL,EF,A,B,C,D CHARACTER*80 CHARS CHARACTER*8 ALIGN LOGICAL NONLIN LOGICAL ROTFIR C-- C Some initial settings - note that the reference pixel C is offset from the "centre": C NMISS=0 IF(ALIGN.EQ.'corner') THEN XCEN=FLOAT(ONX/2)+0.5 YCEN=FLOAT(ONY/2)+0.5 ELSE XCEN=FLOAT(ONX/2)+1.0 YCEN=FLOAT(ONY/2)+1.0 ENDIF C If all the non-linear coefficients are zero we can C ignore that part of things NONLIN=.FALSE. DO I=1,10 IF(XCO(I).NE.0.0 .OR. YCO(I).NE.0.0) NONLIN=.TRUE. ENDDO C Calculate the position of the first whole pixel Y=-YCEN C Calculate some numbers to simplify things later SINTH=SIN(ROT) COSTH=COS(ROT) C Note that XSH is in INPUT pixels and XOFF etc are in C OUTPUT pixels XOFF=XSH/SCALE YOFF=YSH/SCALE XF=1.0/SCALE YF=1.0/SCALE C Some more economies S2=1.0/(XF*YF) XS=XF*SINTH XC=XF*COSTH YS=YF*SINTH YC=YF*COSTH IF(ALIGN.EQ.'corner') THEN XDC=REAL(DNX/2)+0.5 YDC=REAL(DNY/2)+0.5 ELSE XDC=REAL(DNX/2)+1.0 YDC=REAL(DNY/2)+1.0 ENDIF XT=XOFF+XDC YT=YOFF+YDC C Outer loop over output image pixels (X,Y) DO J=1,ONY Y=Y+1.0 X=-XCEN DO I=1,ONX X=X+1.0 C Calculate where this pixel lands up C The angles are assumed to be in radians at this point IF(NONLIN) THEN XP=EVALCU(X,Y,XCO) YP=EVALCU(X,Y,YCO) ELSE XP=X YP=Y ENDIF C Apply the linear transform C Here we have to take care about the order of C operations IF(ROTFIR) THEN XOUT=XC*XP-YS*YP+XT YOUT=XS*XP+YC*YP+YT ELSE XOUT=XC*(XP+XSH)-YS*(YP+YSH)+XDC YOUT=XS*(XP+XSH)+YC*(YP+YSH)+YDC ENDIF C Check it is on the input image IF(XOUT.GE.1.0 .AND. XOUT.LE.REAL(DNX) .AND. : YOUT.GE.1.0 .AND. YOUT.LE.REAL(DNY)) THEN C Do the interpolation C and allow for stretching because of scale change V=MRIEVL(XOUT,YOUT,DATA,DNX,DNY,DNX,INTYPE) NDAT(I,J)=V*EF/S2 ELSE C If there is nothing for us then set the output to missing C value flag NDAT(I,J)=MISVAL C Count cases where the pixel is off the output image NMISS=NMISS+1 ENDIF ENDDO ENDDO C Report number of points which were outside the input frame IF(NMISS.GT.0) THEN WRITE(CHARS,'(''! Warning, '',I7,'' points were outside '','// : '''the output image.'')') NMISS CALL UMSPUT(CHARS,1,0,ISTAT) ENDIF C Project the central position in the output array and make C it the reference pixel for the output WCS IF(NONLIN) THEN XP=XCO(1) YP=YCO(1) ELSE XP=0.0 YP=0.0 ENDIF C Now the centre position IF(ROTFIR) THEN XIN=DBLE(XC*XP-YS*YP+XT) YIN=DBLE(XS*XP+YC*YP+YT) ELSE XIN=DBLE(XC*(XP+XSH)-YS*(YP+YSH)+XDC) YIN=DBLE(XS*(XP+XSH)+YC*(YP+YSH)+YDC) ENDIF C Now we convert the pixel position to a sky position assuming C a small field and using the old WCS XV=((XIN-REAL(WCS(1)))*REAL(WCS(5))+ : (YIN-REAL(WCS(3)))*REAL(WCS(7)))/ : COS(REAL(WCS(4))*3.1415926/180.0)+REAL(WCS(2)) YV=(XIN-REAL(WCS(1)))*REAL(WCS(6))+ : (YIN-REAL(WCS(3)))*REAL(WCS(8))+REAL(WCS(4)) WCS(1)=DBLE(XCEN) WCS(2)=DBLE(XV) WCS(3)=DBLE(YCEN) WCS(4)=DBLE(YV) C Now we scale and rotate the CD matrix (WCS(5-8)) A=COSTH*WCS(5)+SINTH*WCS(7) B=COSTH*WCS(6)+SINTH*WCS(8) C=-SINTH*WCS(5)+COSTH*WCS(7) D=-SINTH*WCS(6)+COSTH*WCS(8) WCS(5)=DBLE(A*XF) WCS(6)=DBLE(B*XF) WCS(7)=DBLE(C*XF) WCS(8)=DBLE(D*XF) RETURN END