Coverage for tests/test_fitsChan.py: 4%

Shortcuts 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

636 statements  

1import os.path 

2import unittest 

3import numpy as np 

4 

5import astshim as ast 

6from astshim.test import ObjectTestCase 

7 

8 

9def pad(card): 

10 """Pad a string withs paces to length 80 characters""" 

11 return "%-80s" % (card,) 

12 

13 

14def writeFitsWcs(frameSet, extraOptions=None): 

15 """Write a FrameSet as FITS-WCS 

16 

17 extraOptions are in addition to Encoding=Fits-WCS, CDMatrix=1 

18 """ 

19 options = "Encoding=FITS-WCS, CDMatrix=1" 

20 if extraOptions is not None: 

21 options = "%s, %s" % (options, extraOptions) 

22 fc = ast.FitsChan(ast.StringStream(), options) 

23 fc.write(frameSet) 

24 return fc 

25 

26 

27class TestFitsChan(ObjectTestCase): 

28 

29 def setUp(self): 

30 self.dataDir = os.path.join(os.path.dirname(__file__), "data") 

31 shortCards = ( 

32 "NAXIS1 = 200", 

33 "NAXIS2 = 200", 

34 "CTYPE1 = 'RA--TAN '", 

35 "CTYPE2 = 'DEC-TAN '", 

36 "CRPIX1 = 100", 

37 "CRPIX2 = 100", 

38 "CDELT1 = 0.001", 

39 "CDELT2 = 0.001", 

40 "CRVAL1 = 0", 

41 "CRVAL2 = 0", 

42 "BOOL = F", 

43 "UNDEF =", 

44 "BOOL = T / Repeat", 

45 "COMMENT one of two comments", 

46 "COMMENT another of two comments", 

47 "HISTORY one of two history fields", 

48 "HISTORY second of three history fields", 

49 "HISTORY third of three history fields", 

50 ) 

51 self.cards = [pad(card) for card in shortCards] 

52 

53 def insertPixelMapping(self, mapping, frameSet): 

54 """Make a new WCS by inserting a new mapping at the beginnning of the 

55 GRID-IWC mapping 

56 

57 Return the new FrameSet (the original is not altered). 

58 """ 

59 frameSet = frameSet.copy() 

60 

61 skyFrame = frameSet.getFrame(ast.FrameSet.CURRENT) # use this copy for the new sky frame 

62 self.assertIsInstance(skyFrame, ast.SkyFrame) 

63 oldSkyIndex = frameSet.current 

64 

65 if not frameSet.findFrame(ast.Frame(2, "Domain=GRID")): 

66 raise KeyError("No GRID frame") 

67 gridIndex = frameSet.current 

68 

69 if not frameSet.findFrame(ast.Frame(2, "Domain=IWC")): 

70 raise KeyError("No IWC frame") 

71 oldIwcIndex = frameSet.current 

72 iwcFrame = frameSet.getFrame(oldIwcIndex) # use this copy for the new IWC frame 

73 

74 oldGridToIwc = frameSet.getMapping(gridIndex, oldIwcIndex) 

75 iwcToSky = frameSet.getMapping(oldIwcIndex, oldSkyIndex) 

76 

77 # Remove frames in order high to low, so removal doesn't alter the 

78 # indices remaining to be removed; 

79 # update gridIndex during removal so it still points to the GRID frame 

80 framesToRemove = reversed(sorted([oldIwcIndex, oldSkyIndex])) 

81 for index in framesToRemove: 

82 if (index < gridIndex): 

83 gridIndex -= 1 

84 frameSet.removeFrame(index) 

85 

86 newGridToIwc = mapping.then(oldGridToIwc).simplified() 

87 frameSet.addFrame(gridIndex, newGridToIwc, iwcFrame) 

88 frameSet.addFrame(ast.FrameSet.CURRENT, iwcToSky, skyFrame) 

89 return frameSet 

90 

91 def test_FitsChanAttributes(self): 

92 """Test getting and setting FitsChan attributes 

93 

94 Does not test the behavior of the attributes. 

95 """ 

96 ss = ast.StringStream("".join(self.cards)) 

97 fc = ast.FitsChan(ss) 

98 self.assertFalse(fc.carLin) 

99 self.assertFalse(fc.cdMatrix) 

100 self.assertFalse(fc.clean) 

101 self.assertFalse(fc.defB1950) 

102 self.assertEqual(fc.encoding, "FITS-WCS") 

103 self.assertEqual(fc.fitsAxisOrder, "<auto>") 

104 self.assertAlmostEqual(fc.fitsTol, 0.1) 

105 self.assertFalse(fc.iwc) 

106 self.assertTrue(fc.sipOK) 

107 self.assertTrue(fc.sipReplace) 

108 self.assertEqual(fc.tabOK, 0) 

109 self.assertEqual(fc.polyTan, -1) 

110 warningSet = set(fc.warnings.split(" ")) 

111 desiredWarningSet = set("BadKeyName BadKeyValue Tnx Zpx BadCel BadMat BadPV BadCTYPE".split(" ")) 

112 self.assertEqual(warningSet, desiredWarningSet) 

113 

114 fc.carLin = True 

115 self.assertTrue(fc.carLin) 

116 fc.cdMatrix = True 

117 self.assertTrue(fc.cdMatrix) 

