//
// FitsAdapter.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad.data.fits;
import nom.tam.fits.BasicHDU;
import nom.tam.fits.BinaryTableHDU;
import nom.tam.fits.Fits;
import nom.tam.fits.FitsException;
import nom.tam.fits.ImageHDU;
import nom.tam.fits.PrimaryHDU;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.Vector;
import visad.Data;
import visad.FlatField;
import visad.FunctionType;
import visad.Integer1DSet;
import visad.Integer2DSet;
import visad.IntegerNDSet;
import visad.RealTupleType;
import visad.RealType;
import visad.VisADException;
public class FitsAdapter
{
Fits fits;
Data data[];
ExceptionStack stack;
public FitsAdapter()
throws VisADException
{
fits = null;
data = null;
stack = null;
}
public FitsAdapter(String filename)
throws VisADException
{
this();
try {
fits = new Fits(filename);
} catch (FitsException e) {
throw new VisADException(e.getClass().getName() + "(" + e.getMessage() +
")");
}
}
public FitsAdapter(URL url)
throws VisADException
{
this();
try {
fits = new Fits(url);
} catch (FitsException e) {
throw new VisADException(e.getClass().getName() + "(" + e.getMessage() +
")");
}
}
private int get1DLength(Object data)
throws VisADException
{
if (!data.getClass().isArray()) {
return 1;
}
int len = Array.getLength(data);
int total = 0;
for (int i = 0; i < len; i++) {
total += get1DLength(Array.get(data, i));
}
return total;
}
private int copyArray(Object data, double[] list, int offset)
throws VisADException
{
if (data instanceof byte[]) {
byte[] bl = (byte[] )data;
for (int i = 0; i < bl.length; i++) {
int val = (bl[i] >= 0 ? bl[i] :
(((int )Byte.MAX_VALUE + 1) * 2 + (int )bl[i]));
list[offset++] = (double )val;
}
} else if (data instanceof short[]) {
short[] sl = (short[] )data;
for (int i = 0; i < sl.length; i++) {
int val = (sl[i] >= 0 ? sl[i] : ((Short.MAX_VALUE + 1) * 2) - sl[i]);
list[offset++] = (double )val;
}
} else if (data instanceof int[]) {
int[] il = (int[] )data;
for (int i = 0; i < il.length; i++) {
list[offset++] = (double )il[i];
}
} else if (data instanceof long[]) {
long[] ll = (long[] )data;
for (int i = 0; i < ll.length; i++) {
list[offset++] = (double )ll[i];
}
} else if (data instanceof float[]) {
float[] fl = (float[] )data;
for (int i = 0; i < fl.length; i++) {
list[offset++] = (double )fl[i];
}
} else if (data instanceof double[]) {
double[] dl = (double[] )data;
for (int i = 0; i < dl.length; i++) {
list[offset++] = dl[i];
}
} else {
throw new VisADException("type '" + data.getClass().getName() +
"' not handled");
}
return offset;
}
private int decompose(Object data, double[] list, int offset)
throws VisADException
{
Class component = data.getClass().getComponentType();
if (component == null) {
return offset;
}
if (!component.isArray()) {
return copyArray(data, list, offset);
}
int len = Array.getLength(data);
for (int i = len - 1; i >= 0; i--) {
offset = decompose(Array.get(data, i), list, offset);
}
return offset;
}
private double[][] buildRange(Object data)
throws VisADException
{
int len = get1DLength(data);
double[] values = new double[len];
int offset = decompose(data, values, 0);
while (offset < len) {
values[offset++] = Double.NaN;
}
double[][] range = new double[1][];
range[0] = values;
return range;
}
private Data addPrimary(PrimaryHDU hdu)
throws FitsException, VisADException, RemoteException
{
int[] axes = hdu.getAxes();
if (axes == null || axes.length == 0) {
return null;
}
// reverse order of axes
for (int i = 0; i < axes.length / 2; i++) {
int j = axes.length - (i + 1);
int tmp = axes[j];
axes[j] = axes[i];
axes[i] = tmp;
}
Object fData = hdu.getData().getData();
if (fData == null) {
throw new VisADException("No HDU Data");
}
if (!fData.getClass().isArray()) {
throw new VisADException("Unknown HDU Data type: " +
fData.getClass().getName());
}
RealType axisType[] = new RealType[axes.length];
for (int i = 0; i < axisType.length; i++) {
String name = "NAxis" + (i+1);
axisType[i] = RealType.getRealType(name, null, null);
}
RealTupleType type = new RealTupleType(axisType);;
RealType value = RealType.getRealType("value", null, null);
FunctionType func = new FunctionType(type, value);
IntegerNDSet iSet = new IntegerNDSet(type, axes);
FlatField fld = new FlatField(func, iSet);
fld.setSamples(buildRange(fData));
return fld;
}
private Data addImage(ImageHDU hdu)
throws VisADException, RemoteException
{
int[] axes;
try {
axes = hdu.getAxes();
} catch (FitsException e) {
axes = null;
}
if (axes == null) {
throw new VisADException("Couldn't get image axes");
}
if (axes.length != 2) {
throw new VisADException("Expected two-dimensional image, not " +
axes.length +" dimensions");
}
Object fData = hdu.getData().getData();
if (fData == null) {
throw new VisADException("No HDU Data");
}
if (!fData.getClass().isArray()) {
throw new VisADException("Unknown HDU Data type: " +
fData.getClass().getName());
}
RealTupleType type = RealTupleType.SpatialCartesian2DTuple;
RealType pixel = RealType.getRealType("pixel", null, null);
FunctionType func = new FunctionType(type, pixel);
Integer2DSet iSet = new Integer2DSet(type, axes[0], axes[1]);
FlatField fld = new FlatField(func, iSet);
fld.setSamples(buildRange(fData));
return fld;
}
private int copyColumn(Object data, double[] list, int offset)
throws VisADException
{
// punt if this isn't a 1D column
Object[] top = (Object[] )data;
if (top.length != 1 && !(top[0] instanceof byte[])) {
System.err.println("FitsAdapter.copyColumn: Punting on wide column (" +
top[0].getClass().getName() + ")");
return offset;
}
if (top[0] instanceof byte[]) {
if (top.length != 1) {
System.err.println("Ignoring assumed " + top.length +
"-char String column");
return offset;
} else {
byte[] bl = (byte[] )top[0];
for (int i = 0; i < bl.length; ) {
list[offset++] = (double )bl[i++];
}
}
} else if (top[0] instanceof short[]) {
short[] sl = (short[] )top[0];
for (int i = 0; i < sl.length; ) {
list[offset++] = (double )sl[i++];
}
} else if (top[0] instanceof int[]) {
int[] il = (int[] )top[0];
for (int i = 0; i < il.length; ) {
list[offset++] = (double )il[i++];
}
} else if (top[0] instanceof long[]) {
long[] ll = (long[] )top[0];
for (int i = 0; i < ll.length; ) {
list[offset++] = (double )ll[i++];
}
} else if (top[0] instanceof float[]) {
float[] fl = (float[] )top[0];
for (int i = 0; i < fl.length; ) {
list[offset++] = (double )fl[i++];
}
} else if (top[0] instanceof double[]) {
double[] dl = (double[] )top[0];
for (int i = 0; i < dl.length; ) {
list[offset++] = dl[i++];
}
} else {
throw new VisADException("type '" + top[0].getClass().getName() +
"' not handled");
}
return offset;
}
private double[][] buildBTRange(BinaryTableHDU hdu)
throws VisADException
{
int rows = hdu.getNumRows();
int cols = hdu.getNumColumns();
double[][] d = new double[cols][rows];
for (int i = 0; i < cols; i++) {
Object list;
try {
list = hdu.getColumn(i).getData();
} catch (FitsException e) {
throw new VisADException("Failed to get column " + i + " type: " +
e.getMessage());
}
int len;
if (list instanceof byte[][]) {
len = copyColumn((byte[][] )list, d[i], 0);
} else if (list instanceof short[][]) {
len = copyColumn((short[][] )list, d[i], 0);
} else if (list instanceof int[][]) {
len = copyColumn((int[][] )list, d[i], 0);
} else if (list instanceof long[][]) {
len = copyColumn((long[][] )list, d[i], 0);
} else if (list instanceof float[][]) {
len = copyColumn((float[][] )list, d[i], 0);
} else if (list instanceof double[][]) {
len = copyColumn((double[][] )list, d[i], 0);
} else {
String type;
try {
type = hdu.getColumnFITSType(i);
} catch (FitsException e) {
type = "?Unknown FITS type?";
}
System.err.println("FitsAdapter.buildBTRange: Faking values for" +
" column #" + i + " (" + type + "=>" +
list.getClass().getName() + ")");
// fill with NaN
int c = i;
for (len = 0 ; len < rows; len++) {
d[c][len] = Double.NaN;
}
}
if (len < rows) {
int c = i;
System.err.println("FitsAdapter.buildBTRange: Column " + i +
" was short " + (rows - len) + " of " + rows +
" rows");
while (len < rows) {
d[c][len++] = Double.NaN;
}
}
}
return d;
}
private Data addBinaryTable(BinaryTableHDU hdu)
throws FitsException, VisADException, RemoteException
{
int[] axes = hdu.getAxes();
if (axes == null) {
throw new FitsException("Couldn't get binary table axes");
}
if (axes.length != 2) {
throw new FitsException("Not a two-dimensional binary table");
}
int numColumns = hdu.getNumColumns();
RealType index = RealType.getRealType("index", null, null);
boolean hasTextColumn = false;
RealType rowType[] = new RealType[numColumns];
for (int i = 0; i < numColumns; i++) {
String name = hdu.getColumnName(i);
if (name == null) {
name = "Column" + i;
}
String colType = hdu.getColumnFITSType(i);
if (colType.startsWith("A") || colType.endsWith("A")) {
hasTextColumn = true;
}
rowType[i] = RealType.getRealType(name, null, null);
}
RealTupleType row = new RealTupleType(rowType);;
FunctionType func = new FunctionType(index, row);
Integer1DSet iSet = new Integer1DSet(hdu.getNumRows());
FlatField fld = new FlatField(func, iSet);
fld.setSamples(buildBTRange(hdu));
return fld;
}
private Data convertHDU(BasicHDU hdu)
throws FitsException, VisADException, RemoteException
{
if (hdu instanceof ImageHDU) {
return addImage((ImageHDU )hdu);
}
if (hdu instanceof PrimaryHDU) {
return addPrimary((PrimaryHDU )hdu);
}
if (hdu instanceof BinaryTableHDU) {
return addBinaryTable((BinaryTableHDU )hdu);
}
return null;
}
public void buildData()
{
Vector vec = new Vector();
int startDepth;
if (stack == null) {
startDepth = 0;
} else {
startDepth = stack.depth();
}
for (int n = 0; true; n++) {
try {
BasicHDU hdu = fits.getHDU(n);
if (hdu == null) {
break;
}
Data d = convertHDU(hdu);
if (d != null) {
vec.addElement(d);
}
} catch (Exception e) {
if (stack == null) {
stack = new ExceptionStack(e);
} else {
stack.addException(e);
if (stack.depth() > startDepth + 10) {
break;
}
}
}
}
if (vec.size() == 0) {
data = null;
} else {
data = new Data[vec.size()];
for (int i = 0; i < data.length; i++) {
data[i] = (Data )vec.elementAt(i);
}
}
}
public void clearExceptionStack()
{
stack = null;
}
Data[] getData()
throws ExceptionStack, RemoteException, VisADException
{
if (data == null) {
buildData();
if (data == null) {
throw new VisADException("No data");
}
}
if (stack != null) {
throw stack;
}
return data;
}
public void save(String name, Data data, boolean replace)
throws IOException, RemoteException, VisADException
{
File file = new File(name);
if (file.exists()) {
throw new IllegalArgumentException("File \"" + name + "\" exists");
}
FitsTourGuide guide;
// make sure this object can be saved as a FITS file
TourInspector count = new TourInspector(replace);
guide = new FitsTourGuide(data, count);
count = null;
// build the new FITS file
Fits f = new Fits();
TourWriter tw = new TourWriter(replace, f);
guide = new FitsTourGuide(data, tw);
tw = null;
guide = null;
// open the final destination
BufferedOutputStream bos;
bos = new BufferedOutputStream(new FileOutputStream(name));
// write the FITS file
try {
f.write(bos);
} catch (FitsException e) {
throw new VisADException(e.getClass().getName() + "(" +
e.getMessage() + ")");
}
bos.close();
}
}