RHEMUtils.java [src/java/rhem/utils] Revision: default  Date:
/*
 * $Id$
 *
 * This file is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-Service framework, API, and application suite.
 *
 * 2012-2020, OMSLab, Colorado State University.
 *
 * OMSLab licenses this file to you under the MIT license.
 * See the LICENSE file in the project root for more information.
 */
package rhem.utils;

import csip.api.server.PayloadParameter;
import csip.api.server.ServiceException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static m.rhem.ApplicationResources.DEFAULT_SLOPE_LENGTH_METER;

/**
 *
 * @author rumpal
 */
public class RHEMUtils {

  private static final String DEFAULT_DOUBLE_PRECISION_FORMAT = "%.14f";
  private static final String DEFAULT_DOUBLE_FORMAT = "%.6f";

  public static final String[] MONTH_NAMES_LIST = {"January", "February", "March", "April", "May",
    "June", "July", "August", "September", "October", "November", "December"};


  public static double interpolate(double pointToEvaluate, double[] functionValuesX, double[] functionValuesY) {
    int index = findIntervalLeftBorderIndex(pointToEvaluate, functionValuesX);
    if (index == functionValuesX.length - 1)
      index--;

    return linearInterpolation(pointToEvaluate, functionValuesX[index], functionValuesY[index],
        functionValuesX[index + 1], functionValuesY[index + 1]);
  }


  public static int findIntervalLeftBorderIndex(double point, double[] intervals) {
    if (point < intervals[0])
      return 0;

    if (point > intervals[intervals.length - 1])
      return intervals.length - 1;

    int leftBorderIndex = 0;
    int indexOfNumberToCompare;
    int rightBorderIndex = intervals.length - 1;

    while ((rightBorderIndex - leftBorderIndex) != 1) {
      indexOfNumberToCompare = leftBorderIndex + (int) Math.floor(((rightBorderIndex - leftBorderIndex) / 2));
      if (point >= intervals[indexOfNumberToCompare])
        leftBorderIndex = indexOfNumberToCompare;
      else
        rightBorderIndex = indexOfNumberToCompare;
    }
    return leftBorderIndex;
  }


  public static double linearInterpolation(double x, double x0, double y0, double x1, double y1) {
    double a = (y1 - y0) / (x1 - x0);
    double b = -a * x0 + y0;
    return a * x + b;
  }


  public String formatPrecisionDouble(double value) {
    return formatDoubleString(value, DEFAULT_DOUBLE_PRECISION_FORMAT);
  }


  public static String formatDouble(double value) {
    return formatDoubleString(value, DEFAULT_DOUBLE_FORMAT);
  }


  private static String formatDoubleString(double value, String format) {
    return String.format(format, value);
  }


  public static String fmt(double d) {
    DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
    df.setMaximumFractionDigits(340);
    return df.format(d);
  }


  public static double[][] roundValues(double[][] matrix) {
    double[][] outputMatrix = new double[matrix.length][matrix[0].length];
    for (int i = 0; i < matrix.length; i++) {
      for (int j = 0; j < matrix[i].length; j++) {
        outputMatrix[i][j] = Math.round(matrix[i][j] * 1000.0) / 1000.0;
      }
    }
    return outputMatrix;
  }


  public static double[][] transposeMatrix(double[][] matrix) {
    int x = matrix.length;
    int y = matrix[0].length;
    double[][] transposedMatrix = new double[y][x];
    for (int i = 0; i < y; i++) {
      for (int j = 0; j < x; j++) {
        transposedMatrix[i][j] = matrix[j][i];
      }
    }
    return transposedMatrix;
  }


  public static List<List> calculateReturnPeriods(double outputRAArray[][]) {
    /*
     * Sample outputRAArray double[][] outputRAArray = { {2, 0.15, 0.28, 1.09,
     * 1.45}, {5, 0.3, 0.54, 2.03, 2.75}, {10, 0.4, 0.72, 2.75, 3.76}, {20,
     * 0.53, 0.96, 3.65, 4.88}, {30, 0.58, 1.05, 3.9, 5.27}, {40, 0.59, 1.07,
     * 3.91, 5.33}, {50, 0.63, 1.14, 4.22, 5.72}, {60, 0.64, 1.16, 4.34, 5.83},
     * {70, 0.66, 1.18, 4.37, 5.91}, {80, 0.68, 1.22, 4.49, 6.1}, {90, 0.7,
     * 1.26, 4.67, 6.32}, {100, 0.71, 1.3, 4.84, 6.52}};
     */
    List<List> interpolatedResultsArray = new ArrayList<>();
    outputRAArray = roundValues(outputRAArray);
    double[][] transposedMatrix = transposeMatrix(outputRAArray);

    for (int x = 0; x < transposedMatrix[0].length; x++) {
      double currentRP = transposedMatrix[0][x];
      double currentSoilLoss = transposedMatrix[1][x];
      double maxBaselineSoilLoss = transposedMatrix[1][x];
      List<Double> rpPeriodArray = new ArrayList<>();
      rpPeriodArray.add(currentSoilLoss);
      rpPeriodArray.add(currentRP);

      for (int i = 2; i < transposedMatrix.length; i++) {
        List<Double> alt_scenario = new ArrayList<>();
        for (int k = 0; k < transposedMatrix[i].length; k++) {
          if (transposedMatrix[i][k] <= maxBaselineSoilLoss)
            alt_scenario.add(transposedMatrix[i][k]);
        }
        // default the altenative scenario interpolated value to 1
        double altScenarioInterp = 1;
        if (!alt_scenario.isEmpty()) {
          altScenarioInterp = interpolate(maxBaselineSoilLoss, transposedMatrix[i], transposedMatrix[0]);
          altScenarioInterp = Math.round(altScenarioInterp * 1000.0) / 1000.0;

          // set the return period to 100 if the interpolated value is greater than 100
          if (altScenarioInterp > 100 || Double.isNaN(altScenarioInterp))
            altScenarioInterp = 100;
        }
        // round the interpolated year to the nearest 10th place
        altScenarioInterp = Math.round(altScenarioInterp * 10) / 10.0;
        rpPeriodArray.add(altScenarioInterp);
      }
      interpolatedResultsArray.add(rpPeriodArray);
    }
    return interpolatedResultsArray;
  }


  public static double getSlopelength(PayloadParameter p) throws ServiceException {
    double slopeLength = DEFAULT_SLOPE_LENGTH_METER;
    if (p.has("slopelength")) {
      slopeLength = p.getDouble("slopelength");
      switch (p.getUnit("slopelength")) {
        case "m":
          break;
        case "ft":
          slopeLength *= 0.3048;
          break;
        default:
          throw new ServiceException("Illegal unit for slopelength: " + p.getUnit("slopelength"));
      }
    }
    if (slopeLength <= 0.0)
      throw new ServiceException("'slopelength' parameter must be greater than 0.");

    return slopeLength;
  }

}