Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22__all__ = ("FitsExposureFormatter", ) 

23 

24from astro_metadata_translator import fix_header 

25from lsst.daf.butler import Formatter 

26from lsst.afw.image import ExposureFitsReader 

27 

28 

29class FitsExposureFormatter(Formatter): 

30 """Interface for reading and writing Exposures to and from FITS files. 

31 """ 

32 extension = ".fits" 

33 _metadata = None 

34 

35 @property 

36 def metadata(self): 

37 """The metadata read from this file. It will be stripped as 

38 components are extracted from it 

39 (`lsst.daf.base.PropertyList`). 

40 """ 

41 if self._metadata is None: 

42 self._metadata = self.readMetadata() 

43 return self._metadata 

44 

45 def readMetadata(self): 

46 """Read all header metadata directly into a PropertyList. 

47 

48 Returns 

49 ------- 

50 metadata : `~lsst.daf.base.PropertyList` 

51 Header metadata. 

52 """ 

53 from lsst.afw.image import readMetadata 

54 md = readMetadata(self.fileDescriptor.location.path) 

55 fix_header(md) 

56 return md 

57 

58 def stripMetadata(self): 

59 """Remove metadata entries that are parsed into components. 

60 

61 This is only called when just the metadata is requested; stripping 

62 entries there forces code that wants other components to ask for those 

63 components directly rather than trying to extract them from the 

64 metadata manually, which is fragile. This behavior is an intentional 

65 change from Gen2. 

66 

67 Parameters 

68 ---------- 

69 metadata : `~lsst.daf.base.PropertyList` 

70 Header metadata, to be modified in-place. 

71 """ 

72 # TODO: make sure this covers everything, by delegating to something 

73 # that doesn't yet exist in afw.image.ExposureInfo. 

74 from lsst.afw.image import bboxFromMetadata 

75 from lsst.afw.geom import makeSkyWcs 

76 bboxFromMetadata(self.metadata) # always strips 

77 makeSkyWcs(self.metadata, strip=True) 

78 

79 def readComponent(self, component, parameters=None): 

80 """Read a component held by the Exposure. 

81 

82 Parameters 

83 ---------- 

84 component : `str`, optional 

85 Component to read from the file. 

86 parameters : `dict`, optional 

87 If specified, a dictionary of slicing parameters that 

88 overrides those in ``fileDescriptor``. 

89 

90 Returns 

91 ------- 

92 obj : component-dependent 

93 In-memory component object. 

94 

95 Raises 

96 ------ 

97 KeyError 

98 Raised if the requested component cannot be handled. 

99 """ 

100 componentMap = {'wcs': ('readWcs', False), 

101 'coaddInputs': ('readCoaddInputs', False), 

102 'psf': ('readPsf', False), 

103 'image': ('readImage', True), 

104 'mask': ('readMask', True), 

105 'variance': ('readVariance', True), 

106 } 

107 method, hasParams = componentMap.get(component, None) 

108 

109 if method: 109 ↛ 125line 109 didn't jump to line 125, because the condition on line 109 was never false

110 reader = ExposureFitsReader(self.fileDescriptor.location.path) 

111 caller = getattr(reader, method, None) 

112 

113 if caller: 113 ↛ exitline 113 didn't return from function 'readComponent', because the condition on line 113 was never false

114 if parameters is None: 114 ↛ 116line 114 didn't jump to line 116, because the condition on line 114 was never false

115 parameters = self.fileDescriptor.parameters 

116 if parameters is None: 116 ↛ 118line 116 didn't jump to line 118, because the condition on line 116 was never false

117 parameters = {} 

118 self.fileDescriptor.storageClass.validateParameters(parameters) 

119 

120 if hasParams and parameters: 120 ↛ 121line 120 didn't jump to line 121, because the condition on line 120 was never true

121 return caller(**parameters) 

122 else: 

123 return caller() 

124 else: 

125 raise KeyError(f"Unknown component requested: {component}") 

126 

127 def readFull(self, parameters=None): 

128 """Read the full Exposure object. 

129 

130 Parameters 

131 ---------- 

132 parameters : `dict`, optional 

133 If specified a dictionary of slicing parameters that overrides 

134 those in ``fileDescriptor`. 

135 

136 Returns 

137 ------- 

138 exposure : `~lsst.afw.image.Exposure` 

139 Complete in-memory exposure. 

140 """ 

141 fileDescriptor = self.fileDescriptor 

142 if parameters is None: 142 ↛ 144line 142 didn't jump to line 144, because the condition on line 142 was never false

143 parameters = fileDescriptor.parameters 

144 if parameters is None: 

145 parameters = {} 

146 fileDescriptor.storageClass.validateParameters(parameters) 

147 try: 

148 output = fileDescriptor.storageClass.pytype(fileDescriptor.location.path, **parameters) 

149 except TypeError: 

150 reader = ExposureFitsReader(fileDescriptor.location.path) 

151 output = reader.read(**parameters) 

152 return output 

153 

154 def read(self, component=None, parameters=None): 

155 """Read data from a file. 

156 

157 Parameters 

158 ---------- 

159 component : `str`, optional 

160 Component to read from the file. Only used if the `StorageClass` 

161 for reading differed from the `StorageClass` used to write the 

162 file. 

163 parameters : `dict`, optional 

164 If specified, a dictionary of slicing parameters that 

165 overrides those in ``fileDescriptor``. 

166 

167 Returns 

168 ------- 

169 inMemoryDataset : `object` 

170 The requested data as a Python object. The type of object 

171 is controlled by the specific formatter. 

172 

173 Raises 

174 ------ 

175 ValueError 

176 Component requested but this file does not seem to be a concrete 

177 composite. 

178 KeyError 

179 Raised when parameters passed with fileDescriptor are not 

180 supported. 

181 """ 

182 fileDescriptor = self.fileDescriptor 

183 if fileDescriptor.readStorageClass != fileDescriptor.storageClass: 

184 if component == "metadata": 184 ↛ 185line 184 didn't jump to line 185, because the condition on line 184 was never true

185 self.stripMetadata() 

186 return self.metadata 

187 elif component is not None: 187 ↛ 190line 187 didn't jump to line 190, because the condition on line 187 was never false

188 return self.readComponent(component, parameters) 

189 else: 

190 raise ValueError("Storage class inconsistency ({} vs {}) but no" 

191 " component requested".format(fileDescriptor.readStorageClass.name, 

192 fileDescriptor.storageClass.name)) 

193 return self.readFull() 

194 

195 def write(self, inMemoryDataset): 

196 """Write a Python object to a file. 

197 

198 Parameters 

199 ---------- 

200 inMemoryDataset : `object` 

201 The Python object to store. 

202 

203 Returns 

204 ------- 

205 path : `str` 

206 The `URI` where the primary file is stored. 

207 """ 

208 # Update the location with the formatter-preferred file extension 

209 self.fileDescriptor.location.updateExtension(self.extension) 

210 inMemoryDataset.writeFits(self.fileDescriptor.location.path) 

211 return self.fileDescriptor.location.pathInStore