Package ucar.nc2.iosp.gempak

Source Code of ucar.nc2.iosp.gempak.GempakFileReader$DMPart

/*
* Copyright 1998-2009 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.ArrayList;
import java.util.Formatter;
import java.util.Iterator;
import java.util.List;


/**
* 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 new RandomAccessFile(filename, "r", 2048);
    }

    /**
     * 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 final void init(RandomAccessFile raf, boolean fullCheck)
            throws IOException {
        setByteOrder();
        rf = raf;
        raf.seek(0);

        boolean ok = init(fullCheck);
        fileSize = rf.length();
        if ( !ok) {
            throw new IOException("Unable to open GEMPAK file: "
                                  + errorMessage);
        }
    }

    /**
     * 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();
    }


    /**
     * 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;
    }

    /**
     * Set the machine type for this system.
     * @see <a href="http://lopica.sourceforge.net/os.html">http://lopica.sourceforge.net/os.html</a>
     */
    private 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<DMFileHeaderInfo>();

        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<Key>(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<Key>(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<int[]>(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<int[]>(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();
            String partName = DM_RSTR(iread++);
            partArray[i].kprtnm = partName;
        }
        // read the part header lengths
        for (int i = 0; i < numParts; i++) {
            int headerLen = DM_RINT(iread++);
            partArray[i].klnhdr = headerLen;
        }
        // read the part types
        for (int i = 0; i < numParts; i++) {
            int partType = DM_RINT(iread++);
            partArray[i].ktyprt = partType;
        }
        // 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<DMParam>(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<DMPart>(numParts);
        for (int i = 0; i < numParts; i++) {
            parts.add(partArray[i]);
        }
        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
     */
    protected 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;

        /** ieee flags */
        public boolean kieeet;

        /**
         * 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
         */
        public boolean init() throws IOException {
            if (rf == null) {
                throw new IOException("File is null");
            }

            rf.order(RandomAccessFile.BIG_ENDIAN);
            int mmmm = DM_RINT(26);
            if (mmmm > 100) {
                mmmm       = GempakUtil.swp4(mmmm);
                needToSwap = true;
            }
            //System.out.println("needToSwap = "  + needToSwap);
            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);
            //System.out.println("label = " + label);
            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() {
            StringBuffer buf = new StringBuffer();

            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);
            return buf.toString();

        }

    }

    /**
     * Class to hold the DM File header info
     */
    protected 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() {
            StringBuffer buf = new StringBuffer();
            buf.append("Name = ");
            buf.append(kfhnam);
            buf.append("; length = ");
            buf.append(kfhlen);
            buf.append("; type = ");
            buf.append(kfhtyp);
            return buf.toString();
        }
    }

    /**
     * Class to mimic the DMKEYS common block.
     */
    protected 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() {
            StringBuffer buf = new StringBuffer();
            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 (int i = 0; i < params.size(); i++) {
                    buf.append("\n  " + params.get(i));
                }
            }
            return buf.toString();
        }
    }

    /**
     * Class to hold DM Parameter info
     */
    protected 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() {
            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 DM Integer packing info
     */
    protected 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 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() {
            StringBuffer buf = new StringBuffer();
            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.
     */
    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 (Iterator iter = fileHeaderInfo.iterator(); iter.hasNext(); ) {
            DMFileHeaderInfo fhi = (DMFileHeaderInfo) iter.next();
            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 = ((DMPart) 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 (int i = 0; i < parts.size(); i++) {
                DMPart part     = (DMPart) parts.get(i);
                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 = (DMPart) 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 = (DMPart) 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  = null;
        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  = (int) (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];
            for (int i = 0; i < nwordp; i++) {
                jdata[i] = ibitst[ii + i];
            }

            //
            //  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) {
        String s = "";
        for (int i = 31; i >= 0; i--) {
            if ((b & (1 << i)) != 0) {
                s = s + "1";
            } else {
                s = s + "0";
            }
            if (i % 8 == 0) {
                s = s + "|";
            }
        }
        return s;
    }

    /**
     * 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
     */
    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;
        }
    }


}
TOP

Related Classes of ucar.nc2.iosp.gempak.GempakFileReader$DMPart

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.