118 fc.clean = True 

119 self.assertTrue(fc.clean) 

120 fc.defB1950 = True 

121 self.assertTrue(fc.defB1950) 

122 fc.encoding = "NATIVE" 

123 self.assertEqual(fc.encoding, "NATIVE") 

124 fc.fitsAxisOrder = "<copy>" 

125 self.assertEqual(fc.fitsAxisOrder, "<copy>") 

126 fc.fitsTol = 0.001 

127 self.assertAlmostEqual(fc.fitsTol, 0.001) 

128 fc.iwc = True 

129 self.assertTrue(fc.iwc) 

130 fc.tabOK = 1 

131 self.assertEqual(fc.tabOK, 1) 

132 fc.polyTan = 0 

133 self.assertEqual(fc.polyTan, 0) 

134 fc.warnings = "BadKeyName BadMat" 

135 self.assertEqual(fc.warnings, "BadKeyName BadMat") 

136 

137 def test_FitsChanPreloaded(self): 

138 """Test a FitsChan that starts out loaded with data 

139 """ 

140 ss = ast.StringStream("".join(self.cards)) 

141 fc = ast.FitsChan(ss) 

142 self.assertEqual(fc.nCard, len(self.cards)) 

143 # there are 2 COMMENT and 3 HISTORY cards, 

144 # and two BOOL cards so 4 fewer unique keys 

145 self.assertEqual(fc.nKey, len(self.cards) - 4) 

146 self.assertEqual(fc.className, "FitsChan") 

147 fv = fc.getFitsF("CRVAL1") 

148 self.assertTrue(fv.found) 

149 self.assertEqual(fv.value, 0.0) 

150 

151 self.assertEqual(fc.encoding, "FITS-WCS") 

152 

153 self.assertEqual(fc.getAllCardNames(), 

154 [card.split(" ", 1)[0] for card in self.cards]) 

155 

156 def test_FitsChanFileStream(self): 

157 """Test a FitsChan with a FileStream/ 

158 

159 In particular, make sure that cards are written as the channel is 

160 destroyed. 

161 """ 

162 path = os.path.join(self.dataDir, "test_fitsChanFileStream.fits") 

163 fc1 = ast.FitsChan(ast.FileStream(path, True)) 

164 fc1.putCards("".join(self.cards)) 

165 # delete the channel, which writes cards, 

166 # and then deletes the file stream, closing the file 

167 del fc1 

168 

169 fc2 = ast.FitsChan(ast.FileStream(path, False)) 

170 self.assertEqual(fc2.nCard, len(self.cards)) 

171 del fc2 

172 os.remove(path) 

173 

174 def test_FitsChanWriteOnDelete(self): 

175 """Test that a FitsChan writes cards when it is deleted 

176 """ 

177 ss = ast.StringStream() 

178 fc = ast.FitsChan(ss) 

179 fc.putCards("".join(self.cards)) 

180 self.assertEqual(ss.getSinkData(), "") 

181 del fc 

182 self.assertEqual(len(ss.getSinkData()), 80 * len(self.cards)) 

183 

184 def test_FitsChanGetFitsSetFits(self): 

185 """Test FitsChan.getFits<X>, FitsChan.setFits<X> and getCardType 

186 """ 

187 fc = ast.FitsChan(ast.StringStream()) 

188 self.assertEqual(fc.className, "FitsChan") 

189 

190 # add a card for each type 

191 complexVal = complex(9.8, -5.15) 

192 continueVal = "This is a continue card" 

193 floatVal = 1.5 

194 intVal = 99 

195 logicalVal = True 

196 strVal = "This is a string" 

197 fc.setFitsCF("ACOMPLEX", complexVal, "Comment for ACOMPLEX") 

198 commentVal = "This is a comment" 

199 fc.setFitsCN("ACONT", continueVal, "Comment for ACONT") 

200 fc.setFitsF("AFLOAT", floatVal, "Comment for AFLOAT") 

201 fc.setFitsI("ANINT", intVal, "Comment for ANINT") 

202 fc.setFitsL("ALOGICAL", logicalVal, "Comment for ALOGICAL") 

203 fc.setFitsS("ASTRING", strVal, "Comment for ASTRING") 

204 fc.setFitsU("UNDEFVAL", "Comment for UNDEFVAL") 

205 fc.setFitsCM(commentVal) 

206 

207 self.assertEqual(fc.nCard, 8) 

208 self.assertEqual(fc.getAllCardNames(), 

209 ["ACOMPLEX", "ACONT", "AFLOAT", "ANINT", 

210 "ALOGICAL", "ASTRING", "UNDEFVAL", " "]) 

211 

212 fv = fc.getFitsI("ANINT") 

213 self.assertEqual(fc.getCardType(), ast.CardType.INT) 

214 self.assertTrue(fv.found) 

215 self.assertEqual(fv.value, intVal) 

216 self.assertEqual(fc.getCardComm(), "Comment for ANINT") 

217 self.assertEqual(fc.getCard(), 4) 

218 

219 fv = fc.getFitsS("ANINT") 

220 self.assertTrue(fv.found) 

221 self.assertEqual(fv.value, str(intVal)) 

222 self.assertEqual(fc.getCard(), 4) 

223 

224 fv = fc.getFitsI() # read the current card 

