private static GridCoverage2D getRasterHelper()

in common/src/main/java/org/apache/sedona/common/raster/netcdf/NetCdfReader.java [158:323]


  private static GridCoverage2D getRasterHelper(
      NetcdfFile netcdfFile,
      Variable recordVariable,
      Variable lonVariable,
      Variable latVariable,
      boolean findCoordIndices)
      throws IOException, FactoryException {
    // assert(netcdfFile.getFileTypeVersion().equalsIgnoreCase("1")); //CDF - 1
    ImmutableList<Attribute> globalAttributes = netcdfFile.getGlobalAttributes();
    Group rootGroup = netcdfFile.getRootGroup();

    Array lonData, latData;
    double minX = 0, maxY = 0, translateX, translateY, skewX = 0, skewY = 0;
    int width, height;
    boolean isIncreasing = false;
    HashMap<String, List<String>> varMetadata = new HashMap<>();

    // ========================= Set Raster geo-reference (width, height, scaleX, scaleY
    // =========================
    lonData = lonVariable.read();
    width = lonData.getShape()[0];
    double firstVal, lastVal;
    firstVal = getElement(lonData, 0);
    lastVal = getElement(lonData, width - 1);
    isIncreasing = firstVal < lastVal;
    minX = isIncreasing ? firstVal : lastVal;
    translateX = Math.abs(lastVal - firstVal) / (width - 1);

    latData = latVariable.read();
    height = latData.getShape()[0];
    firstVal = getElement(latData, 0);
    lastVal = getElement(latData, height - 1);
    isIncreasing = firstVal < lastVal;
    maxY = isIncreasing ? lastVal : firstVal;
    translateY = Math.abs(lastVal - firstVal) / (height - 1);
    // ======================================================================================================

    String lonVariableName = lonVariable.getShortName();
    String latVariableName = latVariable.getShortName();

    List<Dimension> recordDimensions = recordVariable.getDimensions();
    int numRecordDimensions = recordDimensions.size();

    // ==================== Re-check existence of dimensions if necessary ====================
    int lonIndex = numRecordDimensions - 1;
    int latIndex = numRecordDimensions - 2;
    if (findCoordIndices) {
      lonIndex = recordVariable.findDimensionIndex(lonVariableName);
      latIndex = recordVariable.findDimensionIndex(latVariableName);
      if (latIndex == -1 || lonIndex == -1) {
        throw new IllegalArgumentException(NetCdfConstants.COORD_IDX_NOT_FOUND);
      }
    }

    // read recordVariable data
    Array recordData = recordVariable.read();

    // Set noDataValue if available, null otherwise
    Double noDataValue = null;
    if (recordVariable.attributes().hasAttribute(NetCdfConstants.MISSING_VALUE)) {
      Attribute noDataAttribute = recordVariable.findAttribute(NetCdfConstants.MISSING_VALUE);
      if (!Objects.isNull(noDataAttribute)) noDataValue = getAttrDoubleValue(noDataAttribute);
    }
    int[] strides = recordData.getIndex().getShape();
    int[] pointers = recordData.getIndex().getCurrentCounter();
    int numBands = 1;
    int numValuesPerBand = 1;
    for (int i = 0; i < numRecordDimensions; i++) {
      Dimension recordDimension = recordDimensions.get(i);
      String dimensionShortName = recordDimension.getShortName();
      // TODO: Maybe replace this string comparison with index comparison since we already have lon
      // and lat dimension indices stored?
      if (dimensionShortName.equalsIgnoreCase(latVariableName)
          || dimensionShortName.equalsIgnoreCase(lonVariableName)) {
        numValuesPerBand *= strides[i];
      } else {
        numBands *= strides[i];
      }
    }
    Array[] dimensionVars = new Array[numRecordDimensions];
    double scaleFactor = 1, offset = 0;
    for (int i = 0; i < numRecordDimensions; i++) {
      if (i == lonIndex) dimensionVars[i] = lonData;
      else if (i == latIndex) dimensionVars[i] = latData;
      else {
        Dimension recordDimension = recordDimensions.get(i);
        String dimensionShortName = recordDimension.getShortName();
        // TODO: This leads to tree traversal for dimensions, can we do this in one go?
        Variable recordDimVar =
            findVariableRecursive(
                dimensionShortName, rootGroup); // rootGroup.findVariableLocal(dimensionShortName);
        if (Objects.isNull(recordDimVar))
          throw new IllegalArgumentException(
              String.format(NetCdfConstants.COORD_VARIABLE_NOT_FOUND, dimensionShortName));
        AttributeContainer recordDimAttrs = recordDimVar.attributes();
        varMetadata.computeIfAbsent(dimensionShortName, k -> new ArrayList<>());
        for (Attribute attr : recordDimAttrs) {
          varMetadata.get(dimensionShortName).add(attr.getStringValue());
          if (attr.getShortName().equalsIgnoreCase(NetCdfConstants.SCALE_FACTOR)) {
            Array values = attr.getValues();
            if (!Objects.isNull(values)) scaleFactor = getElement(attr.getValues(), 0);
          }
          if (attr.getShortName().equalsIgnoreCase(NetCdfConstants.ADD_OFFSET)) {
            Array values = attr.getValues();
            if (!Objects.isNull(values)) offset = getElement(attr.getValues(), 0);
          }
        }
        dimensionVars[i] = recordDimVar.read();
      }
    }
    double[][] allBandValues = new double[numBands][numValuesPerBand];
    // check for offset in the record variable
    List<List<String>> bandMetaData = new ArrayList<>();
    int currBandNum = 0;
    while (currBandNum < numBands) {
      if (dimensionVars.length > 2) {
        // start from the bottom 3rd dimension going up to form metadata
        List<String> currBandMetadata = new ArrayList<>();
        for (int metadataDim = dimensionVars.length - 3; metadataDim >= 0; metadataDim--) {
          double data = getElement(dimensionVars[metadataDim], pointers[metadataDim]);
          String metadata = recordDimensions.get(metadataDim).getShortName() + " : " + data;
          currBandMetadata.add(metadata);
        }
        bandMetaData.add(currBandMetadata);
      }
      // int dataIndex = currBandNum;
      for (int j = height - 1; j >= 0; j--) {
        for (int i = 0; i < width; i++) {
          int index = (j * width + i);
          int dataIndex = currBandNum * (width * height) + ((height - 1 - j) * width + i);
          double currRecord = scaleFactor * getElement(recordData, dataIndex) + offset;
          allBandValues[currBandNum][index] = currRecord;
        }
      }
      boolean addCarry = true;
      for (int currDim = dimensionVars.length - 3; currDim >= 0; currDim--) {
        int maxIndex = strides[currDim];
        if (addCarry) {
          pointers[currDim]++;
          addCarry = false;
        }
        if (pointers[currDim] == maxIndex) {
          pointers[currDim] = 0;
          addCarry = true;
        }
      }
      currBandNum++;
    }
    Map<String, List<String>> rasterProperties =
        getRasterProperties(globalAttributes, varMetadata, bandMetaData);
    return RasterConstructors.makeNonEmptyRaster(
        numBands,
        width,
        height,
        minX,
        maxY,
        translateX,
        -translateY,
        skewX,
        skewY,
        0,
        allBandValues,
        rasterProperties,
        noDataValue,
        PixelInCell.CELL_CENTER);
  }