LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
fitsChanContinued.py
Go to the documentation of this file.
1__all__ = ["CardType", "FitsChan", "FitsKeyState"]
2
3from .fitsChan import CardType, FitsChan, FitsKeyState
4
5
6def _calc_card_pos(self, index):
7 """Convert a python index into a FitsChan position.
8
9 Parameters
10 ----------
11 self : `FitsChan`
12 The FitsChan to index.
13 index : `int`
14 0-based index into header. If negative, counts from end.
15
16 Raises
17 ------
18 IndexError
19 Raised if the index exceeds the size of the FitsChan. If the index
20 equals the size of the FitsChan (noting that in 0-based indexing the
21 final card is one less than the size) then this refers to the end of
22 the header.
23 """
24 # Calculate 0-based index
25 nCards = len(self)
26 if index < 0:
27 index = nCards + index
28 elif abs(index) > nCards:
29 # We allow index of one higher to indicate append
30 raise IndexError(f"Index {index} exceeds size of FitsChan ({nCards})")
31
32 # Convert to 1-based index
33 return index + 1
34
35
36def _get_current_card_value(self):
37 """Retrieve the value of the current card along with the keyword name.
38
39 Returns
40 -------
41 name : `str`
42 The name of the current card.
43 value : `object`
44 The value in the correct Python type.
45 """
46 # Method look up table for obtaining values
47 typeLut = {CardType.INT: self.getFitsI,
48 CardType.FLOAT: self.getFitsF,
49 CardType.STRING: self.getFitsS,
50 CardType.COMPLEXF: self.getFitsCF,
51 CardType.LOGICAL: self.getFitsL
52 }
53
54 # Get the data type for this matching card
55 ctype = self.getCardType()
56
57 # Get the name of the card
58 name = self.getCardName()
59
60 # Go back one card so we can ask for the value in the correct
61 # data type (getFitsX starts from the next card)
62 # thiscard = self.getCard()
63 # self.setCard(thiscard - 1)
64
65 if ctype == CardType.UNDEF:
66 value = None
67 elif ctype in typeLut:
68 found = typeLut[ctype]("") # "" indicates current card
69 if found.found:
70 value = found.value
71 else:
72 raise RuntimeError(f"Unexpectedly failed to find card '{name}'")
73 elif ctype == CardType.COMMENT:
74 value = self.getCardComm()
75 else:
76 raise RuntimeError(f"Type, {ctype} of FITS card '{name}' not supported")
77
78 return name, value
79
80
81def length(self):
82 return self.nCard
83
84
85FitsChan.__len__ = length
86
87
88def iter(self):
89 """The FitsChan is its own iterator, incrementing the card position on
90 each call.
91
92 The position of the iterator is handled internally in the FitsChan and
93 is moved to the start of the FitsChan by this call.
94 Whilst iterating do not change the internal card position.
95
96 The iterator will return 80-character header cards.
97 """
98 self.clearCard()
99 return self
100
101
102FitsChan.__iter__ = iter
103
104
105def next(self):
106 """Return each 80-character header card until we run out of cards.
107 """
108 card = self.findFits("%f", True)
109 if not card.found:
110 raise StopIteration
111 return card.value
112
113
114FitsChan.__next__ = next
115
116
117def to_string(self):
118 """A FitsChan string representation is a FITS header with newlines
119 after each 80-character header card.
120 """
121 return "\n".join(c for c in self)
122
123
124FitsChan.__str__ = to_string
125
126
127def contains(self, name):
128 """Returns True if either the supplied name is present in the FitsChan
129 or the supplied integer is acceptable to the FitsChan.
130 """
131 if isinstance(name, int):
132 # index will be zero-based
133 if name >= 0 and name < self.nCard:
134 return True
135 elif isinstance(name, str):
136 currentCard = self.getCard()
137 try:
138 self.clearCard()
139 result = self.findFits(name, False)
140 finally:
141 self.setCard(currentCard)
142 if result.found:
143 return True
144
145 return False
146
147
148FitsChan.__contains__ = contains
149
150
151def getitem(self, name):
152 """Return a value associated with the supplied name.
153
154 Parameters
155 ----------
156 name : `str` or `int`
157 If the FitsChan is being accessed by integer index the returned value
158 will be the corresponding 80-character card. Index values are 0-based.
159 A negative index counts from the end of the FitsChan.
160 If the FitsChan is being accessed by string the returned value will
161 be the scalar value associated with the first card that matches the
162 supplied name.
163
164 Returns
165 -------
166 value : `str`, `int`, `float`, `bool`, or `None`
167 The complete 80-character header card if an integer index is supplied,
168 else the first matching value of the named header.
169
170 Raises
171 ------
172 IndexError
173 Raised if an integer index is provided and the index is out of range.
174 KeyError
175 Raised if a string is provided and that string is not present in
176 the FitsChan. Also raised if the supplied name is neither an integer
177 not a string.
178 RuntimeError
179 Raised if there is some problem accessing the value in the FitsChan.
180 """
181
182 # Save current card position
183 currentCard = self.getCard()
184
185 if isinstance(name, int):
186 # Calculate position in FitsChan (0-based to 1-based)
187 newpos = _calc_card_pos(self, name)
188 self.setCard(newpos)
189 try:
190 result = self.findFits("%f", False)
191 finally:
192 self.setCard(currentCard)
193 if not result.found:
194 raise IndexError(f"No FITS card at index {name}")
195 return result.value
196
197 elif isinstance(name, str):
198
199 try:
200 # Rewind FitsChan so we search all cards
201 self.clearCard()
202
203 # We are only interested in the first matching card
204 result = self.findFits(name, False)
205 if not result.found:
206 raise KeyError(f"{name}'")
207
208 this_name, value = _get_current_card_value(self)
209 if this_name != name:
210 raise RuntimeError(f"Internal inconsistency in get: {this_name} != {name}")
211
212 finally:
213 # Reinstate the original card position
214 self.setCard(currentCard)
215
216 return value
217
218 raise KeyError(f"Supplied key, '{name}' of unsupported type")
219
220
221FitsChan.__getitem__ = getitem
222
223
224def setitem(self, name, value):
225 """Set a new value.
226
227 Parameters
228 ----------
229 name : `str` or `int`
230 If the name is an integer index this corresponds to a position within
231 the FitsChan. Index values are 0-based. A negative index counts
232 from the end of the FitsChan. If the index matches the number of
233 cards (e.g. the return value of `len()`) the new value will be
234 appended to the end of the FitsChan.
235 If the name is an empty string or `None`, the value will be inserted
236 at the current card position as a comment card.
237 If the name is a string corresponding to a header card that is already
238 present in the FitsChan, the new value will overwrite the existing
239 value leaving the header name and any comment unchanged.
240 Any other cards matching that name later in the header will
241 be removed. If there is no header with that name, a new card will
242 be inserted at the end of the FitsChan.
243 value : `str`, `int`, `float`, `bool`, `None`
244 The new value to be inserted. If an integer index is given it must be
245 a complete FITS header card. The string will be padded to 80
246 characters.
247
248 Raises
249 ------
250 IndexError
251 Raised if the supplied integer index is out of range.
252 KeyError
253 Raised if the supplied name is neither a string or an integer.
254 TypeError
255 Raised if an integer index is given but the supplied value is not
256 a string.
257 """
258
259 if isinstance(name, int):
260 # Calculate position in FitsChan (0-based to 1-based)
261 newpos = _calc_card_pos(self, name)
262 self.setCard(newpos)
263
264 if not value:
265 value = " "
266
267 # Overwrite the entire value
268 self.putFits(value, True)
269 return
270
271 # A blank name results in a comment card being inserted at the
272 # current position
273 if not name:
274 self.setFitsCM(value, False)
275 return
276
277 if not isinstance(name, str):
278 raise KeyError(f"Supplied key, '{name}' of unsupported type")
279
280 # Get current card position and rewind
281 currentCard = self.getCard()
282 try:
283 self.clearCard()
284
285 # Look for a card with the specified name.
286 # We do not care about the result; if nothing is found we will be
287 # at the end of the header.
288 self.findFits(name, False)
289
290 # pyast seems to want to delete items if the value is None but
291 # astropy and PropertyList think the key should be undefined.
292 if value is None:
293 self.setFitsU(name, overwrite=True)
294 elif isinstance(value, int):
295 self.setFitsI(name, value, overwrite=True)
296 elif isinstance(value, float):
297 self.setFitsF(name, value, overwrite=True)
298 elif isinstance(value, bool):
299 self.setFitsL(name, value, overwrite=True)
300 else:
301 # Force to string.
302 self.setFitsS(name, str(value), overwrite=True)
303
304 # Delete any later cards with matching keyword
305 while True:
306 found = self.findFits(name, False)
307 if not found.found:
308 break
309 self.delFits()
310 finally:
311 # Try to reinstate the current card
312 self.setCard(currentCard)
313
314
315FitsChan.__setitem__ = setitem
316
317
318def delitem(self, name):
319 """Delete an item from the FitsChan either by index (0-based) or by name.
320
321 Parameters
322 ----------
323 name : `str` or `int`
324 If the name is an integer index the card at that position will be
325 removed. The index is zero-based. A negative index counts from the
326 end of the FitsChan.
327 If the name is a string all cards matching that name will be removed.
328
329 Raises
330 ------
331 IndexError
332 Raised if the supplied index is out of range.
333 KeyError
334 Raised if the supplied name is not found in the FitsChan.
335 """
336 if isinstance(name, int):
337 # Correct to 1-based
338 newpos = _calc_card_pos(self, name)
339 if newpos <= 0 or newpos > self.nCard:
340 # AST will ignore this but we raise if the index is out of range
341 raise IndexError(f"No FITS card at index {name}")
342 self.setCard(newpos)
343 self.delFits()
344 return
345
346 if not isinstance(name, str):
347 raise KeyError(f"Supplied key, '{name}' of unsupported type")
348
349 self.clearCard()
350 # Delete any cards with matching keyword
351 deleted = False
352 while True:
353 found = self.findFits(name, False)
354 if not found.found:
355 break
356 self.delFits()
357 deleted = True
358
359 if not deleted:
360 raise KeyError(f"No FITS card named {name}")
361
362
363FitsChan.__delitem__ = delitem
364
365
366def items(self):
367 """Iterate over each card, returning the keyword and value in a tuple.
368
369 Returns
370 -------
371 key : `str`
372 The key associated with the card. Can be an empty string for some
373 comment styles. The same key name can be returned multiple times
374 and be associated with different values.
375 value : `str`, `int`, `bool`, `float`
376 The value.
377
378 Notes
379 -----
380 The position of the iterator is internal to the FitsChan. Do not
381 change the card position when iterating.
382 """
383 self.clearCard()
384 nCards = self.nCard
385 thisCard = self.getCard()
386
387 while thisCard <= nCards:
388 name, value = _get_current_card_value(self)
389 yield name, value
390 self.setCard(thisCard + 1)
391 thisCard = self.getCard()
392
393
394FitsChan.items = items
def setitem(self, name, value)
Angle abs(Angle const &a)
Definition: Angle.h:106