225 self.assertTrue(fv.found) 

226 self.assertEqual(fv.value, intVal) 

227 self.assertEqual(fc.getCard(), 4) 

228 

229 fv = fc.getFitsI("") # alternate way to read the current card 

230 self.assertTrue(fv.found) 

231 self.assertEqual(fv.value, intVal) 

232 self.assertEqual(fc.getCard(), 4) 

233 

234 fv = fc.getFitsF("AFLOAT") 

235 self.assertEqual(fc.getCardType(), ast.CardType.FLOAT) 

236 self.assertEqual(fc.getCard(), 3) 

237 self.assertTrue(fv.found) 

238 self.assertAlmostEqual(fv.value, floatVal) 

239 self.assertEqual(fc.getCardComm(), "Comment for AFLOAT") 

240 

241 fv = fc.getFitsF() # read the current card 

242 self.assertEqual(fc.getCardType(), ast.CardType.FLOAT) 

243 self.assertEqual(fc.getCard(), 3) 

244 self.assertTrue(fv.found) 

245 self.assertAlmostEqual(fv.value, floatVal) 

246 self.assertEqual(fc.getCardComm(), "Comment for AFLOAT") 

247 

248 fv = fc.getFitsCN("ACONT") 

249 self.assertEqual(fc.getCardType(), ast.CardType.CONTINUE) 

250 self.assertEqual(fc.getCard(), 2) 

251 self.assertTrue(fv.found) 

252 self.assertAlmostEqual(fv.value, continueVal) 

253 self.assertEqual(fc.getCardComm(), "Comment for ACONT") 

254 

255 fv = fc.getFitsCN() # read the current card 

256 self.assertEqual(fc.getCardType(), ast.CardType.CONTINUE) 

257 self.assertEqual(fc.getCard(), 2) 

258 self.assertTrue(fv.found) 

259 self.assertAlmostEqual(fv.value, continueVal) 

260 self.assertEqual(fc.getCardComm(), "Comment for ACONT") 

261 

262 fv = fc.getFitsCF("ACOMPLEX") 

263 self.assertEqual(fc.getCardType(), ast.CardType.COMPLEXF) 

264 self.assertEqual(fc.getCard(), 1) 

265 self.assertTrue(fv.found) 

266 self.assertAlmostEqual(fv.value, complexVal) 

267 self.assertEqual(fc.getCardComm(), "Comment for ACOMPLEX") 

268 

269 fv = fc.getFitsCF() # read the current card 

270 self.assertEqual(fc.getCardType(), ast.CardType.COMPLEXF) 

271 self.assertEqual(fc.getCard(), 1) 

272 self.assertTrue(fv.found) 

273 self.assertAlmostEqual(fv.value, complexVal) 

274 self.assertEqual(fc.getCardComm(), "Comment for ACOMPLEX") 

275 

276 fv = fc.getFitsL("ALOGICAL") 

277 self.assertEqual(fc.getCardType(), ast.CardType.LOGICAL) 

278 self.assertEqual(fc.getCard(), 5) 

279 self.assertTrue(fv.found) 

280 self.assertAlmostEqual(fv.value, logicalVal) 

281 self.assertEqual(fc.getCardComm(), "Comment for ALOGICAL") 

282 

283 fv = fc.getFitsL() # read the current card 

284 self.assertEqual(fc.getCardType(), ast.CardType.LOGICAL) 

285 self.assertEqual(fc.getCard(), 5) 

286 self.assertTrue(fv.found) 

287 self.assertAlmostEqual(fv.value, logicalVal) 

288 self.assertEqual(fc.getCardComm(), "Comment for ALOGICAL") 

289 

290 fv = fc.getFitsS("ASTRING") 

291 self.assertEqual(fc.getCardType(), ast.CardType.STRING) 

292 self.assertEqual(fc.getCard(), 6) 

293 self.assertTrue(fv.found) 

294 self.assertAlmostEqual(fv.value, strVal) 

295 self.assertEqual(fc.getCardComm(), "Comment for ASTRING") 

296 

297 fv = fc.getFitsS() # read the current card 

298 self.assertEqual(fc.getCardType(), ast.CardType.STRING) 

299 self.assertEqual(fc.getCard(), 6) 

300 self.assertTrue(fv.found) 

301 self.assertAlmostEqual(fv.value, strVal) 

302 self.assertEqual(fc.getCardComm(), "Comment for ASTRING") 

303 

304 fv = fc.getFitsS("BADNAME") # a card that does not exist 

305 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

306 self.assertEqual(fc.getCard(), fc.nCard + 1) 

307 self.assertFalse(fv.found) 

308 

309 fc.setCard(7) 

310 self.assertEqual(fc.getCardType(), ast.CardType.UNDEF) 

311 with self.assertRaises(RuntimeError): 

312 fc.getFitsS() 

313 self.assertEqual(fc.getCardComm(), "Comment for UNDEFVAL") 

314 

315 fc.setCard(8) 

316 self.assertEqual(fc.getCardType(), ast.CardType.COMMENT) 

317 with self.assertRaises(RuntimeError): 

318 fc.getFitsS() 

319 self.assertEqual(fc.getCardComm(), commentVal) 

320 

321 # replace ANINT card with new everything: name, value type and comment 

322 fc.setCard(4) 

