/**
   * Constructor. If scale/offset attributes are found, remove them from the decorated variable.
   *
   * @param forVar the Variable to decorate.
   * @param useNaNs pre-fill isMissing() data with NaNs
   * @param fillValueIsMissing use _FillValue for isMissing()
   * @param invalidDataIsMissing use valid_range for isMissing()
   * @param missingDataIsMissing use missing_value for isMissing()
   */
  EnhanceScaleMissingImpl(
      VariableDS forVar,
      boolean useNaNs,
      boolean fillValueIsMissing,
      boolean invalidDataIsMissing,
      boolean missingDataIsMissing) {

    this.fillValueIsMissing = fillValueIsMissing;
    this.invalidDataIsMissing = invalidDataIsMissing;
    this.missingDataIsMissing = missingDataIsMissing;

    // see if underlying variable has scale/offset already applied
    Variable orgVar = forVar.getOriginalVariable();
    if (orgVar instanceof VariableDS) {
      VariableDS orgVarDS = (VariableDS) orgVar;
      EnumSet<NetcdfDataset.Enhance> orgEnhanceMode = orgVarDS.getEnhanceMode();
      if ((orgEnhanceMode != null) && orgEnhanceMode.contains(NetcdfDataset.Enhance.ScaleMissing))
        return;
    }

    // the other possibility is that you want to apply scale and offset to a signed value, then
    // declare the result unsigned
    // this.isUnsigned = (orgVar != null) ? orgVar.isUnsigned() : forVar.isUnsigned();
    this.isUnsigned = forVar.isUnsigned();
    this.convertedDataType = forVar.getDataType();

    DataType scaleType = null, missType = null, validType = null, fillType = null;
    if (debug) System.out.println("EnhancementsImpl for Variable = " + forVar.getFullName());
    Attribute att;

    // scale and offset
    if (null != (att = forVar.findAttribute(CDM.SCALE_FACTOR))) {
      if (!att.isString()) {
        scale = att.getNumericValue().doubleValue();
        hasScaleOffset = true;
        scaleType = att.getDataType();
        forVar.remove(att);
        if (debug) System.out.println("scale = " + scale + " type " + scaleType);
      }
    }
    if (null != (att = forVar.findAttribute(CDM.ADD_OFFSET))) {
      if (!att.isString()) {
        offset = att.getNumericValue().doubleValue();
        hasScaleOffset = true;
        DataType offType = att.getDataType();
        if (rank(offType) > rank(scaleType)) scaleType = offType;
        forVar.remove(att);
        if (debug) System.out.println("offset = " + offset);
      }
    }

    ////// missing data : valid_range. assume here its in units of unpacked data. correct this below
    Attribute validRangeAtt;
    if (null != (validRangeAtt = forVar.findAttribute(CDM.VALID_RANGE))) {
      if (!validRangeAtt.isString() && validRangeAtt.getLength() > 1) {
        valid_min = validRangeAtt.getNumericValue(0).doubleValue();
        valid_max = validRangeAtt.getNumericValue(1).doubleValue();
        hasValidRange = true;
        validType = validRangeAtt.getDataType();
        if (hasScaleOffset) forVar.remove(validRangeAtt);
        if (debug) System.out.println("valid_range = " + valid_min + " " + valid_max);
      }
    }

    Attribute validMinAtt = null, validMaxAtt = null;
    if (!hasValidRange) {
      if (null != (validMinAtt = forVar.findAttribute("valid_min"))) {
        if (!validMinAtt.isString()) {
          valid_min = validMinAtt.getNumericValue().doubleValue();
          hasValidMin = true;
          validType = validMinAtt.getDataType();
          if (hasScaleOffset) forVar.remove(validMinAtt);
          if (debug) System.out.println("valid_min = " + valid_min);
        }
      }

      if (null != (validMaxAtt = forVar.findAttribute("valid_max"))) {
        if (!validMaxAtt.isString()) {
          valid_max = validMaxAtt.getNumericValue().doubleValue();
          hasValidMax = true;
          DataType t = validMaxAtt.getDataType();
          if (rank(t) > rank(validType)) validType = t;
          if (hasScaleOffset) forVar.remove(validMaxAtt);
          if (debug) System.out.println("valid_min = " + valid_max);
        }
      }
    }
    boolean hasValidData = hasValidMin || hasValidMax || hasValidRange;
    if (hasValidMin && hasValidMax) hasValidRange = true;

    /// _FillValue
    if ((null != (att = forVar.findAttribute(CDM.FILL_VALUE))) && !att.isString()) {
      double[] values = getValueAsDouble(att); // LOOK double WTF ??
      if (values.length > 0) {
        fillValue = values[0];
        hasFillValue = true;
        fillType = att.getDataType();
        if (hasScaleOffset) forVar.remove(att);
        if (debug) System.out.println("missing_datum from _FillValue = " + fillValue);
      }
    }

    /// missing_value
    if (null != (att = forVar.findAttribute(CDM.MISSING_VALUE))) {
      if (att.isString()) {
        String svalue = att.getStringValue();
        if (forVar.getDataType() == DataType.CHAR) {
          missingValue = new double[1];
          if (svalue.length() == 0) missingValue[0] = 0;
          else missingValue[0] = svalue.charAt(0);

          missType = DataType.CHAR;
          hasMissingValue = true;

        } else { // not a CHAR - try to fix problem where they use a numeric value as a String
                 // attribute

          try {
            missingValue = new double[1];
            missingValue[0] = Double.parseDouble(svalue);
            missType = att.getDataType();
            hasMissingValue = true;
          } catch (NumberFormatException ex) {
            if (debug)
              System.out.println(
                  "String missing_value not parsable as double= " + att.getStringValue());
          }
        }

      } else { // not a string
        missingValue = getValueAsDouble(att);
        missType = att.getDataType();
        for (double mv : missingValue)
          if (!Double.isNaN(mv))
            hasMissingValue = true; // dont need to do anything if its already a NaN
      }
      if (hasScaleOffset) forVar.remove(att);
    }

    // missing
    boolean hasMissing =
        (invalidDataIsMissing && hasValidData)
            || (fillValueIsMissing && hasFillValue)
            || (missingDataIsMissing && hasMissingValue);

    /// assign convertedDataType if needed
    if (hasScaleOffset) {

      convertedDataType = forVar.getDataType();
      if (hasMissing) {
        // has missing data : must be float or double
        if (rank(scaleType) > rank(convertedDataType)) convertedDataType = scaleType;
        if (missingDataIsMissing && rank(missType) > rank(convertedDataType))
          convertedDataType = missType;
        if (fillValueIsMissing && rank(fillType) > rank(convertedDataType))
          convertedDataType = fillType;
        if (invalidDataIsMissing && rank(validType) > rank(convertedDataType))
          convertedDataType = validType;
        if (rank(convertedDataType) < rank(DataType.DOUBLE)) convertedDataType = DataType.FLOAT;

      } else {
        // no missing data; can use wider of data and scale
        if (rank(scaleType) > rank(convertedDataType)) convertedDataType = scaleType;
      }
      if (debug) System.out.println("assign dataType = " + convertedDataType);

      // validData may be external or internal
      if (hasValidData) {
        DataType orgType = forVar.getDataType();

        // If valid_range is the same type as scale_factor (actually the wider of
        // scale_factor and add_offset) and this is wider than the external data, then it
        // will be interpreted as being in the units of the internal (unpacked) data.
        // Otherwise it is in the units of the external (unpacked) data.
        // we assumed unpacked data above, redo if its really packed data
        if (!((rank(validType) == rank(scaleType)) && (rank(scaleType) >= rank(orgType)))) {
          if (validRangeAtt != null) {
            double[] values = getValueAsDouble(validRangeAtt);
            valid_min = values[0];
            valid_max = values[1];
          } else {
            if (validMinAtt != null) {
              double[] values = getValueAsDouble(validMinAtt);
              valid_min = values[0];
            }
            if (validMaxAtt != null) {
              double[] values = getValueAsDouble(validMaxAtt);
              valid_max = values[0];
            }
          }
        }
      }
    }

    if (hasMissing
        && ((convertedDataType == DataType.DOUBLE) || (convertedDataType == DataType.FLOAT)))
      this.useNaNs = useNaNs;
    if (debug) System.out.println("this.useNaNs = " + this.useNaNs);
  }
예제 #2
0
파일: GeoGrid.java 프로젝트: nbald/thredds
 public String getName() {
   return vs.getFullName();
 }