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() {}
}