private void writeStationData(List<ucar.unidata.geoloc.Station> stnList) throws IOException {
    this.stnList = stnList;
    int nstns = stnList.size();
    stationMap = new HashMap<String, StationTracker>(2 * nstns);
    if (debug) System.out.println("stationMap created");

    // now write the station data
    ArrayDouble.D1 latArray = new ArrayDouble.D1(nstns);
    ArrayDouble.D1 lonArray = new ArrayDouble.D1(nstns);
    ArrayDouble.D1 altArray = new ArrayDouble.D1(nstns);
    ArrayObject.D1 idArray = new ArrayObject.D1(String.class, nstns);
    ArrayObject.D1 descArray = new ArrayObject.D1(String.class, nstns);
    ArrayObject.D1 wmoArray = new ArrayObject.D1(String.class, nstns);

    for (int i = 0; i < stnList.size(); i++) {
      ucar.unidata.geoloc.Station stn = stnList.get(i);
      stationMap.put(stn.getName(), new StationTracker(i));

      latArray.set(i, stn.getLatitude());
      lonArray.set(i, stn.getLongitude());
      if (useAlt) altArray.set(i, stn.getAltitude());

      idArray.set(i, stn.getName());
      descArray.set(i, stn.getDescription());
      if (useWmoId) wmoArray.set(i, stn.getWmoId());
    }

    try {
      ncfile.write(latName, latArray);
      ncfile.write(lonName, lonArray);
      if (useAlt) ncfile.write(altName, altArray);
      ncfile.writeStringData(idName, idArray);
      ncfile.writeStringData(descName, descArray);
      if (useWmoId) ncfile.writeStringData(wmoName, wmoArray);

    } catch (InvalidRangeException e) {
      e.printStackTrace();
      throw new IllegalStateException(e);
    }
  }
  private LatLonRect getBoundingBox(List stnList) {
    ucar.unidata.geoloc.Station s = (ucar.unidata.geoloc.Station) stnList.get(0);
    LatLonPointImpl llpt = new LatLonPointImpl();
    llpt.set(s.getLatitude(), s.getLongitude());
    LatLonRect rect = new LatLonRect(llpt, .001, .001);

    for (int i = 1; i < stnList.size(); i++) {
      s = (ucar.unidata.geoloc.Station) stnList.get(i);
      llpt.set(s.getLatitude(), s.getLongitude());
      rect.extend(llpt);
    }

    return rect;
  }
  private void writeDataFinish() throws IOException {
    // finish global variables
    ArrayInt.D0 totalArray = new ArrayInt.D0();
    totalArray.set(profileIndex);
    try {
      ncfile.write(numProfilesTotalName, totalArray);
    } catch (InvalidRangeException e) {
      e.printStackTrace();
      throw new IllegalStateException(e);
    }

    // finish the station data
    int nstns = stnList.size();
    ArrayInt.D1 firstProfileArray = new ArrayInt.D1(nstns);
    ArrayInt.D1 numProfileArray = new ArrayInt.D1(nstns);
    ArrayInt.D1 nextProfileArray = new ArrayInt.D1(nprofiles);

    for (int i = 0; i < stnList.size(); i++) {
      ucar.unidata.geoloc.Station stn = stnList.get(i);
      StationTracker tracker = stationMap.get(stn.getName());

      numProfileArray.set(i, tracker.numChildren);

      int first = (tracker.link.size() > 0) ? tracker.link.get(0) : -1;
      firstProfileArray.set(i, first);

      if (tracker.link.size() > 0) {
        // construct forward link
        List<Integer> nextList = tracker.link;
        for (int j = 0; j < nextList.size() - 1; j++) {
          Integer curr = nextList.get(j);
          Integer next = nextList.get(j + 1);
          nextProfileArray.set(curr, next);
        }
        Integer curr = nextList.get(nextList.size() - 1);
        nextProfileArray.set(curr, -1);
      }
    }

    try {
      ncfile.write(firstProfileName, firstProfileArray);
      ncfile.write(numProfilesName, numProfileArray);
      ncfile.write(nextProfileName, nextProfileArray);

    } catch (InvalidRangeException e) {
      e.printStackTrace();
      throw new IllegalStateException(e);
    }

    // finish the profile data
    ArrayInt.D1 nextObsArray = new ArrayInt.D1(recno);
    ArrayInt.D1 firstObsArray = new ArrayInt.D1(nprofiles);
    ArrayInt.D1 numObsArray = new ArrayInt.D1(nprofiles);

    for (int i = 0; i < stnList.size(); i++) {
      ucar.unidata.geoloc.Station stn = stnList.get(i);
      StationTracker stnTracker = stationMap.get(stn.getName());

      Set<Date> dates = stnTracker.profileMap.keySet();
      for (Date date : dates) {
        ProfileTracker proTracker = stnTracker.profileMap.get(date);
        int trackerIndex = proTracker.parent_index;
        numObsArray.set(trackerIndex, proTracker.numChildren);

        int first = (proTracker.link.size() > 0) ? proTracker.link.get(0) : -1;
        firstObsArray.set(trackerIndex, first);

        if (proTracker.link.size() > 0) {
          // construct forward link
          List<Integer> nextList = proTracker.link;
          for (int j = 0; j < nextList.size() - 1; j++) {
            Integer curr = nextList.get(j);
            Integer next = nextList.get(j + 1);
            nextObsArray.set(curr, next);
          }
          Integer curr = nextList.get(nextList.size() - 1);
          nextObsArray.set(curr, -1);
        }
      }
    }

    try {
      ncfile.write(firstObsName, firstObsArray);
      ncfile.write(numObsName, numObsArray);
      ncfile.write(nextObsName, nextObsArray);

    } catch (InvalidRangeException e) {
      e.printStackTrace();
      throw new IllegalStateException(e);
    }

    // finish the obs data

    ncfile.updateAttribute(
        null, new Attribute("time_coverage_start", dateFormatter.toDateTimeStringISO(minDate)));
    ncfile.updateAttribute(
        null, new Attribute("time_coverage_end", dateFormatter.toDateTimeStringISO(maxDate)));
  }
  private void createStations(List<ucar.unidata.geoloc.Station> stnList) throws IOException {
    int nstns = stnList.size();

    // see if there's altitude, wmoId for any stations
    for (int i = 0; i < nstns; i++) {
      ucar.unidata.geoloc.Station stn = stnList.get(i);

      // if (!Double.isNaN(stn.getAltitude()))
      //  useAlt = true;
      if ((stn.getWmoId() != null) && (stn.getWmoId().trim().length() > 0)) useWmoId = true;
    }

    /* if (useAlt)
    ncfile.addGlobalAttribute("altitude_coordinate", altName); */

    // find string lengths
    for (int i = 0; i < nstns; i++) {
      ucar.unidata.geoloc.Station station = stnList.get(i);
      name_strlen = Math.max(name_strlen, station.getName().length());
      desc_strlen = Math.max(desc_strlen, station.getDescription().length());
      if (useWmoId) wmo_strlen = Math.max(wmo_strlen, station.getName().length());
    }

    LatLonRect llbb = getBoundingBox(stnList);
    ncfile.addGlobalAttribute(
        "geospatial_lat_min", Double.toString(llbb.getLowerLeftPoint().getLatitude()));
    ncfile.addGlobalAttribute(
        "geospatial_lat_max", Double.toString(llbb.getUpperRightPoint().getLatitude()));
    ncfile.addGlobalAttribute(
        "geospatial_lon_min", Double.toString(llbb.getLowerLeftPoint().getLongitude()));
    ncfile.addGlobalAttribute(
        "geospatial_lon_max", Double.toString(llbb.getUpperRightPoint().getLongitude()));

    // add the dimensions
    Dimension recordDim = ncfile.addUnlimitedDimension(recordDimName);
    recordDims.add(recordDim);

    Dimension stationDim = ncfile.addDimension(stationDimName, nstns);
    stationDims.add(stationDim);

    // add the station Variables using the station dimension
    Variable v = ncfile.addVariable(latName, DataType.DOUBLE, stationDimName);
    ncfile.addVariableAttribute(v, new Attribute("units", "degrees_north"));
    ncfile.addVariableAttribute(v, new Attribute("long_name", "station latitude"));

    v = ncfile.addVariable(lonName, DataType.DOUBLE, stationDimName);
    ncfile.addVariableAttribute(v, new Attribute("units", "degrees_east"));
    ncfile.addVariableAttribute(v, new Attribute("long_name", "station longitude"));

    if (useAlt) {
      v = ncfile.addVariable(altName, DataType.DOUBLE, stationDimName);
      ncfile.addVariableAttribute(v, new Attribute("units", "meters"));
      ncfile.addVariableAttribute(v, new Attribute("long_name", "station altitude"));
    }

    v = ncfile.addStringVariable(idName, stationDims, name_strlen);
    ncfile.addVariableAttribute(v, new Attribute("long_name", "station identifier"));

    v = ncfile.addStringVariable(descName, stationDims, desc_strlen);
    ncfile.addVariableAttribute(v, new Attribute("long_name", "station description"));

    if (useWmoId) {
      v = ncfile.addStringVariable(wmoName, stationDims, wmo_strlen);
      ncfile.addVariableAttribute(v, new Attribute("long_name", "station WMO id"));
    }

    v = ncfile.addVariable(numProfilesName, DataType.INT, stationDimName);
    ncfile.addVariableAttribute(
        v, new Attribute("long_name", "number of profiles in linked list for this station"));

    v = ncfile.addVariable(firstProfileName, DataType.INT, stationDimName);
    ncfile.addVariableAttribute(
        v, new Attribute("long_name", "index of first profile in linked list for this station"));
  }
  /**
   * _more_
   *
   * @param args _more_
   * @throws IOException _more_
   */
  public static void main(String args[]) throws IOException {
    StringBuffer errlog = new StringBuffer();
    String dqc_location = "http://thredds.ucar.edu/thredds/idd/radarLevel2";
    DqcRadarDatasetCollection ds = factory("test", dqc_location, errlog);
    System.out.println(" errs= " + errlog);

    List stns = ds.getStations();
    System.out.println(" nstns= " + stns.size());

    Station stn = (Station) (stns.get(2));

    // List ulist = stn.getRadarStationURIs();
    // assert null != ulist;
    Date ts1 = DateUnit.getStandardOrISO("2007-06-9T12:12:00");
    Date ts2 = DateUnit.getStandardOrISO("2007-06-9T23:12:00");

    List tlist = ds.getRadarStationTimes(stn.getValue(), ts1, ts2);
    int sz = tlist.size();
    Date ts0 = DateUnit.getStandardOrISO((String) tlist.get(1));
    RadialDatasetSweep rds = ds.getRadarDataset(stn.getValue(), ts0);
    URI stURL = ds.getRadarDatasetURI(stn.getValue(), ts0);
    assert null != stURL;
    assert 0 != sz;
    DateSelection dateS = new DateSelection(ts1, ts2);
    dateS.setInterval((double) 3600 * 1000);
    dateS.setRoundTo((double) 3600 * 1000);
    dateS.setPreRange((double) 500 * 1000);
    dateS.setPostRange((double) 500 * 1000);

    for (int i = 0; i < stns.size(); i++) {
      stn = (Station) stns.get(i);
      List times =
          ds.getRadarStationTimes(
              stn.getValue(),
              new Date(System.currentTimeMillis() - 3600 * 1000 * 24 * 100),
              new Date(System.currentTimeMillis()));
      if (times.size() > 0) {
        System.err.println(
            stn
                + " times:"
                + times.size()
                + " "
                + times.get(0)
                + " - "
                + times.get(times.size() - 1));
      } else {
        System.err.println(stn + " no times");
      }
    }
    System.exit(0);

    List jList = ds.getDataURIs("KABX", dateS);

    assert null != jList;
    List mList = ds.getData("KABX", dateS, null);
    assert null != mList;

    // Date ts0 =
    // DateFromString.getDateUsingCompleteDateFormat((String)tlist.get(1),"yyyy-MM-dd'T'HH:mm:ss");
    Date ts = DateUnit.getStandardOrISO((String) tlist.get(1));
    java.text.SimpleDateFormat isoDateTimeFormat;
    isoDateTimeFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    isoDateTimeFormat.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
    String st = isoDateTimeFormat.format(ts);
  }
  /**
   * _more_
   *
   * @param desc _more_
   * @param dqc_location _more_
   * @param errlog _more_
   * @return _more_
   * @throws IOException _more_
   */
  public static DqcRadarDatasetCollection factory(
      String desc, String dqc_location, StringBuffer errlog) throws IOException {

    DqcFactory dqcFactory = new DqcFactory(true);
    QueryCapability dqc = dqcFactory.readXML(dqc_location + "?returns=dqc");
    if (dqc.hasFatalError()) {
      errlog.append(dqc.getErrorMessages());
      return null;
    }

    // have a look at what selectors there are before proceeding
    SelectStation selStation = null;
    SelectList selTime = null;
    SelectService selService = null;
    // SelectGeoRegion selRegion = null;

    ArrayList selectors = dqc.getSelectors();
    for (int i = 0; i < selectors.size(); i++) {
      Selector s = (Selector) selectors.get(i);
      if (s instanceof SelectStation) {
        selStation = (SelectStation) s;
      }
      if (s instanceof SelectList) {
        selTime = (SelectList) s;
      }
      if (s instanceof SelectService) {
        selService = (SelectService) s;
      }
      // if (s instanceof SelectGeoRegion)
      //   selRegion = (SelectGeoRegion) s;
    }

    // gotta have these
    if (selService == null) {
      errlog.append("DqcStationaryRadarDataset must have Service selector");
      return null;
    }
    if (selStation == null) {
      errlog.append("DqcStationaryRadarDataset must have Station selector");
      return null;
    }
    if (selTime == null) {
      errlog.append("DqcStationaryRadarDataset must have Date selector");
      return null;
    }
    // if (selRegion == null) {
    //   errlog.append("DqcStationaryRadarDataset must have GeoRegion selector");
    //   return null;
    // }

    // decide on which service
    SelectService.ServiceChoice wantServiceChoice = null;
    List services = selService.getChoices();
    for (int i = 0; i < services.size(); i++) {
      SelectService.ServiceChoice serviceChoice = (SelectService.ServiceChoice) services.get(i);
      if (serviceChoice.getService().equals("HTTPServer")
          && serviceChoice.getDataFormat().equals("text/xml")) {
        // && serviceChoice.getReturns().equals("data")     ) // LOOK kludge
        wantServiceChoice = serviceChoice;
      }
    }

    if (wantServiceChoice == null) {
      errlog.append(
          "DqcStationObsDataset must have HTTPServer Service with DataFormat=text/plain, and returns=data");
      return null;
    }

    return new DqcRadarDatasetCollection(
        desc, dqc, selService, wantServiceChoice, selStation, null, selTime);
  }
  public static boolean isValidFile(NetcdfDataset ds) {
    // Check that has a time dimension and a trajectory dimension.
    List list = ds.getRootGroup().getDimensions();
    if (list.size() != 2) return (false);
    Dimension d;
    for (int i = 0; i < 2; i++) {
      d = (Dimension) list.get(i);
      if (!d.getShortName().equals(timeDimNameDefault)
          && !d.getShortName().equals(trajDimNameDefault)) return (false);
    }

    // Check that has a trajectory coordinate variable.
    Variable var = ds.getRootGroup().findVariable(trajVarNameDefault);
    if (var == null) return (false);
    list = var.getDimensions();
    if (list.size() != 1) return (false);
    d = (Dimension) list.get(0);
    if (!d.getShortName().equals(trajDimNameDefault)) return (false);

    // Check that has a time coordinate variable with units that are udunits time
    var = ds.getRootGroup().findVariable(timeVarNameDefault);
    if (var == null) return (false);
    list = var.getDimensions();
    if (list.size() != 1) return (false);
    d = (Dimension) list.get(0);
    if (!d.getShortName().equals(timeDimNameDefault)) return (false);
    String units = var.findAttribute("units").getStringValue();
    Date date = DateUnit.getStandardDate("0 " + units);
    if (date == null) return (false);

    // Check for variable latitude(time) with units of "deg".
    var = ds.getRootGroup().findVariable(latVarNameDefault);
    if (var == null) return (false);
    list = var.getDimensions();
    if (list.size() != 2) return (false);
    for (int i = 0; i < 2; i++) {
      d = (Dimension) list.get(i);
      if (!d.getShortName().equals(timeDimNameDefault)
          && !d.getShortName().equals(trajDimNameDefault)) return (false);
    }
    //    units = var.findAttribute( "units").getStringValue();
    //    if ( ! SimpleUnit.isCompatible( units, "degrees_north")) return( false);

    // Check for variable longitude(time) with units of "deg".
    var = ds.getRootGroup().findVariable(lonVarNameDefault);
    if (var == null) return (false);
    list = var.getDimensions();
    if (list.size() != 2) return (false);
    for (int i = 0; i < 2; i++) {
      d = (Dimension) list.get(i);
      if (!d.getShortName().equals(timeDimNameDefault)
          && !d.getShortName().equals(trajDimNameDefault)) return (false);
    }
    //    units = var.findAttribute( "units").getStringValue();
    //    if ( ! SimpleUnit.isCompatible( units, "degrees_east")) return( false);

    // Check for variable altitude(time) with units of "m".
    var = ds.getRootGroup().findVariable(elevVarNameDefault);
    if (var == null) return (false);
    list = var.getDimensions();
    if (list.size() != 2) return (false);
    for (int i = 0; i < 2; i++) {
      d = (Dimension) list.get(i);
      if (!d.getShortName().equals(timeDimNameDefault)
          && !d.getShortName().equals(trajDimNameDefault)) return (false);
    }
    units = var.findAttribute("units").getStringValue();
    if (!SimpleUnit.isCompatible(units, "m")) return (false);

    return (true);
  }