323 fc.setFitsCF("NEWNAME", complex(99.9, 99.8), "New comment", overwrite=True) 

324 self.assertEqual(fc.getAllCardNames(), 

325 ["ACOMPLEX", "ACONT", "AFLOAT", "NEWNAME", 

326 "ALOGICAL", "ASTRING", "UNDEFVAL", " "]) 

327 fc.setCard(1) # force a search 

328 fv = fc.getFitsCF("NEWNAME") 

329 self.assertTrue(fv.found) 

330 self.assertEqual(fv.value.real, 99.9) 

331 self.assertEqual(fv.value.imag, 99.8) 

332 self.assertEqual(fc.getCardComm(), "New comment") 

333 self.assertEqual(fc.getCard(), 4) 

334 

335 def test_FitsChanGetCurrentForNonexistentCard(self): 

336 """Test getting info on the current card when it does not exist 

337 """ 

338 fc = ast.FitsChan(ast.StringStream()) 

339 fc.setFitsI("ANINT", 200) 

340 fc.setFitsS("ASTRING", "string value") 

341 fc.setCard(fc.nCard + 1) 

342 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

343 self.assertEqual(fc.testFits(), ast.FitsKeyState.ABSENT) 

344 with self.assertRaises(RuntimeError): 

345 fc.getFitsCN() 

346 with self.assertRaises(RuntimeError): 

347 fc.getFitsF() 

348 with self.assertRaises(RuntimeError): 

349 fc.getFitsI() 

350 with self.assertRaises(RuntimeError): 

351 fc.getFitsL() 

352 with self.assertRaises(RuntimeError): 

353 fc.getFitsS() 

354 with self.assertRaises(RuntimeError): 

355 fc.getFitsCF() 

356 self.assertEqual(fc.getCardName(), "") 

357 self.assertEqual(fc.getCardComm(), "") 

358 

359 def test_FitsChanGetFitsMissing(self): 

360 """Test FitsChan.getFits<X> for missing cards, with and without 

361 defaults 

362 """ 

363 fc = ast.FitsChan(ast.StringStream()) 

364 fc.setFitsI("ANINT", 200) 

365 fc.setFitsS("ASTRING", "string value") 

366 

367 self.assertEqual(fc.nCard, 2) 

368 

369 # test getFitsX for missing cards with default values 

370 fv = fc.getFitsCF("BOGUS", complex(9, -5)) 

371 self.assertFalse(fv.found) 

372 self.assertEqual(fv.value, complex(9, -5)) 

373 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

374 self.assertEqual(fc.getCard(), fc.nCard + 1) 

375 

376 fv = fc.getFitsCN("BOGUS", "not_there") 

377 self.assertFalse(fv.found) 

378 self.assertEqual(fv.value, "not_there") 

379 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

380 self.assertEqual(fc.getCard(), fc.nCard + 1) 

381 

382 fv = fc.getFitsF("BOGUS", 55.5) 

383 self.assertFalse(fv.found) 

384 self.assertEqual(fv.value, 55.5) 

385 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

386 self.assertEqual(fc.getCard(), fc.nCard + 1) 

387 

388 fv = fc.getFitsI("BOGUS", 55) 

389 self.assertFalse(fv.found) 

390 self.assertEqual(fv.value, 55) 

391 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

392 self.assertEqual(fc.getCard(), fc.nCard + 1) 

393 

394 fv = fc.getFitsL("BOGUS", True) 

395 self.assertFalse(fv.found) 

396 self.assertEqual(fv.value, True) 

397 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

398 self.assertEqual(fc.getCard(), fc.nCard + 1) 

399 

400 fv = fc.getFitsS("BOGUS", "missing") 

401 self.assertFalse(fv.found) 

402 self.assertEqual(fv.value, "missing") 

403 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

404 self.assertEqual(fc.getCard(), fc.nCard + 1) 

405 

406 # test getFitsX for missing cards without default values 

407 fv = fc.getFitsCF("BOGUS") 

408 self.assertFalse(fv.found) 

409 self.assertEqual(fv.value, complex()) 

410 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

411 self.assertEqual(fc.getCard(), fc.nCard + 1) 

412 

413 fv = fc.getFitsCN("BOGUS") 

414 self.assertFalse(fv.found) 

415 self.assertEqual(fv.value, "") 

416 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

417 self.assertEqual(fc.getCard(), fc.nCard + 1) 

418 

419 fv = fc.getFitsF("BOGUS") 

420 self.assertFalse(fv.found) 

421 self.assertEqual(fv.value, 0) 

422 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

423 self.assertEqual(fc.getCard(), fc.nCard + 1) 

424 

425 fv = fc.getFitsI("BOGUS") 

426 self.assertFalse(fv.found) 

427 self.assertEqual(fv.value, 0) 

428 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

429 self.assertEqual(fc.getCard(), fc.nCard + 1) 

430 

431 fv = fc.getFitsL("BOGUS") 

432 self.assertFalse(fv.found) 

433 self.assertEqual(fv.value, False) 

434 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

435 self.assertEqual(fc.getCard(), fc.nCard + 1) 

436 

437 fv = fc.getFitsS("BOGUS") 

438 self.assertFalse(fv.found) 

439 self.assertEqual(fv.value, "") 

440 self.assertEqual(fc.getCardType(), ast.CardType.NOTYPE) 

