1 from pytools import numerixenv
2 numerixenv.check()
3
4 import string,os,types,sys
5 import shutil
6 import datetime
7
8
9
10
11 import arrdriz
12
13
14 import buildmask, drutil
15 import outputimage, imtype
16 from exposure import Exposure
17
18 from pytools import fileutil, wcsutil
19 import numpy as N
20 import pyfits
21
22
23 import buildasn, updateasn
24 import traits102
25
26 yes = True
27 no = False
28
29
30 INSTRUMENT = ["ACS","WFPC2","STIS","NICMOS"]
31 DEXPTIME = 'EXPTIME'
32 DEFAULT_PARITY = [[1.0,0.0],[0.0,1.0]]
33
34 from drutil import DEFAULT_IDCDIR
35 from pytools.fileutil import RADTODEG, DEGTORAD
36 from math import *
37
38
39
40 __version__ = "6.1 (25-Jan-2007"
41
42
43
45 import time
46
47
48 _ltime = time.localtime(time.time())
49 tlm_str = time.strftime('%H:%M:%S (%d/%m/%Y)',_ltime)
50
51 return tlm_str
52
53
54
55
56
57
58
59
60
64
66 if self['xsh'] == None: _xsh = repr(self['xsh'])
67 else: _xsh = '%0.4f'%self['xsh']
68 if self['ysh'] == None: _ysh = repr(self['ysh'])
69 else: _ysh = '%0.4f'%self['ysh']
70
71
72
73 if self['driz_mask'] == None: _driz_mask = ''
74 else: _driz_mask = self['driz_mask']
75 if self['single_driz_mask'] == None: _sdriz_mask = ''
76 else: _sdriz_mask = self['single_driz_mask']
77
78
79 _str = ''
80 _str += 'Parameters for input chip: '+self['data']+'\n'
81 _str += ' Shifts: %s %s\n'%(_xsh,_ysh)
82 _str += ' Output size: %d %d \n'%(self['outnx'],self['outny'])
83 _str += ' Rotation: %0.4g deg. Scale: %0.4f \n'%(self['rot'],self['scale'])
84 _str += ' pixfrac: %0.4f Kernel: %s Units: %s\n'%(self['pixfrac'],self['kernel'],self['units'])
85 _str += ' ORIENTAT: %0.4g deg. Pixel Scale: %0.4f arcsec/pix\n'%(self['orient'],self['outscl'])
86 _str += ' Center at RA: %0.9g Dec: %0.9f \n'%(self['racen'],self['deccen'])
87 _str += ' Output product: %s\n'%self['output']
88 _str += ' Coeffs: '+self['coeffs']+' Geomode: '+self['geomode']+'\n'
89 _str += ' XGeoImage : '+self['xgeoim']+'\n'
90 _str += ' YGeoImage : '+self['ygeoim']+'\n'
91 _str += ' Single mask file: '+_sdriz_mask+'\n'
92 _str += ' Final mask file : '+_driz_mask+'\n'
93 _str += ' Output science : '+self['outdata']+'\n'
94 _str += ' Output weight : '+self['outweight']+'\n'
95 _str += ' Output context : '+self['outcontext']+'\n'
96 _str += ' Exptime -- total: %0.4f single: %0.4f\n'%(self['texptime'],self['exptime'])
97 _str += ' start: %s end: %s\n'%(repr(self['expstart']),repr(self['expend']))
98 _str += ' Single image products-- output: %s\n'%self['outsingle']
99 _str += ' weight: %s \n '%self['outsweight']
100 _str += ' Blot output: %s \n'%self['outblot']
101 _str += ' Size of original image to blot: %d %d \n'%(self['blotnx'],self['blotny'])
102
103 _str += '\n'
104
105 return _str
106
108 """
109 Set default values for these to be overridden by
110 instrument specific class variables as necessary.
111 """
112 IDCKEY = 'IDCTAB'
113 PARITY = {'detector':DEFAULT_PARITY}
114 REFDATA = {'detector':[[1.,1.],[1.,1.]]}
115
116 NUM_IMSET = 3
117 PA_KEY = 'PA_V3'
118 DETECTOR_NAME = 'detector'
119 COPY_SUFFIX = '.orig'
120
121 - def __init__(self, filename, output=None, pars=None):
122
123 self.members = []
124 self.pars = pars
125 self.output = output
126 self.name = filename
127
128
129 self.nmembers = 1
130 self.nimages = 1
131
132
133 self.bitvalue = self.pars['bits']
134
135
136 if self.pars['idckey'] == '':
137 self.idckey = self.IDCKEY
138 else:
139 self.idckey = self.pars['idckey']
140 self.idcdir = self.pars['idcdir']
141
142
143
144
145 self.pa_key = self.PA_KEY
146
147 self.exptime = None
148
149 self.binned = 1
150
151
152
153
154 self.refimage = None
155 self.offsets = None
156 self.v2com = None
157 self.v3com = None
158 self.alpha = 0
159 self.beta = 0
160
161
162
163
164
165 image_handle = self.getHeaderHandle()
166
167 if self.pars['section'] != None:
168
169
170 self.nmembers = len(self.pars['section'])
171
172
173 self.imtype = imtype.Imtype(filename,handle=self.image_handle,
174 dqsuffix=self.pars['dqsuffix'])
175
176
177 if image_handle:
178 image_handle.close()
179
180 if self.header and self.header.has_key(self.DETECTOR_NAME):
181 self.detector = self.header[self.DETECTOR_NAME]
182 else:
183 self.detector = 'detector'
184
185
187 """ Sets up the PyFITS image handle and Primary header
188 as self.image_handle and self.header.
189
190 When Pattern being used for output product, filename will be
191 set to None and this returns None for header and image_handle.
192 """
193
194 _numsci = 0
195 if self.name:
196 _handle = fileutil.openImage(self.name,mode='readonly',memmap=self.pars['memmap'])
197 _fname,_extn = fileutil.parseFilename(self.name)
198 _hdr = _handle['PRIMARY'].header.copy()
199
200 for _fext in _handle:
201 if _fext.header.has_key('extname') and _fext.header['extname'] == 'SCI':
202 _numsci += 1
203
204 if _extn > 0:
205
206 for _card in fileutil.getExtn(_handle,_extn).header.ascardlist():
207 _hdr.ascard.append(_card)
208 else:
209
210 _handle = None
211 _hdr = None
212
213
214 self.image_handle = None
215 self.header = _hdr
216 self.nmembers = _numsci
217
218 return _handle
219
221 """ Closes image_handle. """
222 if self.image_handle:
223 self.image_handle.close()
224
225 self.image_handle = None
226
227
229 """ Build rootname for each SCI extension, and
230 create the mask image from the DQ extension.
231 It would then append a new Exposure object to 'members'
232 list for each extension.
233 """
234
235 self.detector = detector = str(self.header[self.DETECTOR_NAME])
236
237 if self.pars['section'] == None:
238 self.pars['section'] = [None]*self.nmembers
239
240 for i in range(self.nmembers):
241 _sciname = self.imtype.makeSciName(i+1,section=self.pars['section'][i])
242 _dqname = self.imtype.makeDQName(i+1)
243 _extname = self.imtype.dq_extname
244
245
246 _masklist = []
247 _masknames = []
248
249
250
251 _maskname = buildmask.buildMaskName(_dqname,i+1)
252 _masknames.append(_maskname)
253
254 outmask = buildmask.buildMaskImage(_dqname,self.bitvalue[0],_maskname,extname=_extname,extver=i+1)
255 _masklist.append(outmask)
256
257
258
259
260
261 _maskname = _maskname.replace('final_mask','single_mask')
262 _masknames.append(_maskname)
263
264
265 outmask = buildmask.buildMaskImage(_dqname,self.bitvalue[1],_maskname,extname=_extname,extver=i+1)
266
267 _masklist.append(outmask)
268 _masklist.append(_masknames)
269
270 self.members.append(Exposure(_sciname, idckey=self.idckey, dqname=_dqname,
271 mask=_masklist, pa_key=self.pa_key, parity=self.PARITY[detector],
272 idcdir=self.pars['idcdir'], group_indx = i+1,
273 handle=self.image_handle,extver=i+1,exptime=self.exptime[0], mt_wcs=self.pars['mt_wcs']))
274
276 """ Apply ASN Shifts to each member and the observations product. """
277 _prod_geo = self.product.geometry
278 _geo = self.product.geometry.wcslin
279 _geo0 = self.product.geometry.wcslin.copy()
280
281 _crval0 = (_geo.crval1,_geo.crval2)
282 _rot0 = _geo.orient
283 self._applyShifts(_geo)
284
285 _dcrval = (_crval0[0] - _geo.crval1,_crval0[1] - _geo.crval2)
286
287 _delta_rot = _rot0 - _geo.orient
288 _crpixlin = _geo.rd2xy(_crval0)
289 _delta_crpix = (_geo.crpix1 - _crpixlin[0],_geo.crpix2 - _crpixlin[1])
290
291
292
293
294
295
296
297
298
299
300
301 for member in self.members:
302
303 _mem_geo = member.geometry
304 _mem_wcs = member.geometry.wcs
305 _wcs_copy = _mem_wcs.copy()
306 _mem_orient = member.geometry.wcs.orient
307
308
309 member.geometry.gpar_xsh = _delta_crpix[0]
310 member.geometry.gpar_ysh = _delta_crpix[1]
311 member.geometry.gpar_rot = _delta_rot
312
313
314
315
316
317
318 _oxy1 = _mem_geo.wtraxy((_mem_wcs.crpix1, _mem_wcs.crpix2 ),_geo0)
319
320
321
322 _oxy1o = _mem_geo.wtraxy((_mem_wcs.crpix1, _mem_wcs.crpix2),_geo)
323
324
325 ra1,dec1 = _geo.xy2rd((_oxy1[0], _oxy1[1]))
326
327
328 _mem_wcs.crval1 = ra1
329 _mem_wcs.crval2 = dec1
330
331 """
332 Now update CD matrix itself
333 We will have a lever-arm effect in place given the shift in
334 reference position.
335
336 """
337
338
339
340
341
342
343
344 _ref_wcs = _mem_wcs.copy()
345 _orig_orient = _ref_wcs.get_orient()
346 _ref_wcs.crpix1 += _oxy1o[0] - _oxy1[0]
347 _ref_wcs.crpix2 += _oxy1o[1] - _oxy1[1]
348 _ref_wcs.recenter()
349
350 _new_orient = _ref_wcs.get_orient()
351 _delta_orient = _new_orient - _orig_orient
352 _final_orient = _new_orient - _delta_rot
353
354 _mem_wcs.rotateCD(_final_orient)
355 _mem_wcs.orient = _mem_orient - _delta_orient
356
357 """
358 Finish updating CD matrix
359 """
360
361
362 _mem_geo.undistortWCS()
363
364
365 member.corners['corrected'] += _delta_crpix
366
367
368 self.getProductCorners()
369
371 """ This method updates each member's WCS to reflect any
372 offsets/corrections specified in the ASN table.
373
374 This method converts shifts given in output pixels
375 into the input frame by using a reference image's
376 WCS. This reference image must exist and have an
377 header with a valid WCS; specifically, one which can
378 be read using WCSObject.
379
380 """
381
382
383
384
385
386
387 if not self.pars.has_key('xshift'):
388 return
389
390
391 if not self.pars['abshift'] and not self.pars['dshift']:
392 return
393
394
395
396
397
398
399
400
401
402
403 _crval = (in_wcs.crval1,in_wcs.crval2)
404 _crpix = (in_wcs.crpix1,in_wcs.crpix2)
405
406
407
408
409 _dcrval = (0.,0.)
410 _dcrpix = (0.,0.)
411
412 _refimage = False
413
414 if self.pars['refimage'] != '' and self.pars['refimage'] != None:
415
416
417
418
419 _out_wcs = wcsutil.WCSObject(self.pars['refimage'])
420 _out_wcs.recenter()
421 _refimage = True
422
423 """
424 Each product now has 'refimage' and 'offsets' attributes
425 refimage = {'pix_shift':(),'ra_shift':(),'name':'','val':0.}
426 offsets = {'pixels':(),'arcseconds':()}
427
428 PyDrizzle measures ALL shifts relative to the center of the final
429 output frame. The user's shifts will be measured relative to a
430 reference image's reference point. The difference between the
431 two frames must be accounted for when comparing shifts measured
432 in the two frames.
433
434 This offset needs to be subtracted from the shift provided by
435 the user in order to apply it to the PyDrizzle shifts.
436 """
437 _ra_offset = (self.offsets['arcseconds'][0] - self.refimage['ra_shift'][0],
438 self.offsets['arcseconds'][1] - self.refimage['ra_shift'][1])
439
440 _rotmat = fileutil.buildRotMatrix(self.pars['delta_rot'])
441
442 if self.pars['abshift']:
443
444
445
446 _delta_x = self.pars['xshift']
447 _delta_y = self.pars['yshift']
448
449
450 if self.pars['shift_units'] == 'pixels':
451 if _refimage:
452
453
454 _dcrpix = N.dot((-_delta_x, -_delta_y),_rotmat)
455
456
457 _refcrval = _out_wcs.xy2rd((_dcrpix[0]+_out_wcs.crpix1,_dcrpix[1]+_out_wcs.crpix2))
458
459 _ra_shift = ( (_refcrval[0] - _out_wcs.crval1),
460 (_refcrval[1] - _out_wcs.crval2))
461
462 else:
463
464 _dcrpix = (_crpix[0] - _delta_x,_crpix[1] - _delta_y)
465
466 _refcrval = in_wcs.xy2rd(_dcrpix)
467
468 _ra_shift = ((_refcrval[0] - in_wcs.crval1),(_refcrval[1] - in_wcs.crval2))
469 else:
470
471
472
473
474 _ra_shift = (_delta_x/3600.,_delta_y/3600.)
475
476
477 _dcrval = (_ra_shift[0] -_ra_offset[0], _ra_shift[1] -_ra_offset[1])
478
479 else:
480
481
482
483 _delta_x = self.pars['delta_x']
484 _delta_y = self.pars['delta_y']
485
486 if self.pars['shift_units'] == 'pixels':
487 if _refimage:
488
489
490
491 _dcrpix = N.dot((-_delta_x, -_delta_y),_rotmat)
492
493 _refcrval = _out_wcs.xy2rd((_dcrpix[0]+_out_wcs.crpix1,_dcrpix[1]+_out_wcs.crpix2))
494 _dcrval = ((_out_wcs.crval1 - _refcrval[0] ),
495 (_out_wcs.crval2 - _refcrval[1] ))
496 else:
497 _dcrpix = (_crpix[0] - _delta_x,_crpix[1] - _delta_y)
498 _refcrval = in_wcs.xy2rd(_dcrpix)
499 _dcrval = (_crval[0] - _refcrval[0],_crval[1] - _refcrval[1])
500 else:
501
502
503
504 _dcrval = (_delta_x/3600.,_delta_y/3600.)
505
506
507
508
509 in_wcs.crval1 -= _dcrval[0]
510 in_wcs.crval2 -= _dcrval[1]
511
512
513 _orient = None
514 if self.pars['delta_rot'] != 0.:
515 _orient = in_wcs.orient + self.pars['delta_rot']
516
517 _scale = None
518 if self.pars['delta_scale'] != 0.:
519 _scale = in_wcs.pscale * self.pars['delta_scale']
520
521
522 in_wcs.crval1,in_wcs.crval2 = in_wcs.xy2rd(_crpix)
523 in_wcs.crpix1 = _crpix[0]
524 in_wcs.crpix2 = _crpix[1]
525
526
527
528 if _orient != None or _scale != None:
529 in_wcs.updateWCS(orient=_orient,pixel_scale=_scale)
530
532 """ Compute the product's corner positions based on input exposure's
533 corner positions.
534 """
535 _prodcorners = []
536 for member in self.members:
537 _prodcorners += member.corners['corrected'].tolist()
538
539 self.product.corners['corrected'] = N.array(_prodcorners,dtype=N.float64)
540
542 """
543 Create Exposure object for meta-chip product after applying
544 distortion model to input members.
545 """
546
547 output_wcs = self.buildMetachip()
548
549
550
551
552
553
554 self.size = (output_wcs.naxis1,output_wcs.naxis2)
555
556
557
558 self.product = Exposure(self.rootname,wcs=output_wcs, new=yes)
559
560 self.product.exptime = self.exptime
561
562
563 self.product.geometry.wcslin = self.product.geometry.wcs.copy()
564
565
566 self.getProductCorners()
567
647
738
739
741 """This method would build a list of parameters to run 'drizzle'
742 one a single input image.
743 The reference image info 'ref' will be passed as a SkyField object.
744 The default output reference frame will be passed as 'def_wcs'
745 for comparison to the user's selected object 'ref'.
746 """
747
748
749 parlist = []
750
751
752 def_wcs = self.product.geometry.wcslin.copy()
753
754 if ref != None:
755
756
757 if ref.exptime == None:
758 _texptime = self.exptime
759 else:
760 _texptime = ref.exptime
761
762
763
764 _field = ref
765
766
767
768
769 _out_wcs = self.product.geometry.wcs.copy()
770
771
772
773
774 _field.mergeWCS(_out_wcs)
775 _field.wcs.rootname=def_wcs.rootname
776
777 self.product.geometry.wcslin = _out_wcs
778 self.product.geometry.wcs = _field.wcs.copy()
779
780
781
782 ref_wcs = _field.wcs
783
784 else:
785
786
787
788
789 ref_wcs = self.product.geometry.wcslin.copy()
790
791
792 self.product.geometry.wcs = ref_wcs.copy()
793
794
795 _texptime = self.product.exptime
796
797
798
799 ref_wcs.recenter()
800
801 for member in self.members:
802 in_wcs = member.geometry.wcslin
803 in_wcs_orig = member.geometry.wcs
804
805
806
807
808
809
810 abxt,cdyt = drutil.wcsfit(member.geometry, ref_wcs)
811
812
813 _delta_roty = _delta_rot = RADTODEG(N.arctan2(abxt[1],cdyt[0]))
814 _delta_rotx = RADTODEG(N.arctan2(abxt[0],cdyt[1]))
815
816
817 _scale = 1./N.sqrt(abxt[0]**2 + abxt[1]**2)
818
819
820
821
822 _delta_x = abxt[2]
823 _delta_y = cdyt[2]
824
825
826 parameters = ParDict()
827 parameters['data'] = member.name
828 parameters['output'] = self.output
829 parameters['exposure'] = member
830 parameters['group'] = member.group_indx
831
832 parameters['instrument'] = self.instrument
833 parameters['detector'] = self.detector
834
835 parameters['driz_mask'] = member.maskname
836 parameters['single_driz_mask'] = member.singlemaskname
837
838
839 parameters['outsingle'] = self.outsingle
840 parameters['outsweight'] = self.outsweight
841 parameters['outscontext'] = self.outscontext
842 parameters['outblot'] = member.outblot
843 parameters['blotnx'] = member.naxis1
844 parameters['blotny'] = member.naxis2
845
846
847 parameters['outdata'] = self.outdata
848 parameters['outweight'] = self.outweight
849 parameters['outcontext'] = self.outcontext
850
851 parameters['outnx'] = ref_wcs.naxis1
852 parameters['outny'] = ref_wcs.naxis2
853
854 parameters['xsh'] = _delta_x
855 parameters['ysh'] = _delta_y
856
857 parameters['alpha'] = self.alpha
858 parameters['beta'] = self.beta
859
860
861
862
863 parameters['rot'] = _delta_rot
864
865
866
867
868
869 parameters['exptime'] = self.exptime[0]
870 parameters['expstart'] = self.exptime[1]
871 parameters['expend'] = self.exptime[2]
872 parameters['texptime'] = _texptime[0]
873 parameters['texpstart'] = _texptime[1]
874 parameters['texpend'] = _texptime[2]
875
876
877
878
879 parameters['scale'] = _scale
880
881
882 parameters['geomode'] = 'wcs'
883 parameters['racen'] = ref_wcs.crval1
884 parameters['deccen'] = ref_wcs.crval2
885 parameters['orient'] = ref_wcs.orient
886 parameters['outscl'] = ref_wcs.pscale
887 parameters['gpar_xsh'] = member.geometry.gpar_xsh
888 parameters['gpar_ysh'] = member.geometry.gpar_ysh
889 parameters['gpar_rot'] = member.geometry.gpar_rot
890
891
892 member.product_wcs = ref_wcs
893
894 member.writeCoeffs()
895 parameters['coeffs'] = member.coeffs
896
897 parameters['plam'] = member.plam
898
899
900 parameters['xgeoim'] = member.xgeoim
901 parameters['ygeoim'] = member.ygeoim
902
903
904 if self.pars['units'] != None:
905 parameters['units'] = self.pars['units']
906 else:
907 parameters['units'] = 'cps'
908
909 if self.pars['in_units'] != None:
910 parameters['in_units'] = self.pars['in_units']
911 else:
912 parameters['in_units'] = 'counts'
913
914 if self.pars['pixfrac'] != None:
915 parameters['pixfrac'] = self.pars['pixfrac']
916 else:
917 parameters['pixfrac'] = 1.0
918
919 if self.pars['kernel'] != None:
920 parameters['kernel'] = self.pars['kernel']
921 else:
922 parameters['kernel'] = 'square'
923
924 if self.pars['wt_scl'] != None:
925 parameters['wt_scl'] = self.pars['wt_scl']
926 else:
927 parameters['wt_scl'] = 'exptime'
928
929 if self.pars['fillval'] != None:
930 parameters['fillval'] = str(self.pars['fillval'])
931 else:
932 parameters['fillval'] = 'INDEF'
933
934
935 parameters['version'] = 'PyDrizzle Version '+__version__
936 parameters['driz_version'] = ''
937 parameters['nimages'] = self.nimages
938
939 parlist.append(parameters)
940
941
942
943
944 return parlist
945
947 """
948 Method for converting cubic and Trauger coefficients tables
949 into a usable form. It also replaces 'computeOffsets' for
950 those tables as well.
951 """
952
953 _pscale1 = None
954 for img in self.members:
955 _chip = img.chip
956 _detector = str(img.header[self.DETECTOR_NAME])
957
958 if _pscale1 == None or img.chip == '1':
959 _pscale1 = self.REFDATA[_detector]['psize']
960 _reftheta = self.REFDATA[_detector]['theta']
961
962 _v2ref = 0.
963 _v3ref = 0.
964 _nmembers = 0
965 for img in self.members:
966
967 _model = img.geometry.model
968 _ikey = img.geometry.ikey
969 _chip = img.chip
970 _detector = str(img.header[self.DETECTOR_NAME])
971 _refdata = self.REFDATA[_detector]
972
973
974 if img.chip == '1':
975 _pscale = _refdata['psize']
976 _ratio = 1.0
977 else:
978 _pscale = _refdata['psize']
979 _ratio = _refdata['psize'] / _pscale1
980
981
982
983 _model.pscale = _pscale
984 _model.refpix['PSCALE'] = _pscale
985
986 if _ikey == 'trauger':
987 _ratio = 1.
988
989
990
991 _model.refpix['V2REF'] = _refdata['xoff']
992 _model.refpix['V3REF'] = _refdata['yoff']
993
994 else:
995 _model.refpix['V2REF'] = _refdata['xoff'] * _pscale
996 _model.refpix['V3REF'] = _refdata['yoff'] * _pscale
997
998
999 _model.cx = _model.cx * N.array([_model.pscale/_ratio],dtype=N.float64)
1000 _model.cy = _model.cy * N.array([_model.pscale/_ratio],dtype=N.float64)
1001 _model.refpix['XREF'] = self.REFPIX['x']
1002 _model.refpix['YREF'] = self.REFPIX['y']
1003
1004
1005 _model.refpix['XDELTA'] = _refdata['xoff']
1006 _model.refpix['YDELTA'] = _refdata['yoff']
1007
1008 _model.refpix['centered'] = yes
1009
1010 if _ikey != 'trauger':
1011 _model.refpix['V2REF'] = _model.refpix['V2REF'] / _ratio
1012 _model.refpix['V3REF'] = _model.refpix['V3REF'] / _ratio
1013 _v2ref += _model.refpix['V2REF']
1014 _v3ref += _model.refpix['V3REF']
1015 _nmembers += 1
1016
1017
1019 """
1020 This version of 'computeOffsets' calculates the zero-point
1021 shifts to be included in the distortion coefficients table
1022 used by 'drizzle'.
1023 It REQUIRES a parity matrix to convert from
1024 V2/V3 coordinates into detector image X/Y coordinates. This
1025 matrix will be specific to each detector.
1026 """
1027 vref = []
1028
1029
1030 if len(self.members) == 1:
1031 refp = self.members[0].geometry.model.refpix
1032 refp['XDELTA'] = 0.
1033 refp['YDELTA'] = 0.
1034
1035 return
1036
1037
1038 if parity == None:
1039
1040 parity = self.PARITY
1041
1042 ref_model=None
1043 for member in self.members:
1044 if not refchip or refchip == int(member.chip):
1045 ref_model = member.geometry.model
1046 ref_scale = ref_model.refpix['PSCALE']
1047 ref_v2v3 = N.array([ref_model.refpix['V2REF'],ref_model.refpix['V3REF']])
1048 ref_theta = ref_model.refpix['THETA']
1049 if ref_theta == None: ref_theta = 0.0
1050 ref_pmat = N.dot(fileutil.buildRotMatrix(ref_theta), member.parity)
1051 ref_xy = (ref_model.refpix['XREF'],ref_model.refpix['YREF'])
1052 break
1053
1054 if not ref_model:
1055 ref_scale = 1.0
1056 ref_theta = 0.0
1057 ref_v2v3 = [0.,0.]
1058 ref_xy = [0.,0.]
1059 ref_pmat = N.array([[1.,0.],[0.,1.0]])
1060
1061
1062
1063
1064 for member in self.members:
1065 in_model = member.geometry.model
1066 refp = in_model.refpix
1067 pscale = in_model.pscale
1068 memwcs = member.geometry.wcs
1069
1070 v2v3 = N.array([in_model.refpix['V2REF'],in_model.refpix['V3REF']])
1071 scale = refp['PSCALE']
1072 theta = refp['THETA']
1073 if theta == None: theta = 0.0
1074
1075 chipcen = ( (memwcs.naxis1/2.) + memwcs.offset_x,
1076 (memwcs.naxis2/2.) + memwcs.offset_y)
1077 xypos = N.dot(ref_pmat,v2v3-ref_v2v3) / scale + ref_xy
1078 chiprot = fileutil.buildRotMatrix(theta - ref_theta)
1079
1080 offcen = ((refp['XREF'] - chipcen[0]), (refp['YREF'] - chipcen[1]))
1081
1082
1083
1084
1085
1086 offset_xy = N.dot(chiprot,xypos-offcen)*scale/ref_scale
1087 refp['XDELTA'] = offset_xy[0]
1088 refp['YDELTA'] = offset_xy[1]
1089
1090
1091 if member.geometry.wcs.subarray != yes:
1092 refp['centered'] = no
1093 else:
1094 refp['centered'] = yes
1095
1096
1098 """
1099 Define standard name attibutes:
1100 outname - Default final output name
1101 outdata - Name for drizzle science output
1102 outsingle - Name for output for single image
1103 """
1104 self.rootname = filename
1105 self.outname = output
1106
1107
1108
1109 self.outdata = fileutil.buildNewRootname(output,extn='_sci.fits')
1110 self.outweight = fileutil.buildNewRootname(output,extn='_weight.fits')
1111 self.outcontext = fileutil.buildNewRootname(output,extn='_context.fits')
1112
1113
1114 self.outsingle = fileutil.buildNewRootname(filename,extn='_single_sci.fits')
1115 self.outsweight = fileutil.buildNewRootname(filename,extn='_single_wht.fits')
1116 self.outscontext = None
1117
1118
1119
1120
1121
1122
1123
1125 return self.members[0].getWCS()
1126
1128 """ Return the class instance for the member with name memname."""
1129 member = None
1130 for mem in self.members:
1131 if mem.name == memname:
1132 member = mem
1133 return member
1134
1136 """ Return the names of all members for this Class.
1137 Output: [{self.name:[list of member names]}]
1138 """
1139 memlist = []
1140 for member in self.members:
1141 memlist.append(member.name)
1142 return [{self.name:memlist}]
1143
1145 _exptime = float(self.header['EXPTIME'])
1146 if _exptime == 0.: _exptime = 1.0
1147
1148 if self.header.has_key('EXPSTART'):
1149 _expstart = float(self.header['EXPSTART'])
1150 _expend = float(self.header['EXPEND'])
1151 else:
1152 _expstart = 0.
1153 _expend = _exptime
1154
1155 return (_exptime,_expstart,_expend)
1156
1158 """
1159 Converts provided delta(x,y) pixel offset into a
1160 delta(RA,Dec) offset in arcseconds.
1161 """
1162 _wcs = self.product.getWCS()
1163 _geom = self.product.geometry
1164
1165 new_rd = _geom.XYtoSky((_wcs.crpix1 - delta[0],_wcs.crpix2 - delta[1]))
1166 delta_ra = (_wcs.crval1 - new_rd[0]) * 3600.
1167 delta_dec = (_wcs.crval2 - new_rd[1]) * 3600.
1168
1169 return (delta_ra,delta_dec)
1170
1172 """
1173 This class defines an observation stored in a Simple FITS format;
1174 i.e., only a Primary header and image without extensions.
1175 """
1176
1177 REFPIX = {'x':512.,'y':512.}
1178 DETECTOR_NAME = 'INSTRUME'
1179
1180 - def __init__(self, filename, output, pars=None):
1181
1182
1183 Pattern.__init__(self, filename, output=output, pars=pars)
1184
1185
1186 if self.header.has_key('INSTRUME'):
1187 _instrument = self.header['INSTRUME']
1188 else:
1189 _instrument = self.DETECTOR_NAME
1190 self.instrument = _instrument
1191
1192 if self.header.has_key('crpix1'):
1193 self.REFPIX['x'] = self.header['crpix1']
1194 self.REFPIX['y'] = self.header['crpix2']
1195 else:
1196 self.REFPIX['x'] = self.header['naxis1'] /2.
1197 self.REFPIX['y'] = self.header['naxis2'] /2.
1198
1199
1200 self.setNames(filename,output)
1201
1202
1203 self.exptime = self.getExptime()
1204
1205 self.nmembers = 1
1206
1207
1208 self.addMembers(filename)
1209
1210 _ikey = self.members[0].geometry.ikey
1211 if _ikey != 'idctab' and _ikey != 'wcs' :
1212
1213 self.computeCubicCoeffs()
1214 else:
1215 self.computeOffsets()
1216
1217
1218 self.buildProduct(filename, output)
1219
1221 """This class defines an observation with information specific
1222 to ACS WFC exposures, including knowledge of how to mosaic both
1223 chips."""
1224
1225
1226 PARITY = {'WFC':[[1.0,0.0],[0.0,-1.0]],'HRC':[[-1.0,0.0],[0.0,1.0]],'SBC':[[-1.0,0.0],[0.0,1.0]]}
1227
1228 - def __init__(self, filename, output, pars=None):
1229
1230
1231 Pattern.__init__(self, filename, output=output, pars=pars)
1232
1233 self.instrument = 'ACS'
1234
1235
1236
1237 self.setNames(filename,output)
1238
1239
1240 self.exptime = self.getExptime()
1241
1242
1243 self.addMembers(filename)
1244
1245
1246 self.computeOffsets()
1247
1248
1249 self.buildProduct(filename, output)
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261 self.alpha = 0
1262 self.beta = 0
1263
1264
1266 """This class defines an observation with information specific
1267 to STIS exposures.
1268 """
1269
1270
1271 IDCKEY = 'cubic'
1272
1273 __theta = 0.0
1274 __parity = fileutil.buildRotMatrix(__theta) * N.array([[-1.,1.],[-1.,1.]])
1275 PARITY = {'CCD':__parity,'NUV-MAMA':__parity,'FUV-MAMA':__parity}
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287 - def __init__(self, filename, output, pars=None):
1288
1289
1290 Pattern.__init__(self, filename, output=output, pars=pars)
1291
1292 self.instrument = 'STIS'
1293 self.__theta = 0.0
1294 self.REFDATA = {'CCD':{'psize':0.05,'xoff':0.0,'yoff':0.0,'v2':-213.999,'v3':-224.897,'theta':self.__theta},
1295 'NUV-MAMA':{'psize':0.024,'xoff':0.0,'yoff':0.0,'v2':-213.999,'v3':-224.897,'theta':self.__theta},
1296 'FUV-MAMA':{'psize':0.024,'xoff':0.0,'yoff':0.0,'v2':-213.999,'v3':-224.897,'theta':self.__theta}}
1297 self.REFPIX = {'x':512.,'y':512.}
1298
1299
1300 self.setNames(filename,output)
1301
1302
1303 self.exptime = self.getExptime()
1304
1305
1306 self.addMembers(filename)
1307
1308 if self.members[0].geometry.ikey != 'idctab':
1309
1310 self.computeCubicCoeffs()
1311 else:
1312 self.computeOffsets()
1313
1314
1315 self.buildProduct(filename, output)
1316
1318
1319 header = fileutil.getHeader(self.name+'[sci,1]')
1320 _exptime = float(header['EXPTIME'])
1321 if _exptime == 0.: _exptime = 1.0
1322
1323 if header.has_key('EXPSTART'):
1324 _expstart = float(header['EXPSTART'])
1325 _expend = float(header['EXPEND'])
1326 else:
1327 _expstart = 0.
1328 _expend = _exptime
1329
1330 return (_exptime,_expstart,_expend)
1331
1332
1333
1334
1336 """This class defines an observation with information specific
1337 to NICMOS exposures.
1338 """
1339
1340
1341 IDCKEY = 'cubic'
1342
1343 DETECTOR_NAME = 'camera'
1344 NUM_IMSET = 5
1345
1346 __theta = 0.0
1347 __parity = fileutil.buildRotMatrix(__theta) * N.array([[-1.,1.],[-1.,1.]])
1348 PARITY = {'1':__parity,'2':__parity,'3':__parity}
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360 - def __init__(self, filename, output, pars=None):
1361
1362
1363 Pattern.__init__(self, filename, output=output, pars=pars)
1364
1365 self.instrument = 'NICMOS'
1366 self.__theta = 0.0
1367 self.REFDATA = {'1':{'psize':0.0432,'xoff':0.0,'yoff':0.0,'v2':-296.9228,'v3':290.1827,'theta':self.__theta},
1368 '2':{'psize':0.076,'xoff':0.0,'yoff':0.0,'v2':-319.9464,'v3':311.8579,'theta':self.__theta},
1369 '3':{'psize':0.0203758,'xoff':0.0,'yoff':0.0,'v2':-249.8170,'v3':235.2371,'theta':self.__theta}}
1370 self.REFPIX = {'x':128.,'y':128.}
1371
1372 self.setNames(filename,output)
1373
1374
1375 self.exptime = self.getExptime()
1376
1377
1378 self.addMembers(filename)
1379
1380 if self.members[0].geometry.ikey != 'idctab':
1381
1382 self.computeCubicCoeffs()
1383 else:
1384 self.computeOffsets()
1385
1386
1387 self.buildProduct(filename, output)
1388
1389
1391 """This class defines an observation with information specific
1392 to WFPC2 exposures, including knowledge of how to mosaic the
1393 chips."""
1394
1395
1396 IDCKEY = 'idctab'
1397
1398
1399
1400
1401 __pmat = N.array([[-1.,0.],[0.,1.]])
1402 __refchip = 3
1403 PARITY = {'1':__pmat,'2':__pmat,'3':__pmat,'4':__pmat,'WFPC':__pmat}
1404
1405 NUM_IMSET = 1
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418 - def __init__(self, filename, output, pars=None):
1419
1420
1421 Pattern.__init__(self, filename, output=output, pars=pars)
1422
1423 self.instrument = 'WFPC2'
1424 self.REFDATA ={'1':{'psize':0.04554,'xoff':354.356,'yoff':343.646,'v2':2.374,'v3':-30.268,'theta':224.8480},
1425 '2':{'psize':0.0996,'xoff':345.7481,'yoff':375.28818,'v2':-51.368,'v3':-5.698,'theta':314.3520},
1426 '3':{'psize':0.0996,'xoff':366.56876,'yoff':354.79435,'v2':0.064,'v3':48.692,'theta':44.67},
1427 '4':{'psize':0.0996,'xoff':355.85016,'yoff':351.29183,'v2':55.044,'v3':-6.098,'theta':135.2210}}
1428
1429 self.REFPIX = {'x':400.,'y':400.}
1430 gcount = None
1431
1432
1433 self.setNames(filename,output)
1434
1435
1436 self.exptime = self.getExptime()
1437
1438 _mode = fileutil.getKeyword(filename, 'MODE')
1439 if _mode == 'AREA':
1440 self.binned = 2
1441 if self.idckey == 'cubic':
1442 for l in self.REFPIX.keys(): self.REFPIX[l]= self.REFPIX[l] / self.binned
1443 for l in self.REFDATA.keys():
1444 self.REFDATA[l]['psize'] = self.REFDATA[l]['psize'] * self.binned
1445 self.REFDATA[l]['xoff'] = self.REFDATA[l]['xoff'] / self.binned
1446 self.REFDATA[l]['yoff'] = self.REFDATA[l]['yoff'] / self.binned
1447
1448
1449 self.addMembers(filename)
1450
1451 if self.members[0].geometry.ikey != 'idctab':
1452
1453 self.computeCubicCoeffs()
1454 else:
1455 self.computeOffsets(refchip=self.__refchip)
1456
1457
1458 self.setOrient()
1459
1460
1461 self.buildProduct(filename, output)
1462
1463
1464
1465
1466
1467
1468
1470
1471
1472
1473 self.detector = 'WFPC'
1474
1475 _chip1_rot = None
1476
1477 if self.pars['section'] == None:
1478 self.pars['section'] = [None]*self.nmembers
1479
1480 for i in range(self.nmembers):
1481 _extname = self.imtype.makeSciName(i+1,section=self.pars['section'][i])
1482
1483 _detnum = fileutil.getKeyword(_extname,self.DETECTOR_NAME)
1484
1485
1486 _dqfile, _dqextn = self._findDQFile()
1487
1488
1489 self.imtype.dqfile = _dqfile
1490 self.imtype.dq_extn = _dqextn
1491
1492
1493 _dqname = self.imtype.makeDQName(extver=_detnum)
1494 _masklist = []
1495 _masknames = []
1496
1497 if _dqname != None:
1498 _maskname = buildmask.buildMaskName(fileutil.buildNewRootname(_dqname),_detnum)
1499 else:
1500 _maskname = None
1501 _masknames.append(_maskname)
1502
1503 outmask = buildmask.buildShadowMaskImage(_dqname,_detnum,_maskname, bitvalue=self.bitvalue[0], binned=self.binned)
1504 _masklist.append(outmask)
1505
1506 _maskname = _maskname.replace('final_mask','single_mask')
1507 _masknames.append(_maskname)
1508 outmask = buildmask.buildShadowMaskImage(_dqname,_detnum,_maskname, bitvalue=self.bitvalue[1], binned=self.binned)
1509 _masklist.append(outmask)
1510 _masklist.append(_masknames)
1511
1512
1513 self.members.append(Exposure(_extname, idckey=self.idckey, dqname=_dqname,
1514 mask=_masklist, parity=self.PARITY[str(i+1)],
1515 idcdir=self.pars['idcdir'], group_indx = i+1,
1516 rot=_chip1_rot, handle=self.image_handle, extver=_detnum,
1517 exptime=self.exptime[0], ref_pscale=self.REFDATA['1']['psize'], binned=self.binned))
1518
1519 if self.idckey != 'idctab':
1520 _chip1_rot = self.members[0].geometry.def_rot
1521
1523 """ Find the DQ file which corresponds to the input WFPC2 image. """
1524 if self.name.find('.fits') < 0:
1525
1526 dqfile = self.name[:-2]+'1h'
1527 dqextn = '[sdq,1]'
1528 else:
1529
1530
1531
1532
1533
1534
1535 if 'c0h.fits' in self.name:
1536 dqfile = self.name.replace('0h.fits','1h.fits')
1537 dqextn = '[sdq,1]'
1538 elif 'c0f.fits' in self.name:
1539 dqfile = self.name.replace('0f.fits','1f.fits')
1540 dqextn = '[sci,1]'
1541
1542 return dqfile, dqextn
1543
1545
1546 """ Determine desired orientation of product."""
1547 meta_orient = None
1548 for exp in self.members:
1549 if int(exp.chip) == 1:
1550 meta_orient = exp.geometry.wcslin.orient
1551
1552 if meta_orient == None:
1553 meta_orient = self.members[0].geometry.wcslin.orient
1554
1555
1556
1557
1558 for exp in self.members:
1559 exp.geometry.wcs.orient = meta_orient
1560
1561
1562
1563
1564
1565
1567 """
1568 Builds an object for a set of dithered inputs, each of which
1569 will be one of the Observation objects.
1570 """
1571 - def __init__(self, prodlist, pars=None):
1572
1573
1574 if prodlist['output'].find('.fits') < 0:
1575 if prodlist['output'].rfind('_drz') < 0:
1576 output = fileutil.buildNewRootname(prodlist['output'],extn='_drz.fits')
1577 else:
1578 output = prodlist['output']+'.fits'
1579 else:
1580 output = prodlist['output']
1581
1582
1583 Pattern.__init__(self, None, output=output, pars=pars)
1584
1585 self.pars = prodlist['members']
1586
1587
1588
1589 self.nmembers = self.nimages = len(prodlist['members']) - 2
1590 self.offsets = None
1591
1592 self.addMembers(prodlist,pars,output)
1593
1594 if len(self.members) == 0:
1595 print 'No suitable inputs from ASN table. Quitting PyDrizzle...'
1596 raise Exception
1597
1598 self.exptime = self.getExptime()
1599
1600 self.buildProduct(output)
1601
1603 """ Close image handle for each member."""
1604 for member in self.members:
1605 member.closeHandle()
1606
1608
1609 output_wcs = self.buildMetachip()
1610
1611 self.size = (output_wcs.naxis1,output_wcs.naxis2)
1612 self.product = Exposure(output,wcs=output_wcs,new=yes)
1613
1614
1615
1616
1617 self.computeOffsets()
1618
1619
1620 self.applyAsnShifts(output)
1621
1622
1623 self.product.exptime = self.exptime
1624
1625 self.product.setCorners()
1626
1627 _prodcorners = []
1628 for prod in self.members:
1629 _prodcorners += prod.product.corners['corrected'].tolist()
1630 self.product.corners['corrected'] = N.array(_prodcorners,dtype=N.float64)
1631