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);
  }
Example #2
0
 /**
  * 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;
 }
Example #3
0
 /**
  * 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;
 }
Example #4
0
  /**
   * 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();
  }