441 self.assertEqual(fc.getCard(), fc.nCard + 1) 

442 

443 fv = fc.findFits("BOGUS", inc=False) 

444 self.assertFalse(fv.found) 

445 self.assertEqual(fv.value, "") 

446 

447 def test_FitsChanEmptyFits(self): 

448 ss = ast.StringStream("".join(self.cards)) 

449 fc = ast.FitsChan(ss) 

450 self.assertEqual(fc.nCard, len(self.cards)) 

451 fc.emptyFits() 

452 self.assertEqual(fc.nCard, 0) 

453 

454 def test_FitsChanPutCardsPutFits(self): 

455 ss = ast.StringStream() 

456 fc = ast.FitsChan(ss) 

457 cards = "CRVAL1 = 0 " + \ 

458 "CRVAL2 = 0 " 

459 fc.putCards(cards) 

460 self.assertEqual(fc.getCard(), 1) 

461 fc.setCard(100) # past the end = end of cards 

462 self.assertEqual(fc.getCard(), 3) 

463 fc.clearCard() 

464 self.assertEqual(fc.getCard(), 1) 

465 self.assertEqual(fc.getAllCardNames(), ["CRVAL1", "CRVAL2"]) 

466 

467 # insert new cards at the beginning 

468 for card in self.cards[0:8]: 

469 fc.putFits(card, overwrite=False) 

470 self.assertEqual(fc.nCard, 10) 

471 self.assertEqual(fc.getCard(), 9) 

472 predCardNames = [c.split()[0] for c in self.cards[0:8]] + ["CRVAL1", "CRVAL2"] 

473 self.assertEqual(fc.getAllCardNames(), predCardNames) 

474 

475 def test_FitsChanFindFits(self): 

476 ss = ast.StringStream("".join(self.cards)) 

477 fc = ast.FitsChan(ss) 

478 expectedNCards = fc.nCard 

479 

480 # append a card with no value 

481 fc.setCard(fc.nCard + 1) 

482 fc.setFitsU("UNDEFVAL") 

483 expectedNCards += 1 

484 self.assertEqual(fc.nCard, expectedNCards) 

485 

486 fc.setCard(9) # index of CRVAL1 

487 self.assertEqual(fc.getCardName(), "CRVAL1") 

488 fv = fc.findFits("%f", inc=False) 

489 self.assertTrue(fv.found) 

490 self.assertEqual(fv.value, pad("CRVAL1 = 0")) 

491 

492 # delete CRVAL1 card 

493 fc.delFits() 

494 expectedNCards -= 1 

495 self.assertEqual(fc.nCard, expectedNCards) 

496 self.assertEqual(fc.getCard(), 9) 

497 fv = fc.findFits("%f", inc=False) 

498 self.assertEqual(fc.getCard(), 9) 

499 self.assertTrue(fv.found) 

500 self.assertEqual(fv.value, pad("CRVAL2 = 0")) 

501 

502 # insert CRVAL1 card before current card; verify that the index 

503 # is incremented to point to the next card 

504 fc.putFits("CRVAL1 = 99", overwrite=False) 

505 expectedNCards += 1 

506 self.assertEqual(fc.nCard, expectedNCards) 

507 self.assertEqual(fc.getCard(), 10) 

508 fv = fc.findFits("%f", inc=False) 

509 self.assertTrue(fv.found) 

510 self.assertEqual(fv.value, pad("CRVAL2 = 0")) 

511 

512 fc.setCard(9) # index of CRVAL1 

513 fv = fc.findFits("%f", inc=False) 

514 self.assertTrue(fv.found) 

515 self.assertEqual(fv.value, pad("CRVAL1 = 99")) 

516 

517 # overwrite CRVAL1 card 

518 fc.setCard(9) 

519 fc.putFits("CRVAL1 = 0", overwrite=True) 

520 self.assertEqual(fc.nCard, expectedNCards) 

521 fc.setCard(9) 

522 fv = fc.findFits("%f", inc=False) 

523 self.assertTrue(fv.found) 

524 self.assertEqual(fv.value, pad("CRVAL1 = 0")) 

525 

526 # test that findFits does not wrap around 

527 fv = fc.findFits("CTYPE2", inc=False) 

528 self.assertFalse(fv.found) 

529 fc.clearCard() 

530 fv = fc.findFits("CTYPE2", inc=False) 

531 self.assertTrue(fv.found) 

532 self.assertEqual(fv.value, pad("CTYPE2 = 'DEC-TAN '")) 

533 self.assertEqual(fc.getCard(), 4) 

534 

535 # test that we can find a card with undefined value 

536 fc.clearCard() 

537 fv = fc.findFits("UNDEFVAL", inc=False) 

538 self.assertTrue(fv.found) 

539 self.assertEqual(fv.value, 

540 "UNDEFVAL= ") 

541 

542 def test_FitsChanReadWrite(self): 

543 ss = ast.StringStream("".join(self.cards)) 

544 fc1 = ast.FitsChan(ss) 

545 obj1 = fc1.read() 

546 self.assertEqual(obj1.className, "FrameSet") 

547 

548 ss2 = ast.StringStream() 

549 fc2 = ast.FitsChan(ss2, "Encoding=FITS-WCS") 

550 n = fc2.write(obj1) 

