private void makeVariableNoCoords(
      NetcdfFile ncfile, int datatype, String shortName, String longName, Variable from) {

    Variable v = new Variable(ncfile, null, null, shortName);
    v.setDataType(DataType.BYTE);
    v.setDimensions(from.getDimensions());
    ncfile.addVariable(null, v);

    v.addAttribute(new Attribute(CDM.UNITS, Cinrad2Record.getDatatypeUnits(datatype)));
    v.addAttribute(new Attribute(CDM.LONG_NAME, longName));

    byte[] b = new byte[2];
    b[0] = Cinrad2Record.MISSING_DATA;
    b[1] = Cinrad2Record.BELOW_THRESHOLD;
    Array missingArray = Array.factory(DataType.BYTE.getPrimitiveClassType(), new int[] {2}, b);

    v.addAttribute(new Attribute(CDM.MISSING_VALUE, missingArray));
    v.addAttribute(
        new Attribute("signal_below_threshold", new Byte(Cinrad2Record.BELOW_THRESHOLD)));
    v.addAttribute(
        new Attribute(CDM.SCALE_FACTOR, new Float(Cinrad2Record.getDatatypeScaleFactor(datatype))));
    v.addAttribute(
        new Attribute(CDM.ADD_OFFSET, new Float(Cinrad2Record.getDatatypeAddOffset(datatype))));
    v.addAttribute(new Attribute(CDM.UNSIGNED, "true"));

    Attribute fromAtt = from.findAttribute(_Coordinate.Axes);
    v.addAttribute(new Attribute(_Coordinate.Axes, fromAtt));

    Vgroup vgFrom = (Vgroup) from.getSPobject();
    Vgroup vg = new Vgroup(datatype, vgFrom.map);
    v.setSPobject(vg);
  }
 /**
  * Make the missing variable
  *
  * @return the missing variable
  */
 protected Variable makeMissingVariable() {
   Variable var = new Variable(ncfile, null, null, MISSING_VAR);
   var.setDataType(DataType.BYTE);
   var.setDimensions((List<Dimension>) null);
   var.addAttribute(new Attribute("description", "missing flag - 1 means all params are missing"));
   var.addAttribute(new Attribute("missing_value", new Byte((byte) 1)));
   return var;
 }
 /**
  * Make a variable from a GempakParmaeter
  *
  * @param param GempakParameter
  * @param dims Variable dimensions
  * @return the Variable
  */
 protected Variable makeParamVariable(GempakParameter param, List<Dimension> dims) {
   Variable var = new Variable(ncfile, null, null, param.getName());
   var.setDataType(DataType.FLOAT);
   var.setDimensions(dims);
   var.addAttribute(new Attribute("long_name", param.getDescription()));
   String units = param.getUnit();
   if ((units != null) && !units.equals("")) {
     var.addAttribute(new Attribute(CDM.UNITS, units));
   }
   var.addAttribute(new Attribute("missing_value", RMISS));
   return var;
 }
  /**
   * Add this as a variable to the netCDF file
   *
   * @param ncfile the netCDF file
   * @param g the group in the file
   */
  void addToNetcdfFile(NetcdfFile ncfile, Group g) {
    Variable v = new Variable(ncfile, g, null, getName());
    v.setDimensions(v.getShortName());
    v.setDataType(DataType.STRING);
    v.addAttribute(new Attribute("long_name", "ensemble"));
    v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Ensemble.toString()));

    String[] data = new String[getNEnsembles()];

    for (int i = 0; i < getNEnsembles(); i++) {
      EnsCoord ec = ensCoords.get(i);
      data[i] = Grib2Tables.codeTable4_6(ec.type) + " " + ec.number;
    }
    Array dataArray = Array.factory(DataType.STRING, new int[] {getNEnsembles()}, data);
    v.setCachedData(dataArray, false);

    ncfile.addVariable(g, v);
  }
  /**
   * Define Dimensions, Variables, Attributes in ncfile
   *
   * @param raf ucar.unidata.io.RandomAccessFile corresponds of SIGMET datafile.
   * @param ncfile an empty NetcdfFile object which will be filled.
   * @param hdrNames java.util.Map with values for "StationName.." Attributes
   * @return ArrayList of Variables of ncfile
   */
  public ArrayList<Variable> init(
      ucar.unidata.io.RandomAccessFile raf,
      ucar.nc2.NetcdfFile ncfile,
      java.util.Map<String, String> hdrNames)
      throws java.io.IOException {
    // prepare attribute values
    String[] data_name = {
      " ", "TotalPower", "Reflectivity", "Velocity", "Width", "Differential_Reflectivity"
    };
    String[] unit = {" ", "dbZ", "dbZ", "m/sec", "m/sec", "dB"};
    int[] type = {1, 2, 3, 4, 5};
    String def_datafile = "SIGMET-IRIS";
    String tim = "";
    int ngates = 0;

    recHdr = readRecordsHdr(raf);
    hdrNames = readStnNames(raf);

    String stnName = hdrNames.get("StationName");
    String stnName_util = hdrNames.get("StationName_SetupUtility");
    float radar_lat =
        recHdr.get("radar_lat").floatValue(); // System.out.println("rad_lat="+radar_lat);
    float radar_lon =
        recHdr.get("radar_lon").floatValue(); // System.out.println("rad_lon="+radar_lon);
    short ground_height =
        recHdr.get("ground_height").shortValue(); // System.out.println("ground_H="+ground_height);
    short radar_height =
        recHdr.get("radar_height").shortValue(); // System.out.println("radar_H="+radar_height);
    int radar_alt =
        (recHdr.get("radar_alt").intValue()) / 100; // System.out.println("rad_alt="+radar_alt);
    short num_rays =
        recHdr.get("num_rays").shortValue(); // System.out.println("num_rays="+num_rays);
    short bins = recHdr.get("bins").shortValue(); // System.out.println("bins="+bins);
    float range_first =
        (recHdr.get("range_first").intValue())
            * 0.01f; // System.out.println("range_1st="+range_first);
    float range_last =
        (recHdr.get("range_last").intValue()) * 0.01f; // System.out.println("step="+step);
    short number_sweeps = recHdr.get("number_sweeps").shortValue();
    // System.out.println("number_sweeps="+number_sweeps);
    int nparams = (recHdr.get("nparams").intValue()); // System.out.println("nparams="+nparams);
    short year = recHdr.get("year").shortValue(); // System.out.println("year="+year);
    short month = recHdr.get("month").shortValue();
    short day = recHdr.get("day").shortValue();
    int base_time = (recHdr.get("base_time").intValue());

    // define number of gates for every sweep
    sweep_bins = new int[nparams * number_sweeps];
    if (number_sweeps > 1) {
      sweep_bins = volScan.getNumberGates();
    } else {
      for (int kk = 0; kk < nparams; kk++) {
        sweep_bins[kk] = bins;
      }
    }

    // add Dimensions
    Dimension scanR = new Dimension("scanR", number_sweeps, true);
    Dimension radial = new Dimension("radial", num_rays, true);
    Dimension[] gateR = new Dimension[number_sweeps];
    String dim_name = "gateR";
    for (int j = 0; j < number_sweeps; j++) {
      if (number_sweeps > 1) {
        dim_name = "gateR_sweep_" + (j + 1);
      }
      gateR[j] = new Dimension(dim_name, sweep_bins[j], true);
    }
    ncfile.addDimension(null, scanR);
    ncfile.addDimension(null, radial);
    for (int j = 0; j < number_sweeps; j++) {
      ncfile.addDimension(null, gateR[j]);
    }
    ArrayList<Dimension> dims0 = new ArrayList<Dimension>();
    ArrayList<Dimension> dims1 = new ArrayList<Dimension>();
    ArrayList<Dimension> dims2 = new ArrayList<Dimension>();
    ArrayList<Dimension> dims3 = new ArrayList<Dimension>();

    ArrayList<Variable> varList = new ArrayList<Variable>();

    Variable[][] v = new Variable[nparams][number_sweeps];
    String var_name = "";
    for (int j = 0; j < nparams; j++) {
      int tp = type[j];
      var_name = data_name[tp];
      for (int jj = 0; jj < number_sweeps; jj++) {
        if (number_sweeps > 1) {
          var_name = data_name[tp] + "_sweep_" + (jj + 1);
        }
        v[j][jj] = new Variable(ncfile, null, null, var_name);
        v[j][jj].setDataType(DataType.FLOAT);
        dims2.add(radial);
        dims2.add(gateR[jj]);
        v[j][jj].setDimensions(dims2);
        v[j][jj].addAttribute(new Attribute(CDM.LONG_NAME, var_name));
        v[j][jj].addAttribute(new Attribute(CDM.UNITS, unit[tp]));
        String coordinates = "time elevationR azimuthR distanceR";
        v[j][jj].addAttribute(new Attribute(_Coordinate.Axes, coordinates));
        v[j][jj].addAttribute(new Attribute(CDM.MISSING_VALUE, -999.99f));
        ncfile.addVariable(null, v[j][jj]);
        varList.add(v[j][jj]);
        dims2.clear();
      }
    }
    tsu_sec = new int[number_sweeps];
    String[] tsu = new String[number_sweeps];
    String[] time_units = new String[number_sweeps];
    tsu_sec = volScan.getStartSweep();
    for (int i = 0; i < number_sweeps; i++) {
      String st1 = Short.toString(month);
      if (st1.length() < 2) st1 = "0" + st1;
      String st2 = Short.toString(day);
      if (st2.length() < 2) st2 = "0" + st2;
      date0 = String.valueOf(year) + "-" + st1 + "-" + st2;
      tsu[i] = date0 + "T" + calcTime(tsu_sec[i], 0) + "Z";
    }
    for (int j = 0; j < number_sweeps; j++) {
      time_units[j] = "secs since " + tsu[j];
    }

    dims0.add(radial);
    // add "time" variable
    Variable[] time = new Variable[number_sweeps];
    String tm = "time";
    String tm_name = "";
    for (int j = 0; j < number_sweeps; j++) {
      tm_name = tm;
      if (number_sweeps > 1) {
        tm_name = tm + "_sweep_" + (j + 1);
      }
      time[j] = new Variable(ncfile, null, null, tm_name);
      time[j].setDataType(DataType.INT);
      time[j].setDimensions(dims0);
      time[j].addAttribute(new Attribute(CDM.LONG_NAME, "time from start of sweep"));
      time[j].addAttribute(new Attribute(CDM.UNITS, time_units[j]));
      time[j].addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString()));
      time[j].addAttribute(new Attribute(CDM.MISSING_VALUE, -99));
      ncfile.addVariable(null, time[j]);
      varList.add(time[j]);
    }

    // add "elevationR" variable
    Variable[] elevationR = new Variable[number_sweeps];
    String ele = "elevationR";
    String ele_name = "";
    for (int j = 0; j < number_sweeps; j++) {
      ele_name = ele;
      if (number_sweeps > 1) {
        ele_name = ele + "_sweep_" + (j + 1);
      }
      elevationR[j] = new Variable(ncfile, null, null, ele_name);
      elevationR[j].setDataType(DataType.FLOAT);
      elevationR[j].setDimensions(dims0);
      elevationR[j].addAttribute(new Attribute(CDM.LONG_NAME, "elevation angle"));
      elevationR[j].addAttribute(new Attribute(CDM.UNITS, "degrees"));
      elevationR[j].addAttribute(
          new Attribute(_Coordinate.AxisType, AxisType.RadialElevation.toString()));
      elevationR[j].addAttribute(new Attribute(CDM.MISSING_VALUE, -999.99f));
      ncfile.addVariable(null, elevationR[j]);
      varList.add(elevationR[j]);
    }

    // add "azimuthR" variable
    Variable[] azimuthR = new Variable[number_sweeps];
    String azim = "azimuthR";
    String azim_name = "";
    for (int j = 0; j < number_sweeps; j++) {
      azim_name = azim;
      if (number_sweeps > 1) {
        azim_name = azim + "_sweep_" + (j + 1);
      }
      azimuthR[j] = new Variable(ncfile, null, null, azim_name);
      azimuthR[j].setDataType(DataType.FLOAT);
      azimuthR[j].setDimensions(dims0);
      azimuthR[j].addAttribute(new Attribute(CDM.LONG_NAME, "azimuth angle"));
      azimuthR[j].addAttribute(new Attribute(CDM.UNITS, "degrees"));
      azimuthR[j].addAttribute(
          new Attribute(_Coordinate.AxisType, AxisType.RadialAzimuth.toString()));
      azimuthR[j].addAttribute(new Attribute(CDM.MISSING_VALUE, -999.99f));
      ncfile.addVariable(null, azimuthR[j]);
      varList.add(azimuthR[j]);
    }

    // add "distanceR" variable
    Variable[] distanceR = new Variable[number_sweeps];
    String dName = "distanceR";
    String dist_name = "";
    for (int j = 0; j < number_sweeps; j++) {
      dist_name = dName;
      if (number_sweeps > 1) {
        dist_name = dName + "_sweep_" + (j + 1);
      }
      distanceR[j] = new Variable(ncfile, null, null, dist_name);
      distanceR[j].setDataType(DataType.FLOAT);
      dims1.add(gateR[j]);
      distanceR[j].setDimensions(dims1);
      distanceR[j].addAttribute(new Attribute(CDM.LONG_NAME, "radial distance"));
      distanceR[j].addAttribute(new Attribute(CDM.UNITS, "m"));
      distanceR[j].addAttribute(
          new Attribute(_Coordinate.AxisType, AxisType.RadialDistance.toString()));
      ncfile.addVariable(null, distanceR[j]);
      varList.add(distanceR[j]);
      dims1.clear();
    }
    // add "numGates" variable
    dims3.add(scanR);
    Variable numGates = new Variable(ncfile, null, null, "numGates");
    numGates.setDataType(DataType.INT);
    numGates.setDimensions(dims3);
    numGates.addAttribute(new Attribute(CDM.LONG_NAME, "number of gates in the sweep"));
    ncfile.addVariable(null, numGates);
    varList.add(numGates);

    // add global attributes
    ncfile.addAttribute(null, new Attribute("definition", "SIGMET-IRIS RAW"));
    ncfile.addAttribute(
        null, new Attribute("description", "SIGMET-IRIS data are reading by Netcdf IOSP"));
    ncfile.addAttribute(null, new Attribute("StationName", stnName));
    ncfile.addAttribute(null, new Attribute("StationName_SetupUtility", stnName_util));
    ncfile.addAttribute(null, new Attribute("radar_lat", radar_lat));
    ncfile.addAttribute(null, new Attribute("radar_lon", radar_lon));
    ncfile.addAttribute(null, new Attribute("ground_height", ground_height));
    ncfile.addAttribute(null, new Attribute("radar_height", radar_height));
    ncfile.addAttribute(null, new Attribute("radar_alt", radar_alt));
    ncfile.addAttribute(null, new Attribute("num_data_types", nparams));
    ncfile.addAttribute(null, new Attribute("number_sweeps", number_sweeps));
    String sn = "start_sweep";
    String snn = "";
    for (int j = 0; j < number_sweeps; j++) {
      snn = sn;
      if (number_sweeps > 1) {
        snn = sn + "_" + (j + 1);
      }
      ncfile.addAttribute(null, new Attribute(snn, tsu[j]));
    }
    ncfile.addAttribute(null, new Attribute("num_rays", num_rays));
    ncfile.addAttribute(null, new Attribute("max_number_gates", bins));
    ncfile.addAttribute(null, new Attribute("range_first", range_first));
    ncfile.addAttribute(null, new Attribute("range_last", range_last));
    ncfile.addAttribute(null, new Attribute("DataType", "Radial"));
    ncfile.addAttribute(null, new Attribute(CDM.CONVENTIONS, _Coordinate.Convention));

    // --------- fill all of values in the ncfile ------
    doNetcdfFileCoordinate(
        ncfile, volScan.base_time, volScan.year, volScan.month, volScan.day, varList, recHdr);

    ncfile.finish();

    return varList;
  }
