All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.ft.point.standard.TableAnalyzer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.ft.point.standard;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Structure;
import ucar.nc2.Variable;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.ft.FeatureDatasetFactoryManager;
import ucar.nc2.ft.point.standard.plug.BuoyShipSynop;
import ucar.nc2.ft.point.standard.plug.CFpointObs;
import ucar.nc2.ft.point.standard.plug.CFpointObsExt;
import ucar.nc2.ft.point.standard.plug.CdmDirect;
import ucar.nc2.ft.point.standard.plug.Cosmic;
import ucar.nc2.ft.point.standard.plug.FslRaob;
import ucar.nc2.ft.point.standard.plug.FslWindProfiler;
import ucar.nc2.ft.point.standard.plug.GempakCdm;
import ucar.nc2.ft.point.standard.plug.Iridl;
import ucar.nc2.ft.point.standard.plug.Jason;
import ucar.nc2.ft.point.standard.plug.Madis;
import ucar.nc2.ft.point.standard.plug.MadisAcars;
import ucar.nc2.ft.point.standard.plug.NdbcCoards;
import ucar.nc2.ft.point.standard.plug.Nldn;
import ucar.nc2.ft.point.standard.plug.RafNimbus;
import ucar.nc2.ft.point.standard.plug.SimpleTrajectory;
import ucar.nc2.ft.point.standard.plug.Suomi;
import ucar.nc2.ft.point.standard.plug.UnidataPointObs;

/**
 * Analyzes the coordinate systems of a dataset to try to identify the Feature Type and the
 *   structure of the data.
 * Used by PointDatasetStandardFactory.
 *
 * @author caron
 * @since Mar 20, 2008
 */
