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