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