/* * 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.gempak; import ucar.unidata.io.RandomAccessFile; import java.io.*; import java.util.*; /** * Read a Gempak grid file */ public class GempakFileReader implements GempakConstants { /** * The file */ protected RandomAccessFile rf; /** * An error message */ private String errorMessage; /** * The label common param */ protected DMLabel dmLabel; /** * header info */ protected List<DMFileHeaderInfo> fileHeaderInfo; /** * headers */ protected DMHeaders headers; /** * key */ protected DMKeys keys; /** * part */ protected List<DMPart> parts; /** * the machine type byte order */ protected int MTMACH = RandomAccessFile.BIG_ENDIAN; /** * swap flag */ protected boolean mvmst = false; /** * swap flag */ protected boolean needToSwap = false; /** * file size */ protected long fileSize = 0; /** * masking pattern */ //private static int mskpat = 0xFFFF; private static int mskpat = ~0; /** * Bean ctor */ GempakFileReader() { } /** * Get a RandomAccessFile for the file location * * @param filename filename to read. * @return RandomAccessFile * @throws IOException problem reading file */ public static RandomAccessFile getFile(String filename) throws IOException { return RandomAccessFile.acquire(filename); } /** * Initialize the file, read in all the metadata (ala DM_OPEN) * * @param raf RandomAccessFile to read. * @param fullCheck if true, check entire structure * @return a GempakFileReader * @throws IOException problem reading file */ public static GempakFileReader getInstance(RandomAccessFile raf, boolean fullCheck) throws IOException { GempakFileReader gfr = new GempakFileReader(); gfr.init(raf, fullCheck); return gfr; } /** * Initialize the file, read in all the metadata (ala DM_OPEN) * * @param raf RandomAccessFile to read. * @param fullCheck if true, check entire structure * @throws IOException problem reading file */ public boolean init(RandomAccessFile raf, boolean fullCheck) throws IOException { setByteOrder(); rf = raf; fileSize = rf.length(); raf.seek(0); return init(fullCheck); } /** * Initialize this reader. Read all the metadata * * @return true if successful * @throws IOException problem reading the data */ protected boolean init() throws IOException { return init(true); } /** * Initialize the file, read in all the metadata (ala DM_OPEN) * * @param fullCheck if true, check entire structure * @return true if successful * @throws IOException problem reading file */ protected boolean init(boolean fullCheck) throws IOException { if (rf == null) { throw new IOException("file has not been set"); } dmLabel = new DMLabel(); boolean labelOk = dmLabel.init(); if (!labelOk) { logError("not a GEMPAK file"); return false; } // Read the keys (DM_RKEY) readKeys(); if (keys == null) { logError("Couldn't read keys"); return false; } // Read the headers (DM_RHDA) readHeaders(); if (headers == null) { logError("Couldn't read headers"); return false; } // Read the parts (DM_RPRT) readParts(); if (parts == null) { logError("Couldn't read parts"); return false; } // Read the file header info (DM_RFIL) readFileHeaderInfo(); if (fileHeaderInfo == null) { logError("Couldn't read file header info"); return false; } return true; } /** * Get the file name. * * @return the name of the file */ public String getFilename() { return (rf == null) ? null : rf.getLocation(); } public String getErrorMessage() { return errorMessage; } /** * Get initial file size * * @return the file size when init was called. */ public long getInitFileSize() { return fileSize; } /** * Get the byte order for this system * * @return byte order */ public int getByteOrder() { return MTMACH; } /** * Get the byte order for the machine type. * * @param kmachn maching type * @return byte order */ public int getByteOrder(int kmachn) { if ((kmachn == MTVAX) || (kmachn == MTULTX) || (kmachn == MTALPH) || (kmachn == MTLNUX) || (kmachn == MTIGPH)) { return RandomAccessFile.LITTLE_ENDIAN; } return RandomAccessFile.BIG_ENDIAN; } /** * LOOK WTF ?? * Set the machine type for this system. * * @see <a href="http://lopica.sourceforge.net/os.html">http://lopica.sourceforge.net/os.html</a> */ void setByteOrder() { String arch = System.getProperty("os.arch"); if (arch.equals("x86") || // Windows, Linux arch.equals("arm") || // Window CE arch.equals("x86_64") || // Windows64, Mac OS-X arch.equals("amd64") || // Linux64? arch.equals("alpha")) { // Utrix, VAX, DECOS MTMACH = RandomAccessFile.LITTLE_ENDIAN; } else { MTMACH = RandomAccessFile.BIG_ENDIAN; } } /** * Read the file header info (DM_RFIL) * * @throws IOException problem reading file */ protected void readFileHeaderInfo() throws IOException { if (dmLabel == null) { return; } int iread = dmLabel.kpfile; int numheaders = dmLabel.kfhdrs; String[] names = new String[numheaders]; int[] lens = new int[numheaders]; int[] types = new int[numheaders]; for (int i = 0; i < numheaders; i++) { names[i] = DM_RSTR(iread++); } for (int i = 0; i < numheaders; i++) { lens[i] = DM_RINT(iread++); } for (int i = 0; i < numheaders; i++) { types[i] = DM_RINT(iread++); } fileHeaderInfo = new ArrayList<>(); for (int i = 0; i < numheaders; i++) { DMFileHeaderInfo ghi = new DMFileHeaderInfo(); ghi.kfhnam = names[i]; ghi.kfhlen = lens[i]; ghi.kfhtyp = types[i]; fileHeaderInfo.add(ghi); } } /** * Read in the row and column keys (DM_KEY) * * @throws IOException problem reading file */ protected void readKeys() throws IOException { if (dmLabel == null) { return; } keys = new DMKeys(); // read the row keys int num = dmLabel.krkeys; List<Key> rkeys = new ArrayList<>(num); for (int i = 0; i < num; i++) { String key = DM_RSTR(dmLabel.kprkey + i); rkeys.add(new Key(key, i, ROW)); } keys.kkrow = rkeys; num = dmLabel.kckeys; List<Key> ckeys = new ArrayList<>(num); for (int i = 0; i < num; i++) { String key = DM_RSTR(dmLabel.kpckey + i); ckeys.add(new Key(key, i, COL)); } keys.kkcol = ckeys; } /** * keys to swap */ private static String[] swapKeys = { "STID", "STD2", "STAT", "COUN", "GPM1", "GVCD" }; /** * number of words to swap */ private static int[] swapNum = { 1, 1, 1, 1, 3, 1 }; /** * Read the headers (DM_RHDA) * * @throws IOException problem reading file */ protected void readHeaders() throws IOException { if (dmLabel == null) { return; } headers = new DMHeaders(); List<int[]> rowHeaders = new ArrayList<>(dmLabel.krow); int istart = dmLabel.kprowh; // first word is a valid flag so we have to add 1 to size int[] header; for (int i = 0; i < dmLabel.krow; i++) { header = new int[dmLabel.krkeys + 1]; DM_RINT(istart, header); if (header[0] != IMISSD) { headers.lstrw = i; } rowHeaders.add(header); istart += header.length; } headers.rowHeaders = rowHeaders; List<int[]> colHeaders = new ArrayList<>(dmLabel.kcol); istart = dmLabel.kpcolh; for (int i = 0; i < dmLabel.kcol; i++) { header = new int[dmLabel.kckeys + 1]; DM_RINT(istart, header); if (header[0] != IMISSD) { headers.lstcl = i; } colHeaders.add(header); istart += header.length; } headers.colHeaders = colHeaders; // some of the words are characters if (needToSwap) { int[] keyLoc = new int[swapKeys.length]; String[] keyType = new String[swapKeys.length]; boolean haveRow = false; boolean haveCol = false; for (int i = 0; i < swapKeys.length; i++) { Key key = findKey(swapKeys[i]); keyLoc[i] = (key != null) ? key.loc + 1 : 0; keyType[i] = (key != null) ? key.type : ""; if (keyType[i].equals(ROW)) { haveRow = true; } if (keyType[i].equals(COL)) { haveCol = true; } } if (haveRow) { for (int[] toCheck : headers.rowHeaders) { for (int j = 0; j < swapKeys.length; j++) { if (keyType[j].equals(ROW)) { if (swapKeys[j].equals("GVCD") && !(toCheck[keyLoc[j]] > GempakUtil.vertCoords.length)) { continue; } GempakUtil.swp4(toCheck, keyLoc[j], swapNum[j]); } } } } if (haveCol) { for (int[] toCheck : headers.colHeaders) { for (int j = 0; j < swapKeys.length; j++) { if (keyType[j].equals(COL)) { if (swapKeys[j].equals("GVCD") && !(toCheck[keyLoc[j]] > GempakUtil.vertCoords.length)) { continue; } GempakUtil.swp4(toCheck, keyLoc[j], swapNum[j]); } } } } } } /** * Read the parts (DM_RPRT) * * @throws IOException problem reading file */ protected void readParts() throws IOException { if (dmLabel == null) { return; } int iread = dmLabel.kppart; int numParts = dmLabel.kprt; DMPart[] partArray = new DMPart[numParts]; // read the part names for (int i = 0; i < numParts; i++) { partArray[i] = new DMPart(); partArray[i].kprtnm = DM_RSTR(iread++); } // read the part header lengths for (int i = 0; i < numParts; i++) { partArray[i].klnhdr = DM_RINT(iread++); } // read the part types for (int i = 0; i < numParts; i++) { partArray[i].ktyprt = DM_RINT(iread++); } // get number of parameters/per part. for (int i = 0; i < numParts; i++) { partArray[i].kparms = DM_RINT(iread++); } // read parameter names for (int i = 0; i < numParts; i++) { int numParms = partArray[i].kparms; List<DMParam> parms = new ArrayList<>(numParms); for (int j = 0; j < numParms; j++) { DMParam dmp = new DMParam(); parms.add(dmp); dmp.kprmnm = DM_RSTR(iread++); } partArray[i].params = parms; } // read the scale for (int i = 0; i < numParts; i++) { int numParms = partArray[i].kparms; List parms = partArray[i].params; for (int j = 0; j < numParms; j++) { DMParam dmp = (DMParam) parms.get(j); dmp.kscale = DM_RINT(iread++); } } // read the offset for (int i = 0; i < numParts; i++) { int numParms = partArray[i].kparms; List parms = partArray[i].params; for (int j = 0; j < numParms; j++) { DMParam dmp = (DMParam) parms.get(j); dmp.koffst = DM_RINT(iread++); } } // read the nbits for (int i = 0; i < numParts; i++) { int numParms = partArray[i].kparms; List parms = partArray[i].params; for (int j = 0; j < numParms; j++) { DMParam dmp = (DMParam) parms.get(j); dmp.kbits = DM_RINT(iread++); } } parts = new ArrayList<>(numParts); parts.addAll(Arrays.asList(partArray).subList(0, numParts)); for (DMPart part : parts) { if (part.ktyprt == MDRPCK) { part.packInfo = new PackingInfo(part); } } } /** * Get the byte offset in 0 based space from a 1 based 4 byte * FORTRAN word. * * @param fortranWord 1 based word offset * @return byte offset to that word */ public static long getOffset(int fortranWord) { return (fortranWord - 1) * 4l; } /** * Run the program * * @param args filename * @throws IOException problem reading the file */ public static void main(String[] args) throws IOException { if (args.length == 0) { System.out.println("need to supply a GEMPAK grid file name"); System.exit(1); } GempakFileReader gfr = getInstance(getFile(args[0]), true); gfr.printFileLabel(); gfr.printKeys(); gfr.printHeaders(); gfr.printParts(); } /** * Class to mimic the GEMPAK DMLABL common block */ class DMLabel { /** * File identifier */ public static final String DMLABEL = "GEMPAK DATA MANAGEMENT FILE "; /** * version number */ public int kversn; /** * # file headers */ public int kfhdrs; /** * ptr to file keys */ public int kpfile; /** * # of rows */ public int krow; /** * # row keys */ public int krkeys; /** * ptr to row keys */ public int kprkey; /** * ptr to row headers */ public int kprowh; /** * # of cols */ public int kcol; /** * # col keys */ public int kckeys; /** * ptr to col keys */ public int kpckey; /** * ptr to col headers */ public int kpcolh; /** * # of parts */ public int kprt; /** * # part info */ public int kppart; /** * ptr to dmg rec */ public int kpdmgt; /** * length of dmg rec */ public int kldmgt; /** * ptr to data */ public int kpdata; /** * file type */ public int kftype; /** * file source */ public int kfsrce; /** * machine data type */ public int kmachn; /** * int missing data values */ public int kmissd; /** * double missing data values */ public double smissd; /** * swap byte flags */ public boolean kvmst; /** * Create a new DMLabel for the GempakFileReader */ public DMLabel() { } /** * Read in all the info based on the block of integer words. * Modeled after DM_RLBL. * * @return true if okay. * @throws IOException problem reading the file */ boolean init() throws IOException { rf.order(RandomAccessFile.BIG_ENDIAN); if (rf.length() < getOffset(31) + 4) return false; int mmmm = DM_RINT(26); if (mmmm > 100) { mmmm = GempakUtil.swp4(mmmm); needToSwap = true; } kmachn = mmmm; mvmst = (getByteOrder() == RandomAccessFile.BIG_ENDIAN); kvmst = ((kmachn == MTVAX) || (kmachn == MTULTX) || (kmachn == MTALPH) || (kmachn == MTLNUX) || (kmachn == MTIGPH)); // Set the file values of the missing data values to the current // system values so that random values will not be converted. kmissd = IMISSD; smissd = RMISSD; String label = DM_RSTR(1, 28); if (!label.equals(DMLABEL)) { return false; } int[] words = new int[23]; DM_RINT(8, words); kversn = words[0]; kfhdrs = words[1]; kpfile = words[2]; krow = words[3]; krkeys = words[4]; kprkey = words[5]; kprowh = words[6]; kcol = words[7]; kckeys = words[8]; kpckey = words[9]; kpcolh = words[10]; kprt = words[11]; kppart = words[12]; kpdmgt = words[13]; kldmgt = words[14]; kpdata = words[15]; kftype = words[16]; kfsrce = words[17]; //kmachn = words[18]; // set above kmissd = words[19]; smissd = DM_RFLT(31); return true; } /** * Get a String representation of this. * * @return a String representation of this. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append("GEMPAK file label:\n"); buf.append("\tVersion: " + kversn + "\n"); buf.append("\t# File keys: " + kfhdrs + "\n"); buf.append("\tptr to file keys: " + kpfile + "\n"); buf.append("\t# rows: " + krow + "\n"); buf.append("\t# row keys: " + krkeys + "\n"); buf.append("\tptr to row keys: " + kprkey + "\n"); buf.append("\tprt to row header: " + kprowh + "\n"); buf.append("\t# cols: " + kcol + "\n"); buf.append("\t# cols keys: " + kckeys + "\n"); buf.append("\tptr to col keys: " + kpckey + "\n"); buf.append("\tptr to col header: " + kpcolh + "\n"); buf.append("\t# parts: " + kprt + "\n"); buf.append("\tptr part info: " + kppart + "\n"); buf.append("\tptr to data mgmt record: " + kpdmgt + "\n"); buf.append("\tlen of data mgmt record: " + kldmgt + "\n"); buf.append("\tdata pointer: " + kpdata + "\n"); buf.append("\tfile type: " + kftype + "\n"); buf.append("\tfile source: " + kfsrce + "\n"); buf.append("\tmachine type: " + kmachn + "\n"); buf.append("\tinteger missing value: " + kmissd + "\n"); buf.append("\tfloat missing value: " + smissd + "\n"); buf.append("\tswap? " + needToSwap); buf.append("\tswap flags: " + kvmst); return buf.toString(); } } /** * Class to hold the DM File header info */ protected static class DMFileHeaderInfo { /** * file header name */ public String kfhnam; /** * file header length */ public int kfhlen; /** * file header type */ public int kfhtyp; /** * Default ctor */ public DMFileHeaderInfo() { } /** * Get a String representation of this object. * * @return a String representation of this object. */ public String toString() { return "Name = " + kfhnam + "; length = " + kfhlen + "; type = " + kfhtyp; } } /** * Class to mimic the DMKEYS common block. */ protected static class DMPart { /** * part name */ public String kprtnm; /** * header length */ public int klnhdr; /** * data type */ public int ktyprt; /** * number of params */ public int kparms; /** * list of params */ public List<DMParam> params; /** * packing number */ public int kpkno; /** * length of packed rec */ public int kwordp; /** * packing info */ public PackingInfo packInfo = null; /** * Default ctor */ public DMPart() { } /** * Get a String representation of this object. * * @return a String representation of this object. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append("Part Name = "); buf.append(kprtnm); buf.append("; header length = "); buf.append(klnhdr); buf.append("; type = "); buf.append(ktyprt); buf.append("; packing num = "); buf.append(kpkno); buf.append("; packed rec len = "); buf.append(kwordp); buf.append("\nParameters: "); if ((params != null) && !params.isEmpty()) { for (DMParam param : params) { buf.append("\n ").append(param); } } return buf.toString(); } } /** * Class to hold DM Parameter info */ protected static class DMParam { /** * part name */ public String kprmnm; /** * scaling terms */ public int kscale; /** * offset terms */ public int koffst; /** * # bits */ public int kbits; /** * Default ctor */ public DMParam() { } /** * Get a String representation of this object. * * @return a String representation of this object. */ public String toString() { return "Param name = " + kprmnm + "; scale = " + kscale + "; offset = " + koffst + "; bits = " + kbits; } } /** * Class to hold DM Integer packing info */ protected static class PackingInfo { /** * offsets */ public int[] koffst; /** * n bits */ public int[] nbitsc; /** * scaling */ public double[] scalec; /** * missing */ public int[] imissc; /** * missing */ public int[] iswrdc; /** * missing */ public int[] isbitc; /** * Set the packing terms for this part * * @param part The part for the packing */ public PackingInfo(DMPart part) { List<DMParam> params = part.params; int numParams = params.size(); koffst = new int[numParams]; nbitsc = new int[numParams]; scalec = new double[numParams]; imissc = new int[numParams]; iswrdc = new int[numParams]; isbitc = new int[numParams]; int i = 0; int itotal = 0; for (DMParam param : params) { koffst[i] = param.koffst; nbitsc[i] = param.kbits; scalec[i] = Math.pow(10, param.kscale); imissc[i] = mskpat >>> (32 - param.kbits); iswrdc[i] = (itotal / 32); isbitc[i++] = (itotal % 32) + 1; itotal += param.kbits; } part.kwordp = (itotal - 1) / 32 + 1; } /** * Get a String representation of this object. * @return a String representation of this object. * public String toString() { * StringBuffer buf = new StringBuffer(); * buf.append("Param name = "); * buf.append(kprmnm); * buf.append("; scale = "); * buf.append(kscale); * buf.append("; offset = "); * buf.append(koffst); * buf.append("; bits = "); * buf.append(kbits); * return buf.toString(); * } */ } /** * Class to hold information about a key. */ protected static class Key { /** * the key name */ public String name; /** * the key location */ public int loc; /** * the key type (ROW or COL) */ public String type; /** * Create a new key * * @param name the name * @param loc the location * @param type the type */ public Key(String name, int loc, String type) { this.name = name; this.loc = loc; this.type = type; } } /** * Class to mimic the DMKEYS common block. */ protected class DMKeys { /** * row keys */ public List<Key> kkrow; /** * col keys */ public List<Key> kkcol; /** * Default ctor */ public DMKeys() { } /** * Get a String representation of this object. * * @return a String representation of this object. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append("\nKeys:\n"); buf.append(" row keys = "); for (Key key : keys.kkrow) { buf.append(key.name); buf.append(", "); } buf.append("\n"); buf.append(" column keys = "); for (Key key : keys.kkcol) { buf.append(key.name); buf.append(", "); } return buf.toString(); } } /** * Find a key with the given name * * @param name the name of the key * @return the key or null; */ public Key findKey(String name) { if (keys == null) { return null; } // search rows for (Key key : keys.kkrow) { if (key.name.equals(name)) { return key; } } // search columns for (Key key : keys.kkcol) { if (key.name.equals(name)) { return key; } } return null; } /** * Class to mimic the DMHDRS common block. */ static protected class DMHeaders { /** * last valid row */ public int lstrw = 0; /** * last valid column */ public int lstcl = 0; /** * row keys */ public List<int[]> rowHeaders; /** * column keys */ public List<int[]> colHeaders; /** * Default ctor */ public DMHeaders() { } /** * Get a String representation of this object. * * @return a String representation of this object. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append("\nHeaders:\n"); if (rowHeaders != null) { buf.append(" num row headers = "); buf.append(rowHeaders.size()); buf.append("\n"); } buf.append(" last row = "); buf.append(lstrw); buf.append("\n"); if (colHeaders != null) { buf.append(" num column headers = "); buf.append(colHeaders.size()); buf.append("\n"); } buf.append(" last column = "); buf.append(lstcl); return buf.toString(); } } /** * Find the file header with this name * * @param name name of header * @return headerinfo or null if not found */ public DMFileHeaderInfo findFileHeader(String name) { if ((fileHeaderInfo == null) || fileHeaderInfo.isEmpty()) { return null; } for (DMFileHeaderInfo fhi : fileHeaderInfo) { if (name.equals(fhi.kfhnam)) { return fhi; } } return null; } /** * Read in the values for the file header * * @param name name of header * @return values or null if not found * @throws IOException problem reading file */ public float[] getFileHeader(String name) throws IOException { DMFileHeaderInfo fh = findFileHeader(name); if ((fh == null) || (fh.kfhtyp != MDREAL)) { return null; } int knt = fileHeaderInfo.indexOf(fh); // 0 based int iread = dmLabel.kpfile + 3 * dmLabel.kfhdrs; for (int i = 0; i < knt; i++) { DMFileHeaderInfo fhi = fileHeaderInfo.get(i); iread = iread + fhi.kfhlen + 1; } int nword = DM_RINT(iread); if (nword <= 0) { logError("Invalid header length for " + name); return null; } iread++; float[] rheader = new float[nword]; if (name.equals("NAVB") && needToSwap) { DM_RFLT(iread, 1, rheader, 0); needToSwap = false; iread++; DM_RFLT(iread, 1, rheader, 1); needToSwap = true; iread++; DM_RFLT(iread, nword - 2, rheader, 2); } else { DM_RFLT(iread, rheader); } return rheader; } /** * Log an error * * @param errMsg message to log */ protected void logError(String errMsg) { errorMessage = errMsg; } /** * Print the file label */ public void printFileLabel() { if (dmLabel == null) { return; } System.out.println(dmLabel); } /** * Print the row and column keys */ public void printKeys() { if (keys == null) { return; } System.out.println(keys); } /** * Print the row and column keys */ public void printHeaders() { if (headers == null) { return; } System.out.println(headers); } /** * Print the part information */ public void printParts() { if (parts == null) { return; } for (int i = 0; i < parts.size(); i++) { System.out.println("\nParts[" + i + "]:"); System.out.println(parts.get(i)); } } /** * Find the part with the particular name. * * @param name name of part to find * @return part number. */ public int getPartNumber(String name) { int part = 0; if ((parts != null) && !parts.isEmpty()) { for (int i = 0; i < parts.size(); i++) { String partName = parts.get(i).kprtnm; if (partName.equals(name)) { // gotta add 1 because parts are 1 based part = i + 1; break; } } } return part; } /** * Find the part with the particular name. * * @param name name of part to find * @return part or null if not found */ public DMPart getPart(String name) { if ((parts != null) && !parts.isEmpty()) { for (DMPart part : parts) { String partName = part.kprtnm; if (partName.equals(name)) { return part; } } } return null; } /** * Get the pointer to the data. Taken from DM_RDTR * * @param irow row number * @param icol column number * @param partName name of the part * @return word (1 based) of start of data. */ public int getDataPointer(int irow, int icol, String partName) { int ipoint = -1; if ((irow < 1) || (irow > dmLabel.krow) || (icol < 1) || (icol > dmLabel.kcol)) { System.out.println("bad row or column number: " + irow + "/" + icol); return ipoint; } int iprt = getPartNumber(partName); if (iprt == 0) { System.out.println("couldn't find part"); return ipoint; } // gotta subtract 1 because parts are 1 but List is 0 based DMPart part = parts.get(iprt - 1); // check for valid data type if ((part.ktyprt != MDREAL) && (part.ktyprt != MDGRID) && (part.ktyprt != MDRPCK)) { System.out.println("Not a valid type"); return ipoint; } int ilenhd = part.klnhdr; ipoint = dmLabel.kpdata + (irow - 1) * dmLabel.kcol * dmLabel.kprt + (icol - 1) * dmLabel.kprt + (iprt - 1); return ipoint; } /** * Read an integer * * @param word word in file (1 based) to read * @return int read * @throws IOException problem reading file */ public int DM_RINT(int word) throws IOException { if (rf == null) { throw new IOException("DM_RINT: no file to read from"); } if (dmLabel == null) { throw new IOException("DM_RINT: reader not initialized"); } rf.seek(getOffset(word)); // set the order if (needToSwap) { //if ((dmLabel.kmachn != MTMACH) && // ((dmLabel.kvmst && ! mvmst) || // (mvmst && !dmLabel.kvmst))) { rf.order(RandomAccessFile.LITTLE_ENDIAN); // swap } else { rf.order(RandomAccessFile.BIG_ENDIAN); } int idata = rf.readInt(); if (IMISSD != dmLabel.kmissd) { if (idata == dmLabel.kmissd) { idata = IMISSD; } } rf.order(RandomAccessFile.BIG_ENDIAN); return idata; } /** * Convenience method to fully read into an array of ints * * @param word word in file (1 based) to read * @param iarray array to read into * @throws IOException problem reading file */ public void DM_RINT(int word, int[] iarray) throws IOException { DM_RINT(word, iarray.length, iarray, 0); } /** * Read into an array of ints. * * @param word word in file (1 based) to read * @param iarray array to read into * @param start starting word in the array (0 based) * @param num number of words to read * @throws IOException problem reading file */ public void DM_RINT(int word, int num, int[] iarray, int start) throws IOException { for (int i = 0; i < num; i++) { if (start + i > iarray.length) { throw new IOException( "DM_RINT: start+num exceeds iarray length"); } iarray[start + i] = DM_RINT(word + i); } } /** * Read a float * * @param word word in file (1 based) to read * @return float read * @throws IOException problem reading file */ public float DM_RFLT(int word) throws IOException { if (rf == null) { throw new IOException("DM_RFLT: no file to read from"); } if (dmLabel == null) { throw new IOException("DM_RFLT: reader not initialized"); } rf.seek(getOffset(word)); if (needToSwap) { // set the order //if ((dmLabel.kmachn != MTMACH) && // ((dmLabel.kvmst && ! mvmst) || // (mvmst && !dmLabel.kvmst))) { rf.order(RandomAccessFile.LITTLE_ENDIAN); // swap } else { rf.order(RandomAccessFile.BIG_ENDIAN); } float rdata = rf.readFloat(); if (RMISSD != dmLabel.smissd) { if (Math.abs(rdata - dmLabel.smissd) < RDIFFD) { rdata = RMISSD; } } // reset to read normally rf.order(RandomAccessFile.BIG_ENDIAN); return rdata; } /** * Convenience method to fully read into an array of floats * * @param word word in file (1 based) to read * @param rarray array to read into * @throws IOException problem reading file */ public void DM_RFLT(int word, float[] rarray) throws IOException { DM_RFLT(word, rarray.length, rarray, 0); } /** * Read into an array of ints. * * @param word word in file (1 based) to read * @param num number of words to read * @param rarray array to read into * @param start starting word in the array (0 based) * @throws IOException problem reading file */ public void DM_RFLT(int word, int num, float[] rarray, int start) throws IOException { for (int i = 0; i < num; i++) { if (start + i > rarray.length) { throw new IOException( "DM_RFLT: start+num exceeds rarray length"); } rarray[start + i] = DM_RFLT(word + i); } } /** * Read a 4-byte String * * @param isword offset in file (1 based FORTRAN word) * @return String read * @throws IOException problem reading file */ public String DM_RSTR(int isword) throws IOException { return DM_RSTR(isword, 4); } /** * Read a String * * @param isword offset in file (1 based FORTRAN word) * @param nchar number of characters to read * @return String read * @throws IOException problem reading file */ public String DM_RSTR(int isword, int nchar) throws IOException { if (rf == null) { throw new IOException("DM_RSTR: no file to read from"); } rf.seek(getOffset(isword)); return rf.readString(nchar); } /** * Read the data * * @param irow row to read * @param icol column to read * @param partName part name * @return the header and data array or null; * @throws IOException problem reading file */ public RData DM_RDTR(int irow, int icol, String partName) throws IOException { return DM_RDTR(irow, icol, partName, 1); } /** * Read the real (float) data * * @param irow row to read * @param icol column to read * @param partName part name * @param decimalScale scaling factor (power of 10); * @return the header and data array or null; * @throws IOException problem reading file */ public RData DM_RDTR(int irow, int icol, String partName, int decimalScale) throws IOException { int ipoint = -1; if ((irow < 1) || (irow > dmLabel.krow) || (icol < 1) || (icol > dmLabel.kcol)) { System.out.println("bad row/column number " + irow + "/" + icol); return null; } //System.out.println("reading row " + irow + ", column " + icol); int iprt = getPartNumber(partName); if (iprt == 0) { System.out.println("couldn't find part: " + partName); return null; } // gotta subtract 1 because parts are 1 but List is 0 based DMPart part = parts.get(iprt - 1); // check for valid real data type if ((part.ktyprt != MDREAL) && (part.ktyprt != MDGRID) && (part.ktyprt != MDRPCK)) { System.out.println("Not a valid type"); return null; } int ilenhd = part.klnhdr; ipoint = dmLabel.kpdata + (irow - 1) * dmLabel.kcol * dmLabel.kprt + (icol - 1) * dmLabel.kprt + (iprt - 1); float[] rdata; int[] header = null; int istart = DM_RINT(ipoint); if (istart == 0) { return null; } // start catching up here because some files are incorrectly written try { int length = DM_RINT(istart); int isword = istart + 1; if (length <= ilenhd) { //System.out.println("length (" + length // + ") is less than header length (" + ilenhd // + ")"); return null; } else if (Math.abs(length) > 10000000) { //System.out.println("length is huge"); return null; } header = new int[ilenhd]; DM_RINT(isword, header); int nword = length - ilenhd; isword += header.length; if (part.ktyprt == MDREAL) { rdata = new float[nword]; DM_RFLT(isword, rdata); } else if (part.ktyprt == MDGRID) { rdata = DM_RPKG(isword, nword, decimalScale); } else { // packed ints int[] idata = new int[nword]; DM_RINT(isword, idata); rdata = DM_UNPK(part, idata); } } catch (EOFException eof) { //System.err.println("reading off end of file"); rdata = null; } RData rd = null; if (rdata != null) { rd = new RData(header, rdata); } return rd; } /** * Unpack an array of packed integers. * * @param part the part with packing info * @param ibitst packed integer bit string * @return unpacked ints as floats */ public float[] DM_UNPK(DMPart part, int[] ibitst) { int nparms = part.kparms; int nwordp = part.kwordp; int npack = (ibitst.length - 1) / nwordp + 1; if (npack * nwordp != ibitst.length) { //logError("number of packed records not correct"); // System.out.println("number of packed records not correct: " // + npack * nwordp + " vs. " + ibitst.length); return null; } float[] data = new float[nparms * npack]; PackingInfo pkinf = part.packInfo; int ir = 0; int ii = 0; for (int pack = 0; pack < npack; pack++) { // // Move bitstring into internal words. TODO: necessary? // int[] jdata = new int[nwordp]; System.arraycopy(ibitst, ii, jdata, 0, nwordp); // // Extract each data value. // for (int idata = 0; idata < nparms; idata++) { // // Extract correct bits from words using shift and mask // operations. // int jbit = pkinf.nbitsc[idata]; int jsbit = pkinf.isbitc[idata]; int jshift = 1 - jsbit; int jsword = pkinf.iswrdc[idata]; int jword = jdata[jsword]; // use >>> to shift avoid carrying sign along int mask = mskpat >>> (32 - jbit); int ifield = jword >>> Math.abs(jshift); ifield = ifield & mask; if ((jsbit + jbit - 1) > 32) { jword = jdata[jsword + 1]; jshift = jshift + 32; int iword = jword << jshift; iword = iword & mask; ifield = ifield | iword; } // // The integer data is now in ifield. Use the scaling and // offset terms to convert to REAL data. // if (ifield == pkinf.imissc[idata]) { data[ir + idata] = RMISSD; } else { data[ir + idata] = (ifield + pkinf.koffst[idata]) * (float) pkinf.scalec[idata]; } } ir += nparms; ii += nwordp; } return data; } /** * Get a bit string for an integer * * @param b the integer * @return a bit string (e.g.: 01100001|11000000|10011010|10110100|) */ protected static String getBits(int b) { Formatter s = new Formatter(); for (int i = 31; i >= 0; i--) { if ((b & (1 << i)) != 0) { s.format("1"); } else { s.format("0"); } if (i % 8 == 0) { s.format("|"); } } return s.toString(); } /** * subclass should implement * * @param isword starting word (1 based) * @param nword number of words to read * @param decimalScale decimal scale * @return returns null unless subclass overrides * @throws IOException problem reading data */ public float[] DM_RPKG(int isword, int nword, int decimalScale) throws IOException { return null; } /** * A class to hold real (float) data * * @author Unidata Development Team */ static public class RData { /** * the header */ public int[] header; /** * the data */ public float[] data; /** * Create a new holder for the header and data * * @param header the header * @param data the data */ public RData(int[] header, float[] data) { this.header = header; this.data = data; } } }