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