ucar.nc2.iosp.grads.GradsDataDescriptorFile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdm Show documentation
Show all versions of cdm Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
The newest version!
/*
* Copyright 1998-2012 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.grads;
import ucar.nc2.constants.CDM;
import ucar.unidata.io.KMPMatch;
import ucar.unidata.io.RandomAccessFile;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class to hold information from a GrADS Data Descriptor File
*
* @author Don Murray - CU/CIRES
* @see "http://www.iges.org/grads/gadoc/descriptorfile.html"
*/
public class GradsDataDescriptorFile {
private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GradsDataDescriptorFile.class);
/**
* DSET identifier
*/
public static final String DSET = "DSET";
/**
* DTYPE identifier
*/
public static final String DTYPE = "DTYPE";
/**
* INDEX identifier
*/
public static final String INDEX = "INDEX";
/**
* TITLE identifier
*/
public static final String TITLE = "TITLE";
/**
* UNDEF identifier
*/
public static final String UNDEF = "UNDEF";
/**
* UNPACK identifier
*/
public static final String UNPACK = "UNPACK";
/**
* FILEHEADER identifier
*/
public static final String FILEHEADER = "FILEHEADER";
/**
* XYHEADER identifier
*/
public static final String XYHEADER = "XYHEADER";
/**
* THEADER identifier
*/
public static final String THEADER = "THEADER";
/**
* HEADERBYTES identifier
*/
public static final String HEADERBYTES = "HEADERBYTES";
/**
* TRAILERBYTES identifier
*/
public static final String TRAILERBYTES = "TRAILERBYTES";
/**
* OPTIONS identifier
*/
public static final String OPTIONS = "OPTIONS";
/**
* XDEF identifier
*/
public static final String XDEF = "XDEF";
/**
* YDEF identifier
*/
public static final String YDEF = "YDEF";
/**
* ZDEF identifier
*/
public static final String ZDEF = "ZDEF";
/**
* TDEF identifier
*/
public static final String TDEF = "TDEF";
/**
* EDEF identifier
*/
public static final String EDEF = "EDEF";
/**
* EDEF identifier
*/
public static final String PDEF = "PDEF";
/**
* ENDEDEF identifier
*/
public static final String ENDEDEF = "ENDEDEF";
/**
* VARS identifier
*/
public static final String VARS = "VARS";
/**
* ENDVARS identifier
*/
public static final String ENDVARS = "ENDVARS";
/**
* y revesed identifier
*/
private static final String YREV = "YREV";
/**
* template identifier
*/
private static final String TEMPLATE = "TEMPLATE";
/**
* chsub identifier
*/
private static final String CHSUB = "CHSUB";
/**
* chsub identifier
*/
private static final String CHSUB_TEMPLATE_ID = "%ch";
/**
* big endian identifier
*/
private static final String BIG_ENDIAN = "BIG_ENDIAN";
/**
* little endian identifier
*/
private static final String LITTLE_ENDIAN = "LITTLE_ENDIAN";
/**
* little endian identifier
*/
private static final String BYTESWAPPED = "BYTESWAPPED";
/**
* sequential identifier
*/
private static final String SEQUENTIAL = "SEQUENTIAL";
/**
* NO template type
*/
public static final int NO_TEMPLATE = 0;
/**
* time template type
*/
public static final int TIME_TEMPLATE = 1;
/**
* Ensemble template type
*/
public static final int ENS_TEMPLATE = 2;
/**
* Ensemble and time template type
*/
public static final int ENS_TIME_TEMPLATE = 3;
// assume there must be a "pdef" string in the first 1K
static private final KMPMatch matcher = new KMPMatch(new byte[] {'p','d','e','f'} );
public static boolean failFast(RandomAccessFile raf) throws IOException {
return !raf.searchForward(matcher, 1000); // look in first 1K
}
/////////////////////////////////////////////////////////////////////////////////////////////
/**
* the file that this relates to
*/
private String ddFile;
/**
* the data file that this points to
*/
private String dataFile;
/**
* Is this a big endian file?
*/
boolean bigEndian = false;
/**
* missing data value
*/
private double missingData = Double.NaN;
/**
* number of xy header bytes
*/
private int xyHeaderBytes = 0;
/**
* number of file header bytes
*/
private int fileHeaderBytes = 0;
/**
* number of time header bytes
*/
private int timeHeaderBytes = 0;
/**
* number of time trilobytes
*/
private int timeTrailerBytes = 0;
/**
* data type
*/
private String dataType = null;
/**
* The list of variables
*/
List variableList;
/**
* The list of dimensions
*/
List dimList;
/**
* The list of dimensions
*/
List attrList;
/**
* The XDEF dimension
*/
private GradsDimension xDim;
/**
* The YDEF dimension
*/
private GradsDimension yDim;
/**
* The ZDEF dimension
*/
private GradsDimension zDim;
/**
* The TDEF dimension
*/
private GradsTimeDimension tDim;
/**
* The EDEF dimension
*/
private GradsEnsembleDimension eDim;
/**
* The title
*/
private String title = null;
/**
* grids per timestep
*/
private int gridsPerTimeStep = 0;
/**
* timesteps per file
*/
private int timeStepsPerFile = 0;
/**
* is this a template file
*/
private boolean isTemplate = false;
/**
* type of template
*/
private int templateType = 0;
/**
* is this a sequential file
*/
private boolean isSequential = false;
/**
* is y reversed
*/
private boolean yReversed = false;
/**
* the list of filenames that this ctl points to
*/
List fileNames;
/**
* defines a projection
*/
private boolean hasProjection = false;
/**
* list of chsub parameters
*/
private List chsubs = null;
/**
* the path to the ddf
*/
private String pathToDDF = null;
/**
* Create a GradsDataDescriptorFile from the file
*
* @param filename the name of the file
* @throws IOException problem reading/parsing the file
*/
public GradsDataDescriptorFile(String filename) throws IOException {
ddFile = filename;
parseDDF();
getFileNames();
}
/**
* Parse the ctl file
*
* @throws IOException problem reading the file
*/
private void parseDDF() throws IOException {
//long start2 = System.currentTimeMillis();
variableList = new ArrayList<>();
dimList = new ArrayList<>();
attrList = new ArrayList<>();
// not using raf - opened file again
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(ddFile), CDM.utf8Charset))) {
boolean inVarSection = false;
boolean inEnsSection = false;
String line;
String original;
GradsDimension curDim = null;
while ((original = r.readLine()) != null) {
original = original.trim();
if (original.isEmpty()) {
continue;
}
line = original.toLowerCase();
if (line.startsWith("@ ")) {
attrList.add(GradsAttribute.parseAttribute(original));
continue;
}
// ignore attribute metadata and comments
if (line.startsWith("*")) {
continue;
}
if (inEnsSection) {
if (line.startsWith(ENDEDEF.toLowerCase())) {
inEnsSection = false;
continue; // done skipping ensemble definitions
}
// parse the ensemble info
}
if (inVarSection) {
if (line.startsWith(ENDVARS.toLowerCase())) {
inVarSection = false;
continue; // done parsing variables
}
GradsVariable var = new GradsVariable(original);
int numLevels = var.getNumLevels();
if (numLevels == 0) {
numLevels = 1;
}
gridsPerTimeStep += numLevels;
// parse a variable
variableList.add(var);
} else {
// not in var section or edef section, look for general metadata
StringTokenizer st = new StringTokenizer(original);
String label = st.nextToken();
// TODO: Handle other options
if (label.equalsIgnoreCase(OPTIONS)) {
curDim = null;
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (token.equalsIgnoreCase(BIG_ENDIAN)) {
bigEndian = true;
} else if (token.equalsIgnoreCase(LITTLE_ENDIAN)) {
bigEndian = false;
} else if (token.equalsIgnoreCase(BYTESWAPPED)) {
swapByteOrder();
} else if (token.equalsIgnoreCase(YREV)) {
yReversed = true;
} else if (token.equalsIgnoreCase(TEMPLATE)) {
isTemplate = true;
} else if (token.equalsIgnoreCase(SEQUENTIAL)) {
isSequential = true;
}
}
} else if (label.equalsIgnoreCase(CHSUB)) {
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
String sub = st.nextToken();
addChsub(new Chsub(start, end, sub));
} else if (label.equalsIgnoreCase(DSET)) {
curDim = null;
dataFile = st.nextToken();
} else if (label.equalsIgnoreCase(UNDEF)) {
curDim = null;
missingData = Double.parseDouble(st.nextToken());
} else if (label.equalsIgnoreCase(XYHEADER)) {
curDim = null;
xyHeaderBytes = Integer.parseInt(st.nextToken());
} else if (label.equalsIgnoreCase(FILEHEADER)) {
curDim = null;
fileHeaderBytes = Integer.parseInt(st.nextToken());
} else if (label.equalsIgnoreCase(XDEF)) {
int xSize = Integer.valueOf(st.nextToken());
String xMapping = st.nextToken();
xDim = new GradsDimension(label, xSize, xMapping);
curDim = xDim;
dimList.add(xDim);
} else if (label.equalsIgnoreCase(YDEF)) {
int ySize = Integer.valueOf(st.nextToken());
String yMapping = st.nextToken();
yDim = new GradsDimension(label, ySize, yMapping);
curDim = yDim;
dimList.add(yDim);
} else if (label.equalsIgnoreCase(ZDEF)) {
int zSize = Integer.valueOf(st.nextToken());
String zMapping = st.nextToken();
zDim = new GradsDimension(label, zSize, zMapping);
curDim = zDim;
dimList.add(zDim);
} else if (label.equalsIgnoreCase(TDEF)) {
int tSize = Integer.valueOf(st.nextToken());
// we can read the following directly
// since tdef never uses "levels"
String tMapping = st.nextToken();
tDim = new GradsTimeDimension(label, tSize, tMapping);
curDim = tDim;
dimList.add(tDim);
} else if (label.equalsIgnoreCase(EDEF)) {
int eSize = Integer.valueOf(st.nextToken());
// Check if EDEF entry is the short or extended version
if (st.nextToken().equalsIgnoreCase(GradsEnsembleDimension.NAMES)) {
inEnsSection = false;
String eMapping = GradsEnsembleDimension.NAMES;
eDim = new GradsEnsembleDimension(label, eSize, eMapping);
curDim = eDim;
dimList.add(curDim);
} else {
// TODO: handle list of ensembles
curDim = null;
inEnsSection = true;
}
} else if (label.equalsIgnoreCase(PDEF)) {
curDim = null;
hasProjection = true;
} else if (label.equalsIgnoreCase(VARS)) {
curDim = null;
inVarSection = true;
} else if (label.equalsIgnoreCase(DTYPE)) {
curDim = null;
dataType = st.nextToken();
} else if (label.equalsIgnoreCase(TITLE)) {
curDim = null;
title = original.substring(original.indexOf(" ")).trim();
} else if (curDim != null) {
curDim.addLevel(label);
}
// get the rest of the tokens
if (curDim != null) {
while (st.hasMoreTokens()) {
curDim.addLevel(st.nextToken());
}
}
}
} // end parsing loop
//System.out.println("Time to parse file = "
// + (System.currentTimeMillis() - start2));
// update the units for the zDimension if they are specified as
// an attribute
if (zDim != null) {
for (GradsAttribute attr : attrList) {
if (attr.getVariable().equalsIgnoreCase(ZDEF) &&
attr.getType().equalsIgnoreCase(GradsAttribute.STRING) &&
attr.getName().equalsIgnoreCase("units")) {
zDim.setUnit(attr.getValue());
break;
}
}
}
} catch (IOException ioe) {
log.error("Error parsing metadata for " + ddFile);
throw new IOException("error parsing metadata for " + ddFile);
}
}
/**
* Swap the byte order from the system default
*/
private void swapByteOrder() {
// NB: we are setting bigEndian to be opposite the system arch
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
bigEndian = true;
} else {
bigEndian = false;
}
}
/**
* Get the dimensions
*
* @return the dimensions
*/
public List getDimensions() {
return dimList;
}
/**
* Get the variables
*
* @return the variables
*/
public List getVariables() {
return variableList;
}
/**
* Get the attributes
*
* @return the attributes
*/
public List getAttributes() {
return attrList;
}
/**
* Get the ensemble dimension
*
* @return the ensemble dimension
*/
public GradsEnsembleDimension getEnsembleDimension() {
return eDim;
}
/**
* Get the time dimension
*
* @return the time dimension
*/
public GradsTimeDimension getTimeDimension() {
return tDim;
}
/**
* Get the Z dimension
*
* @return the Z dimension
*/
public GradsDimension getZDimension() {
return zDim;
}
/**
* Get the Y dimension
*
* @return the Y dimension
*/
public GradsDimension getYDimension() {
return yDim;
}
/**
* Get the X dimension
*
* @return the X dimension
*/
public GradsDimension getXDimension() {
return xDim;
}
/**
* Get the data file path
*
* @return the data file path
*/
public String getDataFile() {
return dataFile;
}
/**
* Get the data descriptor file path
*
* @return the data descriptor file path
*/
public String getDataDescriptorFile() {
return ddFile;
}
/**
* Get the missing value
*
* @return the missing value
*/
public double getMissingValue() {
return missingData;
}
/**
* Get the number of grids per timestep
*
* @return the number of grids per timestep
*/
public int getGridsPerTimeStep() {
return gridsPerTimeStep;
}
/**
* Get the number of timesteps per file and the starting offset
*
* @param filename the filename to check
* @return the starting index and number of times in that file
*/
public int[] getTimeStepsPerFile(String filename) {
if (chsubs != null) {
for (Chsub ch : chsubs) {
if (filename.contains(ch.subString)) {
return new int[]{ch.numTimes, ch.startTimeIndex};
}
}
}
return new int[]{timeStepsPerFile, 0};
}
/**
* Get whether this is using a template or not
*
* @return whether this is using a template or not
*/
public boolean isTemplate() {
return isTemplate;
}
/**
* Get the type of template this is
*
* @return the type of template
*/
public int getTemplateType() {
return templateType;
}
/**
* Get whether this is using a projection or no
*
* @return whether this is using a projection or not
*/
public boolean hasProjection() {
return hasProjection;
}
/**
* Get whether this is a sequentially written file
*
* @return true if this is sequential
*/
public boolean isSequential() {
return isSequential;
}
/**
* Get whether y axis is reversed or not
*
* @return whether y axis is reversed or not
*/
public boolean isYReversed() {
return yReversed;
}
/**
* Get the number of xy header bytes
*
* @return the number of xy header bytes
*/
public int getXYHeaderBytes() {
return xyHeaderBytes;
}
/**
* Get the number of file header bytes
*
* @return the number of file header bytes
*/
public int getFileHeaderBytes() {
return fileHeaderBytes;
}
/**
* Get the number of time header bytes
*
* @return the number of time header bytes
*/
public int getTimeHeaderBytes() {
return timeHeaderBytes;
}
/**
* Get the number of time trailer bytes
*
* @return the number of time trailer bytes
*/
public int getTimeTrailerBytes() {
return timeTrailerBytes;
}
/**
* Is this a big endian file
*
* @return true if big endian
*/
public boolean isBigEndian() {
return bigEndian;
}
/**
* Get the title
*
* @return the title
*/
public String getTitle() {
return title;
}
/**
* Get the data type. Only support raw binary
*
* @return type or null
*/
public String getDataType() {
return dataType;
}
/**
* Return a String representation of this object
*
* @return a String representation of this object
*/
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("Parsed: ");
buf.append(ddFile);
buf.append("\n");
buf.append("Data file: ");
buf.append(dataFile);
buf.append("\n");
for (GradsDimension dim : dimList) {
buf.append(dim.toString());
}
for (GradsVariable var : variableList) {
buf.append(var.toString());
}
return buf.toString();
}
/**
* For testing
*
* @param args the filename
* @throws Exception problem reading the file
*/
public static void main(String[] args) throws Exception {
GradsDataDescriptorFile gdd = new GradsDataDescriptorFile(args[0]);
System.out.println(gdd);
}
/**
* Get the file name for the particular time and ensemble index
*
* @param eIndex ensemble index
* @param tIndex time index
* @return the appropriate filename
*/
public String getFileName(int eIndex, int tIndex) {
String dataFilePath = dataFile;
if ((getTemplateType() == ENS_TEMPLATE) || (getTemplateType() == ENS_TIME_TEMPLATE)) {
dataFilePath = getEnsembleDimension().replaceFileTemplate(dataFilePath, eIndex);
}
dataFilePath = getTimeDimension().replaceFileTemplate(dataFilePath, tIndex);
if ((chsubs != null) && (dataFilePath.contains(CHSUB_TEMPLATE_ID))) {
for (Chsub ch : chsubs) {
if ((tIndex >= ch.startTimeIndex) && (tIndex <= ch.endTimeIndex)) {
dataFilePath = dataFilePath.replace(CHSUB_TEMPLATE_ID, ch.subString);
break;
}
}
}
return getFullPath(dataFilePath);
}
/**
* Get the list of filenames
*
* @return the filenames
* @throws IOException file does not exist.
*/
private List getFileNames() throws IOException {
if (fileNames == null) {
fileNames = new ArrayList<>();
timeStepsPerFile = tDim.getSize();
if (!isTemplate()) { // single file
fileNames.add(getFullPath(getDataFile()));
} else { // figure out template type
long start = System.currentTimeMillis();
List fileSet = new ArrayList<>();
String template = getDataFile();
if (GradsTimeDimension.hasTimeTemplate(template)) {
if (template.contains(GradsEnsembleDimension.ENS_TEMPLATE_ID)) {
templateType = ENS_TIME_TEMPLATE;
} else {
templateType = TIME_TEMPLATE;
}
} else { // not time - either ens or chsub
if (template.contains(GradsEnsembleDimension.ENS_TEMPLATE_ID)) {
templateType = ENS_TEMPLATE;
} else {
templateType = TIME_TEMPLATE;
}
}
if (templateType == ENS_TEMPLATE) {
for (int e = 0; e < eDim.getSize(); e++) {
fileSet.add(
getFullPath(
eDim.replaceFileTemplate(template, e)));
}
} else if ((templateType == TIME_TEMPLATE)
|| (templateType == ENS_TIME_TEMPLATE)) {
int numens = (templateType == TIME_TEMPLATE)
? 1
: eDim.getSize();
for (int t = 0; t < tDim.getSize(); t++) {
for (int e = 0; e < numens; e++) {
String file = getFileName(e, t);
if (!fileSet.contains(file)) {
fileSet.add(file);
}
}
}
// this'll be a bogus number if chsub was used
timeStepsPerFile = tDim.getSize()
/ (fileSet.size() / numens);
}
//System.out.println("Time to generate file list = "
// + (System.currentTimeMillis() - start));
fileNames.addAll(fileSet);
}
//long start2 = System.currentTimeMillis();
// now make sure they exist
for (String file : fileNames) {
File f = new File(file);
if (!f.exists()) {
log.error("File: " + f + " does not exist");
throw new IOException("File: " + f + " does not exist");
}
}
//System.out.println("Time to check file list = "
// + (System.currentTimeMillis() - start2));
}
return fileNames;
}
/**
* Get the path to the Data Descriptor File
*
* @return the path to the Data Descriptor File
*/
private String getDDFPath() {
if (pathToDDF == null) {
int lastSlash = ddFile.lastIndexOf("/");
if (lastSlash < 0) {
lastSlash = ddFile.lastIndexOf(File.separator);
}
pathToDDF = (lastSlash < 0)
? ""
: ddFile.substring(0, lastSlash + 1);
}
return pathToDDF;
}
/**
* Get the full path for a given filename
*
* @param filename the raw filename
* @return the full filename
*/
private String getFullPath(String filename) {
String file;
String ddfPath = getDDFPath();
if (filename.startsWith("^")) {
file = filename.replace("^", "");
file = ddfPath + file;
} else {
File f = new File(filename);
if (!f.isAbsolute()) {
file = ddfPath + filename;
} else {
file = filename;
}
}
return file;
}
/**
* Add a Chsub
*
* @param sub the chsub
*/
private void addChsub(Chsub sub) {
if (chsubs == null) {
chsubs = new ArrayList<>();
}
chsubs.add(sub);
}
/**
* Class to handle the CHSUB parameters
*/
protected static class Chsub {
/**
* start time index (0 based)
*/
protected int startTimeIndex = 0;
/**
* end time index (0 based)
*/
protected int endTimeIndex = 0;
/**
* number of times
*/
protected int numTimes = 0;
/**
* substitution string
*/
protected String subString = null;
/**
* Create a new Chsub
*
* @param start the start index (1 based)
* @param end the start index (1 based)
* @param sub the subsitution string
*/
Chsub(int start, int end, String sub) {
startTimeIndex = start - 1; // zero based
endTimeIndex = end - 1; // zero based
numTimes = endTimeIndex - startTimeIndex + 1;
subString = sub;
}
/**
* Get a String representation of this CHSUB
*
* @return a String representation of this CHSUB
*/
public String toString() {
return "CHSUB " + startTimeIndex + " " + endTimeIndex + " "
+ subString;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy