001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028package edu.wisc.ssec.mcidasv.data;
029
030import java.awt.Image;
031import java.awt.Toolkit;
032import java.io.*;
033import java.rmi.RemoteException;
034
035import ucar.unidata.data.BadDataException;
036import ucar.unidata.data.grid.GridUtil;
037import ucar.unidata.util.IOUtil;
038import visad.CoordinateSystem;
039import visad.Data;
040import visad.FlatField;
041import visad.FunctionType;
042import visad.Gridded2DSet;
043import visad.Linear2DSet;
044import visad.RealTupleType;
045import visad.RealType;
046import visad.VisADException;
047import visad.util.ImageHelper;
048
049import edu.wisc.ssec.mcidasv.data.HeaderInfo;
050import edu.wisc.ssec.mcidasv.data.hydra.LongitudeLatitudeCoordinateSystem;
051import edu.wisc.ssec.mcidasv.data.hydra.SwathNavigation;
052
053public class FlatFileReader {
054        
055    /** The url */
056        private String url = null;
057        
058        /** The dimensions */
059        private int lines = 0;
060        private int elements = 0;
061        private int strideLines = 0;
062        private int strideElements = 0;
063        private int band = 1;
064        private int bandCount = 1;
065        private String unit = "";
066        private int stride = 1;
067        
068        /** The nav dimensions */
069        private int navLines = 0;
070        private int navElements = 0;
071        
072        /** The data parameters */
073        private String interleave = HeaderInfo.kInterleaveSequential;
074        private boolean bigEndian = false;
075        private int offset = 0;
076        private String delimiter = "\\s+";
077        private int dataScale = 1;
078        
079        /** The nav parameters */
080        private double ulLat = Double.NaN;
081        private double ulLon = Double.NaN;
082        private double lrLat = Double.NaN;
083        private double lrLon = Double.NaN;
084        private String latFile = null;
085        private String lonFile = null;
086        private int latlonScale = 1;
087        private boolean eastPositive = false;
088                        
089        /** which format this object is representing */
090        private int myFormat = HeaderInfo.kFormatUnknown;
091        private int myNavigation = HeaderInfo.kNavigationUnknown;
092        
093        /** the actual floats read from the file */
094        private float[] floatData = null;
095        
096        /** cache the nav info when possible */
097        Gridded2DSet navigationSet = null;
098        CoordinateSystem navigationCoords = null;
099        
100    /**
101     * Ctor for xml encoding
102     */
103    public FlatFileReader() {
104        this.floatData = null;
105    }
106
107    /**
108     * CTOR
109     *
110     * @param filename The filename
111     */
112    public FlatFileReader(String filename) {
113        this.floatData = null;
114        this.url = filename;
115    }
116
117    /**
118     * CTOR
119     *
120     * @param filename The filename
121     * @param lines The number of lines
122     * @param elements The number of elements
123     */
124    public FlatFileReader(String filename, int lines, int elements) {
125        this.floatData = null;
126        this.url = filename;
127        this.lines = lines;
128        this.elements = elements;
129        setStride(1);
130    }
131    
132    /**
133     * @param format
134     * @param interleave
135     * @param bigEndian
136     * @param offset
137     * @param band
138     * @param bandCount
139     */
140    public void setBinaryInfo(int format, String interleave, boolean bigEndian, int offset, int band, int bandCount) {
141        this.myFormat = format;
142        this.interleave = interleave;
143        this.bigEndian = bigEndian;
144        this.offset = offset;
145        this.band = band;
146        this.bandCount = bandCount;
147    }
148
149    /**
150     * @param delimiter The data value delimiter
151     * @param dataScale The data scale factor
152     */
153    public void setAsciiInfo(String delimiter, int dataScale) {
154        this.myFormat = HeaderInfo.kFormatASCII;
155        if (delimiter == null || delimiter.trim().length() == 0) delimiter="\\s+";
156        this.delimiter = delimiter;
157        this.dataScale = dataScale;
158    }
159
160    /**
161     */
162    public void setImageInfo() {
163                this.myFormat = HeaderInfo.kFormatImage;
164    }
165
166    /**
167     * @param ulLat The upper left latitude
168     * @param ulLon The upper left longitude
169     * @param lrLat The lower right latitude
170     * @param lrLon The lower right longitude
171     */
172    public void setNavBounds(double ulLat, double ulLon, double lrLat, double lrLon) {
173        this.myNavigation = HeaderInfo.kNavigationBounds;
174        this.ulLat = ulLat;
175        this.ulLon = ulLon;
176        this.lrLat = lrLat;
177        this.lrLon = lrLon;
178        this.latlonScale = 1;
179    }
180    
181    /**
182     * @param latFile Path to the latitude file.
183     * @param lonFile Path to the longitude file.
184     * @param latlonScale The navigation value scaling
185     */
186    public void setNavFiles(String latFile, String lonFile, int latlonScale) {
187        this.myNavigation = HeaderInfo.kNavigationFiles;
188        this.latFile = latFile;
189        this.lonFile = lonFile;
190        this.latlonScale = latlonScale;
191    }
192    
193    /**
194     * @param eastPositive
195     */
196    public void setEastPositive(boolean eastPositive) {
197        this.eastPositive = eastPositive;
198    }
199
200    /**
201     * @param stride
202     */
203    public void setStride(int stride) {
204        if (stride < 1) stride=1;
205        this.stride = stride;
206        this.strideElements = (int)Math.ceil((float)this.elements/(float)stride);
207                this.strideLines = (int)Math.ceil((float)this.lines/(float)stride);
208    }
209
210    /**
211     * @param unit
212     */
213    public void setUnit(String unit) {
214        if (unit.trim().equals("")) unit="";
215        this.unit = unit;
216    }
217
218    /**
219     * Read floats from a binary file
220     */
221    private void readFloatsFromBinary() {
222        System.out.println("FlatFileInfo.readFloatsFromBinary()");
223                        
224        int bytesEach = 1;
225                switch (this.myFormat) {
226                case HeaderInfo.kFormat1ByteUInt:
227                        bytesEach = 1;
228                        break;
229                case HeaderInfo.kFormat2ByteUInt:
230                        bytesEach = 2;
231                        break;
232                case HeaderInfo.kFormat2ByteSInt:
233                        bytesEach = 2;
234                        break;
235                case HeaderInfo.kFormat4ByteSInt:
236                        bytesEach = 4;
237                        break;
238                case HeaderInfo.kFormat4ByteFloat:
239                        bytesEach = 4;
240                        break;
241                default:
242                        System.err.println("FlatFileReader: Unrecognized binary format: " + this.myFormat);
243                        return;
244                }
245
246        int curPixel = 0;
247                int curElement = 0;
248                int curLine = 0;
249                int lastRead = 0;
250                int readEach = 8192;
251                int startPointer = 0;
252                int endPointer = -1;
253                int pixelPointer = 0;
254
255                int readPixels = this.strideElements * this.strideLines;
256        this.floatData = new float[readPixels];
257                byte[] readBytes = new byte[readEach];
258
259        try {            
260                FileInputStream fis = new FileInputStream(url);
261                
262                // byte boundaries
263                assert(readEach % 64 == 0);
264                
265                // assure we read the first time
266                assert(endPointer < 0);
267                
268                while ((curPixel < readPixels && curLine < lines && lastRead > 0) || curPixel == 0) {
269                        
270                        pixelPointer = this.offset;
271                        if (this.interleave.equals(HeaderInfo.kInterleaveSequential)) {
272                                // Skip to the right band
273                                pixelPointer += (this.band - 1) * (this.lines * this.elements * bytesEach);
274                                // Skip into the band
275                                pixelPointer += (curLine * this.elements * bytesEach) + (curElement * bytesEach);
276                        }
277                        else if (this.interleave.equals(HeaderInfo.kInterleaveByLine)) {
278                                // Skip to the right line
279                                pixelPointer += curLine * (this.bandCount * this.elements * bytesEach);
280                                // Skip into the line
281                                pixelPointer += ((this.band - 1) * this.elements * bytesEach) + (curElement * bytesEach);
282                        }
283                        else if (this.interleave.equals(HeaderInfo.kInterleaveByPixel)) {
284                                // Skip to the right line
285                                pixelPointer += curLine * (this.bandCount * this.elements * bytesEach);
286                                // Skip into the line
287                                pixelPointer += (curElement * bandCount * bytesEach) + ((this.band - 1) * bytesEach);
288                        }
289                        else {
290                                System.err.println("FlatFileReader: Unrecognized interleave type: " + this.interleave);
291                        }
292                        
293                        // We need data outside of our buffer
294                        if (pixelPointer > endPointer) {
295                                
296                                // Skip ahead to useful data
297                                int skipBytes = pixelPointer - endPointer - 1;
298                                if (skipBytes > 0) {
299//                                      System.out.println(" Skipping " + skipBytes + " bytes");
300                                        startPointer += lastRead + fis.skip(skipBytes);
301                                        endPointer = startPointer;
302                                }
303
304                                // Read more bytes 
305                                lastRead = fis.read(readBytes);
306                                if (startPointer != endPointer)
307                                        startPointer = endPointer + 1;
308                                endPointer = startPointer + lastRead - 1;
309//                              System.out.println(" Read " + lastRead + " bytes, from " + startPointer);
310
311                        }
312                        
313                        int readOffset = pixelPointer - startPointer;
314                        float readFloat = 0.0f;
315                        switch (this.myFormat) {
316                        case HeaderInfo.kFormat1ByteUInt:
317                                readFloat = (float)bytesTo1ByteUInt(readBytes, readOffset);
318                                break;
319                        case HeaderInfo.kFormat2ByteUInt:
320                                int newInt = bytesTo2ByteUInt(readBytes, readOffset);
321                                if (this.bigEndian) {
322                                        newInt = (((newInt&0xff)<<8)|((newInt&0xff00)>>8));
323                                }
324                                readFloat = (float)newInt;
325                                break;
326                        case HeaderInfo.kFormat2ByteSInt:
327                                readFloat = (float)bytesTo2ByteSInt(readBytes, readOffset);
328                                break;
329                        case HeaderInfo.kFormat4ByteSInt:
330                                readFloat = (float)bytesTo4ByteSInt(readBytes, readOffset);
331                                break;
332                        case HeaderInfo.kFormat4ByteFloat:
333                                readFloat = (float)bytesTo4ByteFloat(readBytes, readOffset);
334                                break;
335                        }
336                        this.floatData[curPixel++] = readFloat;
337
338                        curElement+=stride;
339                        if (curElement >= elements) {
340                                curElement = 0;
341                                curLine+=stride;
342                        }
343                }
344                
345            fis.close();
346            
347            System.out.println("  read " + curPixel + " floats (expected " + readPixels + ")");
348            
349        } catch (NumberFormatException exc) {
350                throw new BadDataException("Error parsing binary file");
351        } catch (Exception e) {
352                throw new BadDataException("Error reading binary file: " + url + "\n" + e);
353        }
354    }
355        
356    /**
357     * Read floats from an ASCII file
358     */
359    private void readFloatsFromAscii() {
360        System.out.println("FlatFileInfo.readFloatsFromAscii()");
361        
362        int curPixel = 0;
363                int curElement = 0;
364                int curLine = 0;
365                
366                int readPixels = this.strideElements * this.strideLines;
367                this.floatData = new float[readPixels];
368        
369        try {            
370                InputStream is = IOUtil.getInputStream(url, getClass());
371                BufferedReader in = new BufferedReader(new InputStreamReader(is));
372            String aLine;
373            
374            while ((aLine = in.readLine()) != null) {
375                aLine = aLine.trim();
376                String[] words = aLine.split(delimiter);
377                for (int i=0; i<words.length; i++) {
378                        
379                                if (curLine % stride == 0 && curElement % stride == 0) {
380                                        this.floatData[curPixel++] = Float.parseFloat(words[i]);
381                                }
382                                
383                                // Keep track of what element/line we are reading so we can stride appropriately
384                                curElement++;
385                                if (curElement >= elements) {
386                                        curElement = 0;
387                                        curLine++;
388                                }
389                                if (curLine > lines || curPixel > readPixels) {
390                                throw new BadDataException("Error parsing ASCII file: Bad dimensions");
391                                }
392
393                }                       
394            }
395            in.close();
396                        
397            System.out.println("  read " + curPixel + " floats (expected " + readPixels + ")");
398           
399        } catch (NumberFormatException exc) {
400                throw new BadDataException("Error parsing ASCII file");
401        } catch (Exception e) {
402                throw new BadDataException("Error reading ASCII file: " + url + "\n" + e);
403        }
404    }
405    
406    /**
407     * Make a FlatField from an Image
408     */
409    private Data getDataFromImage() {
410        System.out.println("FlatFileInfo.getDataFromImage()");
411        try {
412                this.floatData = new float[0];
413                InputStream is = IOUtil.getInputStream(url, getClass());
414                byte[] imageContent = IOUtil.readBytes(is);
415                Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
416                ImageHelper ih = new ImageHelper();
417                image.getWidth(ih);
418                if (ih.badImage) {
419                        throw new IllegalStateException("Bad image: " + url);
420                }
421                
422            makeCoordinateSystem();
423
424                FlatField field = (FlatField) ucar.visad.Util.makeField(image,true);
425                        return GridUtil.setSpatialDomain(field, navigationSet);
426                        
427        } catch (Exception e) {
428                throw new BadDataException("Error reading image file: " + url + "\n" + e);
429        }
430    }
431
432    /**
433     * Make a Gridded2DSet from bounds
434     */
435    private Gridded2DSet getNavigationSetFromBounds() {
436        System.out.println("FlatFileInfo.getNavigationSetFromBounds()");
437            try {
438                this.navElements = this.strideElements;
439                this.navLines = this.strideLines;
440                int lonScale = this.latlonScale;
441                int latScale = this.latlonScale;
442                if (eastPositive) lonScale *= -1;
443                return new Linear2DSet(RealTupleType.SpatialEarth2DTuple,
444                                ulLon / lonScale, lrLon / lonScale, navElements,
445                                ulLat / latScale, lrLat / latScale, navLines);
446            } catch (Exception e) {
447                        throw new BadDataException("Error setting navigation bounds:\n" + e);
448            }
449    }
450
451    /**
452     * Make a Gridded2DSet from files
453     */
454    private Gridded2DSet getNavigationSetFromFiles() {
455        System.out.println("FlatFileInfo.getNavigationSetFromFiles()");
456        try {
457            float[][] lalo = new float[0][0];
458            
459            FlatFileReader lonData, latData;
460            
461            // ASCII nav files
462            if (this.myFormat == HeaderInfo.kFormatASCII) {
463                System.out.println("  ASCII nav file");
464                    
465                        this.navElements = this.elements;
466                        this.navLines = this.lines;
467                lalo = new float[2][navElements * navLines];
468                                                                        
469                        // Longitude band
470                        lonData = new FlatFileReader(lonFile, navLines, navElements);
471                        lonData.setAsciiInfo(delimiter, 1);
472                        
473                        // Latitude band
474                        latData = new FlatFileReader(latFile, navLines, navElements);
475                        latData.setAsciiInfo(delimiter, 1);
476                                            
477            }
478            
479            // Binary nav files
480            else {
481                
482                System.out.println("  Binary nav file");
483
484                // ENVI header for nav
485                EnviInfo enviLat = new EnviInfo(latFile);
486                EnviInfo enviLon = new EnviInfo(lonFile);
487                if (enviLat.isNavHeader() && enviLon.isNavHeader()) {
488                        System.out.println("    ENVI nav file");
489
490                        this.navElements = enviLat.getParameter(HeaderInfo.ELEMENTS, 0);
491                        this.navLines = enviLat.getParameter(HeaderInfo.LINES, 0);
492                    lalo = new float[2][navElements * navLines];
493                                                                        
494                        // Longitude band
495                        lonData = new FlatFileReader(enviLon.getLonBandFile(), 
496                                        enviLon.getParameter(HeaderInfo.LINES, 0),
497                                        enviLon.getParameter(HeaderInfo.ELEMENTS, 0));
498                        lonData.setBinaryInfo(
499                                        enviLon.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown),
500                                        enviLon.getParameter(HeaderInfo.INTERLEAVE, HeaderInfo.kInterleaveSequential),
501                                        enviLon.getParameter(HeaderInfo.BIGENDIAN, false),
502                                        enviLon.getParameter(HeaderInfo.OFFSET, 0),
503                                        enviLon.getLonBandNum(),
504                                        enviLon.getBandCount());
505                        
506                        // Latitude band
507                        latData = new FlatFileReader(enviLat.getLatBandFile(), 
508                                        enviLat.getParameter(HeaderInfo.LINES, 0),
509                                        enviLat.getParameter(HeaderInfo.ELEMENTS, 0));
510                        latData.setBinaryInfo(
511                                        enviLat.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown),
512                                        enviLat.getParameter(HeaderInfo.INTERLEAVE, HeaderInfo.kInterleaveSequential),
513                                        enviLat.getParameter(HeaderInfo.BIGENDIAN, false),
514                                        enviLat.getParameter(HeaderInfo.OFFSET, 0),
515                                        enviLat.getLatBandNum(),
516                                        enviLat.getBandCount());
517                        
518                }
519                
520                else {
521                        System.out.println("    AXFORM nav file");
522
523                        this.navElements = this.elements;
524                        this.navLines = this.lines;
525                    lalo = new float[2][navElements * navLines];
526                                                                        
527                        // Longitude band
528                        lonData = new FlatFileReader(lonFile, navLines, navElements);
529                        lonData.setBinaryInfo(HeaderInfo.kFormat2ByteUInt, HeaderInfo.kInterleaveSequential, bigEndian, offset, 1, 1);
530                        
531                        // Latitude band
532                        latData = new FlatFileReader(latFile, navLines, navElements);
533                        latData.setBinaryInfo(HeaderInfo.kFormat2ByteUInt, HeaderInfo.kInterleaveSequential, bigEndian, offset, 1, 1);
534                        
535                }
536                                
537            }
538                        
539                // Set the stride if the dimensions are the same and read the floats
540                if (this.lines == this.navLines && this.elements == this.navElements && stride != 1) {
541                        System.out.println("Setting stride for nav files: " + stride);
542                        lonData.setStride(this.stride);
543                        latData.setStride(this.stride);
544                        this.navElements = this.strideElements;
545                        this.navLines = this.strideLines;
546                lalo = new float[2][this.navElements * this.navLines];
547                }
548                lalo[0] = lonData.getFloats();
549                lalo[1] = latData.getFloats();
550                
551                // Take into account scaling and east positive
552                int latScale = this.latlonScale;
553                        int lonScale = this.latlonScale;
554                        if (eastPositive) lonScale = -1 * lonScale;
555                for (int i=0; i<lalo[0].length; i++) {
556                        lalo[0][i] = lalo[0][i] / (float)lonScale;
557                }
558                for (int i=0; i<lalo[1].length; i++) {
559                        lalo[1][i] = lalo[1][i] / (float)latScale;
560                }
561                
562            return new Gridded2DSet(RealTupleType.SpatialEarth2DTuple,
563                        lalo, navElements, navLines,
564                        null, null, null,
565                        false, false);
566            
567        } catch (NumberFormatException exc) {
568                throw new BadDataException("Error parsing ASCII navigation file");
569        } catch (Exception e) {
570                throw new BadDataException("Error setting navigation from file: " + url + "\n" + e);
571        }
572    }
573        
574    /**
575     * Create navigation info if it hasn't been built
576     */
577    private void makeCoordinateSystem() {
578        System.out.println("FlatFileInfo.makeCoordinateSystem()");
579        
580        if (navigationSet != null && navigationCoords != null) return;
581
582        switch (this.myNavigation) {
583        case HeaderInfo.kNavigationBounds:
584                navigationSet = getNavigationSetFromBounds();
585                break;
586        case HeaderInfo.kNavigationFiles:
587                navigationSet = getNavigationSetFromFiles();
588                break;
589                default:
590                        System.err.println("Unknown navigation format");
591        }
592        
593                // myElements, myLines: Nav dimensions
594                // this.elements, this.lines: Data dimensions
595                float ratioElements = (float)this.strideElements / (float)this.navElements;
596                float ratioLines = (float)this.strideLines / (float)this.navLines;
597                int[] geo_start = new int[2];
598                int[] geo_count = new int[2];
599                int[] geo_stride = new int[2];
600                try {
601                        Linear2DSet domainSet = SwathNavigation.getNavigationDomain(
602                                        0, strideElements-1, 1,
603                                        0, strideLines-1, 1, 
604                                        ratioElements, ratioLines, 0, 0, 
605                                        geo_start, geo_count, geo_stride);
606                                                
607//                      System.out.println("makeCoordinateSystem stats for " + url + ":");
608//                      System.out.println("  Elements: " + strideElements + ", Lines: " + strideLines);
609//                      System.out.println("  navElements: " + navElements + ", navLines: " + navLines);
610//                      System.out.println("  ratioElements: " + ratioElements + ", ratioLines: " + ratioLines);
611//                      System.out.println("  navigationSet: " + navigationSet.getLength(0) + " x " + navigationSet.getLength(1));
612//                      System.out.println("  geo_start: " + geo_start[0] + ", " + geo_start[1]);
613//                      System.out.println("  geo_count: " + geo_count[0] + ", " + geo_count[1]);
614//                      System.out.println("  geo_stride: " + geo_stride[0] + ", " + geo_stride[1]);
615//                      System.out.println("  domainSet: " + domainSet.getLength(0) + " x " + domainSet.getLength(1));
616//                      System.out.println("  domainSet.toString(): " + domainSet.toString());
617                        
618                        navigationCoords = new LongitudeLatitudeCoordinateSystem(domainSet, navigationSet);
619                } catch (Exception e) {
620                        // TODO Auto-generated catch block
621                        e.printStackTrace();
622                }
623    }
624
625    /**
626     * Return a valid data object for a DataSource
627     */
628    public Data getData() {
629        System.out.println("FlatFileInfo.getData()");
630
631        Data d = null;
632        FlatField field;
633
634        try {
635
636                switch (this.myFormat) {
637                case HeaderInfo.kFormatImage:
638                        d = getDataFromImage();
639                        break;
640                default:
641                        this.floatData = getFloats();
642                        field = getFlatField();
643//                      d = GridUtil.setSpatialDomain(field, navigationSet);
644                        d = field;
645                        break;
646                }
647
648        } catch (IOException e) {
649                // TODO Auto-generated catch block
650                e.printStackTrace();
651        } catch (VisADException e) {
652                // TODO Auto-generated catch block
653                e.printStackTrace();
654                }
655
656        return d;
657    }
658    
659    /**
660     * Return the array of floats making up the data
661     */
662    public float[] getFloats() {
663        System.out.println("FlatFileInfo.getFloats()");
664        
665        if (this.floatData != null) return this.floatData;
666        
667        switch (this.myFormat) {
668        case HeaderInfo.kFormatImage:
669                break;
670        case HeaderInfo.kFormatASCII:
671                readFloatsFromAscii();
672                break;
673                default:
674                readFloatsFromBinary();
675                        break;
676        }
677        
678        
679        
680                // DEBUG!
681//      File justName = new File(url);
682//      try {
683//              BufferedWriter out = new BufferedWriter(new FileWriter("/tmp/mcv/" + justName.getName()));
684//              for (int i=0; i<this.floatData.length; i++) {
685//                      if (i%strideElements==0) out.write("New line " + (i/strideElements) + " at element " + i + "\n");
686//                      out.write(this.floatData[i] + "\n");
687//              }
688//              out.close();
689//      } 
690//      catch (IOException e) { 
691//              System.out.println("Exception ");
692//      }
693
694                
695        
696        return this.floatData;
697    }
698        
699    /**
700     * float array -> flatfield
701     */
702    private FlatField getFlatField()
703    throws IOException, VisADException {
704        
705        makeCoordinateSystem();
706        
707//      RealType[]    unit                      = new RealType[] { RealType.Generic };
708//      RealTupleType unitType       = new RealTupleType(unit);
709        
710        RealType          unitType                      = RealType.getRealType(unit);
711
712        RealType      line              = RealType.getRealType("ImageLine");
713        RealType      element           = RealType.getRealType("ImageElement");
714        RealType[]    domain_components = { element, line };
715        RealTupleType image_domain      = new RealTupleType(domain_components, navigationCoords, null);
716        FunctionType  image_type                = new FunctionType(image_domain, unitType);
717        Linear2DSet   domain_set                = new Linear2DSet(image_domain,
718                        0.0, (float) (strideElements - 1.0), strideElements,
719                        0.0, (float) (strideLines - 1.0), strideLines);
720
721        FlatField    field      = new FlatField(image_type, domain_set);
722
723        float[][]    samples    = new float[][] { this.floatData };
724        try {
725            field.setSamples(samples, false);
726        } catch (RemoteException e) {
727            throw new VisADException("Couldn't finish FlatField initialization");
728        }
729        
730        return field;
731    }
732        
733    /**
734     * toString
735     *
736     * @return toString
737     */
738    public String toString() {
739        return "url: " + url + ", lines: " + lines + ", elements: " + elements;
740    }
741    
742    // byte[] conversion functions
743    // TODO: are these replicated elsewhere in McV?
744
745        private static int bytesTo1ByteUInt (byte[] bytes, int offset) {
746                return (int) ( bytes[offset] & 0xff );
747        }
748        
749        private static int bytesTo2ByteUInt (byte[] bytes, int offset) {
750                int accum = 0;
751                for ( int shiftBy = 0; shiftBy < 16; shiftBy += 8 ) {
752                        accum |= ( (long)( bytes[offset] & 0xff ) ) << shiftBy;
753                        offset++;
754                }
755                return (int)( accum );
756        }
757        
758        private static int bytesTo2ByteSInt (byte[] bytes, int offset) {
759                return (bytesTo2ByteUInt(bytes, offset)) - 32768;
760        }
761        
762        private static int bytesTo4ByteSInt (byte[] bytes, int offset) {
763                int accum = 0;
764                for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 ) {
765                        accum |= ( (long)( bytes[offset] & 0xff ) ) << shiftBy;
766                        offset++;
767                }
768                return (int)( accum );
769        }
770
771        private static float bytesTo4ByteFloat (byte[] bytes, int offset) {
772                int accum = 0;
773                for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 ) {
774                        accum |= ( (long)( bytes[offset] & 0xff ) ) << shiftBy;
775                        offset++;
776                }
777                return Float.intBitsToFloat(accum);
778        }       
779        
780        private static long bytesToLong (byte[] bytes) {
781                if (bytes.length != 4) return 0;
782                long accum = 0;
783                int i = 0;
784                for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 ) {
785                        accum |= ( (long)( bytes[i] & 0xff ) ) << shiftBy;
786                        i++;
787                }
788                return accum;
789        }
790        
791        private static double bytesToDouble (byte[] bytes) {
792                if (bytes.length != 8) return 0;
793                long accum = 0;
794                int i = 0;
795                for ( int shiftBy = 0; shiftBy < 64; shiftBy += 8 ) {
796                        accum |= ( (long)( bytes[i] & 0xff ) ) << shiftBy;
797                        i++;
798                }
799                return Double.longBitsToDouble(accum);
800        }
801
802}