package org.cass; import java.io.*; import java.util.*; /** A utility class containing various functions for reading FITS files. @see Grid Grid @author Zachary Vaughan @version 1.1, 06/30/2006 */ public final class Fits { /** The block unit size of a FITS file in bytes. */ public static final int BLOCKSIZE = 2880; /** Extracts the next FITS image header from the given stream as a Map. The resulting Map consists only of valid key-value entries in the header - no comment entries or END tags are included.

All FITS files in the SMEI database conform to FITS standards and are two-dimensional, or in other words the initial header of a particular file will always have the key-value pairs of SIMPLE and NAXIS equal to T and 2 respectively.

@param in the given stream. @throws NullPointerException if in is null. @throws EOFException if an unexpected EOF is recognized before an entire header is completely read. @throws IOException if some other I/O error occurs. @return a Map containing the header key-value entries, or null if already at the end of stream. @see java.util.Map Map<K,V>*/ public static Map readHeader(InputStream in) throws IOException { Map header = new TreeMap(); byte[] block = new byte[BLOCKSIZE]; boolean done = false; String s, k, v; int size = 0, i, j; while(done == false) { for(i = 0; i < BLOCKSIZE; i += j) if((j = in.read(block, i, BLOCKSIZE - i)) == -1) if(i == 0 && size == 0) return null; else throw new EOFException(); size += BLOCKSIZE; for(i = 0; i < BLOCKSIZE; i += 80) { s = new String(block, i, 80, "US-ASCII"); if(s.startsWith("END ")) {done = true; break;} if(s.substring(8, 10).equals("= ") == false) continue; j = s.indexOf('/', 10); k = s.substring(0, 8).trim(); v = s.substring(10, j == -1 ? 80 : j).trim(); header.put(k, v); } } return header; } /** Extracts the next FITS image map as a matrix of floats from the given stream using the given header as a reference.

Although this method relies on the BITPIX entry of the given header in order to determine the overall image size and conversion strategy, the output will always be an array of floats for simplicity in converting images to binary GRID format via the Grid class utility functions. This may result in truncation of possible values when converting images with BITPIX values of 32, 64 and -64.

@param in the given input stream. @param header the given header of this image map. @throws NullPointerException if either in or header is null. @throws EOFException if an EOF is encountered before the complete image map is loaded. @throws IOException if another I/O error occurs. @return an array of floats containing the pixel values in width-by-height format. @see Grid Grid */ public static float[][] readMap(InputStream in, Map header) throws IOException { float[][] map = null; byte[] buf = null; int width, height, bitpix, bytepix, size; long bits; int i, j, x, y; width = Integer.parseInt(header.get("NAXIS1")); height = Integer.parseInt(header.get("NAXIS2")); bitpix = Integer.parseInt(header.get("BITPIX")); bytepix = Math.abs(bitpix) / 8; size = width * height * bytepix; buf = new byte[((size - 1) / BLOCKSIZE + 1) * BLOCKSIZE]; map = new float[width][height]; /* read the entire image section into the buffer */ for(i = 0; i < buf.length; i += j) if((j = in.read(buf, i, buf.length - i)) == -1) if(i < size) throw new EOFException(); else break; /* extract the bit information for each pixel in the map */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) { bits = 0; for(i = 0; i < bytepix; i++) { j = buf[bytepix * (x + y * width) + i]; bits = (bits << 8) | (j < 0 ? 256 - j : j); } switch(bitpix) { case 8: map[x][y] = (float) bits; break; case 16: map[x][y] = (float) (bits ^ 0x8000); break; case 32: map[x][y] = (float) ((int) bits); break; case 64: map[x][y] = (float) bits; break; case -32: map[x][y] = Float.intBitsToFloat((int) bits); break; case -64: map[x][y] = (float) Double.longBitsToDouble(bits); break; default: throw new IOException("Invalid BITPIX entry in header."); } } return map; } /** Skips over the next FITS image map in the given stream using the given header as a reference. Returns a boolean value indicating the possibility of more images following the skipped image map.

Keep in mind that a return value of true does NOT guarantee that more FITS images follow the skipped image map! However, subsequent calls to the readHeader() method should not throw any EOFExceptions and should instead exit gracefully with a return value of null if no more images are to be read.

@param in the given input stream. @param header the given header of this image map. @throws NullPointerException if in or header are null. @throws EOFException if an EOF is read before the entire image map is skipped. @throws IOException if some other I/O error occurs. @return true if it is possible that there are more images to be read from the given stream, or false if there are no more images. @see #readHeader(InputStream) readHeader(in) */ public static boolean skipMap(InputStream in, Map header) throws IOException { int width, height, bitpix, bytepix, size; byte[] buf = null; int i, j; width = Integer.parseInt(header.get("NAXIS1")); height = Integer.parseInt(header.get("NAXIS2")); bitpix = Integer.parseInt(header.get("BITPIX")); bytepix = Math.abs(bitpix) / 8; size = width * height * bytepix; buf = new byte[((size - 1) / BLOCKSIZE + 1) * BLOCKSIZE]; for(i = 0; i < buf.length; i += j) if((j = in.read(buf, i, buf.length - i)) == -1) if(i < size) return false; else throw new EOFException(); return true; } private Fits() {} }