//
// 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 usedUnits = new Vector();
    boolean noText = true;
    int numDouble = 0;
    int numString = 0;
    List realTypes = new ArrayList();
    List textTypes = new ArrayList();
    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;
  }

}

 

Ads help maintain this website.