001    /* @(#)FitsMatrix.java     $Revision: 1.9 $ $Date: 2003/04/11 08:41:15 $
002     *
003     * Copyright (C) 2000 European Southern Observatory 
004     * License:  GNU General Public License version 2 or later
005     */
006    package org.eso.fits;
007    
008    import java.lang.*;
009    import java.io.*;
010    
011    /** FitsMatrix class represents a FITS data matrix either as a prime
012     *  HD unit or as an image extension.
013     *
014     *  @version $Revision: 1.9 $ $Date: 2003/04/11 08:41:15 $
015     *  @author  P.Grosbol, DMD/ESO, <pgrosbol@eso.org>
016     */
017    public class FitsMatrix extends FitsData {
018    
019        private int noValues;
020        private int dataFormat = Fits.FLOAT;
021        private int bytesPerData = 4;
022        private boolean scaling = false;
023        private double zero = 0.0;
024        private double scale = 1.0;
025        private FitsWCS wcs;
026    
027        /** Constructor for FitsMatrix class given a FITS prime matrix or
028         *  an image extension header with associated data unit as a file.
029         *
030         *  @param header  FitsHeader object with the image header
031         *  @param file    RandomAccess file positioned at the start of the
032         *                 associated data unit
033         *  @param sflag   Flag for storing data matrix internally
034         *  @exception FitsException */
035        public FitsMatrix(FitsHeader header, DataInput file, boolean sflag) throws FitsException {
036            super(header, file, sflag);
037    
038            if (type != Fits.IMAGE) {
039                throw  new FitsException("Wrong header type", FitsException.HEADER);
040            }
041    
042            FitsKeyword kw = header.getKeyword("BITPIX");
043            if ((kw == null) || (kw.getType() != FitsKeyword.INTEGER)) {
044                throw  new FitsException("Invalid or missing BITPIX", FitsException.HEADER);
045            }
046     
047            dataFormat = kw.getInt();
048            bytesPerData = Math.abs(dataFormat)/8;
049            noValues = (int) (size / bytesPerData);
050        
051            wcs = new FitsWCS(header);
052    
053            kw = header.getKeyword("BSCALE");     // check if scale is given
054            if (kw!=null) {
055                scale = kw.getReal();
056                if (scale != 1.0) {
057                    scaling = true;
058                }
059            }
060    
061            kw = header.getKeyword("BZERO");      // check if zero point is given
062            if (kw!=null) {
063                zero = kw.getReal();
064                if (zero != 0.0) {
065                    scaling = true;
066                }
067            }
068        }
069    
070        /** Constructor for FitsMatrix class given definition of the matrix
071         *  size and the associated 1D data array.
072         *
073         *  @param bitpix  value of FITS BITPIX keyword bits/pixel
074         *  @param nax     Integer array defining the dimensions of the
075         *                 data matrix or for BINTABLE the heap size
076         *  @exception FitsException */
077        public FitsMatrix(int bitpix, int nax[]) throws FitsException {
078            super(bitpix, nax);
079            type = Fits.IMAGE;
080            dataFormat = bitpix;
081            bytesPerData = Math.abs(dataFormat)/8;
082            noValues = (int) (size / bytesPerData);
083            wcs = new FitsWCS(nax.length);
084        }
085    
086        /** Create and return a minimum FITS header for data Matrix.
087         */
088        public FitsHeader getHeader() {
089            FitsHeader hdr = super.getHeader();
090    
091            hdr.addKeyword(new FitsKeyword("", ""));
092            for (int n=1; n<=naxis.length; n++) {
093                hdr.addKeyword(new FitsKeyword("CRPIX"+n, wcs.crpix[n-1], "Reference pixel"));
094                hdr.addKeyword(new FitsKeyword("CRVAL"+n, wcs.crval[n-1], "Coordinate at reference pixel"));
095                hdr.addKeyword(new FitsKeyword("CDELT"+n, wcs.cdelt[n-1], "Coordinate increament per pixel"));
096            }
097    
098            return hdr;
099        }
100    
101        /** Gets set of data points from the matrix as a short values.
102         *  Only FITS file with BITPIX 8, 16 and 32 are read.
103         *
104         *  @param  offset   pixel offset within hte data matrix
105         *  @param  size     no. of pixel values to be read
106         *  @param  data     array which will hold the return values.
107         *                   If null an array of size is created.
108         *  @return          data[] array updated with pixel values
109         *  @exception FitsException */
110        public short[] getShortValues(int offset, int size, short data[]) throws FitsException {
111    
112            if ((offset<0) || (size<1)) return data;
113            if (noValues < offset+size) size = (int) (noValues - offset);
114            if ((data == null) || (data.length<size)) data = new short[size];
115    
116            int n = 0;
117            DataInputStream dis = getInStream(offset, size);
118            try {
119                switch (dataFormat) {
120                case Fits.BYTE:
121                    while (n<size) data[n++] = (short) dis.readUnsignedByte();
122                    break;
123                case Fits.SHORT:
124                    while (n<size) data[n++] = dis.readShort();
125                    break;
126                case Fits.INT:
127                    while (n<size) data[n++] = (short) dis.readInt();
128                    break;
129                case Fits.FLOAT:
130                case Fits.DOUBLE:
131                default: return data;
132                } 
133            } catch (IOException e) {
134                throw new FitsException("Cannot convert data", FitsException.DATA);
135            }
136    
137            if (scaling) {
138              for (int i=0; i<n; i++) data[i] = (short) (scale*data[i]+zero);
139            }
140    
141            return data;
142        }
143    
144        /** Gets set of data points from the matrix as a int values.
145         *  Only FITS file with BITPIX 8, 16 and 32 are read.
146         *
147         *  @param  offset   pixel offset within hte data matrix
148         *  @param  size     no. of pixel values to be read
149         *  @param  data     array which will hold the return values.
150         *                   If null an array of size is created.
151         *  @return          data[] array updated with pixel values
152         *  @exception FitsException */
153        public int[] getIntValues(int offset, int size, int data[]) throws FitsException {
154    
155            if ((offset<0) || (size<1)) return data;
156            if (noValues < offset+size) size = (int) (noValues - offset);
157            if ((data == null) || (data.length<size)) data = new int[size];
158    
159            int n = 0;
160            DataInputStream dis = getInStream(offset, size);
161            try {
162                switch (dataFormat) {
163                case Fits.BYTE:
164                    while (n<size) data[n++] = (int) dis.readUnsignedByte();
165                    break;
166                case Fits.SHORT:
167                    while (n<size) data[n++] = (int) dis.readShort();
168                    break;
169                case Fits.INT:
170                    while (n<size) data[n++] = dis.readInt();
171                    break;
172                case Fits.FLOAT:
173                case Fits.DOUBLE:
174                default: return data;
175                } 
176            } catch (IOException e) {
177                throw new FitsException("Cannot read data", FitsException.DATA);
178            }
179    
180            if (scaling) {
181              for (int i=0; i<n; i++) data[i] = (int) (scale*data[i]+zero);
182            }
183    
184            return data;
185        }
186    
187        /** Read set of data values from the matrix as a float array.  The
188         *  values are returned as a float array.
189         *
190         *  @param  offset   pixel offset within hte data matrix
191         *  @param  size     no. of pixel values to be read
192         *  @param  data     array which will hold the return values.
193         *                   If null an array of size is created.
194         *  @return          data[] array updated with pixel values
195         *  @exception FitsException */
196        public float[] getFloatValues(int offset, int size, float data[])
197                throws FitsException {
198    
199            if ((offset<0) || (size<1)) return data;
200            if (noValues < offset+size) size = (int) (noValues - offset);
201            if ((data == null) || (data.length<size)) data = new float[size];
202    
203            int n = 0;
204            DataInputStream dis = getInStream(offset, size);
205            try {
206                switch (dataFormat) {
207                case Fits.BYTE:
208                    while (n<size) data[n++] = (float) dis.readUnsignedByte();
209                    break;
210                case Fits.SHORT:
211                    while (n<size) data[n++] = (float) dis.readShort();
212                    break;
213                case Fits.INT:
214                    while (n<size) data[n++] = (float) dis.readInt();
215                    break;
216                case Fits.FLOAT:
217                    while (n<size) data[n++] = dis.readFloat();
218                    break;
219                case Fits.DOUBLE:
220                    while (n<size) data[n++] = (float) dis.readDouble();
221                    break;
222                default: return data;
223                } 
224            } catch (IOException e) {
225                throw new FitsException("Cannot read data", FitsException.DATA);
226            }
227    
228            if (scaling) {
229              for (int i=0; i<n; i++) data[i] = (float) (scale*data[i]+zero);
230            }
231    
232            return data;
233        }
234    
235        private DataInputStream getInStream(int offset, int size)
236                throws FitsException {
237            DataInputStream dis;
238            try {
239                dataFile.seek(dataOffset+offset*bytesPerData);
240                if (noValues < offset + size) {
241                    size = (int) (noValues - offset);
242                }
243                byte[] dbuf = new byte[size*bytesPerData];
244                dataFile.read(dbuf);
245                dis = new DataInputStream(new ByteArrayInputStream(dbuf));
246            } catch (IOException e) {
247                throw new FitsException("Cannot read InStream data",
248                                        FitsException.DATA);
249            }
250            return dis;
251        }
252    
253        /** Store set of data values from a short array into the data matrix.
254         *
255         *  @param  offset   pixel offset within the data matrix
256         *  @param  sdata    array with values.
257         *  @exception FitsException */
258        public void setShortValues(int offset, short sdata[])
259                throws FitsException {
260    
261            if ((offset<0) || (noValues<=offset))
262                throw new FitsException("Invalid pixel offset",
263                                        FitsException.DATA);
264            if (sdata == null) throw new FitsException("Invalid data array",
265                                                      FitsException.DATA);
266    
267            ByteArrayOutputStream baos = new ByteArrayOutputStream(sdata.length * 
268                                                                   bytesPerData);
269            DataOutputStream dos = new DataOutputStream(baos);
270    
271            if (scaling) {
272                throw new FitsException("Scaling of short not supported",
273                                        FitsException.DATA);
274            }
275    
276            int n = 0;
277            try {
278                switch (dataFormat) {
279                case Fits.BYTE:
280                    throw new FitsException("Cannot convert data to BYTE",
281                                            FitsException.DATA);
282                case Fits.SHORT:
283                    while (n<sdata.length) dos.writeShort((short) sdata[n++]);
284                    break;
285                case Fits.INT:
286                    while (n<sdata.length) dos.writeInt((int) sdata[n++]);
287                    break;
288                case Fits.FLOAT:
289                    while (n<sdata.length) dos.writeFloat((float) sdata[n++]);
290                    break;
291                case Fits.DOUBLE:
292                    while (n<sdata.length) dos.writeDouble((double) sdata[n++]);
293                    break;
294                default: throw new FitsException("Invalid data format",
295                                                 FitsException.DATA);
296                }
297    
298                if (isRAFile) {
299                    dataFile.seek(dataOffset+offset*bytesPerData);
300                    dataFile.write(baos.toByteArray());
301                } else {
302                    byte[] vals = baos.toByteArray();
303                    n = (int) (dataOffset * bytesPerData);
304                    for (int i=0; i<vals.length; i++) {
305                        dataArray[n++] = vals[i];
306                    }
307                }
308            } catch (IOException e) {
309                throw new FitsException("Cannot convert data", FitsException.DATA);
310            }
311        }
312    
313        /** Store set of data values from an int array into the data matrix.
314         *
315         *  @param  offset   pixel offset within the data matrix
316         *  @param  idata    array with data values.
317         *  @exception FitsException */
318        public void setIntValues(int offset, int idata[])
319                throws FitsException {
320            if ((offset<0) || (noValues<=offset))
321                throw new FitsException("Invalid pixel offset",
322                                        FitsException.DATA);
323            if (idata == null) throw new FitsException("Invalid data array",
324                                                      FitsException.DATA);
325    
326            ByteArrayOutputStream baos = new ByteArrayOutputStream(idata.length * 
327                                                                   bytesPerData);
328            DataOutputStream dos = new DataOutputStream(baos);
329    
330            if (scaling) {
331                throw new FitsException("Scaling of int not supported",
332                                        FitsException.DATA);
333            }
334    
335            int n = 0;
336            try {
337                switch (dataFormat) {
338                case Fits.BYTE:
339                    throw new FitsException("Cannot convert data",
340                                            FitsException.DATA);
341                case Fits.SHORT:
342                    while (n<idata.length) dos.writeShort((short) idata[n++]);
343                    break;
344                case Fits.INT:
345                    while (n<idata.length) dos.writeInt((int) idata[n++]);
346                    break;
347                case Fits.FLOAT:
348                    while (n<idata.length) dos.writeFloat((float) idata[n++]);
349                    break;
350                case Fits.DOUBLE:
351                    while (n<idata.length) dos.writeDouble((double) idata[n++]);
352                    break;
353                default: throw new FitsException("Invalid data format",
354                                                 FitsException.DATA);
355                }
356    
357                if (isRAFile) {
358                    dataFile.seek(dataOffset+offset*bytesPerData);
359                    dataFile.write(baos.toByteArray());
360                } else {
361                    byte[] vals = baos.toByteArray();
362                    n = (int) (dataOffset * bytesPerData);
363                    for (int i=0; i<vals.length; i++) {
364                        dataArray[n++] = vals[i];
365                    }
366                }
367            } catch (IOException e) {
368                throw new FitsException("Cannot write data", FitsException.DATA);
369            }
370        }
371    
372        /** Store set of data values from a float array into the data matrix.
373         *
374         *  @param  offset   pixel offset within the data matrix
375         *  @param  data     array with data values.
376         *  @exception FitsException */
377        public void setFloatValues(int offset, float data[])
378                throws FitsException {
379    
380            if ((offset<0) || (noValues<=offset))
381                throw new FitsException("Invalid pixel offset",
382                                        FitsException.DATA);
383            if (data == null) throw new FitsException("Invalid data array",
384                                                      FitsException.DATA);
385    
386            ByteArrayOutputStream baos = new ByteArrayOutputStream(data.length * 
387                                                                   bytesPerData);
388            DataOutputStream dos = new DataOutputStream(baos);
389    
390            if (scaling) {
391              for (int i=0; i<data.length; i++)
392                  data[i] = (float) ((data[i]-zero)/scale);
393            }
394    
395            int n = 0;
396            try {
397                switch (dataFormat) {
398                case Fits.BYTE:
399                    throw new FitsException("Cannot convert data",
400                                            FitsException.DATA);
401                case Fits.SHORT:
402                    while (n<data.length) dos.writeShort((short) data[n++]);
403                    break;
404                case Fits.INT:
405                    while (n<data.length) dos.writeInt((int) data[n++]);
406                    break;
407                case Fits.FLOAT:
408                    while (n<data.length) dos.writeFloat((float) data[n++]);
409                    break;
410                case Fits.DOUBLE:
411                    while (n<data.length) dos.writeDouble((double) data[n++]);
412                    break;
413                default: throw new FitsException("Invalid data format",
414                                                 FitsException.DATA);
415                }
416    
417                if (isRAFile) {
418                    dataFile.seek(dataOffset+offset*bytesPerData);
419                    dataFile.write(baos.toByteArray());
420                } else {
421                    byte[] vals = baos.toByteArray();
422                    n = (int) (dataOffset * bytesPerData);
423                    for (int i=0; i<vals.length; i++) {
424                        dataArray[n++] = vals[i];
425                    }
426                }
427            } catch (IOException e) {
428                throw new FitsException("Cannot write data", FitsException.DATA);
429            }
430        }
431    
432        /** Gets the total number of data values in the data matrix.  */
433        public int getNoValues(){
434            return noValues;
435        }
436        
437        /** Sets reference pixel for the axes (see CRPIXn). */
438        public void setCrpix(double crp[]){
439            wcs.crpix = new double[naxis.length];
440            for (int n=0; n<naxis.length; n++) {
441                wcs.crpix[n] = ((crp!=null) && (n<crp.length)) ? crp[n] : 1.0;
442            }
443        }
444        /** Gets reference pixel for the axes (see CRPIXn). */
445        public double[] getCrpix(){
446            return wcs.crpix;
447        }
448    
449        /** Sets coordinate value for the reference pixel of the axes
450         *  (see CRVALn). */
451        public void setCrval(double crv[]){
452            wcs.crval = new double[naxis.length];
453            for (int n=0; n<naxis.length; n++) {
454                wcs.crval[n] = ((crv!=null) && (n<crv.length)) ? crv[n] : 1.0;
455            }
456        }
457    
458        /** Gets coordinate value for the reference pixel of the axes
459         *  (see CRVALn). */
460        public double[] getCrval(){
461            return wcs.crval;
462        }
463    
464        /** Sets step size for the axes (see CDELTn). */
465        public void setCdelt(double cd[]){
466            wcs.cdelt = new double[naxis.length];
467            for (int n=0; n<naxis.length; n++) {
468                wcs.cdelt[n] = ((cd!=null) && (n<cd.length)) ? cd[n] : 1.0;
469            }
470        }
471    
472        /** Gets step size for the axes (see CDELTn). */
473        public double[] getCdelt(){
474            return wcs.cdelt;
475        }
476    
477        /** Gets the WCS object for the image.  */
478        public FitsWCS getWCS(){
479            return wcs;
480        }
481    
482        /** Compute World Coordinates from pixel coordinates.
483         *
484         *  @param  pix  Array with pixel coordinates
485         */
486        public double[] toWCS(double[] pix) {
487            return wcs.toWCS(pix);
488        }
489    
490        /** Compute pixel coordinates from a set of World Coordinates.
491         *
492         *  @param  wc  Array with World Coordinates
493         */
494        public double[] toPixel(double[] wc) {
495            return wcs.toPixel(wc);
496        }
497    }
498    
499