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;
  }
  /**
   * @param refPt The geodetic position of the reference point.
   * @param neiPt The holder for the geodetic position of the neighboring grid point.
   * @param lat The latitude of the neighboring grid point.
   * @param lon The longitude of the neighboring grid point.
   * @param azTerm An amount in degrees to be added to the computed azimuth.
   * @param bearing The holder for the computed range and bearing from the reference point to the
   *     neighboring grid point.
   * @param hat 2-element output array for computed (x,y) components of unit vector.
   */
  private static void compute(
      LatLonPointImpl refPt,
      LatLonPointImpl neiPt,
      float lat,
      float lon,
      float azTerm,
      Bearing bearing,
      float[] hat) {

    neiPt.set(lat, lon);
    Bearing.calculateBearing(refPt, neiPt, bearing);

    float az = (float) Math.toRadians(bearing.getAngle() + azTerm);
    // System.out.println("bearing.Angle = " + bearing.getAngle() + "; azTerm = " + azTerm+ ";
    // result= " + Math.toDegrees(az));

    hat[0] = (float) Math.sin(az);
    hat[1] = (float) Math.cos(az);
  }
  /**
   * @param refPt The geodetic position of the reference point.
   * @param neiPt The holder for the geodetic position of the neighboring grid point.
   * @param lat The latitude of the neighboring grid point.
   * @param lon The longitude of the neighboring grid point.
   * @param azTerm An amount in degrees to be added to the computed azimuth.
   * @param wind Magnitude of grid-relative wind component.
   * @param bearing The holder for the computed range and bearing from the reference point to the
   *     neighboring grid point.
   * @param uv 2-element output array for computed (U,V) wind components.
   */
  private static void compute(
      LatLonPointImpl refPt,
      LatLonPointImpl neiPt,
      float lat,
      float lon,
      float azTerm,
      float wind,
      Bearing bearing,
      float[] uv) {

    neiPt.set(lat, lon);
    Bearing.calculateBearing(refPt, neiPt, bearing);

    float az = (float) Math.toRadians(bearing.getAngle() + azTerm);
    float xhat = (float) Math.sin(az);
    float yhat = (float) Math.cos(az);

    uv[0] = xhat * wind;
    uv[1] = yhat * wind;
  }
  /**
   * get all radar station within box.
   *
   * @param boundingBox _more_
   * @param cancel _more_
   * @return List of type DqcRadarStation objects
   * @throws IOException java io exception
   */
  public List getStations(
      ucar.unidata.geoloc.LatLonRect boundingBox, ucar.nc2.util.CancelTask cancel)
      throws IOException {
    List sl = selStation.getStations();
    ArrayList dsl = new ArrayList();

    for (Iterator it = sl.iterator(); it.hasNext(); ) {
      Station s = (Station) it.next();
      LatLonPointImpl latlonPt = new LatLonPointImpl();
      latlonPt.set(s.getLocation().getLatitude(), s.getLocation().getLongitude());
      if (boundingBox.contains(latlonPt)) {
        dsl.add(s);
      }
      if ((cancel != null) && cancel.isCancel()) {
        return null;
      }
    }

    return dsl;
  }
  /**
   * @param grid The grid.
   * @param gridWinds The grid-relative winds.
   * @param cs The coordinate system transformation of the grid.
   * @param index The index of the grid-relative wind component.
   * @param latI The index of latitude in the reference coordinate system.
   * @param lonI The index of longitude in the reference coordinate system.
   * @param us The array in which to add the computed U-component of the wind.
   * @param us The array in which to add the computed V-component of the wind.
   * @param vs
   * @throws IndexOutOfBoundsException if <code>gridWinds</code>, <code>us
   *                                   </code>, or <code>vs</code> is too small.
   * @throws VisADException if a VisAD failure occurs.
   */
  private static void addComponent(
      SampledSet grid,
      float[][] gridWinds,
      CoordinateSystem cs,
      int index,
      int latI,
      int lonI,
      float[] us,
      float[] vs)
      throws VisADException {

    int[][] neighbors = grid.getNeighbors(index);
    LatLonPointImpl refPt = new LatLonPointImpl();
    LatLonPointImpl neiPt = new LatLonPointImpl();
    Bearing bearing = new Bearing();
    float[] uv1 = new float[2];
    float[] uv2 = new float[2];
    boolean hasCS = cs != null;
    float[][] domainSamples = grid.getSamples(false);
    float[][] crefCoords = (hasCS) ? cs.toReference(Set.copyFloats(domainSamples)) : domainSamples;
    // If the grid is lat/lon or has an IdentityCoordinateSystem
    // don't do the rotation
    // TODO:  handle rotated lat/lon grids
    if (!hasCS
        || (crefCoords == domainSamples)
        || (Arrays.equals(crefCoords[latI], domainSamples[latI])
            && Arrays.equals(crefCoords[lonI], domainSamples[lonI]))) {
      // us = gridWinds[0];
      // vs = gridWinds[1];
      System.arraycopy(gridWinds[0], 0, us, 0, us.length);
      System.arraycopy(gridWinds[1], 0, vs, 0, vs.length);
    } else {

      for (int i = 0; i < neighbors.length; i++) {

        float[][] refCoords = grid.indexToValue(new int[] {i});
        if (hasCS) {
          refCoords = cs.toReference(refCoords);
        }

        float[][] neiCoords = grid.indexToValue(neighbors[i]);
        if (hasCS) {
          neiCoords = cs.toReference(neiCoords);
        }

        refPt.set(refCoords[latI][0], refCoords[lonI][0]);

        compute(
            refPt,
            neiPt,
            neiCoords[latI][0],
            neiCoords[lonI][0],
            -180,
            gridWinds[index][i],
            bearing,
            uv1);

        float d1 = (float) bearing.getDistance();

        compute(
            refPt,
            neiPt,
            neiCoords[latI][1],
            neiCoords[lonI][1],
            0,
            gridWinds[index][i],
            bearing,
            uv2);

        float d2 = (float) bearing.getDistance();
        boolean bad1 = Double.isNaN(d1);
        boolean bad2 = Double.isNaN(d2);

        if (bad1 && bad2) {
          us[i] = Float.NaN;
          vs[i] = Float.NaN;
        } else {
          if (bad1) {
            us[i] += uv2[0];
            vs[i] += uv2[1];
          } else if (bad2) {
            us[i] += uv1[0];
            vs[i] += uv1[1];
          } else {
            float tot = d1 + d2;
            float c1 = d2 / tot;
            float c2 = d1 / tot;

            us[i] += c1 * uv1[0] + c2 * uv2[0];
            vs[i] += c1 * uv1[1] + c2 * uv2[1];
          }
        }
      }
    }
  }
  /**
   * I have no idea what this does.
   *
   * @param grid sampling grid
   * @param index some sort of index
   * @return a new flat field with something different
   * @throws RemoteException Java RMI error
   * @throws VisADException VisAD error
   */
  private static FlatField hatFieldOld(Set grid, int index) throws VisADException, RemoteException {

    CoordinateSystem cs = grid.getCoordinateSystem();
    boolean hasCS = (cs != null);

    RealTupleType rtt = (hasCS) ? cs.getReference() : ((SetType) grid.getType()).getDomain();

    int latI = rtt.getIndex(RealType.Latitude);

    if (latI == -1) {
      throw new IllegalArgumentException(grid.toString());
    }

    int lonI = rtt.getIndex(RealType.Longitude);

    if (lonI == -1) {
      throw new IllegalArgumentException(grid.toString());
    }

    if (grid.getManifoldDimension() < 2) {
      throw new IllegalArgumentException(grid.toString());
    }

    int[][] neighbors = grid.getNeighbors(index);
    LatLonPointImpl refPt = new LatLonPointImpl();
    LatLonPointImpl neiPt = new LatLonPointImpl();
    Bearing bearing = new Bearing();
    float[] hat1 = new float[2];
    float[] hat2 = new float[2];
    float[][] hat = new float[2][grid.getLength()];

    for (int i = 0; i < neighbors.length; i++) {
      float[][] refCoords = grid.indexToValue(new int[] {i});
      if (hasCS) {
        refCoords = cs.toReference(refCoords);
      }

      float[][] neiCoords = grid.indexToValue(neighbors[i]);
      if (hasCS) {
        neiCoords = cs.toReference(neiCoords);
      }

      refPt.set(refCoords[latI][0], refCoords[lonI][0]);
      compute(refPt, neiPt, neiCoords[latI][0], neiCoords[lonI][0], -180, bearing, hat1);

      float d1 = (float) bearing.getDistance();

      compute(refPt, neiPt, neiCoords[latI][1], neiCoords[lonI][1], 0, bearing, hat2);

      float d2 = (float) bearing.getDistance();
      boolean bad1 = Double.isNaN(d1);
      boolean bad2 = Double.isNaN(d2);

      if (bad1 && bad2) {
        hat[0][i] = Float.NaN;
        hat[1][i] = Float.NaN;
      } else {
        if (bad1) {
          hat[0][i] = hat2[0];
          hat[1][i] = hat2[1];
        } else if (bad2) {
          hat[0][i] = hat1[0];
          hat[1][i] = hat1[1];
        } else {
          float tot = d1 + d2;
          float c1 = d2 / tot;
          float c2 = d1 / tot;
          float xhat = c1 * hat1[0] + c2 * hat2[0];
          float yhat = c1 * hat1[1] + c2 * hat2[1];
          float mag = (float) Math.sqrt(xhat * xhat + yhat * yhat);

          hat[0][i] = xhat / mag;
          hat[1][i] = yhat / mag;
        }
      }
    }

    FlatField hatField =
        new FlatField(
            new FunctionType(
                ((SetType) grid.getType()).getDomain(),
                new RealTupleType(
                    RealType.getRealType("xHat", CommonUnit.dimensionless),
                    RealType.getRealType("yHat", CommonUnit.dimensionless))),
            grid);

    hatField.setSamples(hat, false);

    return hatField;
  }
  /**
   * The returned {@link visad.FlatField} will have NaN-s for those unit vector components that
   * could not be computed.
   *
   * @param grid The spatial grid.
   * @param index The index of the manifold dimension along which to compute the unit vector.
   * @return A field of components of the unit vector for the given manifold dimension.
   * @throws NullPointerException if the grid is <code>null</code>.
   * @throws IllegalArgumentException if the manifold dimension of the grid is less than 2 or if the
   *     grid doesn't contain {@link visad.RealType#Latitude} and {@link visad.RealType#Longitude}.
   * @throws VisADException if a VisAD failure occurs.
   * @throws RemoteException if a Java RMI failure occurs.
   */
  private static FlatField hatFieldNew(Set grid, int index) throws VisADException, RemoteException {

    CoordinateSystem cs = grid.getCoordinateSystem();
    boolean hasCS = cs != null;

    RealTupleType rtt = (hasCS) ? cs.getReference() : ((SetType) grid.getType()).getDomain();

    int latI = rtt.getIndex(RealType.Latitude);

    if (latI == -1) {
      throw new IllegalArgumentException(rtt.toString());
    }

    int lonI = rtt.getIndex(RealType.Longitude);
    if (lonI == -1) {
      throw new IllegalArgumentException(rtt.toString());
    }

    if (grid.getManifoldDimension() < 2) {
      throw new IllegalArgumentException(grid.toString());
    }

    int[][] neighbors = grid.getNeighbors(index);
    LatLonPointImpl refPt = new LatLonPointImpl();
    LatLonPointImpl neiPt = new LatLonPointImpl();
    Bearing bearing = new Bearing();
    float[] hat1 = new float[2];
    float[] hat2 = new float[2];
    float[][] hat = new float[2][grid.getLength()];

    float[][] refCoords = null;
    float[][] neiCoords = null;
    float[][] domainSamples = grid.getSamples(false);

    refCoords = (hasCS) ? cs.toReference(Set.copyFloats(domainSamples)) : domainSamples;
    // If the grid is lat/lon or has an IdentityCoordinateSystem
    // don't do the rotation
    // TODO:  handle rotated lat/lon grids
    if (!hasCS
        || (refCoords == domainSamples)
        || (Arrays.equals(refCoords[latI], domainSamples[latI])
            && Arrays.equals(refCoords[lonI], domainSamples[lonI]))) {
      if (index == 0) {
        Arrays.fill(hat[0], 1);
        Arrays.fill(hat[1], 0);
      } else {
        Arrays.fill(hat[0], 0);
        Arrays.fill(hat[1], 1);
      }
    } else {

      float latBefore, lonBefore, latAfter, lonAfter;
      // int backOffset = (index==0) ? -180 : 0;
      // int foreOffset = (index==0) ? 0 : -180;
      int backOffset = -180;
      int foreOffset = 0;
      for (int i = 0; i < neighbors.length; i++) {
        refPt.set(refCoords[latI][i], refCoords[lonI][i]);
        if ((neighbors[i][0] < 0) || (neighbors[i][0] >= neighbors.length)) {
          latBefore = Float.NaN;
          lonBefore = Float.NaN;
        } else {
          latBefore = refCoords[latI][neighbors[i][0]];
          lonBefore = refCoords[lonI][neighbors[i][0]];
        }
        if ((neighbors[i][1] < 0) || (neighbors[i][1] >= neighbors.length)) {
          latAfter = Float.NaN;
          lonAfter = Float.NaN;
        } else {
          latAfter = refCoords[latI][neighbors[i][1]];
          lonAfter = refCoords[lonI][neighbors[i][1]];
        }

        compute(refPt, neiPt, latBefore, lonBefore, backOffset, bearing, hat1);

        float d1 = (float) bearing.getDistance();

        compute(refPt, neiPt, latAfter, lonAfter, foreOffset, bearing, hat2);

        float d2 = (float) bearing.getDistance();
        boolean bad1 = Double.isNaN(d1);
        boolean bad2 = Double.isNaN(d2);

        if (bad1 && bad2) {
          hat[0][i] = Float.NaN;
          hat[1][i] = Float.NaN;
        } else {
          if (bad1) {
            hat[0][i] = hat2[0];
            hat[1][i] = hat2[1];
          } else if (bad2) {
            hat[0][i] = hat1[0];
            hat[1][i] = hat1[1];
          } else {
            float tot = d1 + d2;
            float c1 = d2 / tot;
            float c2 = d1 / tot;
            float xhat = c1 * hat1[0] + c2 * hat2[0];
            float yhat = c1 * hat1[1] + c2 * hat2[1];
            float mag = (float) Math.sqrt(xhat * xhat + yhat * yhat);

            hat[0][i] = xhat / mag;
            hat[1][i] = yhat / mag;
          }
        }
      }
    }

    FlatField hatField =
        new FlatField(
            new FunctionType(
                ((SetType) grid.getType()).getDomain(),
                new RealTupleType(
                    RealType.getRealType("xHat", CommonUnit.dimensionless),
                    RealType.getRealType("yHat", CommonUnit.dimensionless))),
            grid);

    hatField.setSamples(hat, false);
    return hatField;
  }