[{"data":1,"prerenderedAt":26},["ShallowReactive",2],{"analysis-playbook-general-fits-format":3},{"@context":4,"@type":5,"name":6,"description":7,"proficiencyLevel":8,"dependencies":9,"programmingLanguage":14,"keywords":15,"articleBody":25},"https:\u002F\u002Fschema.org","TechArticle","Working with FITS Files","Format-focused guide for inspecting and visualizing an unknown FITS file. Covers listing HDU structure with astropy, distinguishing Image HDUs (including all-sky projections such as Aitoff-Hammer and Mollweide) from HEALPix BinTable HDUs and general event\u002Fcatalog BinTables, and plotting each case with matplotlib and healpy.","beginner",[10,11,12,13],"astropy","healpy","matplotlib","numpy","Python",[16,17,18,19,20,21,22,23,24,10,11],"FITS","HDU","Image HDU","BinTable","HEALPix","WCS","all-sky map","Aitoff-Hammer","Mollweide","## Working with FITS Files\n\nFITS (Flexible Image Transport System) is the standard file format in astronomy. A FITS file is made up of one or more **Header\u002FData Units (HDUs)**, each consisting of a plain-text header (keyword–value pairs) and an optional data block. The data block can be an N-dimensional array (Image HDU) or a structured table (BinTable or ASCII Table HDU). This playbook shows how to inspect an unfamiliar FITS file, identify what type of data it contains, and visualize it.\n\n---\n\n### Requirements\n\n```\npip install astropy matplotlib numpy healpy\n```\n\n---\n\n### Step 1: Inspect the HDU structure\n\nStart by listing all HDUs without reading the full data into memory.\n\n```python\nfrom astropy.io import fits\n\nurl = \"https:\u002F\u002F...\"  # local path or remote URL\n\nfits.info(url)\n```\n\n`fits.info` prints a table of HDU index, name, type, dimensions, and format. This alone usually tells you whether the file contains images, tables, or both.\n\nFor a closer look at each HDU—especially to check WCS keywords or column names—open the file and iterate:\n\n```python\nfrom astropy.io import fits\n\nwith fits.open(url) as hdul:\n    for i, hdu in enumerate(hdul):\n        hdr = hdu.header\n        print(f\"\\n--- HDU {i}: {hdu.name} ({type(hdu).__name__}) ---\")\n        print(f\"  NAXIS  = {hdr.get('NAXIS')}\")\n        for kw in (\"NAXIS1\", \"NAXIS2\", \"NAXIS3\",\n                   \"CTYPE1\", \"CTYPE2\", \"CUNIT1\", \"CUNIT2\",\n                   \"BUNIT\", \"PIXTYPE\", \"ORDERING\", \"NSIDE\", \"COORDSYS\"):\n            if kw in hdr:\n                print(f\"  {kw:10s} = {hdr[kw]!r}\")\n        if hasattr(hdu, \"columns\"):\n            print(f\"  columns = {hdu.columns.names}\")\n```\n\nAfter running this, you will typically fall into one of the three cases below.\n\n---\n\n### Case A: Image HDU\n\nAn Image HDU has `NAXIS >= 2` and no column definitions. The data is a NumPy array accessible via `hdu.data`.\n\n```python\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom astropy.io import fits\nfrom astropy.wcs import WCS\n\nwith fits.open(url) as hdul:\n    hdu = hdul[0]          # adjust index as needed\n    data = hdu.data\n    wcs  = WCS(hdu.header)\n\nprint(data.shape, data.dtype)\n\n# Replace non-positive values with NaN for log scaling\nimg = np.where(data > 0, data, np.nan)\n\nfig, ax = plt.subplots(figsize=(8, 6))\nim = ax.imshow(np.log10(img), origin=\"lower\", cmap=\"afmhot\", aspect=\"auto\")\nfig.colorbar(im, ax=ax, label=\"log₁₀ (value)\")\nplt.tight_layout()\nplt.close()\n```\n\n**All-sky projection images** are a common sub-case. When `CTYPE1` \u002F `CTYPE2` end in `-AIT` (Aitoff-Hammer) or `-MOL` (Mollweide), the 2D image is a full-sky map in that projection, stored in Galactic or Equatorial coordinates as indicated by `CTYPE`. The pixel layout directly matches the projection; no additional reprojection is needed to display it with `imshow`. See the [MAXI All-Sky Map Visualization playbook](https:\u002F\u002Fdarts.isas.jaxa.jp\u002Fanalysis-playbooks\u002Fmaxi-allskymap-products) for a concrete example with this structure.\n\n---\n\n### Case B: HEALPix BinTable HDU\n\nHEALPix stores full-sky data as a flat table: one row per sky pixel, ordered by the HEALPix pixel index. Recognition cues in the header:\n\n| Keyword     | Typical value        | Meaning                              |\n|-------------|----------------------|--------------------------------------|\n| `PIXTYPE`   | `'HEALPIX'`          | Confirms this is a HEALPix map       |\n| `ORDERING`  | `'RING'` or `'NESTED'` | Pixel ordering scheme              |\n| `NSIDE`     | 64, 128, 512, …      | Resolution parameter (Npix = 12·NSIDE²) |\n| `COORDSYS`  | `'G'`, `'E'`, `'C'` | Galactic, Ecliptic, or Equatorial    |\n\n#### Reading the table and plotting with healpy\n\n```python\nimport numpy as np\nimport healpy as hp\nfrom astropy.io import fits\nfrom astropy.table import Table\n\nwith fits.open(url) as hdul:\n    # Find the HEALPix extension (often HDU 1)\n    for i, hdu in enumerate(hdul):\n        if hdu.header.get(\"PIXTYPE\") == \"HEALPIX\":\n            hdr   = hdu.header\n            table = Table(hdu.data)\n            break\n\nnside    = hdr[\"NSIDE\"]\nordering = hdr.get(\"ORDERING\", \"RING\").upper()\nnest     = (ordering == \"NESTED\")\n\nprint(\"Columns:\", table.colnames)\n\n# Pick the intensity column — adapt the name to your file\ncol   = table.colnames[0]\nm     = table[col].data.astype(float)\nm[m \u003C= 0] = hp.UNSEEN    # healpy ignores UNSEEN pixels\n\nhp.mollview(m, nest=nest, title=col, unit=hdr.get(\"TUNIT1\", \"\"),\n            coord=[\"G\"] if hdr.get(\"COORDSYS\") == \"G\" else None)\nhp.graticule()\nimport matplotlib.pyplot as plt\nplt.close()\n```\n\n#### Shortcut with `healpy.read_map`\n\n`healpy.read_map` can read a standard HEALPix FITS table in one call. It returns the map array and automatically interprets the `ORDERING` keyword.\n\n```python\nimport healpy as hp\n\nm, hdr = hp.read_map(url, h=True, dtype=float)\nhdr = dict(hdr)\n\nhp.mollview(m, title=\"HEALPix map\", unit=hdr.get(\"TUNIT1\", \"\"))\nhp.graticule()\nimport matplotlib.pyplot as plt\nplt.close()\n```\n\n> **Note:** `healpy.read_map` reads the first column by default. Pass `field=N` to read column N.\n\n---\n\n### Case C: BinTable HDU (events or catalogs)\n\nA BinTable that is *not* a HEALPix map typically holds event lists (one row = one photon or particle) or source catalogs. Loading is straightforward:\n\n```python\nfrom astropy.io import fits\nfrom astropy.table import Table\n\nwith fits.open(url) as hdul:\n    table = Table(hdul[\"EVENTS\"].data)   # or use the HDU index\n\nprint(table.colnames)\nprint(table[:5])\n```\n\nFor working examples of event-list FITS files, see the [MAXI All-Sky Map from Event Data playbook](https:\u002F\u002Fdarts.isas.jaxa.jp\u002Fanalysis-playbooks\u002Fmaxi-allskymap-from-events) and the [Hinode\u002FEIS Level-0 playbook](https:\u002F\u002Fdarts.isas.jaxa.jp\u002Fanalysis-playbooks\u002Fhinode-eis-level0).\n\n---\n\n### References\n\n- [FITS Standard (NASA\u002FGSFC)](https:\u002F\u002Ffits.gsfc.nasa.gov\u002Ffits_standard.html)\n- [astropy.io.fits documentation](https:\u002F\u002Fdocs.astropy.org\u002Fen\u002Fstable\u002Fio\u002Ffits\u002F)\n- [astropy.wcs documentation](https:\u002F\u002Fdocs.astropy.org\u002Fen\u002Fstable\u002Fwcs\u002F)\n- [HEALPix primer](https:\u002F\u002Fhealpix.sourceforge.io\u002Fdocumentation.php)\n- [healpy documentation](https:\u002F\u002Fhealpy.readthedocs.io\u002F)\n",1780296758076]