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

ucar.nc2.dt.radial.CFRadialAdapter Maven / Gradle / Ivy

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


import ucar.ma2.*;

import ucar.nc2.Attribute;
import ucar.nc2.Variable;
import ucar.nc2.VariableSimpleIF;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dt.RadialDatasetSweep;
import ucar.nc2.ft.FeatureDataset;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateUnit;
import ucar.nc2.units.DateUnit;

import ucar.unidata.geoloc.Earth;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonRect;

import java.io.IOException;

import java.util.*;

import static ucar.ma2.MAMath.nearlyEquals;

/**
 * CF-Radial
 */
public class CFRadialAdapter extends AbstractRadialAdapter {

  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CFRadialAdapter.class);

  private NetcdfDataset ds = null;
  private double latv, lonv, elev;
  private double[] time;
  private float[] elevation;
  private float[] azimuth;
  private float[] range;
  private int[] rayStartIdx;
  private int[] rayEndIdx;
  private int[] ray_n_gates;
  private int[] ray_start_index;
  private int nsweeps;
  private boolean isStationary;
  private boolean isStationaryChecked = false;

  /////////////////////////////////////////////////
  // TypedDatasetFactoryIF

  public Object isMine( FeatureType wantFeatureType, NetcdfDataset ncd, Formatter errlog) throws IOException {
    String convStr = ncd.findAttValueIgnoreCase(null, "Conventions", null);
    if ((null != convStr) && convStr.startsWith("CF/Radial"))
      return this;
    return null;
  }

  public FeatureDataset open( FeatureType ftype, NetcdfDataset ncd, Object analysis, ucar.nc2.util.CancelTask task, Formatter errlog) throws IOException {
    return new CFRadialAdapter(ncd);
  }

  public FeatureType getScientificDataType() {
    return FeatureType.RADIAL;
  }

  // needed for FeatureDatasetFactory
  public CFRadialAdapter() {
  }

  /**
   * Constructor.
   *
   * @param ds Source NetCDF dataset
   */
  public CFRadialAdapter(NetcdfDataset ds) {
    this.ds = ds;
    desc = "CF/Radial radar dataset";
    init();

    for (Variable var : ds.getVariables()) {
      addRadialVariable(ds, var);
    }
  }

  public void init() {

    setEarthLocation();
    try {
      Variable t = ds.findVariable("time");
      Array tArray = t.read();
      time = (double[]) tArray.copyTo1DJavaArray();

      Variable ele = ds.findVariable("elevation");
      Array eArray = ele.read();
      elevation = (float[]) eArray.copyTo1DJavaArray();

      Variable azi = ds.findVariable("azimuth");
      Array aArray = azi.read();
      azimuth = (float[]) aArray.copyTo1DJavaArray();

      Variable rng = ds.findVariable("range");
      Array rArray = rng.read();
      range = (float[]) rArray.copyTo1DJavaArray();

      Variable sidx0 = ds.findVariable("sweep_start_ray_index");
      rayStartIdx = (int[]) sidx0.read().copyTo1DJavaArray();

      Variable sidx1 = ds.findVariable("sweep_end_ray_index");
      rayEndIdx = (int[]) sidx1.read().copyTo1DJavaArray();

      nsweeps = ds.findDimension("sweep").getLength();

      Variable var = ds.findVariable("ray_n_gates");
      if (var != null)
          ray_n_gates = (int[]) var.read().copyTo1DJavaArray();

      var = ds.findVariable("ray_start_index");
      if (var != null)
          ray_start_index = (int[]) var.read().copyTo1DJavaArray();

      setTimeUnits();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    setStartDate();
    setEndDate();
    setBoundingBox();
  }

  protected void setBoundingBox() {
    LatLonRect bb;

    if (origin == null) {
      return;
    }

    double dLat = Math.toDegrees(getMaximumRadialDist()
            / Earth.getRadius());
    double latRadians = Math.toRadians(origin.getLatitude());
    double dLon = dLat * Math.cos(latRadians);

    double lat1 = origin.getLatitude() - dLat / 2;
    double lon1 = origin.getLongitude() - dLon / 2;
    bb = new LatLonRect(new LatLonPointImpl(lat1, lon1), dLat, dLon);

    boundingBox = bb;
  }

  double getMaximumRadialDist() {
    double maxdist = 0.0;

    for (Object dataVariable : dataVariables) {
      RadialVariable rv = (RadialVariable) dataVariable;
      Sweep sp = rv.getSweep(0);
      double dist = sp.getGateNumber() * sp.getGateSize();

      if (dist > maxdist) {
        maxdist = dist;
      }
    }

    return maxdist;
  }

  protected void setEarthLocation() {

    try {
      Variable ga = ds.findVariable("latitude");
      if (ga != null) {
        if(ga.isScalar()) {
          latv = ga.readScalarDouble();
        } else {
          Array gar = ga.read();
          latv = gar.getDouble(0);
        }
      } else {
        latv = 0.0;
      }

      ga = ds.findVariable("longitude");

      if (ga != null) {
        if(ga.isScalar()) {
          lonv = ga.readScalarDouble();
        } else {
          Array gar = ga.read();
          lonv = gar.getDouble(0);
        }      } else {
        lonv = 0.0;
      }

      ga = ds.findVariable("altitude");
      if (ga != null) {
        if(ga.isScalar()) {
          elev = ga.readScalarDouble();
        } else {
          Array gar = ga.read();
          elev = gar.getDouble(0);
        }
      } else {
        elev = 0.0;
      }
    } catch (IOException e) {
    }

    origin = new ucar.unidata.geoloc.EarthLocationImpl(latv, lonv, elev);
  }

  @Override
  public ucar.unidata.geoloc.EarthLocation getCommonOrigin() {
    return origin;
  }

  @Override
  public String getRadarID() {
    Attribute ga = ds.findGlobalAttribute("Station");
    if (ga != null) {
      return ga.getStringValue();
    } else {
      return "XXXX";
    }
  }

  @Override
  public String getRadarName() {
    Attribute ga = ds.findGlobalAttribute("instrument_name");
    if (ga != null) {
      return ga.getStringValue();
    } else {
      return "Unknown Station";
    }
  }

  @Override
  public String getDataFormat() {
    return "CF/RadialNetCDF";
  }


  @Override
  public boolean isVolume() {
    return true;
  }

  public boolean isHighResolution(NetcdfDataset nds) {
    return true;
  }

  public boolean isStationary() {
    // only check once
    if (!isStationaryChecked) {
      Variable lat = ds.findVariable("latitude");
      if (lat != null) {
        if (lat.isScalar())
          isStationary = lat.getSize() == 1;
        else {
          // if array, check to see if all of the values are
          // approximately the same
          Array gar = null;
          try {
            gar = lat.read();
            Object firstVal = gar.getObject(0);
            Array gar2 = gar.copy();
            for (int i = 1; i < gar.getSize(); i++) {
              gar2.setObject(i, firstVal);
            }
            isStationary = nearlyEquals(gar, gar2);
          } catch (IOException e) {
            log.error("Error reading latitude variable {}. Cannot determine if " +
                    "platform is stationary. Setting to default (false).", lat.getFullName());
          }
        }
      }
      isStationaryChecked = true;
    }

    return isStationary;
  }

  protected void setTimeUnits() throws Exception {
    Variable t = ds.findVariable("time");
    String ut = t.getUnitsString();
    dateUnits = new DateUnit(ut);
    calDateUnits = CalendarDateUnit.of(null, ut);
  }

  protected void setStartDate() {
    String datetime = ds.findAttValueIgnoreCase(null, "time_coverage_start", null);
    if (datetime != null) {
      startDate = CalendarDate.parseISOformat(null, datetime).toDate();
    } else {
      startDate = calDateUnits.makeCalendarDate(time[0]).toDate();
    }
  }

  protected void setEndDate() {
    String datetime = ds.findAttValueIgnoreCase(null, "time_coverage_end", null);
    if (datetime != null) {
      endDate = CalendarDate.parseISOformat(null, datetime).toDate();
    } else {
      endDate = calDateUnits.makeCalendarDate(time[time.length - 1]).toDate();
    }
  }

  public void clearDatasetMemory() {
    for (VariableSimpleIF rvar : getDataVariables()) {
        RadialVariable radVar = (RadialVariable) rvar;
        radVar.clearVariableMemory();
    }
  }


  /**
   * _more_
   *
   * @param nds _more_
   * @param var _more_
   */
  protected void addRadialVariable(NetcdfDataset nds, Variable var) {

    RadialVariable rsvar = null;
    String vName = var.getShortName();
    int tIdx = var.findDimensionIndex("time");
    int rIdx = var.findDimensionIndex("range");
    int ptsIdx = var.findDimensionIndex("n_points");

    if (((tIdx == 0) && (rIdx == 1)) || (ptsIdx == 0)) {
      VariableSimpleIF v = new MyRadialVariableAdapter(vName, var.getAttributes());
      rsvar = makeRadialVariable(nds, v, var);
    }

    if (rsvar != null) {
      dataVariables.add(rsvar);
    }
  }


  /**
   * _more_
   *
   * @param nds _more_
   * @param v   _more_
   * @param v0  _more_
   * @return _more_
   */
  protected RadialVariable makeRadialVariable(NetcdfDataset nds,
                                              VariableSimpleIF v, Variable v0) {
    // this function is null in level 2
    return new CFRadial2Variable(nds, v0);
  }

  /**
   * _more_
   *
   * @return _more_
   */
  public String getInfo() {
    StringBuffer sbuff = new StringBuffer();
    sbuff.append("CFRadial2Dataset\n");
    sbuff.append(super.getDetailInfo());
    sbuff.append("\n\n");
    sbuff.append(parseInfo.toString());
    return sbuff.toString();
  }


  /**
   * Class description
   *
   * @author Enter your name here...
   * @version Enter version here..., Mon, Jun 13, '11
   */
  private class CFRadial2Variable extends MyRadialVariableAdapter implements RadialDatasetSweep.RadialVariable {

    /**
     * _more_
     */
    ArrayList sweeps;

    /**
     * _more_
     */
    String name;
    private boolean flattened;


    /**
     * _more_
     *
     * @param nds _more_
     * @param v0  _more_
     */
    private CFRadial2Variable(NetcdfDataset nds, Variable v0) {
      super(v0.getShortName(), v0.getAttributes());

      sweeps = new ArrayList<>();
      name = v0.getShortName();

      int[] shape = v0.getShape();
      int ngates = shape[v0.getRank() - 1];
      flattened = v0.findDimensionIndex("n_points") == 0;

      for (int i = 0; i < nsweeps; i++) {
        // For flattened (1D stored data) find max number of gates
        if (flattened) {
            ngates = ray_n_gates[rayStartIdx[i]];
            for (int ray = rayStartIdx[i]; ray <= rayEndIdx[i]; ++ray)
                ngates = ray_n_gates[ray] > ngates ? ray_n_gates[ray] : ngates;
        }
        sweeps.add(new CFRadial2Sweep(v0, i, ngates, rayStartIdx[i],
                rayEndIdx[i]));
      }
    }

    /**
     * _more_
     *
     * @return _more_
     */
    public String toString() {
      return name;
    }

    /**
     * _more_
     *
     * @return _more_
     */
    public int getNumSweeps() {
      return nsweeps;
    }

    /**
     * _more_
     *
     * @param sweepNo _more_
     * @return _more_
     */
    public Sweep getSweep(int sweepNo) {
      return sweeps.get(sweepNo);
    }

    /**
     * _more_
     *
     * @return _more_
     */
    public int getNumRadials() {
      return azimuth.length;
    }

    // a 3D array nsweep * nradials * ngates
    // if high resolution data, it will be transferred to the same dimension

    /**
     * _more_
     *
     * @return _more_
     * @throws IOException _more_
     */
    public float[] readAllData() throws IOException {
      Array allData;
      Sweep spn = sweeps.get(0);
      Variable v = spn.getsweepVar();
      Attribute missing = v.findAttribute("_FillValue");
      float missingVal = missing == null ?
              Float.NaN : missing.getNumericValue().floatValue();

      int minRadial = getMinRadialNumber();
      int radials = getNumRadials();
      int gates = range.length;
      try {
        allData = v.read();
      } catch (IOException e) {
        throw new IOException(e.getMessage());
      }
      if (flattened) {
          float[] fa0 = (float[]) allData.get1DJavaArray(float.class);
          float[] fa = new float[minRadial * gates * nsweeps];
          Arrays.fill(fa, missingVal);
          for (int s = 0; s < nsweeps; ++s) {
              for (int r = 0; r < minRadial; ++r) {
                  System.arraycopy(fa0, ray_start_index[rayStartIdx[s] + r],
                          fa, s * minRadial * gates + r * gates,
                          ray_n_gates[rayStartIdx[s] + r]);
              }
          }

          return fa;
      } else if (minRadial == radials) {
        return (float[]) allData.get1DJavaArray(float.class);
      } else {
        float[] fa = new float[minRadial * gates * nsweeps];
        float[] fa0 = (float[]) allData.get1DJavaArray(float.class);
        int pos = 0;
        for (int i = 0; i < nsweeps; i++) {

          int startIdx = rayStartIdx[i];
          // int endIdx = rayEndIdx[i];
          int len = minRadial * gates;
          System.arraycopy(fa0, startIdx * gates, fa, pos, len);
          pos = pos + len;
        }
        return fa;
      }
    }


    public int getMinRadialNumber() {
      int minRadialNumber = Integer.MAX_VALUE;
      for (int i = 0; i < nsweeps; i++) {
        Sweep swp = this.sweeps.get(i);
        int radialNumber = swp.getRadialNumber();
        if (radialNumber < minRadialNumber) {
          minRadialNumber = radialNumber;
        }
      }

      return minRadialNumber;
    }

    /**
     * _more_
     */
    public void clearVariableMemory() {
      for (int i = 0; i < nsweeps; i++) {
      }
    }


    //////////////////////////////////////////////////////////////////////
    // Checking all azi to make sure there is no missing data at sweep
    // level, since the coordinate is 1D at this level, this checking also
    // remove those missing radials within a sweep.

    /**
     * Class description
     *
     * @author Enter your name here...
     * @version Enter version here..., Mon, Jun 13, '11
     */
    private class CFRadial2Sweep implements RadialDatasetSweep.Sweep {

      /**
       * _more_
       */
      double meanElevation = Double.NaN;

      /**
       * _more_
       */
      double meanAzimuth = Double.NaN;

      /**
       * _more_
       */
      int ngates;

      /**
       * _more_
       */
      public int startIdx, endIdx, numRays;

      /**
       * _more_
       */
      int sweepno;

      /**
       * _more_
       */
      Variable sweepVar;


      /**
       * _more_
       *
       * @param v        _more_
       * @param sweepno  _more_
       * @param gates    _more_
       * @param startIdx _more_
       * @param endIdx   _more_
       */
      CFRadial2Sweep(Variable v, int sweepno, int gates,
                     int startIdx, int endIdx) {
        this.sweepVar = v;
        this.sweepno = sweepno;
        this.ngates = gates;
        this.startIdx = startIdx;
        this.endIdx = endIdx;
        this.numRays = endIdx - startIdx + 1;
      }

      public int getStartIdx() {
        return startIdx;
      }

      public int getEndIdx() {
        return endIdx;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public Variable getsweepVar() {
        return sweepVar;
      }

            /* read 2d sweep data nradials * ngates */

      /**
       * _more_
       *
       * @return _more_
       * @throws java.io.IOException _more_
       */
      public float[] readData() throws java.io.IOException {
        return sweepData();
      }


      /**
       * _more_
       *
       * @return _more_
       */
      private float[] sweepData() throws IOException {
        int[] origin;
        int[] shape;

        // init section
        try {
            if (flattened) {
                // Get the 1D data for the sweep
                origin = new int[1];
                origin[0] = ray_start_index[startIdx];
                shape = new int[1];
                shape[0] = ray_start_index[endIdx] + ray_n_gates[endIdx] -
                        origin[0];
                Array tempArray = sweepVar.read(origin, shape).reduce();
                float[] tempD = (float[]) tempArray.get1DJavaArray(Float.TYPE);

                // Figure out what to use as the initializer
                float missingVal = Float.NaN;
                Attribute missing = sweepVar.findAttribute("_FillValue");
                if (missing != null)
                    missingVal = missing.getNumericValue().floatValue();

                // Create evenly strided output array and fill
                float[] ret = new float[ngates * numRays];
                Arrays.fill(ret, missingVal);
                int srcInd = 0;
                for (int ray = 0; ray < numRays; ++ray) {
                    int gates = ray_n_gates[startIdx + ray];
                    System.arraycopy(tempD, srcInd, ret, ray * ngates, gates);
                    srcInd += gates;
                }

                return ret;
            } else {
                origin = new int[2];
                origin[0] = startIdx;
                shape = sweepVar.getShape();
                shape[0] = numRays;
                Array sweepTmp = sweepVar.read(origin, shape).reduce();
                return (float[]) sweepTmp.get1DJavaArray(Float.TYPE);
            }
        } catch (ucar.ma2.InvalidRangeException e) {
          throw new IOException(e);
        }
      }

      /**
       * Return data for 1 ray
       *
       * @param ray _more_
       * @return _more_
       * @throws java.io.IOException _more_
       */
      public float[] readData(int ray) throws java.io.IOException {
        return rayData(ray);
      }

            /* read the radial data from the radial variable */

      /**
       * _more_
       *
       * @param ray _more_
       * @return _more_
       * @throws java.io.IOException _more_
       */
      public float[] rayData(int ray) throws java.io.IOException {
        int[] origin;
        int[] shape;

        // init section
        if (flattened) {
            origin = new int[1];
            origin[0] = ray_start_index[startIdx + ray];
            shape = new int[1];
            shape[0] = ray_n_gates[startIdx + ray];
        } else {
            origin = new int[2];
            origin[0] = startIdx + ray;
            shape = sweepVar.getShape();
            shape[0] = 1;
        }

        try {
          Array sweepTmp = sweepVar.read(origin, shape).reduce();
          return (float[]) sweepTmp.get1DJavaArray(Float.TYPE);
        } catch (ucar.ma2.InvalidRangeException e) {
          throw new IOException(e);
        }
      }


      /**
       * _more_
       */
      public void setMeanElevation() {
        double sum = 0.0;
        int sumSize = 0;
        for (int i = 0; i < numRays; i++) {
          if (!Double.isNaN(elevation[i])) {
            sum = sum + elevation[startIdx + i];
            sumSize++;
          }
        }
        if (sumSize > 0)
            meanElevation = sum / sumSize;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getMeanElevation() {
        if (Double.isNaN(meanElevation)) {
          setMeanElevation();
        }
        return (float) meanElevation;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public int getGateNumber() {
        return ngates;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public int getRadialNumber() {
        return numRays;
      }


      /**
       * _more_
       *
       * @return _more_
       */
      public RadialDatasetSweep.Type getType() {
        return null;
      }

      /**
       * _more_
       *
       * @param ray _more_
       * @return _more_
       */
      public ucar.unidata.geoloc.EarthLocation getOrigin(int ray) {
        return origin;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public Date getStartingTime() {
        return startDate;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public Date getEndingTime() {
        return endDate;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public int getSweepIndex() {
        return sweepno;
      }


      /**
       * _more_
       */
      public void setMeanAzimuth() {
        double sum = 0.0;
        int sumSize = 0;
        for (int i = 0; i < numRays; i++) {
          if (!Double.isNaN(azimuth[i])) {
            sum = sum + azimuth[startIdx + i];
            sumSize++;
          }
        }
        if (sumSize > 0)
            meanAzimuth = sum / sumSize;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getMeanAzimuth() {
        if (Double.isNaN(meanAzimuth)) {
          setMeanAzimuth();
        }
        return (float) meanAzimuth;
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public boolean isConic() {
        return true;
      }

      /**
       * _more_
       *
       * @param ray _more_
       * @return _more_
       * @throws IOException _more_
       */
      public float getElevation(int ray) throws IOException {
        return elevation[ray + startIdx];
      }

      /**
       * _more_
       *
       * @return _more_
       * @throws IOException _more_
       */
      public float[] getElevation() throws IOException {
        float[] elev = new float[numRays];
        System.arraycopy(elevation, startIdx, elev, 0, numRays);
        return elev;
      }


      /**
       * _more_
       *
       * @return _more_
       * @throws IOException _more_
       */
      public float[] getAzimuth() throws IOException {
        float[] azimu = new float[numRays];
        System.arraycopy(azimuth, startIdx, azimu, 0, numRays);
        return azimu;
      }


      /**
       * _more_
       *
       * @param ray _more_
       * @return _more_
       * @throws IOException _more_
       */
      public float getAzimuth(int ray) throws IOException {
        return azimuth[ray + startIdx];
      }


      /**
       * _more_
       *
       * @param gate _more_
       * @return _more_
       * @throws IOException _more_
       */
      public float getRadialDistance(int gate) throws IOException {
        return range[gate];
      }

      /**
       * _more_
       *
       * @param ray _more_
       * @return _more_
       * @throws IOException _more_
       */
      public float getTime(int ray) throws IOException {

        return (float) time[ray + startIdx];

      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getBeamWidth() {
        return 0.95f;  // degrees, info from Chris Burkhart
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getNyquistFrequency() {
        return 0;  // LOOK this may be radial specific
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getRangeToFirstGate() {
        try {
          return getRadialDistance(0);
        } catch (IOException e) {
          e.printStackTrace();
          return 0.0f;
        }
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public float getGateSize() {
        try {
          return getRadialDistance(1) - getRadialDistance(0);
        } catch (IOException e) {
          e.printStackTrace();
          return 0.0f;
        }
      }

      /**
       * _more_
       *
       * @return _more_
       */
      public boolean isGateSizeConstant() {
        return true;
      }

      /**
       * _more_
       */
      public void clearSweepMemory() {
      }
    }  // LevelII2Sweep class

  }      // LevelII2Variable
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy