// // PointDataAdapter.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.mcidas; import edu.wisc.ssec.mcidas.*; import edu.wisc.ssec.mcidas.adde.*; import visad.*; import visad.data.units.*; import visad.jmet.MetUnits; import visad.util.DataUtility; import java.util.Vector; import java.util.List; import java.util.ArrayList; /** * A class for adapting the results of an ADDE point data request into a * VisAD Data object. * * @author Don Murray, Unidata */ public class PointDataAdapter { AddePointDataReader reader; FieldImpl field = null; private boolean debug = false; private boolean useAliases = true; private boolean makeUniqueNames = false; private static final String TEXT_EXT = "[Text]"; /** * Construct a PointDataAdapter using the adde request passed as a string. * This will take the data returned from the request and turn it into * VisAD Data objects that can be returned by the getData() call. * * @param addePointRequest - string representing the ADDE request * @throws VisADException bad request, no data available, VisAD error * @see #getData() */ public PointDataAdapter(String addePointRequest) throws VisADException { this(addePointRequest, true); } /** * Construct a PointDataAdapter using the adde request passed as a string. * This will take the data returned from the request and turn it into * VisAD Data objects that can be returned by the getData() call. * * @param addePointRequest - string representing the ADDE request * @param useAliases - for quantities like Latitude, Longitude,etc * alias the RealTypes to the original McIDAS * variable name. * @throws VisADException bad request, no data available, VisAD error * @see #getData() */ public PointDataAdapter(String addePointRequest, boolean useAliases) throws VisADException { this(addePointRequest, useAliases, false); } /** * Construct a PointDataAdapter using the adde request passed as a string. * This will take the data returned from the request and turn it into * VisAD Data objects that can be returned by the getData() call. * * @param addePointRequest - string representing the ADDE request * @param useAliases - for quantities like Latitude, Longitude,etc * alias the RealTypes to the original McIDAS * variable name. * @param makeUniqueNames - if true, make unique names to avoid null Types * @throws VisADException bad request, no data available, VisAD error * @see #getData() */ public PointDataAdapter(String addePointRequest, boolean useAliases, boolean makeUniqueNames) throws VisADException { try { reader = new AddePointDataReader(addePointRequest); debug = addePointRequest.indexOf("debug=true") > 0; this.useAliases = useAliases; this.makeUniqueNames = makeUniqueNames; } catch (AddeException excp) { throw new VisADException("Problem accessing data", excp); } makeField(); } // out of this will either come a FieldImpl, a ObservationDBImpl, // or a StationObDBImpl private void makeField() throws VisADException { // First, let's make a generic FieldImpl from the data // the structure will be index -> parameter tuple // get all the stuff from the reader int[][] data; String[] units; String[] params; Unit[] defaultUnits; int[] scalingFactors; try { data = reader.getData(reader.OB_ORDER); units = reader.getUnits(); params = reader.getParams(); scalingFactors = reader.getScales(); } catch (AddeException ae) { throw new VisADException("Error retrieving data info", ae); } //int numObs = data[0].length; int numObs = data.length; if (numObs == 0) throw new VisADException("No data available"); if (debug) System.out.println("Number of observations = " + numObs); RealType domainType = RealType.getRealType("index"); Integer1DSet domain = new Integer1DSet(domainType, numObs); // now make range (Tuple) type MetUnits unitTranslator = new MetUnits(); int numParams = params.length; if (debug) System.out.println("Number of parameters = " + numParams); ScalarType[] types = new ScalarType[numParams]; defaultUnits = new Unit[numParams]; Vector<Unit> usedUnits = new Vector<Unit>(); boolean noText = true; int numDouble = 0; int numString = 0; List<RealType> realTypes = new ArrayList<RealType>(); List<TextType> textTypes = new ArrayList<TextType>(); for (int i = 0; i < numParams; i++) { // get the name String name = params[i]; if (units[i].equalsIgnoreCase("CHAR")) { noText = false; numString++; if (debug) { System.out.println(params[i] + " has units of CHAR"); } TextType textType = TextType.getTextType(params[i]); if (textType == null && makeUniqueNames) { // might be a RealType name textType = TextType.getTextType(params[i]+TEXT_EXT); } if (textType == null) { throw new VisADException("can't create TextType for " + params[i]); } textTypes.add(textType); types[i] = textType; defaultUnits[i] = null; } else { // make the unit Unit unit = null; try { unit = (!name.equalsIgnoreCase("LON") ) ? Parser.parse(unitTranslator.makeSymbol(units[i])) : Parser.parse("degrees_west"); // fix McIDAS conv. } catch (NoSuchUnitException ne) { if (debug) System.out.println("Unknown unit: " + units[i] + " for " + name); unit = null; } catch (ParseException pe) { unit = null;} defaultUnits[i] = unit; if (debug) { System.out.println(params[i] + " has units " + unit); System.out.println("scaling factor = " + scalingFactors[i]); } numDouble++; types[i] = getQuantity(params[i], unit); realTypes.add((RealType) types[i]); } } TupleType rangeType; if (noText) // all Reals { RealType[] newTypes = new RealType[types.length]; for (int i = 0; i < types.length; i++) { newTypes[i] = (RealType) types[i]; } rangeType = new RealTupleType(newTypes); } else // all Texts or mixture of Text and Reals { rangeType = DoubleStringTuple.makeTupleType(realTypes, textTypes); } // make the field FunctionType functionType = new FunctionType(domainType, rangeType); field = new FieldImpl(functionType, domain); if (debug) System.out.println("filling in data" ); long millis = System.currentTimeMillis(); // now, fill in the data Scalar[] firstTuple = null; // use this for saving memory/time Unit[] actualUnits = null; Real[] protos = (numDouble > 0) ? new Real[numDouble] : null; for (int i = 0; i < numObs; i++) { double[] values = new double[numDouble]; String[] strings = new String[numString]; int stringIdx = 0; int doubleIdx = 0; for (int j = 0; j < numParams; j++) { if (types[j] instanceof TextType) { String text = McIDASUtil.intBitsToString(data[i][j]); strings[stringIdx++] = text; } else { double value = data[i][j] == McIDASUtil.MCMISSING ? Double.NaN : data[i][j]/Math.pow(10.0, (double) scalingFactors[j] ); values[doubleIdx] = value; if (firstTuple == null) { // create the prototypes try { protos[doubleIdx] = new Real( (RealType) types[j], value, defaultUnits[j]); } catch (VisADException excp) { // units problem protos[doubleIdx] = new Real((RealType) types[j], value); } usedUnits.add(((Real) protos[doubleIdx]).getUnit()); } doubleIdx++; } } if (actualUnits == null && !usedUnits.isEmpty()) { actualUnits = new Unit[usedUnits.size()]; for (int k = 0; k < usedUnits.size(); k++) { actualUnits[k] = (Unit) usedUnits.get(k); } } try { Data sample = (noText == true) ? new DoubleTuple( (RealTupleType)rangeType, protos, values, actualUnits) : new DoubleStringTuple( rangeType, protos, values, strings, actualUnits); field.setSample(i, sample, false, (i==0)); // don't make copy, don't // check type after first } catch (VisADException e) {e.printStackTrace();} catch (java.rmi.RemoteException e) {;} if (firstTuple == null) { firstTuple = protos; } } if (debug) { System.out.println("data fill took " + (System.currentTimeMillis() - millis) + " ms"); } } /** * Get the VisAD Data object that represents the output from the * request. * * @return requested data. The format is a FieldImpl of * (obnum -> (tuple of parameters) */ public DataImpl getData() { return field; } /** * test with 'java visad.data.mcidas.PointDataAdapter args' * @param args ADDE point data request */ public static void main(String[] args) throws Exception { if (args.length == 0) { System.out.println("You must specify an ADDE Point Data URL"); System.exit(-1); } try { PointDataAdapter pda = new PointDataAdapter(args[0]); Field data = (Field) pda.getData(); //System.out.println(data.getType()); visad.python.JPythonMethods.dumpTypes(data); /* int length = data.getDomainSet().getLength() - 1; System.out.println( "Sample "+ length + " = " + data.getSample(length)); */ } catch (VisADException ve) { System.out.println("Error reading data"); } } /** * First cut at a standard quantities database. */ private RealType getQuantity(String name, Unit unit) throws VisADException { RealType type = null; if (name.equalsIgnoreCase("lat")) { type = RealType.Latitude; } else if (name.equalsIgnoreCase("lon")) { type = RealType.Longitude; //} else if (name.equalsIgnoreCase("z") || // name.equalsIgnoreCase("zs") ) { } else if (name.equalsIgnoreCase("zs")) { type = RealType.Altitude; } else if (name.equalsIgnoreCase("z") && useAliases) { type = RealType.Altitude; } else { type = RealType.getRealType(name, unit); if (type == null) { //System.err.println("Problem creating RealType with name " + // name + " and unit " + unit); if (makeUniqueNames) { type = DataUtility.getUniqueRealType(name, unit); } else { type = RealType.getRealTypeByName(name); } if (type == null) { // Still a problem throw new VisADException( "getQuantity(): Couldn't create RealType for " + name); } //System.err.println("Using RealType with name " + name); } } if (useAliases) { if (RealType.getRealTypeByName(name) == null) { type.alias(name); } else if (!RealType.getRealTypeByName(name).equals(type)) { // alias used throw new VisADException( "getQuanity(): Two different variables can't have the same alias"); } } return type; } }