551 self.assertEqual(n, 1) 

552 self.assertEqual(fc2.nCard, 10) 

553 fc2.clearCard() 

554 

555 fv = fc2.findFits("%f", inc=True) 

556 self.assertTrue(fv.found) 

557 self.assertEqual(fv.value, pad("WCSAXES = 2 / Number of WCS axes")) 

558 

559 fv = fc2.findFits("%f", inc=True) 

560 self.assertTrue(fv.found) 

561 self.assertEqual(fv.value, pad("CRPIX1 = 100.0 / Reference pixel on axis 1")) 

562 

563 fv = fc2.findFits("%f", inc=True) 

564 self.assertTrue(fv.found) 

565 self.assertEqual(fv.value, pad("CRPIX2 = 100.0 / Reference pixel on axis 2")) 

566 

567 fv = fc2.findFits("%f", inc=True) 

568 self.assertTrue(fv.found) 

569 self.assertEqual(fv.value, pad("CRVAL1 = 0.0 / Value at ref. pixel on axis 1")) 

570 

571 fv = fc2.findFits("%f", inc=True) 

572 self.assertTrue(fv.found) 

573 self.assertEqual(fv.value, pad("CRVAL2 = 0.0 / Value at ref. pixel on axis 2")) 

574 

575 fv = fc2.findFits("%f", inc=True) 

576 self.assertTrue(fv.found) 

577 self.assertEqual(fv.value, pad("CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1")) 

578 

579 fv = fc2.findFits("%f", inc=True) 

580 self.assertTrue(fv.found) 

581 self.assertEqual(fv.value, pad("CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2")) 

582 

583 fv = fc2.findFits("%f", inc=True) 

584 self.assertTrue(fv.found) 

585 self.assertEqual(fv.value, pad("CDELT1 = 0.001 / Pixel size on axis 1")) 

586 

587 fv = fc2.findFits("%f", inc=True) 

588 self.assertTrue(fv.found) 

589 self.assertEqual(fv.value, pad("CDELT2 = 0.001 / Pixel size on axis 2")) 

590 

591 fv = fc2.findFits("%f", inc=True) 

592 self.assertTrue(fv.found) 

593 self.assertEqual(fv.value, pad("RADESYS = 'ICRS ' / Reference frame for RA/DEC values")) 

594 

595 self.assertEqual(ss2.getSinkData(), "") 

596 self.assertEqual(fc2.nCard, 10) 

597 fc2.writeFits() 

598 self.assertEqual(fc2.nCard, 0) 

599 a = ss2.getSinkData() 

600 

601 ss3 = ast.StringStream(ss2.getSinkData()) 

602 fc3 = ast.FitsChan(ss3, "Encoding=FITS-WCS") 

603 fc3.readFits() 

604 obj3 = fc3.read() 

605 

606 ss4 = ast.StringStream() 

607 fc4 = ast.FitsChan(ss4, "Encoding=FITS-WCS") 

608 n = fc4.write(obj3) 

609 self.assertEqual(n, 1) 

610 del fc4 

611 b = ss4.getSinkData() 

612 self.assertEqual(a, b) 

613 

614 def test_FitsChanTestFits(self): 

615 fc = ast.FitsChan(ast.StringStream()) 

616 self.assertEqual(fc.className, "FitsChan") 

617 

618 # add a card for each type 

619 fc.setFitsF("AFLOAT", 1.5) 

620 fc.setFitsS("ASTRING", "a string") 

621 fc.setFitsU("UNDEFVAL") 

622 

623 self.assertEqual(fc.testFits("AFLOAT"), ast.FitsKeyState.PRESENT) 

624 self.assertEqual(fc.testFits("ASTRING"), ast.FitsKeyState.PRESENT) 

625 self.assertEqual(fc.testFits("UNDEFVAL"), ast.FitsKeyState.NOVALUE) 

626 self.assertEqual(fc.testFits("BADNAME"), ast.FitsKeyState.ABSENT) 

627 

628 fc.setCard(1) 

629 self.assertEqual(fc.getCardName(), "AFLOAT") 

630 self.assertEqual(fc.testFits(), ast.FitsKeyState.PRESENT) 

631 fc.setCard(3) 

632 self.assertEqual(fc.getCardName(), "UNDEFVAL") 

633 self.assertEqual(fc.testFits(), ast.FitsKeyState.NOVALUE) 

634 

635 def test_FitsChanInsertShift(self): 

636 """Check that a simple WCS can still be written as FITS-WCS 

637 after inserting a shift at the beginning of GRID to IWC 

638 

639 This tests LSST ticket DM-12524 

640 """ 

641 ss = ast.StringStream("".join(self.cards)) 

642 fc = ast.FitsChan(ss, "Encoding=FITS-WCS, IWC=1") 

643 frameSet = fc.read() 

644 self.assertIsInstance(frameSet, ast.FrameSet) 

645 self.assertAlmostEqual(fc.fitsTol, 0.1) 

646 

647 shift = 30 

648 shiftMap = ast.ShiftMap([shift, shift]) 

649 shiftedFrameSet = self.insertPixelMapping(shiftMap, frameSet) 

650 

651 fc2 = writeFitsWcs(frameSet) 

652 self.assertGreater(fc2.nCard, 9) 

653 for i in (1, 2): 