Exemple #6
0
  private FeatureType amendGrid(
      Element gridElem, NetcdfFile ncfile, Group parent, String location) {
    List<Dimension> unknownDims = new ArrayList<>();

    // always has x and y dimension
    String xdimSizeS = gridElem.getChild("XDim").getText().trim();
    String ydimSizeS = gridElem.getChild("YDim").getText().trim();
    int xdimSize = Integer.parseInt(xdimSizeS);
    int ydimSize = Integer.parseInt(ydimSizeS);
    parent.addDimensionIfNotExists(new Dimension("XDim", xdimSize));
    parent.addDimensionIfNotExists(new Dimension("YDim", ydimSize));

    /* see HdfEosModisConvention
    UpperLeftPointMtrs=(-20015109.354000,1111950.519667)
    		LowerRightMtrs=(-18903158.834333,-0.000000)
    		Projection=GCTP_SNSOID
    		ProjParams=(6371007.181000,0,0,0,0,0,0,0,0,0,0,0,0)
    		SphereCode=-1
     */
    Element proj = gridElem.getChild("Projection");
    if (proj != null) {
      Variable crs = new Variable(ncfile, parent, null, HDFEOS_CRS);
      crs.setDataType(DataType.SHORT);
      crs.setDimensions(""); // scalar
      crs.setCachedData(Array.makeArray(DataType.SHORT, 1, 0, 0)); // fake data
      parent.addVariable(crs);

      addAttributeIfExists(gridElem, HDFEOS_CRS_Projection, crs, false);
      addAttributeIfExists(gridElem, HDFEOS_CRS_UpperLeft, crs, true);
      addAttributeIfExists(gridElem, HDFEOS_CRS_LowerRight, crs, true);
      addAttributeIfExists(gridElem, HDFEOS_CRS_ProjParams, crs, true);
      addAttributeIfExists(gridElem, HDFEOS_CRS_SphereCode, crs, false);
    }

    // global Dimensions
    Element d = gridElem.getChild("Dimension");
    List<Element> dims = d.getChildren();
    for (Element elem : dims) {
      String name = elem.getChild("DimensionName").getText().trim();
      name = NetcdfFile.makeValidCdmObjectName(name);
      if (name.equalsIgnoreCase("scalar")) continue;

      String sizeS = elem.getChild("Size").getText().trim();
      int length = Integer.parseInt(sizeS);
      Dimension old = parent.findDimension(name);
      if ((old == null) || (old.getLength() != length)) {
        if (length > 0) {
          Dimension dim = new Dimension(name, length);
          if (parent.addDimensionIfNotExists(dim) && showWork)
            System.out.printf(" Add dimension %s %n", dim);
        } else {
          log.warn("Dimension {} has size {} {} ", sizeS, name, location);
          Dimension udim = new Dimension(name, 1);
          udim.setGroup(parent);
          unknownDims.add(udim);
          if (showWork) System.out.printf(" Add dimension %s %n", udim);
        }
      }
    }

    // Geolocation Variables
    Group geoFieldsG = parent.findGroup(GEOLOC_FIELDS);
    if (geoFieldsG == null) geoFieldsG = parent.findGroup(GEOLOC_FIELDS2);

    if (geoFieldsG != null) {
      Element floc = gridElem.getChild("GeoField");
      List<Element> varsLoc = floc.getChildren();
      for (Element elem : varsLoc) {
        String varname = elem.getChild("GeoFieldName").getText().trim();
        Variable v = geoFieldsG.findVariable(varname);
        // if (v == null)
        //  v = geoFieldsG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;

        Element dimList = elem.getChild("DimList");
        List<Element> values = dimList.getChildren("value");
        setSharedDimensions(v, values, unknownDims, location);
      }
    }

    // Data Variables
    Group dataG = parent.findGroup(DATA_FIELDS);
    if (dataG == null)
      dataG =
          parent.findGroup(
              DATA_FIELDS2); // eg C:\data\formats\hdf4\eos\mopitt\MOP03M-200501-L3V81.0.1.hdf

    if (dataG != null) {
      Element f = gridElem.getChild("DataField");
      List<Element> vars = f.getChildren();
      for (Element elem : vars) {
        String varname = elem.getChild("DataFieldName").getText().trim();
        varname = NetcdfFile.makeValidCdmObjectName(varname);
        Variable v = dataG.findVariable(varname);
        // if (v == null)
        //  v = dataG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;

        Element dimList = elem.getChild("DimList");
        List<Element> values = dimList.getChildren("value");
        setSharedDimensions(v, values, unknownDims, location);
      }

      // get projection
      String projS = null;
      Element projElem = gridElem.getChild("Projection");
      if (projElem != null) projS = projElem.getText().trim();
      boolean isLatLon = "GCTP_GEO".equals(projS);

      // look for XDim, YDim coordinate variables
      if (isLatLon) {
        for (Variable v : dataG.getVariables()) {
          if (v.isCoordinateVariable()) {
            if (v.getShortName().equals("YDim")) {
              v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
              v.addAttribute(new Attribute(CDM.UNITS, CDM.LAT_UNITS));
            }
            if (v.getShortName().equals("XDim"))
              v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
          }
        }
      }
    }
    return FeatureType.GRID;
  }
