/*
* Copyright 1998-2015 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.nc2.iosp.mcidas;
import edu.wisc.ssec.mcidas.*;
import ucar.nc2.iosp.grid.*;
import ucar.unidata.io.RandomAccessFile;
import java.io.IOException;
import java.util.*;
/**
* Read grid(s) from a McIDAS grid file
*
* @author dmurray
*/
public class McIDASGridReader {
/**
* The file
*/
protected RandomAccessFile rf;
/**
* An error message
*/
private String errorMessage;
/**
* Grid index
*/
private GridIndex gridIndex;
/**
* swap flag
*/
protected boolean needToSwap = false;
/**
* hashMap of GridDefRecords
*/
private HashMap<String, McGridDefRecord> gdsMap = new HashMap<>();
/**
* maximum number of grids in a file
*/
// is this the right number?
private static final int MAX_GRIDS = 999999;
/**
* Bean ctor
*/
public McIDASGridReader() {
}
/**
* Create a McIDASGrid Reader from the file
*
* @param filename filename
* @throws IOException problem reading file
*/
public McIDASGridReader(String filename) throws IOException {
this(RandomAccessFile.acquire(filename));
}
/**
* Create a McIDASGrid Reader from the file
*
* @param raf RandomAccessFile
* @throws IOException problem reading file
*/
public McIDASGridReader(RandomAccessFile raf) throws IOException {
init(raf);
}
/**
* Initialize the file, read in all the metadata (ala DM_OPEN)
*
* @param raf RandomAccessFile to read.
* @throws IOException problem reading file
*/
public final void init(RandomAccessFile raf) throws IOException {
if (!init(raf, true))
throw new IOException("Unable to open McIDAS Grid file: " + errorMessage);
}
/**
* Initialize the file, read in all the metadata (ala DM_OPEN)
*
* @param fullCheck for a full check reading grids
* @param raf RandomAccessFile to read.
* @throws IOException problem reading file
*/
public final boolean init(RandomAccessFile raf, boolean fullCheck)
throws IOException {
rf = raf;
raf.order(RandomAccessFile.BIG_ENDIAN);
return init(fullCheck);
}
/**
* Initialize this reader. Get the Grid specific info
*
* @return true if successful
* @throws IOException problem reading the data
*/
protected boolean init() throws IOException {
return init(true);
}
/**
* Initialize this reader. Get the Grid specific info
*
* @param fullCheck for a full check reading grids
* @return true if successful, false if not a mcidas grid file
* @throws IOException problem reading the data
*/
protected boolean init(boolean fullCheck) throws IOException {
if (rf == null) {
logError("File is null");
return false;
}
gridIndex = new GridIndex(rf.getLocation());
rf.order(RandomAccessFile.BIG_ENDIAN);
if (rf.length() < 44) return false;
int numEntries = Math.abs(readInt(10));
if (numEntries > 1000000) {
needToSwap = true;
numEntries = Math.abs(McIDASUtil.swbyt4(numEntries));
}
if (numEntries > MAX_GRIDS) {
return false;
}
//System.out.println("need to Swap = " + needToSwap);
//System.out.println("number entries="+numEntries);
// go back to the beginning
rf.seek(0);
// read the fileheader
String label = rf.readString(32);
// GEMPAK too closely like McIDAS
if (label.contains("GEMPAK DATA MANAGEMENT FILE")) {
logError("label indicates this is a GEMPAK grid");
return false;
} else { // check that they are all printable ASCII chars
for (int i = 0; i < label.length(); i++) {
String s0 = label.substring(i, i + 1);
if (!(0 <= s0.compareTo(" ") && s0.compareTo("~") <= 0)) {
logError("bad label, not a McIDAS grid");
return false;
}
}
}
//System.out.println("label = " + label);
// int project = readInt(8);
//System.out.println("Project = " + project);
int date = readInt(9);
// dates are supposed to be yyyddd, but account for ccyyddd up to year 4000
if ((date < 10000) || (date > 400000)) {
logError("date wrong, not a McIDAS grid");
return false;
}
//System.out.println("date = " + date);
if (rf.length() < 4 * (numEntries + 12)) return false;
int[] entries = new int[numEntries];
for (int i = 0; i < numEntries; i++) {
entries[i] = readInt(i + 11);
// sanity check that this is indeed a McIDAS Grid file
if (entries[i] < -1) {
logError("bad grid offset " + i + ": " + entries[i]);
return false;
}
}
if (!fullCheck) {
return true;
}
// Don't swap:
rf.order(RandomAccessFile.BIG_ENDIAN);
for (int i = 0; i < numEntries; i++) {
if (entries[i] == -1) {
continue;
}
int[] header = new int[64];
rf.seek(entries[i] * 4);
rf.readInt(header, 0, 64);
if (needToSwap) {
swapGridHeader(header);
}
try {
McIDASGridRecord gr = new McIDASGridRecord(entries[i],
header);
//if (gr.getGridDefRecordId().equals("CONF X:93 Y:65")) {
//if (gr.getGridDefRecordId().equals("CONF X:54 Y:47")) {
// figure out how to handle Mercator projections
// if ( !(gr.getGridDefRecordId().startsWith("MERC"))) {
gridIndex.addGridRecord(gr);
if (gdsMap.get(gr.getGridDefRecordId()) == null) {
McGridDefRecord mcdef = gr.getGridDefRecord();
//System.out.println("new nav " + mcdef.toString());
gdsMap.put(mcdef.toString(), mcdef);
gridIndex.addHorizCoordSys(mcdef);
}
//}
} catch (McIDASException me) {
logError("problem creating grid dir");
return false;
}
}
// check to see if there are any grids that we can handle
if (gridIndex.getGridRecords().isEmpty()) {
logError("no grids found");
return false;
}
return true;
}
/**
* Swap the grid header, avoiding strings
*
* @param gh grid header to swap
*/
private void swapGridHeader(int[] gh) {
McIDASUtil.flip(gh, 0, 5);
McIDASUtil.flip(gh, 7, 7);
McIDASUtil.flip(gh, 9, 10);
McIDASUtil.flip(gh, 12, 14);
McIDASUtil.flip(gh, 32, 51);
}
/**
* Read the grid
*
* @param gr the grid record
* @return the data
*/
public float[] readGrid(McIDASGridRecord gr) throws IOException {
float[] data;
//try {
int te = (gr.getOffsetToHeader() + 64) * 4;
int rows = gr.getRows();
int cols = gr.getColumns();
rf.seek(te);
float scale = (float) gr.getParamScale();
data = new float[rows * cols];
rf.order(needToSwap ? RandomAccessFile.LITTLE_ENDIAN : RandomAccessFile.BIG_ENDIAN);
// int n = 0;
// store such that 0,0 is in lower left corner...
for (int nc = 0; nc < cols; nc++) {
for (int nr = 0; nr < rows; nr++) {
int temp = rf.readInt(); // check for missing value
data[(rows - nr - 1) * cols + nc] = (temp
== McIDASUtil.MCMISSING)
? Float.NaN
: ((float) temp) / scale;
}
}
rf.order(RandomAccessFile.BIG_ENDIAN);
//} catch (Exception esc) {
// System.out.println(esc);
//}
return data;
}
/**
* to get the grid header corresponding to the last grid read
*
* @return McIDASGridDirectory of the last grid read
*/
public GridIndex getGridIndex() {
return gridIndex;
}
/**
* Read an integer
*
* @param word word in file (0 based) to read
* @return int read
* @throws IOException problem reading file
*/
public int readInt(int word) throws IOException {
if (rf == null) {
throw new IOException("no file to read from");
}
rf.seek(word * 4);
// set the order
if (needToSwap) {
rf.order(RandomAccessFile.LITTLE_ENDIAN); // swap
} else {
rf.order(RandomAccessFile.BIG_ENDIAN);
}
int idata = rf.readInt();
rf.order(RandomAccessFile.BIG_ENDIAN);
return idata;
}
/**
* Log an error
*
* @param errMsg message to log
*/
private void logError(String errMsg) {
errorMessage = errMsg;
}
/**
* for testing purposes
*
* @param args file name
* @throws IOException problem reading file
*/
public static void main(String[] args) throws IOException {
String file = "GRID2001";
if (args.length > 0) {
file = args[0];
}
McIDASGridReader mg = new McIDASGridReader(file);
GridIndex gridIndex = mg.getGridIndex();
List grids = gridIndex.getGridRecords();
System.out.println("found " + grids.size() + " grids");
int num = Math.min(grids.size(), 10);
for (int i = 0; i < num; i++) {
System.out.println(grids.get(i));
}
}
}