001    /* @(#)FitsData.java     $Revision: 1.7 $ $Date: 2003/04/11 08:41:15 $
002     *
003     * Copyright (C) 2002 European Southern Observatory 
004     * License:  GNU General Public License version 2 or later
005     */
006    package org.eso.fits;
007    
008    import java.io.*;
009    
010    /** FitsData class represents a FITS data unit
011     *
012     *  @version $Revision: 1.7 $ $Date: 2003/04/11 08:41:15 $
013     *  @author  P.Grosbol, DMD/ESO, <pgrosbol@eso.org>
014     */
015    public class FitsData {
016    
017        protected int type;
018        protected int[] naxis;
019        protected long size;
020        protected int bitpix = 0;
021        protected int noParm = 0;
022        protected int noGroup = 1;
023        protected boolean changeData = false;
024    
025        protected RandomAccessFile dataFile = null;
026        protected long dataOffset = 0;
027        protected byte[] dataArray = null;
028        protected boolean isRAFile = false;
029    
030        /** Constructor for FitsData class given a FITS header with 
031         *  associated data unit as a file.
032         *
033         *  @param header  FitsHeader object with the image header
034         *  @param file    RandomAccess file positioned at the start of the
035         *                 associated data unit
036         *  @param sflag   Flag for storing data matrix internally
037         *  @exception FitsException
038         */
039        public FitsData(FitsHeader header, DataInput file, boolean sflag) throws FitsException {
040    
041            if (file instanceof RandomAccessFile) {
042                dataFile = (RandomAccessFile) file;
043                try {
044                    dataOffset = dataFile.getFilePointer();
045                } catch (IOException e) {
046                    throw new FitsException("Cannot read data offset", FitsException.FILE);
047                }
048                isRAFile = true;
049            }
050    
051            size = header.getDataSize();
052            type = header.getType();
053            long skip = size;
054            if (size%Fits.RECORD != 0) {
055                // John Talbot : Fails on SDSS spectra - data size is not an integer multiple of 2880
056                skip = (size/Fits.RECORD+1)*Fits.RECORD;
057            }
058    
059            try {
060                if (sflag && !isRAFile) {
061                    dataArray = new byte[(int) skip];
062                    file.readFully(dataArray);
063                } else {
064                    file.skipBytes((int) skip);
065                }
066            } catch (IOException e) {
067                throw new FitsException("Cannot read/skip over data matrix", FitsException.FILE);
068            }
069    
070            decodeBasicHeader(header);
071        }
072    
073        /** Constructor for FitsData class given the size of the data matrix.
074         *  An array equal to the size of the data matrix will be allocated.
075         *
076         *  @param bitpix  value of FITS BITPIX keyword bits/pixel
077         *  @param nax     Integer array defining the dimensions of the
078         *                 data matrix or for BINTABLE the heap size
079         *  @exception FitsException
080         */
081        public FitsData(int bitpix, int nax[]) 
082            throws FitsException {
083            switch (bitpix) {
084                case Fits.BYTE :
085                case Fits.SHORT :
086                case Fits.INT :
087                case Fits.FLOAT :
088                case Fits.DOUBLE : this.bitpix = bitpix; break;
089                default: throw new FitsException("Invalid BITPIX value",
090                                                 FitsException.DATA);
091            }
092            type = Fits.UNKNOWN;
093            size = 1;
094            naxis = new int[nax.length];
095            for (int n=0; n<nax.length; n++) {
096                naxis[n] = nax[n];
097                size *= naxis[n];
098            }
099            size *= Math.abs(this.bitpix)/8;
100            if (size < 0) throw new FitsException("Data size less than zero",
101                                                  FitsException.DATA);
102    
103            long  skip = size;
104            if (size%Fits.RECORD != 0) {
105                skip = (size/Fits.RECORD+1)*Fits.RECORD;
106            }
107            
108            dataArray = new byte[(int) skip];
109        }
110    
111        /** Decodes basic header information for data matrix.
112         *
113         *  @param  header  FITS header
114         */
115        private void decodeBasicHeader(FitsHeader header) throws FitsException {
116    
117            FitsKeyword kw = header.getKeyword("NAXIS");
118            if (kw == null) {
119                throw new FitsException("Missing NAXIS keyword", FitsException.HEADER);
120            }
121            int nax = kw.getInt();
122            naxis = new int[nax];
123            kw = header.getKeyword("BITPIX");
124            if (kw == null) {
125                throw new FitsException("Missing BITPIX keyword", FitsException.HEADER);
126            }
127            bitpix = kw.getInt();
128    
129            for (int n=1; n<=nax; n++) {
130                kw = header.getKeyword("NAXIS"+n);
131                if (kw == null) {
132                    throw new FitsException("Missing NAXIS" + n + " keyword", FitsException.HEADER);
133                }
134                naxis[n-1] = kw.getInt();
135            }
136    
137            kw = header.getKeyword("GCOUNT");
138            noGroup = (kw == null) ? 1 : kw.getInt();
139            kw = header.getKeyword("PCOUNT");
140            noParm = (kw == null) ? 0 : kw.getInt();
141            changeData = false;
142        }
143    
144        /** Create and return a minimum FITS header for data Matrix.
145         */
146        public FitsHeader getHeader() {
147            FitsHeader hdr = new FitsHeader();
148    
149            hdr.addKeyword(new FitsKeyword("SIMPLE", true, "Standard FITS format; NOST 100-2.0"));
150            hdr.addKeyword(new FitsKeyword("BITPIX", bitpix, "No. of bits per pixel"));
151            hdr.addKeyword(new FitsKeyword("NAXIS", naxis.length, "No. of axes in image"));
152            for (int n=1; n<=naxis.length; n++) {
153                hdr.addKeyword(new FitsKeyword("NAXIS"+n, naxis[n-1], "No. of pixels"));
154            }
155            hdr.addKeyword(new FitsKeyword("PCOUNT", 0, "Parameter count"));
156            hdr.addKeyword(new FitsKeyword("GCOUNT", 1, "Group count"));
157    
158            return hdr;
159        }
160    
161        /** Write data martix to DataOutput stream.
162         *
163         *  @param  file  DataOutput stream to which data are written
164         *  @exception  IOException, FitsException  */
165        public void writeFile(DataOutput file) throws IOException, FitsException {
166            byte[] buf;
167            int nbytes = 0;
168            int block = 10*Fits.RECORD;
169    
170            if (isRAFile) {
171                buf = new byte[block];
172                dataFile.seek(dataOffset);
173                int  nrec = (int) size/block;    // write large block first
174                while (0<nrec--) {
175                    dataFile.read(buf);
176                    file.write(buf);
177                }
178    
179                nbytes = (int) size%block;      // and then the remaining blocks
180                if (nbytes == 0) return;
181    
182                buf = new byte[nbytes];
183                dataFile.read(buf);
184                file.write(buf);
185            } else if (dataArray != null) {
186                file.write(dataArray);
187            }
188    
189            if (nbytes%Fits.RECORD != 0) {      // finally fill with zeros
190                nbytes = Fits.RECORD*(nbytes/Fits.RECORD + 1) - nbytes;
191                buf = new byte[nbytes];
192                if (type == Fits.ATABLE) {     // or if ASCII table the space fill
193                    for (int n=0; n<nbytes; n++) buf[n] = 0x20;
194                }
195                file.write(buf);
196            }
197        }
198    
199        /** Closes the associated data file */
200        public void closeFile(){
201            dataFile = null;
202            dataOffset = 0;
203            size = 0;
204            type = 0;
205        }
206    
207        /** Retrives number of axes defined for the data unit (ref. NAXIS) */
208        public int getNoAxes(){
209            return naxis.length;
210        }
211    
212        /** Gets FITS type of data unit. This is specified in the
213         *  assocated header such as Fits.IMAGE or Fits.BTABLE */
214        public int getType(){
215            return type;
216        }
217    
218        /** Gets the dimentions of the axes.  This is defined for the data
219         *  unit by the NAXISn keywords. */
220        public int[] getNaxis(){
221            return naxis;
222        }
223    }
224    
225