654 fv = fc2.getFitsF("CRPIX%d" % (i,)) 

655 self.assertAlmostEqual(fv.value, 100) 

656 

657 fc3 = writeFitsWcs(shiftedFrameSet) 

658 self.assertGreaterEqual(fc3.nCard, fc2.nCard) 

659 for i in (1, 2): 

660 fv = fc3.getFitsF("CRPIX%d" % (i,)) 

661 self.assertAlmostEqual(fv.value, 100 - shift) 

662 for name in fc2.getAllCardNames(): 

663 self.assertEqual(fc3.testFits(name), ast.FitsKeyState.PRESENT) 

664 

665 def test_FitsChanFitsTol(self): 

666 """Test that increasing FitsTol allows writing a WCS with distortion 

667 as FITS-WCS. 

668 """ 

669 ss = ast.StringStream("".join(self.cards)) 

670 fc = ast.FitsChan(ss, "Encoding=FITS-WCS, IWC=1") 

671 frameSet = fc.read() 

672 

673 distortion = ast.PcdMap(0.001, [0.0, 0.0]) 

674 distortedFrameSet = self.insertPixelMapping(distortion, frameSet) 

675 

676 # Writing as FTIS-WCS should fail with the default FitsTol 

677 fc = writeFitsWcs(distortedFrameSet) 

678 self.assertEqual(fc.nCard, 0) 

679 

680 # Writing as FITS-WCS should succeed with adequate FitsTol 

681 fc2 = writeFitsWcs(distortedFrameSet, "FitsTol=1000") 

682 self.assertGreater(fc2.nCard, 9) 

683 for i in (1, 2): 

684 fv = fc2.getFitsF("CRVAL%d" % (i,)) 

685 self.assertAlmostEqual(fv.value, 0) 

686 fv2 = fc2.getFitsS("CTYPE1") 

687 self.assertEqual(fv2.value, "RA---TAN") 

688 fv3 = fc2.getFitsS("CTYPE2") 

689 self.assertEqual(fv3.value, "DEC--TAN") 

690 

691 def test_FitsChanDM13686(self): 

692 """Test that a particular FrameSet will not segfault when 

693 we attempt to write it to a FitsChan as FITS-WCS 

694 """ 

695 def readObjectFromShow(path): 

696 """Read an ast object saved as Object.show()""" 

697 with open(path, "r") as f: 

698 objectText = f.read() 

699 stream = ast.StringStream(objectText) 

700 chan = ast.Channel(stream) 

701 return chan.read() 

702 

703 path = os.path.join(self.dataDir, "frameSetDM13686.txt") 

704 frameSet = readObjectFromShow(path) 

705 strStream = ast.StringStream() 

706 fitsChan = ast.FitsChan(strStream, "Encoding=FITS-WCS") 

707 # This FrameSet can be represtented as FITS-WCS, so 1 object is written 

708 self.assertEqual(fitsChan.write(frameSet), 1) 

709 

710 def test_FitsChanTAB(self): 

711 """Test that FITS -TAB WCS can be created. 

712 """ 

713 

714 wavelength = np.array([0., 0.5, 1.5, 3., 5.]) 

715 

716 # Create a FrameSet using a LutMap with non-linear coordinates 

717 pixelFrame = ast.Frame(1, "Domain=PIXELS") 

718 wavelengthFrame = ast.SpecFrame("System=wave, unit=nm") 

719 lutMap = ast.LutMap(wavelength, 1, 1) 

720 frameSet = ast.FrameDict(pixelFrame) 

721 frameSet.addFrame("PIXELS", lutMap, wavelengthFrame) 

722 

723 # Now serialize it using -TAB WCS 

724 fc = writeFitsWcs(frameSet, "TabOk=1") 

725 

726 fv = fc.getFitsS("CTYPE1") 

727 self.assertEqual(fv.value, "WAVE-TAB") 

728 

729 # PS1_0 is the table extension name 

730 fv = fc.getFitsS("PS1_0") 

731 waveext = fv.value 

732 self.assertEqual(waveext, "WCS-TAB") 

733 

734 # PS1_1 is the column name for the wavelength 

735 fv = fc.getFitsS("PS1_1") 

736 wavecol = fv.value 

737 self.assertEqual(wavecol, "COORDS1") 

738 

739 # Get the WCS table from the FitsChan 

740 km = fc.getTables() 

741 table = km.getA(waveext, 0) 

742 fc_bintab = table.getTableHeader() 

743 

744 fv = fc_bintab.getFitsS("TDIM1") 

745 self.assertEqual(fv.value, "(1,5)") 

746 

747 self.assertEqual(table.nRow, 1) 

748 self.assertEqual(table.nColumn, 1) 

749 

750 # 1-based column numbering to match FITS 

751 cname = table.columnName(1) 

752 self.assertEqual(cname, "COORDS1") 

753 self.assertEqual(table.columnType(cname), ast.DataType.DoubleType) 

754 self.assertEqual(table.columnSize(cname), 40) 

755 self.assertEqual(table.columnNdim(cname), 2) 

756 self.assertEqual(table.columnUnit(cname), "nm") 

757 self.assertEqual(table.columnLength(cname), 5) 

758 self.assertEqual(table.columnShape(cname), [1, 5]) 

759 coldata = table.getColumnData1D(cname) 

