//
// TextAdapter.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad.data.text;
import java.io.IOException;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import visad.Set;
import java.net.URL;
import visad.*;
import visad.VisADException;
import visad.data.in.ArithProg;
import visad.util.DataUtility;
import java.util.regex.*;
/** this is an VisAD file adapter for comma-, tab- and blank-separated
* ASCII text file data. It will attempt to create a FlatField from
* the data and descriptions given in the file and/or the constructor.
*
* The text files contained delimited data. The delimiter is
* determined as follows: if the file has a well-known extension
* (.csv, .tsv, .bsv) then the delimiter is implied by the extension.
* In all other cases, the delimiter for the data (and for the
* "column labels") is determined by reading the first line and
* looking, in order, for a tab, comma, or blank. Which ever one
* is found first is taken as the delimiter.
*
* Two extra pieces of information are needed: the VisAD "MathType"
* which is specified as a string (e.g., (x,y)->(temperature))
* and may either be the first line of the file or passed in through
* one of the constructors.
*
* The second item are the "column labels" which contain the names
* of each field in the data. The names of all range components
* specified in the "MathType" must appear. The names of domain
* components are optional. The values in this string are separated
* by the delimiter, as defined above.
*
* See visad.data.text.README.text for more details.
*
* @author Tom Whittaker
*
*/
public class TextAdapter {
private static final String ATTR_COLSPAN = "colspan";
private static final String ATTR_VALUE = "value";
private static final String ATTR_OFFSET = "off";
private static final String ATTR_ERROR = "err";
private static final String ATTR_SCALE = "sca";
private static final String ATTR_POSITION ="pos";
private static final String ATTR_FORMAT = "fmt";
private static final String ATTR_TIMEZONE = "tz";
private static final String ATTR_UNIT= "unit";
private static final String ATTR_MISSING = "mis";
private static final String ATTR_INTERVAL = "int";
private static final String COMMA = ",";
private static final String SEMICOLON = ";";
private static final String TAB = "\t";
private static final String BLANK = " ";
private static final String BLANK_DELIM = "\\s+";
private FlatField ff = null;
private Field field = null;
private boolean debug = false;
private String DELIM;
private boolean DOQUOTE = true;
private boolean GOTTIME = false;
HeaderInfo []infos;
double[] rangeErrorEstimates;
Unit[] rangeUnits;
Set[] rangeSets;
double[] domainErrorEstimates;
Unit[] domainUnits;
int[][] hdrColumns;
int[][] values_to_index;
private Hashtable properties;
private boolean onlyReadOneLine = false;
private Pattern skipPattern;
/**If this is defined then when processing each tuple pass the processor the tuple
and do not try to create the field */
private StreamProcessor streamProcessor;
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param filename name of local file.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(String filename) throws IOException, VisADException {
this(filename, null, null);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param filename name of local file.
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(String filename, String map, String params)
throws IOException, VisADException {
InputStream is = new FileInputStream(filename);
DELIM = getDelimiter(filename);
readit(is, map, params);
}
/** Create a VisAD FlatField from a remote Text (comma-, tab- or
* blank-separated values) ASCII file
*
* @param url File URL.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(URL url) throws IOException, VisADException {
this(url, null, null);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param url File URL.
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(URL url, String map, String params)
throws IOException, VisADException {
DELIM = getDelimiter(url.getFile());
InputStream is = url.openStream();
readit(is, map, params);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param inputStream The input stream to read from
* @param delimiter the delimiter
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(InputStream inputStream, String delimiter, String map, String params)
throws IOException, VisADException {
this(inputStream, delimiter, map,params,false);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param inputStream The input stream to read from
* @param delimiter the delimiter
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @param onlyReadOneLine If true then only read one line of data. This is used so client code can
* read the meta data.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(InputStream inputStream, String delimiter, String map, String params,boolean onlyReadOneLine)
throws IOException, VisADException {
this(inputStream, delimiter, map, params, null, onlyReadOneLine);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param inputStream The input stream to read from
* @param delimiter the delimiter
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @param properties properties
* @param onlyReadOneLine If true then only read one line of data. This is used so client code can
* read the meta data.
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine)
throws IOException, VisADException {
this(inputStream, delimiter, map, params, properties, onlyReadOneLine, null);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param inputStream The input stream to read from
* @param delimiter the delimiter
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @param properties properties
* @param onlyReadOneLine If true then only read one line of data. This is used so client code can
* read the meta data.
* @param skipPatternString if non-null then skip any line that matches this pattern
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine,String skipPatternString)
throws IOException, VisADException {
this(inputStream, delimiter, map, params, properties, onlyReadOneLine, skipPatternString,null);
}
/** Create a VisAD FlatField from a local Text (comma-, tab- or
* blank-separated values) ASCII file
* @param inputStream The input stream to read from
* @param delimiter the delimiter
* @param map the VisAD "MathType" as a string defining the FlatField
* @param params the list of parameters used to define what columns
* of the text file correspond to what MathType parameters.
* @param properties properties
* @param onlyReadOneLine If true then only read one line of data. This is used so client code can
* read the meta data.
* @param skipPatternString if non-null then skip any line that matches this pattern
* @param streamProcessor Optional processor of the Tuple stream for point obs
* @exception IOException if there was a problem reading the file.
* @exception VisADException if an unexpected problem occurs.
*/
public TextAdapter(InputStream inputStream, String delimiter, String map, String params,Hashtable properties, boolean onlyReadOneLine,String skipPatternString,StreamProcessor streamProcessor)
throws IOException, VisADException {
this.onlyReadOneLine = onlyReadOneLine;
this.streamProcessor = streamProcessor;
DELIM = delimiter;
this.properties = properties;
if(skipPatternString!=null && skipPatternString.length()>0) {
skipPattern = Pattern.compile(skipPatternString);
}
readit(inputStream, map, params);
}
public static String getDelimiter(String filename) {
if(filename == null) return null;
filename = filename.trim().toLowerCase();
if (filename.endsWith(".csv")) return COMMA;
if (filename.endsWith(".tsv")) return TAB;
if (filename.endsWith(".bsv")) return BLANK;
return null;
}
/**
* Is the given text line a comment
*
* @return is it a comment line
*/
public static boolean isComment(String line) {
return (line.startsWith("#") ||
line.startsWith("!") ||
line.startsWith("%") ||
line.length() < 1);
}
public static String readLine(BufferedReader bis)
throws IOException {
while (true) {
String line = bis.readLine();
if (line == null) return null;
if (!isText(line)) return null;
if (isComment(line)) continue;
return line.trim();
}
}
void readit(InputStream is, String map, String params)
throws IOException, VisADException {
// read the ASCII file, using commas as field separators
// first line is a header line
List realTypes = new ArrayList();
ff = null;
field = null;
if (debug) System.out.println("#### Text Adapter v2.x running");
BufferedReader bis = new BufferedReader(new InputStreamReader(is));
// mapping defines how the names are mapped
// for example: (x,y) => (one, two, three)
String maps = null;
if (map == null) {
maps = readLine(bis);
if(maps != null) {
maps = maps.trim();
}
} else {
maps = map;
}
if (maps != null) {
maps = makeMT(maps);
}
if (maps == null) {
throw new visad.data.BadFormException(
"TextAdapter: Invalid or missing MathType");
}
List<String[]>nameChanges = new ArrayList<String[]>();
if (debug) System.out.println("Specified MathType = "+maps);
// but first, we need to get the column headers because they
// may have [units] associated with them. The column headers
// primarily define where the data are.
String hdr = null;
if (params == null) {
hdr = readLine(bis);
} else {
hdr = params;
}
String hdrDelim = DELIM;
if (DELIM == null) {
if (hdr.indexOf(BLANK) != -1) hdrDelim = BLANK_DELIM;
if (hdr.indexOf(COMMA) != -1) hdrDelim = COMMA;
if (hdr.indexOf(SEMICOLON) != -1) hdrDelim = SEMICOLON;
if (hdr.indexOf(TAB) != -1) hdrDelim = TAB;
if (debug) System.out.println("Using header delimiter = "+ hdrDelim + "("+
(hdrDelim.getBytes())[0] + ")");
}
// squeeze out extra blank spaces
if (hdrDelim.equals(BLANK) || hdrDelim.equals(BLANK_DELIM)) {
//System.out.println("line before squeeze: " + line);
hdr = hdr.replaceAll("\\s++", " ").trim();
//System.out.println("line after squeeze: " + line);
}
String[] sthdr = hdr.split(hdrDelim);
// since blanks separate the metadata, if we have a blank
// delimiter, we run into problems. Loop through the header and
// put humpty dumpty back together again
if (hdrDelim.equals(BLANK_DELIM) || hdrDelim.equals(BLANK)) {
List<String> chunks = new ArrayList<String>();
for (int i = 0; i < sthdr.length; i++) {
String subchunk = sthdr[i].trim();
int m = subchunk.indexOf("[");
if (m == -1) {
chunks.add(subchunk);
continue;
}
// have "[", find "]"
int m2 = subchunk.indexOf("]");
while (m2 < 0 && i < sthdr.length) {
i++;
subchunk += " " +sthdr[i].trim();
m2 = subchunk.indexOf("]");
}
chunks.add(subchunk);
}
sthdr = (String[]) chunks.toArray(new String[chunks.size()]);
}
int nhdr = sthdr.length;
infos = new HeaderInfo[nhdr];
for(int i=0;i<infos.length;i++) {
infos[i] = new HeaderInfo();
}
hdrColumns = new int[2][nhdr];
int numHdrValues=0;
// pre-scan of the header names to seek out Units
// since we cannot change a RealType once it's defined!!
for (int i=0; i<nhdr; i++) {
String name = sthdr[i].trim();
String hdrUnitString = null;
hdrColumns[0][i] = -1; // indicating no fixed columns
int m = name.indexOf("[");
if (m == -1) {
infos[i].name = name;
hdrUnitString = null;
} else {
int m2 = name.indexOf("]");
if (m2 == -1) {
throw new VisADException("TextAdapter: Bad [descriptor] named in:"+name);
}
// now parse items: unit=xxx miss=xxx interval=xxx error=xxx
// 0. Remove any spaces around the "=" signs
// 1. tokenize on " "
// 2. scan each token, retokenizing on "="
// 3. if (has no "=") && (is first one) then treat as Unit
// 4. otherwise, look for keys "unit" "miss" "inter" "err" "scale" "offset" "pos"
// and fill in the values in array[i]
if (m2 >= name.length()) {
infos[i].name = name.substring(0,m).trim();
} else {
infos[i].name = (name.substring(0,m)+name.substring(m2+1)).trim();
}
// 0. Remove any spaces around the "=" signs
String cl = name.substring(m+1,m2).trim();
cl = cl.replaceAll(" +=","=");
cl = cl.replaceAll("= +","=");
String[] stcl = cl.split(BLANK_DELIM);
int ncl = stcl.length;
if (ncl == 1 && cl.indexOf("=") == -1) {
hdrUnitString = cl; // backward compatible...
} else {
for (int l = 0; l < ncl; l++) {
String s = stcl[l];
String[] sts = s.split("=");
if (sts.length != 2) {
throw new VisADException("TextAdapter: Invalid clause in: "+s);
}
String tok = sts[0];
String val = sts[1];
// check for quoted strings
if (val.startsWith("\"")) {
// see if ending quote also fetched
if (val.endsWith("\"")) {
String v2 = val.substring(1,val.length()-1);
val = v2;
} else {
// if not, then reparse stcl to suck up spaces...
try {
String v2="";
for (int q=l+1; q < ncl; q++) {
String vTmp = stcl[q];
// find next token that has a " in it
int pos = vTmp.indexOf("\"");
l++;
if (pos < 0) { // no "
v2 = v2+" "+vTmp;
} else {
v2 = v2+" "+vTmp.substring(0,pos);
break;
}
}
String v3 = val.substring(1)+v2;
val = v3;
//} catch (NoSuchElementException nse2) {
} catch (ArrayIndexOutOfBoundsException nse2) {
val="";
}
}
}
if (debug) System.out.println("#### tok = "+tok+ " val = '"+val+"'");
if (tok.toLowerCase().startsWith(ATTR_UNIT)) {
hdrUnitString = val;
} else if (tok.toLowerCase().startsWith(ATTR_MISSING)) {
infos[i].missingString = val.trim();
try {
infos[i].missingValue = Double.parseDouble(val);
} catch (java.lang.NumberFormatException me) {
infos[i].missingValue = Double.NaN;
}
} else if (tok.toLowerCase().startsWith(ATTR_INTERVAL)) {
infos[i].isInterval = -1;
if (val.toLowerCase().startsWith("t")) infos[i].isInterval = 1;
if (val.toLowerCase().startsWith("f")) infos[i].isInterval = 0;
if (infos[i].isInterval == -1) {
throw new VisADException("TextAdapter: Value of \'interval\' must be \'true\' or \'false\'");
}
} else if (tok.toLowerCase().startsWith(ATTR_ERROR)) {
infos[i].errorEstimate = Double.parseDouble(val);
} else if (tok.toLowerCase().startsWith(ATTR_SCALE)) {
infos[i].scale = Double.parseDouble(val);
} else if (tok.toLowerCase().startsWith(ATTR_OFFSET)) {
infos[i].offset = Double.parseDouble(val);
} else if (tok.toLowerCase().startsWith(ATTR_VALUE)) {
infos[i].fixedValue = val.trim();
numHdrValues++;
} else if (tok.toLowerCase().startsWith(ATTR_COLSPAN)) {
infos[i].colspan = (int)Double.parseDouble(val.trim());
} else if (tok.toLowerCase().startsWith(ATTR_POSITION)) {
String[] stp = val.split(":");
if (stp.length != 2) {
throw new VisADException("TextAdapter: invalid Position parameter in:"+s);
}
hdrColumns[0][i] = Integer.parseInt(stp[0].trim());
hdrColumns[1][i] = Integer.parseInt(stp[1].trim());
} else if (tok.toLowerCase().startsWith(ATTR_FORMAT)) {
infos[i].formatString = val.trim();
} else if (tok.toLowerCase().startsWith(ATTR_TIMEZONE)) {
infos[i].tzString = val.trim();
} else {
throw new VisADException("TextAdapter: invalid token name: "+s);
}
}
}
}
if(properties!=null) {
for(int headerIdx=0;headerIdx<infos.length;headerIdx++) {
String value = (String)properties.get(infos[headerIdx].name+".value");
if(value!=null) infos[headerIdx].fixedValue = value;
}
}
if (debug)
System.out.println("hdr name = "+infos[i]+" units="+
hdrUnitString+
" miss="+infos[i].missingValue+" interval="+infos[i].isInterval+
" errorest="+infos[i].errorEstimate+" scale="+infos[i].scale+
" offset="+infos[i].offset+" pos="+hdrColumns[0][i]+":"+
hdrColumns[1][i]);
Unit hdrUnit = null;
if (hdrUnitString != null &&
!hdrUnitString.trim().equalsIgnoreCase("null") ) {
hdrUnitString = hdrUnitString.trim();
try {
hdrUnit = visad.data.units.Parser.parse(hdrUnitString);
} catch (Exception ue) {
try {
hdrUnitString = hdrUnitString.replace(' ','_');
hdrUnit = visad.data.units.Parser.parse(hdrUnitString);
} catch (Exception ue2) {
System.out.println("Unit name problem:"+ue+" with: "+hdrUnitString);
hdrUnit = null;
}
}
if(hdrUnit!=null) {
//We clone this unit so it has the original unit string, not the SI unit we get from the parser
try {
hdrUnit = hdrUnit.clone(hdrUnitString);
} catch(Exception ignoreThis) {}
}
}
if (debug) System.out.println("#### assigned Unit as u="+hdrUnit);
String rttemp = infos[i].name.trim();
if (rttemp.indexOf("(Text)") == -1) {
int parenIndex = rttemp.indexOf("(");
if (parenIndex < 0) parenIndex = rttemp.indexOf("[");
if (parenIndex < 0) parenIndex = rttemp.indexOf("{");
if (parenIndex < 0) parenIndex = rttemp.indexOf(" ");
String rtname = parenIndex < 0 ? rttemp.trim() : rttemp.substring(0,parenIndex);
RealType rt = RealType.getRealType(rtname, hdrUnit, null, infos[i].isInterval);
// System.err.println("rtname:" + rtname + " " + rt);
if (rt == null) { // tried to re-use with different units
if (debug) System.out.println("#### rt was returned as null");
if (debug && hdrUnit != null)
System.out.println("#### Could not make RealType using specified Unit ("+hdrUnitString+") for parameter name: "+rtname);
//Make the realType with just the name
rt = RealType.getRealType(rtname);
//Check if the realtype unit works with the unit from the header
if(rt.getDefaultUnit()!=null && hdrUnit!=null) {
if(!Unit.canConvert(rt.getDefaultUnit(), hdrUnit)) {
rt = null;
}
} else if(hdrUnit!=null) {
rt = null;
}
//If the realtype is bad then we make a new one with the unitsuffix and add
//a name change entry so later we change the mathtype string to have the new name
if(rt == null) {
rt = DataUtility.getUniqueRealType(rtname,hdrUnit, null, infos[i].isInterval);
if (rt != null) {
String newName = rt.getName();
nameChanges.add(new String[]{rtname, newName});
infos[i].name = newName;
if(debug)
System.out.println("made new realtype:" + rt + " unit:" + rt.getDefaultUnit());
}
}
}
//Add the realType here because its possible that it can be GC'ed
//and removed from the global list of realtypes before we
//get back to it. Then the MathType.stringToType(maps) below
//will produce a realtype with no units
realTypes.add(rt);
// get a compatible unit, if necessary
if (rt.equals(visad.RealType.Time)) {
GOTTIME = true;
if (debug) System.out.println("#### found a visad.RealType.Time component");
} else {
GOTTIME = false;
}
if (hdrUnit == null) hdrUnit = rt.getDefaultUnit();
if(debug) System.out.println("#### retrieve units from RealType = "+hdrUnit);
}
infos[i].unit = hdrUnit;
}
for(String[] tuple: nameChanges) {
if(debug) System.err.println ("changing mathtype component from:" + tuple[0] +" to:" + tuple[1]);
maps = maps.replaceAll("(,|\\() *" + tuple[0]+" *(,|\\))", "$1" + tuple[1]+"$2");
}
// get the MathType of the function
MathType mt = null;
try {
mt = MathType.stringToType(maps);
} catch (Exception mte) {
mte.printStackTrace();
throw new VisADException("TextAdapter: MathType badly formed or missing: "+maps);
}
if (debug) {
System.out.println(mt);
new visad.jmet.DumpType().dumpMathType(mt,System.out);
}
//Note, we need to have a reference to the realTypes list somewhere
//after the above call to stringToType so that the list doesn't get gc'ed
//and the realtypes it contains don't get gc'ed
if(realTypes.size()==0) {
}
// now get the names of the domain variables and range variables.
String[] domainNames = null;
String[] rangeNames = null;
int numDom = 0;
int numRng = 0;
RealTupleType domType;
TupleType rangeType;
if (mt instanceof FunctionType) {
domType = ((FunctionType)mt).getDomain();
numDom = domType.getDimension();
domainNames = new String[numDom];
for (int i=0; i<numDom; i++) {
MathType comp = domType.getComponent(i);
domainNames[i] = ((RealType)comp).toString().trim();
if (debug) System.out.println("dom "+i+" = "+domainNames[i]);
}
// debug =true;
rangeType = (TupleType) ((FunctionType)mt).getRange();
numRng = rangeType.getDimension();
rangeNames = new String[numRng];
rangeSets = new Set[numRng];
for (int i=0; i<numRng; i++) {
MathType comp = rangeType.getComponent(i);
rangeNames[i] = (comp).toString().trim();
if (debug) System.out.println("range "+i+" = "+rangeNames[i]);
if (comp instanceof RealType) {
rangeSets[i] = ((RealType) comp).getDefaultSet();
if (rangeSets[i] == null) {
if (comp.equals(RealType.Time)) {
rangeSets[i] = new DoubleSet(new SetType(comp));
} else {
rangeSets[i] = new FloatSet(new SetType(comp));
}
}
} else {
rangeSets[i] = null; // something else is wrong here...
}
if (debug) System.out.println("#### rangeSet = "+rangeSets[i]);
;
}
} else {
throw new visad.VisADException("TextAdapter: Math Type is not a simple FunctionType");
}
// now for each header label, determine if it's a domain or
// range component -- and if so, which one.
// also, if it's a domain component, allow for name(first:last[:number])
//
// and if none of the domain components appear in the list, then
// they are computed as name(0:N-1)
int[] domainPointer = new int[numDom];
double[][] domainRanges = new double[3][numDom]; // min, max, numb
boolean[] gotDomainRanges = new boolean[numDom];
domainErrorEstimates = new double[numDom];
domainUnits = new Unit[numDom];
rangeErrorEstimates = new double[numRng];
rangeUnits = new Unit[numRng];
int countDomain = 0;
for (int i=0; i<numDom; i++) {
domainPointer[i] = -1;
gotDomainRanges[i] = false;
domainErrorEstimates[i] = Double.NaN;
domainUnits[i] = null;
}
int[] rangePointer = new int[numRng];
int countRange = 0;
for (int i=0; i<numRng; i++) {
rangePointer[i] = -1;
rangeErrorEstimates[i] = Double.NaN;
rangeUnits[i] = null;
}
int countValues = -1;
values_to_index = new int[3][nhdr];
for (int i=0; i<nhdr; i++) {
values_to_index[0][i] = -1; // points to domains
values_to_index[1][i] = -1; // points to ranges
values_to_index[2][i] = -1; // points to names/units/etc
countValues ++;
String name = infos[i].name;
// see if it's a domain name
boolean gotName = false;
// is there a "min:max" clause?
String test_name = name;
int n = test_name.indexOf("(");
if (n != -1) {
// but allow for "(Text)"
if ((test_name.indexOf("(Text)")) == -1) {
test_name = name.substring(0,n).trim();
countValues --; // this value wont appear in data!
countDomain --; // and is a pre-defined, linear set
}
}
// try to find the column header name in the domain name list
for (int k=0; k<numDom; k++) {
if (test_name.equals(domainNames[k]) ) {
domainPointer[k] = countValues;
domainErrorEstimates[k] = infos[i].errorEstimate;
domainUnits[k] = infos[i].unit;
gotName = true;
countDomain ++;
// now see if a list is given...
if (n != -1) {
try {
String ss = name.substring(n+1,name.length()-1);
String[] sct = ss.split(":");
String first = sct[0].trim();
String second = sct[1].trim();
String third = "1";
if (sct.length == 3) third = sct[2].trim();
domainRanges[0][k] = Double.parseDouble(first);
domainRanges[1][k] = Double.parseDouble(second);
domainRanges[2][k] = Double.parseDouble(third);
gotDomainRanges[k] = true;
} catch (Exception ef) {
throw new VisADException(
"TextAdapter: Error while interpreting min:max values for domain "+name);
}
} else if (countValues > -1) { // if no list, get from file
values_to_index[0][countValues] = k;
values_to_index[2][countValues] = i;
}
break;
}
}
if (gotName) continue;
// or see if its a range name...
for (int k=0; k<numRng; k++) {
if (name.equals(rangeNames[k]) ) {
rangePointer[k] = countValues;
rangeErrorEstimates[k] = infos[i].errorEstimate;
rangeUnits[k] = infos[i].unit;
countRange ++;
values_to_index[1][countValues] = k;
values_to_index[2][countValues] = i;
gotName = true;
}
}
}
// huge debug printout...
// *****************************************************************
if (debug) {
System.out.println("countDom/numDom="+countDomain+" "+numDom);
System.out.println("countRange/numRng="+countRange+" "+numRng);
System.out.println("Domain info:");
for (int i=0; i<numDom; i++) {
System.out.println("Dom name / index = "+domainNames[i]+" "+
domainPointer[i]);
if (gotDomainRanges[i]) {
System.out.println(" ..."+domainRanges[0][i]+" "+
domainRanges[1][i]+" "+domainRanges[2][i]);
}
}
System.out.println("Range info:");
for (int i=0; i<numRng; i++) {
System.out.println("Rng name / index / error est = "+rangeNames[i]+" "+
rangePointer[i]+ " " + rangeErrorEstimates[i] +" "+
rangeUnits[i]);
}
System.out.println("values_to_index pointers = ");
for (int i=0; i<nhdr; i++) {
System.out.println(" inx / value = "+i+
" "+values_to_index[0][i]+" "+values_to_index[1][i]+
" "+values_to_index[2][i]);
}
}
// ***************************************************************
// for each line of text, put the values into the ArrayList
ArrayList domainValues = new ArrayList();
ArrayList rangeValues = new ArrayList();
ArrayList tupleValues = new ArrayList();
boolean tryToMakeTuple = true;
Tuple tuple = null;
String dataDelim = DELIM;
boolean isRaster = false;
int numElements = 1;
// in the 'raster array' case, the numRng value will be 1,
// along with the countRange. numDomain must be 2.
// if the domain is 2D, then get values from the first
// matching column to the end...
if (countRange == 1 && numRng == 1 &&
numDom == 2 && countDomain < 2) isRaster = true;
Real[] prototypeReals = new Real[nhdr];
TupleType tupleType = null;
int index;
int lineCnt = 0;
while (true) {
String line = readLine(bis);
if (debug) System.out.println("read:"+line);
if (line == null) break;
if(skipPattern!=null && skipPattern.matcher(line).find()) continue;
if((index=line.indexOf("="))>=0) { // fixed value
String name = line.substring(0,index).trim();
String value = line.substring(index+1).trim();
boolean foundIt = false;
for(int paramIdx=0;paramIdx<infos.length;paramIdx++) {
if(infos[paramIdx].isParam(name)) {
if(infos[paramIdx].fixedValue==null) {
numHdrValues++;
}
infos[paramIdx].fixedValue = value;
foundIt = true;
break;
}
}
if(!foundIt) {
throw new VisADException(
"TextAdapter: Cannot find field with name:" +name +" from line:" + line);
}
continue;
}
if (dataDelim == null) {
if (line.indexOf(BLANK) != -1) dataDelim = BLANK_DELIM;
if (line.indexOf(COMMA) != -1) dataDelim = COMMA;
if (line.indexOf(SEMICOLON) != -1) dataDelim = SEMICOLON;
if (line.indexOf(TAB) != -1) dataDelim = TAB;
if (debug) System.out.println("Using data delimiter = "+
((dataDelim == null)
? "null"
: dataDelim + " (" + (dataDelim.getBytes())[0] +")"));
}
// squeeze out extra blank spaces
if (dataDelim.equals(BLANK) || dataDelim.equals(BLANK_DELIM)) {
//System.out.println("line before squeeze: " + line);
line = line.replaceAll("\\s++", " ").trim();
//System.out.println("line after squeeze: " + line);
}
String[] tokens = line.split(dataDelim);
int n = tokens.length;
if (n < 1) continue; // something is wrong if this happens!
lineCnt++;
double [] dValues = null;
double [] rValues = null;
Data [] dataArray= null;
if (streamProcessor==null) {
dValues = new double[numDom];
}
if (isRaster) {
if (debug) System.out.println("probably a raster...");
boolean gotFirst = false;
int rvaluePointer = 0;
int irange = 0;
for (int i=0; i<n; i++) {
String sa = tokens[i];
if (i >= nhdr) { // are we past where domain would be found?
if (!gotFirst) {
throw new VisADException(
"TextAdapter: Cannot find first raster value");
}
rvaluePointer ++;
rValues[rvaluePointer] = getVal(sa, irange);
} else { // or are we still looking for domain?
if (values_to_index[0][i] != -1) {
dValues[values_to_index[0][i]] = getVal(sa, i);
}
if (gotFirst) { // already gathering data
rvaluePointer ++;
rValues[rvaluePointer] = getVal(sa, irange);
} else {
if (values_to_index[1][i] != -1) {
// cannot dimension the array until we have found
// the first set of range values!!
rValues = new double[n - i];
irange = i;
rValues[rvaluePointer] = getVal(sa, irange);
gotFirst = true;
}
}
}
}
} else { // is probably NOT a raster
dataArray = new Data[numRng];
if (debug) System.out.println("probably not a raster...");
if (streamProcessor==null) {
rValues = new double[numRng];
}
MathType thisMT;
if (n > nhdr) n = nhdr; // in case the # tokens > # parameters
n +=numHdrValues;
int tokenIdx = 0; // token counter
for (int i=0; i<nhdr; i++) { // loop over the columns
String sa=null;
if(infos[i].fixedValue!=null) {
sa = infos[i].fixedValue;
} else if (tokenIdx >= tokens.length) { // more params than tokens
sa = ""; // need to have a missing value
} else {
sa = tokens[tokenIdx++].trim();
int moreColumns = infos[i].colspan-1;
while (moreColumns>0) {
sa = sa + " " + tokens[tokenIdx++].trim();
moreColumns--;
}
}
String sThisText;
if (values_to_index[0][i] != -1) {
if(dValues!=null)
dValues[values_to_index[0][i]] = getVal(sa, i);
} else if (values_to_index[1][i] != -1) {
int tupleIndex = values_to_index[1][i];
int infosIndex = values_to_index[2][i];
thisMT = rangeType.getComponent(tupleIndex);
if (thisMT instanceof TextType) {
// if Text, then check for quoted string
if (sa.startsWith("\"")) {
if (sa.endsWith("\"")) { // if single token ends with quote
String sa2 = sa.substring(1,sa.length()-1);
sThisText = sa2;
} else {
// TODO: work on this
try {
String delim =
dataDelim.equals(BLANK_DELIM) ? BLANK : dataDelim;
String sa2="";
for (int q=tokenIdx; q < tokens.length; q++) {
String saTmp = tokens[q];
// find next token that has a " in it
int pos = saTmp.indexOf("\"");
tokenIdx++;
if (pos < 0) { // no dataDelim
sa2 = sa2+delim+saTmp;
} else {
sa2 = sa2+saTmp.substring(0,pos);
//tokens[tokenIdx] = saTmp.substring(pos+1);
break;
}
}
//sThisText = sa.substring(1)+sa2;
sThisText = sa.substring(1)+delim+sa2;
//} catch (NoSuchElementException nse) {
} catch (ArrayIndexOutOfBoundsException nse) {
sThisText = "";
}
}
if (debug) System.out.println("#### Text value='"+sThisText+"'");
// if not quoted, then take "as is"
} else {
sThisText = sa;
}
// now make the VisAD Data
try {
dataArray[tupleIndex] =
new Text((TextType)thisMT, sThisText);
if (debug) System.out.println("dataArray[" +
tupleIndex + "] = " +
dataArray[tupleIndex]);
} catch (Exception e) {
System.out.println(" Exception converting " +
thisMT + " to TextType " + e);
}
// if not Text, then treat as numeric
} else {
// if(true) continue;
double value = getVal(sa,i);
if(rValues!=null)
rValues[tupleIndex] = value;
try {
if(prototypeReals[i]==null) {
prototypeReals[i] = new Real((RealType) thisMT, value, infos[infosIndex].unit);
}
dataArray[tupleIndex] =
prototypeReals[i].cloneButValue(value);
if(debug)System.out.println("dataArray[" +
tupleIndex + "] = " +
dataArray[tupleIndex]);
} catch (Exception e) {
System.out.println(" Exception converting " + thisMT + " " + e);
e.printStackTrace();
}
}
}
}
}
if(tryToMakeTuple) {
try {
if (dataArray != null) {
if (streamProcessor!=null) {
streamProcessor.processValues(dataArray);
} else {
if(tupleType == null) {
tuple = new Tuple(dataArray);
tupleType = (TupleType)tuple.getType();
} else {
tuple = new Tuple(tupleType, dataArray, false, false);
}
}
}
} catch (visad.TypeException te) {
// do nothing: it means they are all reals
// tuple = new RealTuple(dataArray);
tuple = null;
tryToMakeTuple = false;
} catch(NullPointerException npe) {
for(int i=0;i<dataArray.length;i++) {
if(dataArray[i] == null) {
throw new IllegalArgumentException("An error occurred reading line number:" + lineCnt+" column number:" + (i+1)+"\n" +
line);
}
}
throw npe;
}
}
if (streamProcessor==null) {
if(dValues!=null)
domainValues.add(dValues);
if(rValues!=null)
rangeValues.add(rValues);
if (tuple != null)
tupleValues.add(tuple);
}
if (isRaster) numElements = rValues.length;
if(onlyReadOneLine) break;
}
if (streamProcessor!=null) {
bis.close();
return;
}
int numSamples = rangeValues.size(); // # lines of data
if (numSamples == 0) {
throw new VisADException("No data available to read");
}
// ***********************************************************
if (debug) {
try {
System.out.println("domain size = "+domainValues.size());
double[] dt = (double[]) domainValues.get(1);
System.out.println("domain.array[0] = "+dt[0]);
System.out.println("range size = "+rangeValues.size());
System.out.println("# samples = "+numSamples);
} catch (Exception er) {System.out.println("out range");}
}
// ***********************************************************
// make Linear1DSets for each possible domain component
Linear1DSet[] lset = new Linear1DSet[numDom];
boolean keepConstant = false;
int numVal = numRng;
if (numDom == 1) numVal = numSamples;
if (numDom == 2 && numRng == 1 && numElements > 1) numVal = numElements;
if (numDom > 2 && numRng == 1 && numElements == 1) {
numVal = numSamples / (2 * numDom);
keepConstant = true;
}
for (int i=0; i<numDom; i++) {
if (gotDomainRanges[i]) {
// if domain was given with a count, use it for 'raster'-type
if (numDom == 2 && numRng == 1 && numElements == 1)
numVal = (int) domainRanges[2][i];
lset[i] = new Linear1DSet(domType.getComponent(i), domainRanges[0][i],
domainRanges[1][i], numVal);
if (debug) System.out.println("lset from domain = "+lset[i]);
} else if (domainPointer[i] == -1 ) {
lset[i] = new Linear1DSet(0., (double)(numVal-1), numVal);
if (debug) System.out.println("lset from range = "+lset[i]);
} else {
lset[i] = null;
}
if (!keepConstant) numVal = numSamples;
}
// now make up the actual domain sets for the function
Set domain = null;
if (numDom == 1) { // for 1-D domains
if (lset[0] == null) {
domain = createAppropriate1DDomain(domType, numSamples, domainValues);
} else {
domain = lset[0];
}
} else if (numDom == 2) { // for 2-D domains
if (lset[0] != null && lset[1] != null) {
domain = new Linear2DSet(domType, lset);
} else {
float[][] samples = new float[numDom][numSamples];
for (int k = 0; k < numDom; k++) {
if (lset[k] == null) {
samples[k] = (getDomSamples(k, numSamples, domainValues))[0];
} else {
samples[k] = (lset[k].getSamples())[0];
}
}
domain = (Set) new Irregular2DSet(domType, samples);
}
} else if (numDom == 3) { // for 3-D domains
if (lset[0] != null && lset[1] != null && lset[2] != null) {
domain = new Linear3DSet(domType, lset);
} else {
float[][] samples = new float[numDom][numSamples];
for (int k = 0; k < numDom; k++) {
if (lset[k] == null) {
samples[k] = (getDomSamples(k, numSamples, domainValues))[0];
} else {
samples[k] = (lset[k].getSamples())[0];
}
}
domain = (Set) new Irregular3DSet(domType, samples);
}
} else { // N-D domains (can only use LinearSets!!
boolean allLinear = true;
for (int k = 0; k<numDom; k++) {
if (lset[k] == null) allLinear = false;
}
if (allLinear) {
if (debug) System.out.println("#### Making LinearNDset");
domain = new LinearNDSet(domType, lset);
} else {
if (debug) System.out.println("#### Making IrregularSet");
float[][] samples = new float[numDom][numSamples];
for (int k=0; k<numDom; k++) {
if (lset[k] == null) {
samples[k] = (getDomSamples(k, numSamples, domainValues))[0];
} else {
samples[k] = (lset[k].getSamples())[0];
}
}
domain = new IrregularSet(domType, samples);
}
}
try {
ff = new FlatField((FunctionType) mt, domain,
null, null, rangeSets, rangeUnits);
} catch (FieldException fe) {
field = new FieldImpl((FunctionType) mt, domain);
} catch (UnitException fe) {
System.out.println("#### Problem with Units; attempting to make Field anyway");
field = new FieldImpl((FunctionType) mt, domain);
}
//*************************************************
if (debug) {
if (ff != null) {
System.out.println("ff.Length "+ff.getLength());
System.out.println("ff.getType "+ff.getType());
}
if (field != null) {
System.out.println("field.Length "+field.getLength());
System.out.println("field.getType "+field.getType());
}
System.out.println("domain = "+domain);
System.out.println("size of a = "+numRng+" x "+(numSamples*numElements));
}
//*************************************************
double[][]a = new double[numRng][numSamples * numElements];
Tuple[] at = new Tuple[numSamples];
// if this is a raster then the samples are in a slightly
// difielderent form ...
if (isRaster) {
int samPointer = 0;
for (int i=0; i<numSamples; i++) {
double[] rs = (double[])(rangeValues.get(i));
for (int j=0; j<numElements; j++) {
a[0][samPointer] = rs[j];
samPointer ++;
}
}
} else {
for (int i=0; i<numSamples; i++) {
double[] rs = (double[])(rangeValues.get(i));
for (int j=0; j<numRng; j++) {
a[j][i] = rs[j];
}
if (!tupleValues.isEmpty()) {
at[i] = (Tuple) tupleValues.get(i);
}
}
}
// set samples
if (debug) System.out.println("about to field.setSamples");
try {
if (ff != null) {
if (debug) System.out.println("#### ff is not null");
ff.setSamples(a, false);
field = (Field) ff;
} else {
if (debug) System.out.println("#### ff is null..use FieldImpl");
field.setSamples(at, false);
}
} catch (Exception ffe) {ffe.printStackTrace(); }
// make up error estimates and set them
ErrorEstimate[] es = new ErrorEstimate[numRng];
for (int i=0; i<numRng; i++) {
es[i] = new ErrorEstimate(a[i], rangeErrorEstimates[i], rangeUnits[i]);
}
try {
((FlatField) field).setRangeErrors(es);
} catch (FieldException fe) {
if (debug) System.out.println("caught "+fe);
// not a flatfield
// don't setRangeErrors
} catch (ClassCastException cce) {
if (debug) System.out.println("caught "+cce);
// not a flatfield
// don't setRangeErrors
}
if (debug) {
new visad.jmet.DumpType().dumpDataType(field,System.out);
System.out.println("field = "+field);
}
bis.close();
}
// munges a pseudo MathType string into something legal
private String makeMT(String s) {
int k = s.indexOf("->");
if (k < 0) {
// System.out.println("TextAdapter: invalid MathType form; -> required");
return null;
}
StringBuffer sb = new StringBuffer("");
for (int i=0; i<s.length(); i++) {
String r = s.substring(i,i+1);
if (!r.equals(" ") && !r.equals("\t") && !r.equals("\n")) {
sb.append(r);
}
}
String t = sb.toString();
k = t.indexOf("->");
if (t.charAt(k-1) != ')' ) {
if (t.charAt(k+2) != '(' ) {
String t2 = "("+t.substring(0,k) + ")->("+t.substring(k+2)+")";
t = t2;
} else {
String t2 = "("+t.substring(0,k) + ")"+t.substring(k);
t = t2;
}
} else if (t.charAt(k+2) != '(' ) {
String t2 = t.substring(0,k+2)+"("+t.substring(k+2)+")";
t = t2;
}
if (!t.startsWith("((") ) {
String t2= "("+t+")";
t = t2;
}
return t;
}
private static final boolean isText(String s)
{
final int len = (s == null ? -1 : s.length());
if (len <= 0) {
// well, it's not really *binary*, so pretend it's text
return true;
}
for (int i = 0; i < len; i++) {
final char ch = s.charAt(i);
if (Character.isISOControl(ch) && !Character.isWhitespace(ch)) {
// we might want to special-case formfeed/linefeed/newline here...
return false;
}
}
return true;
}
/**
* generate a DateTime from a string
* @param string - Formatted date/time string
*
* @return - the equivalent VisAD DateTime for the string
*
* (lifted from au.gov.bom.aifs.common.ada.VisADXMLAdapter.java)
*/
private static visad.DateTime makeDateTimeFromString(String string,
String format, String tz)
throws java.text.ParseException
{
visad.DateTime dt = null;
// try to parse the string using the supplied DateTime format
try {
if(dateParsers!=null) {
for(int i=0;i<dateParsers.size();i++) {
DateParser dateParser = (DateParser) dateParsers.get(i);
dt = dateParser.createDateTime(string, format, TimeZone.getTimeZone(tz));
if(dt !=null) {
return dt;
}
}
}
String key = format+"__" + tz;
SimpleDateFormat sdf = (SimpleDateFormat) formats.get(key);
if(sdf == null) {
sdf = new SimpleDateFormat();
sdf.setTimeZone(TimeZone.getTimeZone(tz));
sdf.applyPattern(format);
formats.put(key,sdf);
}
Date d = sdf.parse(string);
dt = new DateTime(d);
// dt = visad.DateTime.createDateTime(string, format, TimeZone.getTimeZone(tz));
} catch (VisADException e) {}
if (dt==null) {
throw new java.text.ParseException("Couldn't parse visad.DateTime from \""
+string+"\"", -1);
} else {
return dt;
}
}
/** A set of cached simpledateformats */
private static Hashtable formats = new Hashtable();
/** This list of DateFormatter-s will be checked when we are making a DateTime wiht a given format */
private static List dateParsers;
/** used to allow applications to define their own date parsing */
public static interface DateParser {
/** If this particular DateParser does not know how to handle the give format then this method should return null */
public DateTime createDateTime(String value, String format, TimeZone timezone) throws VisADException;
}
/** used to allow applications to define their own date parsing */
public static void addDateParser(DateParser dateParser) {
if(dateParsers==null) {
dateParsers = new ArrayList();
}
dateParsers.add(dateParser);
}
double getVal(String s, int k) {
int i = values_to_index[2][k];
if (i < 0 || s == null || s.length()<1 || (infos[i].missingString!=null && s.equals(infos[i].missingString))) {
return Double.NaN;
}
HeaderInfo info = infos[i];
// try parsing as a double first
if (info.formatString == null) {
// no format provided : parse as a double
try {
double v;
try {
v = Double.parseDouble(s);
} catch (java.lang.NumberFormatException nfe1) {
//If units are degrees then try to decode this as a lat/lon
// We should probably not rely on throwing an exception to handle this but...
if(info.unit !=null && Unit.canConvert(info.unit, visad.CommonUnit.degree)) {
v=decodeLatLon(s);
} else {
throw nfe1;
}
if(v!=v) throw new java.lang.NumberFormatException(s);
}
if (v == info.missingValue) {
return Double.NaN;
}
v = v * info.scale + info.offset;
return v;
} catch (java.lang.NumberFormatException ne) {
System.out.println("Invalid number format for "+s);
}
} else {
// a format was specified: only support DateTime format
// so try to parse as a DateTime
try{
visad.DateTime dt = makeDateTimeFromString(s, info.formatString, info.tzString);
return dt.getReal().getValue();
} catch (java.text.ParseException pe) {
System.out.println("Invalid number/time format for "+s);
}
}
return Double.NaN;
}
// get the samples from the ArrayList.
float[][] getDomSamples(int comp, int numDomValues, ArrayList domValues) {
float [][] a = new float[1][numDomValues];
for (int i=0; i<numDomValues; i++) {
double[] d = (double[])(domValues.get(i));
a[0][i] = (float)d[comp];
}
return a;
}
/** get the data
* @return a Field of the data read from the file
*
*/
public Field getData() {
return field;
}
/**
* Returns an appropriate 1D domain.
*
* @param type the math-type of the domain
* @param numSamples the number of samples in the domain
* @param domValues domain values are extracted from this array list.
*
* @return a Linear1DSet if the domain samples form an arithmetic
* progression, a Gridded1DDoubleSet if the domain samples are ordered
* but do not form an arithmetic progression, otherwise an Irregular1DSet.
*
* @throws VisADException there was a problem creating the domain set.
*/
private Set createAppropriate1DDomain(MathType type, int numSamples,
ArrayList domValues)
throws VisADException {
if (0 == numSamples) {
// Can't create a domain set with zero samples.
return null;
}
// Extract the first element from each element of the array list.
double[][] values = new double[1][numSamples];
for (int i=0; i<numSamples; ++i) {
double[] d = (double []) domValues.get(i);
values[0][i] = d[0];
}
// This implementation for testing that the values are ordered
// is based on visad.Gridded1DDoubleSet.java
boolean ordered = true;
boolean ascending = values[0][numSamples -1] > values[0][0];
if (ascending) {
for (int i=1; i<numSamples; ++i) {
if (values[0][i] < values[0][i - 1]) {
ordered = false;
break;
}
}
} else {
for (int i=1; i<numSamples; ++i) {
if (values[0][i] > values[0][i - 1]) {
ordered = false;
break;
}
}
}
Set set = null;
if (ordered) {
ArithProg arithProg = new ArithProg();
if (arithProg.accumulate(values[0])) {
// The domain values form an arithmetic progression (ordered and
// equally spaced) so use a linear set.
set = new Linear1DSet(type, values[0][0], values[0][numSamples - 1],
numSamples);
} else {
// The samples are ordered, so use a gridded set.
set = new Gridded1DDoubleSet(type, values, numSamples);
}
} else {
set = new Irregular1DSet(type, Set.doubleToFloat(values));
}
return set;
}
private static class HeaderInfo {
String name;
Unit unit;
double missingValue = Double.NaN;
String missingString;
String formatString;
String tzString = "GMT";
int isInterval = 0;
double errorEstimate=0;
double scale=1.0;
double offset=0.0;
String fixedValue;
int colspan = 1;
boolean isText = false;
public boolean isParam(String param) {
return name.equals(param) || name.equals(param+"(Text)");
}
public String toString() {
return name;
}
}
/**
* Read in the given file and return the processed data
*
* @param file The file to read in
* @return the data
*/
public static Data processFile(String file) throws Exception {
TextAdapter ta = new TextAdapter(file);
System.out.println(ta.getData().getType());
return ta.getData();
}
// uncomment to test
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("Must supply a filename");
System.exit(1);
}
TextAdapter ta = new TextAdapter(args[0]);
System.out.println(ta.getData().getType());
new visad.jmet.DumpType().dumpMathType(ta.getData().getType(),System.out);
new visad.jmet.DumpType().dumpDataType(ta.getData(),System.out);
System.out.println("#### Data = "+ta.getData());
System.out.println("EOF... ");
}
/**
* A cut-and-paste from the IDV Misc method
* Decodes a string representation of a latitude or longitude and
* returns a double version (in degrees). Acceptible formats are:
* <pre>
* +/- ddd:mm, ddd:mm:, ddd:mm:ss, ddd::ss, ddd.fffff ===> [+/-] ddd.fffff
* +/- ddd, ddd:, ddd:: ===> [+/-] ddd
* +/- :mm, :mm:, :mm:ss, ::ss, .fffff ===> [+/-] .fffff
* +/- :, :: ===> 0.0
* Any of the above with N,S,E,W appended
* </pre>
*
* @param latlon string representation of lat or lon
* @return the decoded value in degrees
*/
public static double decodeLatLon(String latlon) {
// first check to see if there is a N,S,E,or W on this
latlon = latlon.trim();
int dirIndex = -1;
int southOrWest = 1;
double value = Double.NaN;
if (latlon.indexOf("S") > 0) {
southOrWest = -1;
dirIndex = latlon.indexOf("S");
} else if (latlon.indexOf("W") > 0) {
southOrWest = -1;
dirIndex = latlon.indexOf("W");
} else if (latlon.indexOf("N") > 0) {
dirIndex = latlon.indexOf("N");
} else if (latlon.indexOf("E") > 0) {
dirIndex = latlon.indexOf("E");
}
if (dirIndex > 0) {
latlon = latlon.substring(0, dirIndex).trim();
}
// now see if this is a negative value
if (latlon.indexOf("-") == 0) {
southOrWest *= -1;
latlon = latlon.substring(latlon.indexOf("-") + 1).trim();
}
if (latlon.indexOf(":") >= 0) { //have something like DD:MM:SS, DD::, DD:MM:, etc
int firstIdx = latlon.indexOf(":");
String hours = latlon.substring(0, firstIdx);
String minutes = latlon.substring(firstIdx + 1);
String seconds = "";
if (minutes.indexOf(":") >= 0) {
firstIdx = minutes.indexOf(":");
String temp = minutes.substring(0, firstIdx);
seconds = minutes.substring(firstIdx + 1);
minutes = temp;
}
try {
value = (hours.equals("") == true)
? 0
: Double.parseDouble(hours);
if ( !minutes.equals("")) {
value += Double.parseDouble(minutes) / 60.;
}
if ( !seconds.equals("")) {
value += Double.parseDouble(seconds) / 3600.;
}
} catch (NumberFormatException nfe) {
value = Double.NaN;
}
} else { //have something like DD.ddd
try {
value = Double.parseDouble(latlon);
} catch (NumberFormatException nfe) {
value = Double.NaN;
}
}
return value * southOrWest;
}
public interface StreamProcessor {
public void processValues(Data[] tuple) throws VisADException ;
}
}