Exemple #7
0
  private FeatureType amendSwath(NetcdfFile ncfile, Element swathElem, Group parent) {
    FeatureType featureType = FeatureType.SWATH;
    List<Dimension> unknownDims = new ArrayList<>();

    // Dimensions
    Element d = swathElem.getChild("Dimension");
    List<Element> dims = d.getChildren();
    for (Element elem : dims) {
      String name = elem.getChild("DimensionName").getText().trim();
      name = NetcdfFile.makeValidCdmObjectName(name);

      if (name.equalsIgnoreCase("scalar")) continue;
      String sizeS = elem.getChild("Size").getText().trim();
      int length = Integer.parseInt(sizeS);
      if (length > 0) {
        Dimension dim = parent.findDimensionLocal(name);
        if (dim != null) { // already added - may be dimension scale ?
          if (dim.getLength() != length) { // ok as long as it matches
            log.error("Conflicting Dimensions = {} {}", dim, ncfile.getLocation());
            throw new IllegalStateException("Conflicting Dimensions = " + name);
          }
        } else {
          dim = new Dimension(name, length);
          if (parent.addDimensionIfNotExists(dim) && showWork)
            System.out.printf(" Add dimension %s %n", dim);
        }
      } else {
        log.warn("Dimension " + name + " has size " + sizeS, ncfile.getLocation());
        Dimension udim = new Dimension(name, 1);
        udim.setGroup(parent);
        unknownDims.add(udim);
        if (showWork) System.out.printf(" Add dimension %s %n", udim);
      }
    }

    // Dimension Maps
    Element dmap = swathElem.getChild("DimensionMap");
    List<Element> dimMaps = dmap.getChildren();
    for (Element elem : dimMaps) {
      String geoDimName = elem.getChild("GeoDimension").getText().trim();
      geoDimName = NetcdfFile.makeValidCdmObjectName(geoDimName);
      String dataDimName = elem.getChild("DataDimension").getText().trim();
      dataDimName = NetcdfFile.makeValidCdmObjectName(dataDimName);

      String offsetS = elem.getChild("Offset").getText().trim();
      String incrS = elem.getChild("Increment").getText().trim();
      int offset = Integer.parseInt(offsetS);
      int incr = Integer.parseInt(incrS);

      // make new variable for this dimension map
      Variable v = new Variable(ncfile, parent, null, dataDimName);
      v.setDimensions(geoDimName);
      v.setDataType(DataType.INT);
      int npts = (int) v.getSize();
      Array data = Array.makeArray(v.getDataType(), npts, offset, incr);
      v.setCachedData(data, true);
      v.addAttribute(new Attribute("_DimensionMap", ""));
      parent.addVariable(v);
      if (showWork) System.out.printf(" Add dimensionMap %s %n", v);
    }

    // Geolocation Variables
    Group geoFieldsG = parent.findGroup(GEOLOC_FIELDS);
    if (geoFieldsG == null) geoFieldsG = parent.findGroup(GEOLOC_FIELDS2);
    if (geoFieldsG != null) {
      Variable latAxis = null, lonAxis = null;
      Element floc = swathElem.getChild("GeoField");
      List<Element> varsLoc = floc.getChildren();
      for (Element elem : varsLoc) {
        String varname = elem.getChild("GeoFieldName").getText().trim();
        Variable v = geoFieldsG.findVariable(varname);
        // if (v == null)
        //  v = geoFieldsG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;
        AxisType axis = addAxisType(ncfile, v);
        if (axis == AxisType.Lat) latAxis = v;
        if (axis == AxisType.Lon) lonAxis = v;

        Element dimList = elem.getChild("DimList");
        List<Element> values = dimList.getChildren("value");
        setSharedDimensions(v, values, unknownDims, ncfile.getLocation());
        if (showWork) System.out.printf(" set coordinate %s %n", v);
      }
      if ((latAxis != null) && (lonAxis != null)) {
        List<Dimension> xyDomain = CoordinateSystem.makeDomain(new Variable[] {latAxis, lonAxis});
        if (xyDomain.size() < 2) featureType = FeatureType.PROFILE; // ??
      }
    }

    // Data Variables
    Group dataG = parent.findGroup(DATA_FIELDS);
    if (dataG == null) dataG = parent.findGroup(DATA_FIELDS2);
    if (dataG != null) {
      Element f = swathElem.getChild("DataField");
      List<Element> vars = f.getChildren();
      for (Element elem : vars) {
        Element dataFieldNameElem = elem.getChild("DataFieldName");
        if (dataFieldNameElem == null) continue;
        String varname = NetcdfFile.makeValidCdmObjectName(dataFieldNameElem.getText().trim());
        Variable v = dataG.findVariable(varname);
        // if (v == null)
        //  v = dataG.findVariable( H4header.createValidObjectName(varname));
        if (v == null) {
          log.error("Cant find variable {} {}", varname, ncfile.getLocation());
          continue;
        }

        Element dimList = elem.getChild("DimList");
        List<Element> values = dimList.getChildren("value");
        setSharedDimensions(v, values, unknownDims, ncfile.getLocation());
      }
    }

    return featureType;
  }
  /**
   * Make a station variable
   *
   * @param varname variable name
   * @param firstDim station dimension
   * @return corresponding variable
   */
  protected Variable makeStationVariable(String varname, Dimension firstDim) {
    String longName = varname;
    String unit = null;
    DataType type = DataType.CHAR;
    List<Dimension> dims = new ArrayList<Dimension>();
    List<Attribute> attrs = new ArrayList<Attribute>();
    if (firstDim != null) {
      dims.add(firstDim);
    }

    if (varname.equals(GempakStation.STID)) {
      longName = "Station identifier";
      dims.add(DIM_LEN8);
    } else if (varname.equals(GempakStation.STNM)) {
      longName = "WMO station id";
      type = DataType.INT;
    } else if (varname.equals(GempakStation.SLAT)) {
      longName = "latitude";
      unit = "degrees_north";
      type = DataType.FLOAT;
      attrs.add(new Attribute("standard_name", "latitude"));
    } else if (varname.equals(GempakStation.SLON)) {
      longName = "longitude";
      unit = "degrees_east";
      type = DataType.FLOAT;
      attrs.add(new Attribute("standard_name", "longitude"));
    } else if (varname.equals(GempakStation.SELV)) {
      longName = "altitude";
      unit = "meter";
      type = DataType.FLOAT;
      attrs.add(new Attribute(CF.POSITIVE, CF.POSITIVE_UP));
      attrs.add(new Attribute("standard_name", "station_altitude"));
    } else if (varname.equals(GempakStation.STAT)) {
      longName = "state or province";
      dims.add(DIM_LEN2);
    } else if (varname.equals(GempakStation.COUN)) {
      longName = "country code";
      dims.add(DIM_LEN2);
    } else if (varname.equals(GempakStation.STD2)) {
      longName = "Extended station id";
      dims.add(DIM_LEN4);
    } else if (varname.equals(GempakStation.SPRI)) {
      longName = "Station priority";
      type = DataType.INT;
    } else if (varname.equals(GempakStation.SWFO)) {
      longName = "WFO code";
      dims.add(DIM_LEN4);
    } else if (varname.equals(GempakStation.WFO2)) {
      longName = "Second WFO code";
      dims.add(DIM_LEN4);
    }
    Variable v = new Variable(ncfile, null, null, varname);
    v.setDataType(type);
    v.addAttribute(new Attribute("long_name", longName));
    if (unit != null) {
      v.addAttribute(new Attribute(CDM.UNITS, unit));
    }
    if (type.equals(DataType.FLOAT)) {
      v.addAttribute(new Attribute("missing_value", RMISS));
    } else if (type.equals(DataType.INT)) {
      v.addAttribute(new Attribute("missing_value", IMISS));
    }
    if (!attrs.isEmpty()) {
      for (Attribute attr : attrs) {
        v.addAttribute(attr);
      }
    }
    if (!dims.isEmpty()) {
      v.setDimensions(dims);
    } else {
      v.setDimensions((String) null);
    }
    return v;
  }
  /**
   * Add this coord as a variable in the netCDF file
   *
   * @param ncfile netCDF file to add to
   * @param g group in file
   */
  void addToNetcdfFile(NetcdfFile ncfile, Group g) {
    if (dontUseVertical) {
      typicalRecord = null;
      return;
    }

    if (g == null) {
      g = ncfile.getRootGroup();
    }

    // coordinate axis
    Variable v = new Variable(ncfile, g, null, getVariableName());
    v.setDataType(DataType.DOUBLE);

    String desc = lookup.getLevelDescription(typicalRecord);
    if (lookup instanceof Grib2GridTableLookup && usesBounds) {
      desc = "Layer between " + desc;
    }

    v.addAttribute(new Attribute("long_name", desc));
    v.addAttribute(new Attribute("units", lookup.getLevelUnit(typicalRecord)));

    // positive attribute needed for CF-1 Height and Pressure
    if (positive != null) {
      v.addAttribute(new Attribute("positive", positive));
    }

    if (units != null) {
      AxisType axisType;
      if (SimpleUnit.isCompatible("millibar", units)) {
        axisType = AxisType.Pressure;
      } else if (SimpleUnit.isCompatible("m", units)) {
        axisType = AxisType.Height;
      } else {
        axisType = AxisType.GeoZ;
      }

      if (lookup instanceof Grib2GridTableLookup || lookup instanceof Grib1GridTableLookup) {
        v.addAttribute(
            new Attribute("GRIB_level_type", Integer.toString(typicalRecord.getLevelType1())));
      } else {
        v.addAttribute(
            new Attribute("level_type", Integer.toString(typicalRecord.getLevelType1())));
      }
      v.addAttribute(new Attribute(_Coordinate.AxisType, axisType.toString()));
    }

    if (coordValues == null) {
      coordValues = new double[levels.size()];
      for (int i = 0; i < levels.size(); i++) {
        LevelCoord lc = (LevelCoord) levels.get(i);
        coordValues[i] = lc.mid;
      }
    }
    Array dataArray = Array.factory(DataType.DOUBLE, new int[] {coordValues.length}, coordValues);

    v.setDimensions(getVariableName());
    v.setCachedData(dataArray, true);

    ncfile.addVariable(g, v);

    if (usesBounds) {
      String boundsDimName = "bounds_dim";
      if (g.findDimension(boundsDimName) == null) {
        ncfile.addDimension(g, new Dimension(boundsDimName, 2, true));
      }

      String bname = getVariableName() + "_bounds";
      v.addAttribute(new Attribute("bounds", bname));
      v.addAttribute(new Attribute(_Coordinate.ZisLayer, "true"));

      Variable b = new Variable(ncfile, g, null, bname);
      b.setDataType(DataType.DOUBLE);
      b.setDimensions(getVariableName() + " " + boundsDimName);
      b.addAttribute(new Attribute("long_name", "bounds for " + v.getName()));
      b.addAttribute(new Attribute("units", lookup.getLevelUnit(typicalRecord)));

      Array boundsArray = Array.factory(DataType.DOUBLE, new int[] {coordValues.length, 2});
      ucar.ma2.Index ima = boundsArray.getIndex();
      for (int i = 0; i < coordValues.length; i++) {
        LevelCoord lc = (LevelCoord) levels.get(i);
        boundsArray.setDouble(ima.set(i, 0), lc.value1);
        boundsArray.setDouble(ima.set(i, 1), lc.value2);
      }
      b.setCachedData(boundsArray, true);

      ncfile.addVariable(g, b);
    }

    if (factors != null) {
      // check if already created
      if (g == null) {
        g = ncfile.getRootGroup();
      }
      if (g.findVariable("hybrida") != null) return;
      v.addAttribute(new Attribute("standard_name", "atmosphere_hybrid_sigma_pressure_coordinate"));
      v.addAttribute(new Attribute("formula_terms", "ap: hybrida b: hybridb ps: Pressure"));
      // create  hybrid factor variables
      // add hybrida variable
      Variable ha = new Variable(ncfile, g, null, "hybrida");
      ha.setDataType(DataType.DOUBLE);
      ha.addAttribute(new Attribute("long_name", "level_a_factor"));
      ha.addAttribute(new Attribute("units", ""));
      ha.setDimensions(getVariableName());
      // add data
      int middle = factors.length / 2;
      double[] adata;
      double[] bdata;
      if (levels.size() < middle) { // only partial data wanted
        adata = new double[levels.size()];
        bdata = new double[levels.size()];
      } else {
        adata = new double[middle];
        bdata = new double[middle];
      }
      for (int i = 0; i < middle && i < levels.size(); i++) adata[i] = factors[i];
      Array haArray = Array.factory(DataType.DOUBLE, new int[] {adata.length}, adata);
      ha.setCachedData(haArray, true);
      ncfile.addVariable(g, ha);

      // add hybridb variable
      Variable hb = new Variable(ncfile, g, null, "hybridb");
      hb.setDataType(DataType.DOUBLE);
      hb.addAttribute(new Attribute("long_name", "level_b_factor"));
      hb.addAttribute(new Attribute("units", ""));
      hb.setDimensions(getVariableName());
      // add data
      for (int i = 0; i < middle && i < levels.size(); i++) bdata[i] = factors[i + middle];
      Array hbArray = Array.factory(DataType.DOUBLE, new int[] {bdata.length}, bdata);
      hb.setCachedData(hbArray, true);
      ncfile.addVariable(g, hb);

      /*  // TODO: delete next time modifying code
      double[] adata = new double[ middle ];
      for( int i = 0; i < middle; i++ )
        adata[ i ] = factors[ i ];
      Array haArray = Array.factory(DataType.DOUBLE, new int[]{adata.length}, adata);
      ha.setCachedData(haArray, true);
      ncfile.addVariable(g, ha);

      // add hybridb variable
      Variable hb = new Variable(ncfile, g, null, "hybridb");
      hb.setDataType(DataType.DOUBLE);
      hb.addAttribute(new Attribute("long_name",  "level_b_factor" ));
      //hb.addAttribute(new Attribute("standard_name", "atmosphere_hybrid_sigma_pressure_coordinate" ));
      hb.addAttribute(new Attribute("units", ""));
      hb.setDimensions(getVariableName());
      // add data
      double[] bdata = new double[ middle ];
      for( int i = 0; i < middle; i++ )
        bdata[ i ] = factors[ i + middle ];
      Array hbArray = Array.factory(DataType.DOUBLE, new int[]{bdata.length}, bdata);
      hb.setCachedData(hbArray, true);
      ncfile.addVariable(g, hb);
      */
    }
  }
  public Variable makeVariable(
      NetcdfFile ncfile,
      int datatype,
      String shortName,
      String longName,
      String abbrev,
      List groups)
      throws IOException {
    int nscans = groups.size();

    if (nscans == 0) {
      throw new IllegalStateException("No data for " + shortName);
    }

    // get representative record
    List firstGroup = (List) groups.get(0);
    Cinrad2Record firstRecord = (Cinrad2Record) firstGroup.get(0);
    int ngates = firstRecord.getGateCount(datatype);

    String scanDimName = "scan" + abbrev;
    String gateDimName = "gate" + abbrev;
    Dimension scanDim = new Dimension(scanDimName, nscans);
    Dimension gateDim = new Dimension(gateDimName, ngates);
    ncfile.addDimension(null, scanDim);
    ncfile.addDimension(null, gateDim);

    ArrayList dims = new ArrayList();
    dims.add(scanDim);
    dims.add(radialDim);
    dims.add(gateDim);

    Variable v = new Variable(ncfile, null, null, shortName);
    v.setDataType(DataType.BYTE);
    v.setDimensions(dims);
    ncfile.addVariable(null, v);

    v.addAttribute(new Attribute(CDM.UNITS, Cinrad2Record.getDatatypeUnits(datatype)));
    v.addAttribute(new Attribute(CDM.LONG_NAME, longName));

    byte[] b = new byte[2];
    b[0] = Cinrad2Record.MISSING_DATA;
    b[1] = Cinrad2Record.BELOW_THRESHOLD;
    Array missingArray = Array.factory(DataType.BYTE.getPrimitiveClassType(), new int[] {2}, b);

    v.addAttribute(new Attribute(CDM.MISSING_VALUE, missingArray));
    v.addAttribute(
        new Attribute("signal_below_threshold", new Byte(Cinrad2Record.BELOW_THRESHOLD)));
    v.addAttribute(
        new Attribute(CDM.SCALE_FACTOR, new Float(Cinrad2Record.getDatatypeScaleFactor(datatype))));
    v.addAttribute(
        new Attribute(CDM.ADD_OFFSET, new Float(Cinrad2Record.getDatatypeAddOffset(datatype))));
    v.addAttribute(new Attribute(CDM.UNSIGNED, "true"));

    ArrayList dim2 = new ArrayList();
    dim2.add(scanDim);
    dim2.add(radialDim);

    // add time coordinate variable
    String timeCoordName = "time" + abbrev;
    Variable timeVar = new Variable(ncfile, null, null, timeCoordName);
    timeVar.setDataType(DataType.INT);
    timeVar.setDimensions(dim2);
    ncfile.addVariable(null, timeVar);

    // int julianDays = volScan.getTitleJulianDays();
    // Date d = Cinrad2Record.getDate( julianDays, 0);
    // Date d = Cinrad2Record.getDate(volScan.getTitleJulianDays(), volScan.getTitleMsecs());
    Date d = volScan.getStartDate();
    String units = "msecs since " + formatter.toDateTimeStringISO(d);

    timeVar.addAttribute(new Attribute(CDM.LONG_NAME, "time since base date"));
    timeVar.addAttribute(new Attribute(CDM.UNITS, units));
    timeVar.addAttribute(new Attribute(CDM.MISSING_VALUE, new Integer(MISSING_INT)));
    timeVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString()));

    // add elevation coordinate variable
    String elevCoordName = "elevation" + abbrev;
    Variable elevVar = new Variable(ncfile, null, null, elevCoordName);
    elevVar.setDataType(DataType.FLOAT);
    elevVar.setDimensions(dim2);
    ncfile.addVariable(null, elevVar);

    elevVar.addAttribute(new Attribute(CDM.UNITS, "degrees"));
    elevVar.addAttribute(
        new Attribute(
            CDM.LONG_NAME,
            "elevation angle in degres: 0 = parallel to pedestal base, 90 = perpendicular"));
    elevVar.addAttribute(new Attribute(CDM.MISSING_VALUE, new Float(MISSING_FLOAT)));
    elevVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.RadialElevation.toString()));

    // add azimuth coordinate variable
    String aziCoordName = "azimuth" + abbrev;
    Variable aziVar = new Variable(ncfile, null, null, aziCoordName);
    aziVar.setDataType(DataType.FLOAT);
    aziVar.setDimensions(dim2);
    ncfile.addVariable(null, aziVar);

    aziVar.addAttribute(new Attribute(CDM.UNITS, "degrees"));
    aziVar.addAttribute(
        new Attribute(CDM.LONG_NAME, "azimuth angle in degrees: 0 = true north, 90 = east"));
    aziVar.addAttribute(new Attribute(CDM.MISSING_VALUE, new Float(MISSING_FLOAT)));
    aziVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.RadialAzimuth.toString()));

    // add gate coordinate variable
    String gateCoordName = "distance" + abbrev;
    Variable gateVar = new Variable(ncfile, null, null, gateCoordName);
    gateVar.setDataType(DataType.FLOAT);
    gateVar.setDimensions(gateDimName);
    Array data =
        Array.makeArray(
            DataType.FLOAT,
            ngates,
            (double) firstRecord.getGateStart(datatype),
            (double) firstRecord.getGateSize(datatype));
    gateVar.setCachedData(data, false);
    ncfile.addVariable(null, gateVar);
    radarRadius = firstRecord.getGateStart(datatype) + ngates * firstRecord.getGateSize(datatype);

    gateVar.addAttribute(new Attribute(CDM.UNITS, "m"));
    gateVar.addAttribute(new Attribute(CDM.LONG_NAME, "radial distance to start of gate"));
    gateVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.RadialDistance.toString()));

    // add number of radials variable
    String nradialsName = "numRadials" + abbrev;
    Variable nradialsVar = new Variable(ncfile, null, null, nradialsName);
    nradialsVar.setDataType(DataType.INT);
    nradialsVar.setDimensions(scanDim.getName());
    nradialsVar.addAttribute(new Attribute(CDM.LONG_NAME, "number of valid radials in this scan"));
    ncfile.addVariable(null, nradialsVar);

    // add number of gates variable
    String ngateName = "numGates" + abbrev;
    Variable ngateVar = new Variable(ncfile, null, null, ngateName);
    ngateVar.setDataType(DataType.INT);
    ngateVar.setDimensions(scanDim.getName());
    ngateVar.addAttribute(new Attribute(CDM.LONG_NAME, "number of valid gates in this scan"));
    ncfile.addVariable(null, ngateVar);

    makeCoordinateDataWithMissing(
        datatype, timeVar, elevVar, aziVar, nradialsVar, ngateVar, groups);

    // back to the data variable
    String coordinates =
        timeCoordName + " " + elevCoordName + " " + aziCoordName + " " + gateCoordName;
    v.addAttribute(new Attribute(_Coordinate.Axes, coordinates));

    // make the record map
    int nradials = radialDim.getLength();
    Cinrad2Record[][] map = new Cinrad2Record[nscans][nradials];
    for (int i = 0; i < groups.size(); i++) {
      Cinrad2Record[] mapScan = map[i];
      List group = (List) groups.get(i);
      for (int j = 0; j < group.size(); j++) {
        Cinrad2Record r = (Cinrad2Record) group.get(j);
        int radial = r.radial_num - 1;
        mapScan[radial] = r;
      }
    }

    Vgroup vg = new Vgroup(datatype, map);
    v.setSPobject(vg);

    return v;
  }
  public void open(RandomAccessFile raf, NetcdfFile ncfile, CancelTask cancelTask)
      throws IOException {
    NexradStationDB.init();

    volScan = new Cinrad2VolumeScan(raf, cancelTask);
    if (volScan.hasDifferentDopplarResolutions())
      throw new IllegalStateException("volScan.hasDifferentDopplarResolutions");

    radialDim = new Dimension("radial", volScan.getMaxRadials());
    ncfile.addDimension(null, radialDim);

    makeVariable(
        ncfile,
        Cinrad2Record.REFLECTIVITY,
        "Reflectivity",
        "Reflectivity",
        "R",
        volScan.getReflectivityGroups());
    int velocity_type =
        (volScan.getDopplarResolution() == Cinrad2Record.DOPPLER_RESOLUTION_HIGH_CODE)
            ? Cinrad2Record.VELOCITY_HI
            : Cinrad2Record.VELOCITY_LOW;
    Variable v =
        makeVariable(
            ncfile,
            velocity_type,
            "RadialVelocity",
            "Radial Velocity",
            "V",
            volScan.getVelocityGroups());
    makeVariableNoCoords(
        ncfile, Cinrad2Record.SPECTRUM_WIDTH, "SpectrumWidth", "Spectrum Width", v);

    if (volScan.getStationId() != null) {
      ncfile.addAttribute(null, new Attribute("Station", volScan.getStationId()));
      ncfile.addAttribute(null, new Attribute("StationName", volScan.getStationName()));
      ncfile.addAttribute(
          null, new Attribute("StationLatitude", new Double(volScan.getStationLatitude())));
      ncfile.addAttribute(
          null, new Attribute("StationLongitude", new Double(volScan.getStationLongitude())));
      ncfile.addAttribute(
          null,
          new Attribute("StationElevationInMeters", new Double(volScan.getStationElevation())));

      double latRadiusDegrees = Math.toDegrees(radarRadius / ucar.unidata.geoloc.Earth.getRadius());
      ncfile.addAttribute(
          null,
          new Attribute(
              "geospatial_lat_min", new Double(volScan.getStationLatitude() - latRadiusDegrees)));
      ncfile.addAttribute(
          null,
          new Attribute(
              "geospatial_lat_max", new Double(volScan.getStationLatitude() + latRadiusDegrees)));
      double cosLat = Math.cos(Math.toRadians(volScan.getStationLatitude()));
      double lonRadiusDegrees =
          Math.toDegrees(radarRadius / cosLat / ucar.unidata.geoloc.Earth.getRadius());
      ncfile.addAttribute(
          null,
          new Attribute(
              "geospatial_lon_min", new Double(volScan.getStationLongitude() - lonRadiusDegrees)));
      ncfile.addAttribute(
          null,
          new Attribute(
              "geospatial_lon_max", new Double(volScan.getStationLongitude() + lonRadiusDegrees)));

      // add a radial coordinate transform (experimental)
      Variable ct = new Variable(ncfile, null, null, "radialCoordinateTransform");
      ct.setDataType(DataType.CHAR);
      ct.setDimensions(""); // scalar
      ct.addAttribute(new Attribute("transform_name", "Radial"));
      ct.addAttribute(new Attribute("center_latitude", new Double(volScan.getStationLatitude())));
      ct.addAttribute(new Attribute("center_longitude", new Double(volScan.getStationLongitude())));
      ct.addAttribute(new Attribute("center_elevation", new Double(volScan.getStationElevation())));
      ct.addAttribute(new Attribute(_Coordinate.TransformType, "Radial"));
      ct.addAttribute(
          new Attribute(_Coordinate.AxisTypes, "RadialElevation RadialAzimuth RadialDistance"));

      Array data =
          Array.factory(DataType.CHAR.getPrimitiveClassType(), new int[0], new char[] {' '});
      ct.setCachedData(data, true);
      ncfile.addVariable(null, ct);
    }

    DateFormatter formatter = new DateFormatter();

    ncfile.addAttribute(null, new Attribute(CDM.CONVENTIONS, _Coordinate.Convention));
    ncfile.addAttribute(null, new Attribute("format", volScan.getDataFormat()));
    ncfile.addAttribute(null, new Attribute(CF.FEATURE_TYPE, FeatureType.RADIAL.toString()));
    // Date d = Cinrad2Record.getDate(volScan.getTitleJulianDays(), volScan.getTitleMsecs());
    // ncfile.addAttribute(null, new Attribute("base_date", formatter.toDateOnlyString(d)));

    ncfile.addAttribute(
        null,
        new Attribute(
            "time_coverage_start", formatter.toDateTimeStringISO(volScan.getStartDate())));
    ; // .toDateTimeStringISO(d)));
    ncfile.addAttribute(
        null,
        new Attribute("time_coverage_end", formatter.toDateTimeStringISO(volScan.getEndDate())));

    ncfile.addAttribute(
        null,
        new Attribute(CDM.HISTORY, "Direct read of Nexrad Level 2 file into NetCDF-Java 2.2 API"));
    ncfile.addAttribute(null, new Attribute("DataType", "Radial"));

    ncfile.addAttribute(
        null,
        new Attribute(
            "Title",
            "Nexrad Level 2 Station "
                + volScan.getStationId()
                + " from "
                + formatter.toDateTimeStringISO(volScan.getStartDate())
                + " to "
                + formatter.toDateTimeStringISO(volScan.getEndDate())));

    ncfile.addAttribute(
        null,
        new Attribute(
            "Summary",
            "Weather Surveillance Radar-1988 Doppler (WSR-88D) "
                + "Level II data are the three meteorological base data quantities: reflectivity, mean radial velocity, and "
                + "spectrum width."));

    ncfile.addAttribute(
        null,
        new Attribute(
            "keywords",
            "WSR-88D; NEXRAD; Radar Level II; reflectivity; mean radial velocity; spectrum width"));

    ncfile.addAttribute(
        null,
        new Attribute(
            "VolumeCoveragePatternName",
            Cinrad2Record.getVolumeCoveragePatternName(volScan.getVCP())));
    ncfile.addAttribute(
        null, new Attribute("VolumeCoveragePattern", new Integer(volScan.getVCP())));
    ncfile.addAttribute(
        null,
        new Attribute(
            "HorizonatalBeamWidthInDegrees", new Double(Cinrad2Record.HORIZONTAL_BEAM_WIDTH)));

    ncfile.finish();
  }
  public static NetcdfDataset buildStation(IObservationGroup obsGroup) {
    if (obsGroup.getDepths().length > 1) {
      return buildStationProfile(obsGroup);
    }
    NetcdfDataset ncds = null;
    File temp = null;
    try {
      /* Instantiate an empty NetcdfDataset object from the template ncml */
      temp = File.createTempFile("ooi", ".ncml");
      ncds = getNcdsFromTemplate(temp, "station.ncml");

      Map<String, String> allAttributes = new HashMap<String, String>();
      allAttributes.putAll(obsGroup.getAttributes());

      /* Do the station Id */
      ArrayInt.D0 aid = new ArrayInt.D0();
      aid.set(obsGroup.getId());
      ncds.findVariable("stnId").setCachedData(aid);

      /* Do the times */
      Number[] times = obsGroup.getTimes();
      IObservationGroup.DataType tdt = obsGroup.getTimeDataType();
      ncds.findDimension("time").setLength(times.length);
      Variable tvar = ncds.findVariable("time");
      tvar.setDataType(getNcDataType(tdt));
      tvar.setCachedData(getNcArray(times, tdt));

      /* Do the lat & lon */
      IObservationGroup.DataType lldt = obsGroup.getLatLonDataType();
      DataType ncdtLl = getNcDataType(lldt);
      Variable laVar = ncds.findVariable("lat");
      laVar.setDataType(ncdtLl);
      laVar.setCachedData(getNcScalar(obsGroup.getLat(), lldt));
      Variable loVar = ncds.findVariable("lon");
      loVar.setDataType(ncdtLl);
      loVar.setCachedData(getNcScalar(obsGroup.getLon(), lldt));

      /* Do the depth */
      IObservationGroup.DataType ddt = obsGroup.getDepthDataType();
      VariableDS zVar =
          new VariableDS(
              ncds, null, null, "stnDepth", getNcDataType(ddt), "", "m", "station depth");
      zVar.addAttribute(new Attribute("positive", "down"));
      ncds.addVariable(null, zVar);
      Number depth = obsGroup.getDepths()[0];
      zVar.setCachedData(getNcScalar(obsGroup.getDepths()[0], ddt));

      /* Do the data variables */
      for (VariableParams dn : obsGroup.getDataNames()) {
        DataType ncdtData = getNcDataType(dn.getDataType());
        VariableDS dvar =
            new VariableDS(
                ncds,
                null,
                null,
                dn.getShortName(),
                ncdtData,
                "time",
                dn.getUnits(),
                dn.getDescription());
        dvar.addAttribute(new Attribute(CF.COORDINATES, "time lon lat"));
        dvar.addAttribute(new Attribute(CF.STANDARD_NAME, dn.getStandardName()));
        Array adata = Array.factory(ncdtData, new int[] {times.length});
        IndexIterator aii = adata.getIndexIterator();
        dvar.setCachedData(adata);
        ncds.addVariable(null, dvar);

        for (int ti = 0; ti < times.length; ti++) {
          putArrayData(aii, ncdtData, obsGroup.getData(dn, times[ti], depth));
        }
      }

      /* Add global attributes */
      for (String key : allAttributes.keySet()) {
        ncds.addAttribute(null, new Attribute(key, allAttributes.get(key)));
      }

    } catch (FileNotFoundException ex) {
      Logger.getLogger(NcdsFactory.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
      Logger.getLogger(NcdsFactory.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
      if (temp != null) {
        if (!temp.delete()) {
          temp.deleteOnExit();
        }
      }
      if (ncds != null) {
        ncds.finish();
      }
    }

    return ncds;
  }
  public static NetcdfDataset buildTrajectory(List<IObservationGroup> obsGroups) {
    //        IObservationGroup.DataType tdt = IObservationGroup.DataType.values()[0];
    //        IObservationGroup.DataType lldt = IObservationGroup.DataType.values()[0];
    //        IObservationGroup.DataType ddt = IObservationGroup.DataType.values()[0];
    //        for(IObservationGroup og : obsGroups) {
    //            tdt = (og.getTimeDataType().compareTo(tdt) > 0) ? og.getTimeDataType() : tdt;
    //            lldt = (og.getLatLonDataType().compareTo(lldt) > 0) ? og.getLatLonDataType() :
    // lldt;
    //            ddt = (og.getDepthDataType().compareTo(ddt) > 0) ? og.getDepthDataType() : ddt;
    //        }

    NetcdfDataset ncds = null;
    File temp = null;
    try {
      /* Instantiate an empty NetcdfDataset object from the template ncml */
      temp = File.createTempFile("ooi", ".ncml");
      ncds = getNcdsFromTemplate(temp, "trajectory.ncml");

      int nobs = obsGroups.size();
      List<Number> allDepths = new ArrayList<Number>();
      List<VariableParams> allDn = new ArrayList<VariableParams>();
      int nt = nobs;
      int nd = 0;
      for (IObservationGroup og : obsGroups) {
        nd = Math.max(nd, og.getDepths().length);
        for (Number d : og.getDepths()) {
          if (!allDepths.contains(d)) {
            allDepths.add(d);
          }
        }
        for (VariableParams dn : og.getDataNames()) {
          if (!allDn.contains(dn)) {
            allDn.add(dn);
          }
        }
      }

      /* Do the trajectory ID */

      /* Do the times */
      Number[] times = obsGroups.get(0).getTimes();
      IObservationGroup.DataType tdt = obsGroups.get(0).getTimeDataType();
      DataType ncdtTime = getNcDataType(tdt);
      ncds.findDimension("time").setLength(nt);
      Array tarr = Array.factory(ncdtTime, new int[] {nt});
      IndexIterator tii = tarr.getIndexIterator();
      Variable tvar = ncds.findVariable("time");
      tvar.setDataType(getNcDataType(tdt));
      tvar.setCachedData(tarr);

      /* Do the lats */
      IObservationGroup.DataType lldt = obsGroups.get(0).getLatLonDataType();
      DataType ncdtLl = getNcDataType(lldt);
      Array laarr = Array.factory(ncdtLl, new int[] {nt});
      IndexIterator laii = laarr.getIndexIterator();
      Variable lavar = ncds.findVariable("lat");
      lavar.setDataType(ncdtLl);
      lavar.setCachedData(laarr);

      /* Do the lons */
      Array loarr = Array.factory(ncdtLl, new int[] {nt});
      IndexIterator loii = loarr.getIndexIterator();
      Variable lovar = ncds.findVariable("lon");
      lovar.setDataType(ncdtLl);
      lovar.setCachedData(loarr);

      /* Iterate over the observation groups and fill the data */
      Map<String, String> allAttributes = new HashMap<String, String>();
      IObservationGroup og;
      HashMap<String, IndexIterator> darrs = new HashMap<String, IndexIterator>();
      Number time;
      Number depth = allDepths.get(0);
      for (int obs = 0; obs < nobs; obs++) {
        og = obsGroups.get(obs);
        time = og.getTimes()[0];
        putArrayData(tii, ncdtTime, time);
        putArrayData(loii, ncdtLl, og.getLon());
        putArrayData(laii, ncdtLl, og.getLat());

        for (VariableParams dn : allDn) {
          if (og.getDataNames().contains(dn)) {
            DataType ncdtData = getNcDataType(dn.getDataType());
            VariableDS dvar = (VariableDS) ncds.findVariable(dn.getShortName());
            if (dvar == null) {
              dvar =
                  new VariableDS(
                      ncds,
                      null,
                      null,
                      dn.getShortName(),
                      ncdtData,
                      "time",
                      dn.getUnits(),
                      dn.getDescription());
              dvar.addAttribute(new Attribute(CF.COORDINATES, "time lon lat"));
              //                            dvar.addAttribute(new Attribute("missing_value",
              // missingData));
              dvar.addAttribute(new Attribute(CF.STANDARD_NAME, dn.getStandardName()));
              Array darr = Array.factory(ncdtData, new int[] {nt});
              dvar.setCachedData(darr);

              darrs.put(dn.getStandardName(), darr.getIndexIterator());
              ncds.addVariable(null, dvar);
            }
            putArrayData(darrs.get(dn.getStandardName()), ncdtData, og.getData(dn, time, depth));

          } else {
            /*
             * station doesn't have this variable - don't believe
             * this can even happen...
             *
             * NOTE: This would indicate a problem with the above processing where the data-name list
             * has been modified prior to contains-checking
             */
          }
        }
      }

      /* Add global attributes */
      for (String key : allAttributes.keySet()) {
        ncds.addAttribute(null, new Attribute(key, allAttributes.get(key)));
      }
    } catch (FileNotFoundException ex) {
      Logger.getLogger(NcdsFactory.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
      Logger.getLogger(NcdsFactory.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
      if (temp != null) {
        if (!temp.delete()) {
          temp.deleteOnExit();
        }
      }
      if (ncds != null) {
        ncds.finish();
      }
    }

    return ncds;
  }