//
// 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();
  }
}

 

Ads help maintain this website.