760 self.assertEqual(list(coldata), list(wavelength)) 

761 

762 # This will be shaped correctly as a numpy array with third dimension 

763 # the row count. 

764 coldata = table.getColumnData(cname) 

765 self.assertEqual(coldata.ndim, 3) 

766 self.assertEqual(coldata.shape, (1, 5, 1)) 

767 

768 def test_python(self): 

769 """Test Python Mapping/Sequence interface to FitsChan. 

770 """ 

771 ss = ast.StringStream("".join(self.cards)) 

772 fc = ast.FitsChan(ss) 

773 self.assertEqual(len(fc), 18) 

774 cards = "".join(c for c in fc) 

775 

776 self.assertEqual(cards, "".join(self.cards)) 

777 self.assertIn("CTYPE2", fc) 

778 self.assertIn(10, fc) 

779 self.assertNotIn(-1, fc) 

780 self.assertNotIn(20, fc) 

781 self.assertNotIn("CTYPE3", fc) 

782 

783 self.assertEqual(fc["CTYPE1"], "RA--TAN") 

784 self.assertEqual(fc["NAXIS2"], 200) 

785 self.assertEqual(fc["CDELT2"], 0.001) 

786 self.assertFalse(fc["BOOL"]) 

787 self.assertEqual(fc[4].rstrip(), "CRPIX1 = 100") 

788 with self.assertRaises(KeyError): 

789 fc["NOTIN"] 

790 with self.assertRaises(IndexError): 

791 fc[100] 

792 

793 # Update values 

794 fc["BOOL"] = True # This will remove second card 

795 self.assertEqual(fc["BOOL"], True) 

796 fc["CRVAL2"] = None 

797 self.assertIsNone(fc["CRVAL2"]) 

798 fc["NEWSTR"] = "Test" 

799 self.assertEqual(fc["NEWSTR"], "Test") 

800 fc["NEWINT"] = 1024 

801 self.assertEqual(fc["NEWINT"], 1024) 

802 fc["NEWFLT"] = 3.5 

803 self.assertEqual(fc["NEWFLT"], 3.5) 

804 fc["UNDEF"] = "not undef" 

805 self.assertEqual(fc["UNDEF"], "not undef") 

806 

807 fc[""] = "A new BLANK comment" 

808 fc[0] = "COMMENT Introduction comment" 

809 self.assertEqual(fc[0].rstrip(), "COMMENT Introduction comment") 

810 

811 # This will fail since a string is required 

812 with self.assertRaises(TypeError): 

813 fc[0] = 52 

814 

815 # Delete the 3rd card 

816 del fc[2] 

817 self.assertEqual(fc[2].rstrip(), "CTYPE2 = 'DEC-TAN '") 

818 self.assertEqual(len(fc), 20) 

819 

820 # Delete all the HISTORY cards 

821 del fc["HISTORY"] 

822 self.assertEqual(len(fc), 17) 

823 

824 # Change a card to blank 

825 fc[3] = None 

826 self.assertEqual(fc[3].strip(), "") 

827 fc[3] = "COMMENT 1" 

828 self.assertEqual(fc[3].strip(), "COMMENT 1") 

829 fc[3] = "" 

830 self.assertEqual(fc[3].strip(), "") 

831 

832 # Use negative index 

833 self.assertEqual(fc[-1], fc[fc.nCard-1]) 

834 fc[-2] = "COMMENT new comment" 

835 self.assertEqual(fc[-2], fc[fc.nCard-2]) 

836 self.assertEqual(fc[-2].rstrip(), "COMMENT new comment") 

837 

838 # Append a comment to the end 

839 nCards = len(fc) 

840 fc[fc.nCard] = "COMMENT X" 

841 self.assertEqual(len(fc), nCards + 1) 

842 self.assertEqual(fc[-1].rstrip(), "COMMENT X") 

843 

844 # Try to access and append using a high index 

845 with self.assertRaises(IndexError): 

846 fc[fc.nCard + 1] 

847 with self.assertRaises(IndexError): 

848 fc[fc.nCard + 1] = "" 

849 with self.assertRaises(IndexError): 

850 fc[fc.nCard] 

851 

852 # Or the wrong type of key 

853 with self.assertRaises(KeyError): 

854 fc[3.14] = 52 

855 

856 # Delete final card 

857 nCards = len(fc) 

858 del fc[-1] 

859 self.assertEqual(fc[-1].rstrip(), "NEWFLT = 3.5") 

860 self.assertEqual(len(fc), nCards - 1) 

861 

862 with self.assertRaises(IndexError): 

863 del fc[-fc.nCard - 1] 

864 

865 with self.assertRaises(IndexError): 

866 del fc[fc.nCard] 

867 

868 with self.assertRaises(KeyError): 

869 del fc[3.14] 

870 

871 with self.assertRaises(KeyError): 

872 del fc["NOTTHERE"] 

873 

874 # Test stringification 

875 header = str(fc) 

876 self.assertIn("BOOL = 1", header) 

877 

878 # All the items 

879 collected = [] 

880 for k, v in fc.items(): 

881 collected.append((k, v)) 

882 self.assertEqual(len(collected), len(fc)) 

883 

884 

885if __name__ == "__main__": 885 ↛ 886line 885 didn't jump to line 886, because the condition on line 885 was never true

886 unittest.main()