public class TableAnalyzer {
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TableAnalyzer.class);

  static private final List conventionList = new ArrayList<>();
  static private boolean userMode = false;
  static private final boolean debug = false;

  // search in the order added
  static {

    registerAnalyzer("CDM", CdmDirect.class, null);
    registerAnalyzer(CDM.CF_EXTENDED, CFpointObsExt.class, null);

    registerAnalyzer("CF-1.", CFpointObs.class, new ConventionNameOk() {
      @Override
      public boolean isMatch(String convName, String wantName) {
        return convName.startsWith(wantName); //  && !convName.equals("CF-1.0"); // throw 1.0 to default analyser
      }
    });

    registerAnalyzer("GEMPAK/CDM", GempakCdm.class, null);
    registerAnalyzer("Unidata Observation Dataset v1.0", UnidataPointObs.class, null);

    registerAnalyzer("Cosmic", Cosmic.class, null);
    registerAnalyzer("Jason", Jason.class, null);
    registerAnalyzer("FslWindProfiler", FslWindProfiler.class, null);
    registerAnalyzer("MADIS-ACARS", MadisAcars.class, null); // must be before Madis
    registerAnalyzer("MADIS surface observations, v1.0", Madis.class, null);  // must be before FslRaob
    registerAnalyzer("FSL Raobs", FslRaob.class, null);  // must be before FslRaob

    registerAnalyzer("IRIDL", Iridl.class, null);
    registerAnalyzer("Ndbc", NdbcCoards.class, null);
    registerAnalyzer("Suomi-Station-CDM", Suomi.class, null);
    registerAnalyzer("BuoyShip-NetCDF", BuoyShipSynop.class, null);
    registerAnalyzer("NCAR-RAF/nimbus", RafNimbus.class, null);
    registerAnalyzer("NLDN-CDM", Nldn.class, null);
    registerAnalyzer("SimpleTrajectory", SimpleTrajectory.class, null);

    // further calls to registerConvention are by the user
    userMode = true;
  }

  static public void registerAnalyzer(String conventionName, Class c, ConventionNameOk match) {
    if (!(TableConfigurer.class.isAssignableFrom(c)))
      throw new IllegalArgumentException("Class " + c.getName() + " must implement TableConfigurer");

    // fail fast - check newInstance works
    TableConfigurer tc;
    try {
      tc = (TableConfigurer) c.newInstance();
    } catch (InstantiationException e) {
      throw new IllegalArgumentException("TableConfigurer Class " + c.getName() + " cannot instantiate, probably need default Constructor");
    } catch (IllegalAccessException e) {
      throw new IllegalArgumentException("TableConfigurer Class " + c.getName() + " is not accessible");
    }

    Configurator anal = new Configurator(conventionName, c, tc, match);
    if (userMode) // user stuff gets put at top
      conventionList.add(0, anal);
    else
      conventionList.add(anal);
  }

  static private interface ConventionNameOk {
    boolean isMatch(String convName, String wantName);
  }

  static private class Configurator {
    String convName;
    Class confClass;
    TableConfigurer confInstance;
    ConventionNameOk match;

    Configurator(String convName, Class confClass, TableConfigurer confInstance, ConventionNameOk match) {
      this.convName = convName;
      this.confClass = confClass;
      this.confInstance = confInstance;
      this.match = match;
    }
  }

  static private Configurator matchConfigurator(String convName) {
    for (Configurator anal : conventionList) {
      if ((anal.match == null) && anal.convName.equalsIgnoreCase(convName)) return anal;
      if ((anal.match != null) && anal.match.isMatch(convName, anal.convName)) return anal;
    }
    return null;
  }

  /**
   * Find a TableConfigurer for this dataset, if there is one.
   *
   * @param wantFeatureType want this FeatureType
   * @param ds for this dataset
   * @return TableConfigurer or null if not found
   * @throws IOException on read error
   */
  static public TableConfigurer getTableConfigurer(FeatureType wantFeatureType, NetcdfDataset ds) throws IOException {
    String convUsed = null;

    // search for the Conventions attribute
    String convName = ds.findAttValueIgnoreCase(null, CDM.CONVENTIONS, null);
    if (convName == null)
      convName = ds.findAttValueIgnoreCase(null, "Convention", null);

    // now search for TableConfigurer using that Convention
    Configurator anal = null;
    if (convName != null) {
      convName = convName.trim();

      // search for Convention parsing class
      anal = matchConfigurator(convName);
      if (anal != null) {
        convUsed = convName;
        if (debug) System.out.println("  TableConfigurer found using convName "+convName);
      }

      // now search for comma or semicolon or / delimited list
      if (anal == null) {
        List names = new ArrayList<>();

        if ((convName.indexOf(',') > 0) || (convName.indexOf(';') > 0)) {
          StringTokenizer stoke = new StringTokenizer(convName, ",;");
          while (stoke.hasMoreTokens()) {
            String name = stoke.nextToken();
            names.add(name.trim());
          }
        } else if ((convName.indexOf('/') > 0)) {
          StringTokenizer stoke = new StringTokenizer(convName, "/");
          while (stoke.hasMoreTokens()) {
            String name = stoke.nextToken();
            names.add(name.trim());
          }
        }

        if (names.size() > 0) {
          // search the registered conventions, in order
          for (Configurator conv : conventionList) {
            for (String name : names) {
              if (name.equalsIgnoreCase(conv.convName)) {
                anal = conv;
                convUsed = name;
                if (debug) System.out.println("  TableConfigurer found using convName "+convName);
              }
            }
            if (anal != null) break;
          }
        }
      }
    }

    // search for ones that dont use Convention attribute, in order added.
    // call method isMine() using reflection.
    if (anal == null) {
      for (Configurator conv : conventionList) {
        Class c = conv.confClass;
        Method isMineMethod;

        try {
          isMineMethod = c.getMethod("isMine", new Class[]{FeatureType.class, NetcdfDataset.class});
        } catch (NoSuchMethodException ex) {
          continue;
        }

        try {
          Boolean result = (Boolean) isMineMethod.invoke(conv.confInstance, wantFeatureType, ds);
          if (debug) System.out.println("  TableConfigurer.isMine "+c.getName()+ " result = " + result);
          if (result) {
            anal = conv;
            convUsed = conv.convName;
            break;
          }
        } catch (Exception ex) {
          System.out.println("ERROR: Class " + c.getName() + " Exception invoking isMine method%n" + ex);
        }
      }
    }

    // Instantiate a new TableConfigurer object
    TableConfigurer tc = null;
    if (anal != null) {
      try {
        tc = (TableConfigurer) anal.confClass.newInstance();
        tc.setConvName( convName);
        tc.setConvUsed( convUsed);
      } catch (InstantiationException | IllegalAccessException e) {
        log.error("TableConfigurer create failed", e);
      }
    }

    return tc;
  }

  /**
   * Create a TableAnalyser for this dataset with the given TableConfigurer
   *
   * @param tc TableConfigurer, may be null.
   * @param wantFeatureType want this FeatureType
   * @param ds for this dataset
   * @return TableAnalyser
   * @throws IOException on read error
   */
  static public TableAnalyzer factory(TableConfigurer tc, FeatureType wantFeatureType, NetcdfDataset ds) throws IOException {

    // Create a TableAnalyzer with this TableConfigurer (may be null)
    TableAnalyzer analyzer = new TableAnalyzer(ds, tc);

    if (tc != null) {
      if (tc.getConvName() == null)
        analyzer.userAdvice.format(" No 'Conventions' global attribute.%n");
      else
        analyzer.userAdvice.format(" Conventions global attribute = %s %n", tc.getConvName());

      // add the convention name used
      if (tc.getConvUsed() != null) {
        analyzer.setConventionUsed(tc.getConvUsed());
        if (!tc.getConvUsed().equals(tc.getConvName()))
          analyzer.userAdvice.format(" TableConfigurer used = "+tc.getConvUsed()+".%n");
      }

    } else {
      analyzer.userAdvice.format(" No TableConfigurer found, using default analysis.%n");

    }

    // construct the nested table object
    analyzer.analyze(wantFeatureType);
    return analyzer;
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////
  private TableConfigurer tc;
  private NetcdfDataset ds;
  private Map tableFind = new HashMap<>();
  private Set tableSet = new HashSet<>();
  private List leaves = new ArrayList<>();
  private FeatureType ft;
  private TableConfig configResult;

  private TableAnalyzer(NetcdfDataset ds, TableConfigurer tc) {
    this.tc = tc;
    this.ds = ds;

    if (tc == null)
      userAdvice.format("Using default TableConfigurer.%n");
  }

  public List getFlatTables() {
    return leaves;
  }

  public boolean featureTypeOk(FeatureType ftype, Formatter errlog) {
    for (NestedTable nt : leaves) {
      if (!nt.hasCoords()) {
        errlog.format("Table %s featureType %s: lat/lon/time coord not found%n", nt.getName(), nt.getFeatureType());
        try {
          writeConfigXML(errlog);
        } catch (IOException e) {
         log.error("featuretypeOk", e);
        }
      }

      if (!FeatureDatasetFactoryManager.featureTypeOk(ftype, nt.getFeatureType()))
        errlog.format("Table %s featureType %s doesnt match desired type %s%n", nt.getName(), nt.getFeatureType(), ftype);

      if (nt.hasCoords() && FeatureDatasetFactoryManager.featureTypeOk(ftype, nt.getFeatureType()))
        return true;
    }

    return false;
  }

  public String getName() {
    if (tc != null) return tc.getClass().getName();
    return "Default";
  }

  // for debugging messages
  public FeatureType getFirstFeatureType() {
    for (NestedTable nt : leaves) {
      if (nt.hasCoords())
        return nt.getFeatureType();
    }
    return null;
  }

  public NetcdfDataset getNetcdfDataset() {
    return ds;
  }

  private Formatter userAdvice = new Formatter();
  private Formatter errlog = new Formatter();

  public String getUserAdvice() {
    return userAdvice.toString();
  }

  public String getErrlog() {
    return errlog.toString();
  }

  private String conventionName;

  private void setConventionUsed(String convName) {
    this.conventionName = convName;
  }

  TableConfig getTableConfig() {
    return this.configResult;
  }

  TableConfigurer getTableConfigurer() {
    return tc;
  }

  /////////////////////////////////////////////////////////

  /**
   * Make a NestedTable object for the dataset.
   * @param wantFeatureType want this FeatureType
   * @throws IOException on read error
   */
  private void analyze(FeatureType wantFeatureType) throws IOException {
    // for netcdf-3 files, convert record dimension to structure
    // LOOK may be problems when served via opendap
    boolean structAdded = (Boolean) ds.sendIospMessage(NetcdfFile.IOSP_MESSAGE_ADD_RECORD_STRUCTURE);

    if (tc == null) {
      makeTablesDefault(structAdded);
      makeNestedTables();

    } else {
      configResult = tc.getConfig(wantFeatureType, ds, errlog);
      if (configResult != null)
        addTableRecurse( configResult); // kinda stupid
      else { // use default
        makeTablesDefault(structAdded);
        makeNestedTables();
      }
    }

    // find the leaves
    for (TableConfig config : tableSet) {
      if (config.children == null) { // its a leaf
        NestedTable flatTable = new NestedTable(ds, config, errlog);
        leaves.add(flatTable);
      }
    }

    if (PointDatasetStandardFactory.showTables)
      getDetailInfo( new Formatter( System.out));
  }


  private void addTable(TableConfig t) {
    tableFind.put(t.name, t);
    if (t.dimName != null)
      tableFind.put(t.dimName, t);
    tableSet.add(t);
  }

  private void addTableRecurse(TableConfig t) {
    addTable(t);
    if (t.children != null) {
      for (TableConfig child : t.children)
        addTableRecurse(child);
    }
  }


  ///////////////////////////////////////////////////////////
  // default analasis aka guessing

  // no TableConfig was passed in - gotta wing it
  private void makeTablesDefault(boolean structAdded) throws IOException {

    // make Structures into a table
    List vars = new ArrayList<>(ds.getVariables());
    Iterator iter = vars.iterator();
    while (iter.hasNext()) {
      Variable v = iter.next();
      if (v instanceof Structure) {  // handles Sequences too
        TableConfig st = new TableConfig(Table.Type.Structure, v.getFullName());
        CoordSysEvaluator.findCoords(st, ds, null);
        st.structName = v.getFullName();
        st.nestedTableName = v.getShortName();

        addTable(st);
        checkIfTrajectory(st);

        iter.remove();
        findNestedStructures((Structure) v, st); // look for nested structures

      } else if (structAdded && v.isUnlimited()) {
        iter.remove();
      }
    }

    if (tableSet.size() > 0) return;

    // search at dimensions that lat, lon, time coordinates use
    Set dimSet = new HashSet<>(10);
    for (CoordinateAxis axis : ds.getCoordinateAxes()) {
      if ((axis.getAxisType() == AxisType.Lat) || (axis.getAxisType() == AxisType.Lon)|| (axis.getAxisType() == AxisType.Time))
        for (Dimension dim : axis.getDimensions())
          dimSet.add(dim);
    }

    // lat, lon, time all use same dimension - use it
    if (dimSet.size() == 1) {
      final Dimension obsDim = (Dimension) dimSet.toArray()[0];
      TableConfig st = new TableConfig(Table.Type.Structure, obsDim.getShortName());
      st.structureType = obsDim.isUnlimited() ? TableConfig.StructureType.Structure : TableConfig.StructureType.PsuedoStructure;
      st.structName = obsDim.isUnlimited() ? "record" : obsDim.getShortName();
      st.dimName = obsDim.getShortName();
      CoordSysEvaluator.findCoords(st, ds, new CoordSysEvaluator.Predicate() {
        public boolean match(CoordinateAxis axis) {
          return obsDim.equals(axis.getDimension(0));
        }
      });

      CoordinateAxis time = CoordSysEvaluator.findCoordByType(ds, AxisType.Time);
      if ((time != null) && (time.getRank() == 0)) {
        st.addJoin(new JoinArray(time, JoinArray.Type.scalar, 0));
        st.time = time.getShortName();
      }
      addTable( st);
      checkIfTrajectory(st);
    }

    if (tableSet.size() > 0) return;

    // try the time dimension
    CoordinateAxis time = null;
    for (CoordinateAxis axis : ds.getCoordinateAxes()) {
      if ((axis.getAxisType() == AxisType.Time) && axis.isIndependentCoordinate()) {
        time = axis;
        break;
      }
    }
    if (time != null) {
      Dimension obsDim = time.getDimension(0);
      TableConfig st = new TableConfig(Table.Type.Structure, obsDim.getShortName());
      st.structureType = TableConfig.StructureType.PsuedoStructure;
      st.dimName = obsDim.getShortName();
      CoordSysEvaluator.findCoords(st, ds, null);

      addTable( st);
    }

  }

  private void checkIfTrajectory(TableConfig st) {
    // deal with possible trajectory - only do this if dataset has metadata
    FeatureType ft = FeatureDatasetFactoryManager.findFeatureType(ds);
    if (ft == FeatureType.TRAJECTORY) {
      st.featureType = FeatureType.TRAJECTORY;
      TableConfig pc = new TableConfig(Table.Type.Top, "single");
      st.parent = pc;
      pc.addChild(st);
    } else
      st.featureType = FeatureType.POINT;
  }

  private void findNestedStructures(Structure s, TableConfig parent) {
    for (Variable v : s.getVariables()) {
      if (v instanceof Structure) {  // handles Sequences too
        TableConfig nestedTable = new TableConfig(Table.Type.NestedStructure, v.getFullName());
        nestedTable.structName = v.getFullName();
        nestedTable.nestedTableName = v.getShortName();

        addTable(nestedTable);
        parent.addChild(nestedTable);

        // LOOK why not add the join(parent,child) here ?
        //nestedTable.join = new TableConfig.JoinConfig(Join.Type.NestedStructure);
        //joins.add(nestedTable.join);

        findNestedStructures((Structure) v, nestedTable); // search for nested structures
      }
    }
  }

  private void makeNestedTables() {
    // We search among all the possible Tables in a dataset for joins, and coordinate
    // variables. Based on those, we form "interesting" sets and make them into NestedTables.

    /* link the tables together with joins
    for (TableConfig.JoinConfig join : joins) {
      NestedTable.Table parent = join.parent;
      NestedTable.Table child = join.child;

      if (child.parent != null) throw new IllegalStateException("Multiple parents");
      child.parent = parent;
      child.join = join;

      if (parent.children == null) parent.children = new ArrayList();
      parent.children.add(join);
    } */
  }


  /////////////////////////////////////////////////////
  /* track station info

  private StationInfo stationInfo = new StationInfo();

  private StationInfo getStationInfo() {
    return stationInfo;
  }

  public class StationInfo {
    public String stationId, stationDesc, stationNpts;
    public int nstations;
    public String latName, lonName, elevName;
  }  */

  ////////////////////////////////////////////////////////////////////////////////////////////////

  /* public void showCoordSys(java.util.Formatter sf) {
    sf.format("%nCoordinate Systems%n");
    for (CoordinateSystem cs : ds.getCoordinateSystems()) {
      sf.format(" %s%n", cs);
    }
  }

  public void showCoordAxes(java.util.Formatter sf) {
    sf.format("%nAxes%n");
    for (CoordinateAxis axis : ds.getCoordinateAxes()) {
      sf.format(" %s %s%n", axis.getAxisType(), axis.getNameAndDimensions());
    }
  } */

  /* public void showTables(java.util.Formatter sf) {
    sf.format("%nTables%n");
    for (NestedTable.Table t : tableSet)
      sf.format(" %s%n", t);

    sf.format("%nJoins%n");
    for (Join j : joins)
      sf.format(" %s%n", j);
  } */

  public void showNestedTables(java.util.Formatter sf) {
    for (NestedTable nt : leaves) {
      nt.show(sf);
    }
  }

  public String getImplementationName() {
    return (tc != null) ? tc.getClass().getSimpleName() : "defaultAnalyser";
  }

  public void getDetailInfo(java.util.Formatter sf) {
    sf.format("-----------------------------------------------------%nTableAnalyzer on Dataset %s%n", ds.getLocation());
    sf.format(" TableAnalyser = %s%n", getName());
    showNestedTables(sf);
    String errlogS = errlog.toString();
    if (errlogS.length() > 0)
      sf.format("%n Errlog=%n%s",errlogS);
    String userAdviceS = userAdvice.toString();
    if (userAdviceS.length() > 0)
      sf.format("%n userAdvice=%n%s%n",userAdviceS);

    try {
      writeConfigXML(sf);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private void writeConfigXML(java.util.Formatter sf) throws IOException {
    if (configResult != null) {
        PointConfigXML tcx = new PointConfigXML();
        tcx.writeConfigXML(configResult, getName(), sf);
        return;
    }
    XMLOutputter fmt = new XMLOutputter( Format.getPrettyFormat());
    sf.format("%s", fmt.outputString ( makeDocument()));
  }

  /** Create an XML document from this info
   * @return netcdfDatasetInfo XML document
   */
  private Document makeDocument() {
    Element rootElem = new Element("featureDataset");
    Document doc = new Document(rootElem);
    rootElem.setAttribute("location", ds.getLocation());
    rootElem.addContent( new Element("analyser").setAttribute("class", getName()));
    if (ft != null)
      rootElem.setAttribute("featureType", ft.toString());

    for (NestedTable nt : leaves) {
      writeTable( rootElem, nt.getLeaf());
    }

    return doc;
  }

  private Element writeTable( Element parent, Table table) {
    if (table.parent != null) {
      parent = writeTable( parent, table.parent);
    }
    Element tableElem = new Element("table");
    parent.addContent( tableElem);

    if (table.getName() != null)
      tableElem.setAttribute("name", table.getName());
    if (table.getFeatureType() != null)
      tableElem.setAttribute("featureType", table.getFeatureType().toString());
    tableElem.setAttribute("class", table.getClass().toString());

    addCoordinates(tableElem, table);
    for (String colName : table.cols.keySet()) {
      if (!table.nondataVars.contains(colName))
        tableElem.addContent( new Element("variable").addContent(colName));
    }

    if (table.extraJoins != null) {
      for (Join j : table.extraJoins) {
        if (j instanceof JoinArray)
          tableElem.addContent( writeJoinArray( (JoinArray)j));
        else if (j instanceof JoinMuiltdimStructure)
          tableElem.addContent( writeJoinMuiltdimStructure( (JoinMuiltdimStructure) j));
        else if (j instanceof JoinParentIndex)
          tableElem.addContent( writeJoinParentIndex( (JoinParentIndex) j));
      }
    }
    return tableElem;
  }

  private void addCoordinates( Element tableElem, Table table) {
    addCoord(tableElem, table.lat, "lat");
    addCoord(tableElem, table.lon, "lon");
    addCoord(tableElem, table.elev, "elev");
    addCoord(tableElem, table.time, "time");
    addCoord(tableElem, table.timeNominal, "timeNominal");
    addCoord(tableElem, table.stnId, "stnId");
    addCoord(tableElem, table.stnDesc, "stnDesc");
    addCoord(tableElem, table.stnNpts, "stnNpts");
    addCoord(tableElem, table.stnWmoId, "stnWmoId");
    addCoord(tableElem, table.stnAlt, "stnAlt");
    addCoord(tableElem, table.limit, "limit");
  }

  private void addCoord(Element tableElem, String name, String kind) {
    if (name != null) {
      Element elem = new Element("coordinate").setAttribute("kind", kind);
      elem.addContent(name);
      tableElem.addContent(elem);
    }
  }

  private Element writeJoinArray(JoinArray join) {
    Element joinElem = new Element("join");
    joinElem.setAttribute("class", join.getClass().toString());
    if (join.type != null)
      joinElem.setAttribute("type", join.type.toString());
    if (join.v != null)
      joinElem.addContent( new Element("variable").setAttribute("name", join.v.getFullName()));
    joinElem.addContent( new Element("param").setAttribute("value", Integer.toString(join.param)));
    return joinElem;
  }

  private Element writeJoinMuiltdimStructure(JoinMuiltdimStructure join) {
    Element joinElem = new Element("join");
    joinElem.setAttribute("class", join.getClass().toString());
    if (join.parentStructure != null)
      joinElem.addContent( new Element("parentStructure").setAttribute("name", join.parentStructure.getFullName()));
    joinElem.addContent( new Element("dimLength").setAttribute("value", Integer.toString(join.dimLength)));
    return joinElem;
  }

  private Element writeJoinParentIndex(JoinParentIndex join) {
    Element joinElem = new Element("join");
    joinElem.setAttribute("class", join.getClass().toString());
    if (join.parentStructure != null)
      joinElem.addContent( new Element("parentStructure").setAttribute("name", join.parentStructure.getFullName()));
    if (join.parentIndex != null)
      joinElem.addContent( new Element("parentIndex").setAttribute("name", join.parentIndex));
    return joinElem;
  }

  ////////////////////////////////////////////////////////////////////////////////

  static void doit(String filename) throws IOException {
    System.out.println(filename);
    NetcdfDataset ncd = ucar.nc2.dataset.NetcdfDataset.openDataset(filename);
    TableAnalyzer csa = TableAnalyzer.factory(null, null, ncd);
    csa.getDetailInfo(new Formatter(System.out));
    System.out.println("%n-----------------");
  }

  static public void main(String args[]) throws IOException {
    /* doit("C:/data/dt2/station/Surface_METAR_20080205_0000.nc");
    doit("C:/data/bufr/edition3/idd/profiler/PROFILER_3.bufr");
    doit("C:/data/bufr/edition3/idd/profiler/PROFILER_2.bufr");
    doit("C:/data/profile/PROFILER_wind_01hr_20080410_2300.nc"); */
    //doit("C:/data/test/20070301.nc");
    //doit("C:/data/dt2/profile/PROFILER_3.bufr");

    //doit("C:/data/dt2/station/ndbc.nc");
    //doit("C:/data/dt2/station/madis2.sao");
    // doit("C:/data/metars/Surface_METAR_20070326_0000.nc");  // ok
    //doit("C:/data/dt2/station/Sean_multidim_20070301.nc"); // ok
    //doit("Q:/cdmUnitTest/formats/gempak/surface/20090521_sao.gem");
    doit("D:/datasets/metars/Surface_METAR_20070513_0000.nc");

    //doit("C:/data/profile/PROFILER_wind_01hr_20080410_2300.nc");
    //doit("C:/data/cadis/tempting");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy