WEPPUtils.java [src/java/util] Revision:   Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.codehaus.jettison.json.JSONObject;

/**
 *
 * @author od
 */
public class WEPPUtils {

  /**
   * Round a double.
   *
   * @param value value to round
   * @param places number of decimal places to round to
   * @return new value
   */
  public static double round(double value, int places) {
    if (places < 0) {
      throw new IllegalArgumentException();
    }
    BigDecimal bd = new BigDecimal(value);
    bd = bd.setScale(places, RoundingMode.HALF_UP);
    return bd.doubleValue();
  }


  /**
   * Get soil loss between a specific date range. Account for wrapping.
   *
   * @param startDt starting date as an index
   * @param endDt ending date as an index
   * @param sloss array of soil loss
   * @param lossPts number of points in array
   * @return summed soil loss between indices
   */
  public static double getLoss(int startDt, int endDt, double[] sloss, int lossPts) {
    double loss = 0.0;
    if (startDt < endDt) {
      for (int i = startDt; i < endDt; i++) {
        loss = loss + sloss[i];
      }
    } else {
      // take start to end of array
      for (int i = startDt; i < lossPts; i++) {
        loss = loss + sloss[i];
      }
      // take beginning to endDt
      for (int i = 0; i < endDt; i++) {
        loss = loss + sloss[i];
      }
    }
    if (loss < -999) {
      loss = -999;
    }
    return loss;
  }


  /**
   * Subtracts one from the date string.
   *
   * @param date original date
   * @return new date one day earlier
   */
  public static String decDay(String date) {
    String dstr;

    String[] dparts = date.split("-");
    int year = Integer.parseInt(dparts[2]);
    int mon = Integer.parseInt(dparts[0]);
    int day = Integer.parseInt(dparts[1]);

    GregorianCalendar gc = new GregorianCalendar(2001, mon - 1, day);
    gc.add(Calendar.DAY_OF_MONTH, -1);
    day = gc.get(Calendar.DAY_OF_MONTH);
    mon = gc.get(Calendar.MONTH) + 1;
    int nyr = gc.get(Calendar.YEAR);
    year = year + (nyr - 2001);

    dstr = String.format("%02d-%02d-%04d", mon, day, year);
    return dstr;
  }


  /**
   * Builds soil loss arrays so they have the same length.
   *
   * @param sloss this OFE soil loss
   * @param slossPrev previous OFE soil loss
   * @param years number of years in this rotation
   * @param prevYears number of years in previous OFE rotation
   * @return soil loss array based on years
   */
  public static double[] buildLoss(double[] sloss, double[] slossPrev, int years, int prevYears) {
    int toCopy;
    double[] updSloss;
    updSloss = new double[sloss.length];

    // if current range is less than previous just delete unused entries
    if (years < prevYears) {
      System.arraycopy(slossPrev, 0, updSloss, 0, sloss.length);
    } else {
      // need to repeat to fill up the list
      int copied = 0;
      while (copied < updSloss.length) {
        toCopy = updSloss.length - slossPrev.length;
        if (toCopy > slossPrev.length) {
          toCopy = slossPrev.length;
        }
        System.arraycopy(slossPrev, 0, updSloss, copied, toCopy);
        copied = copied + toCopy;
      }
    }
    return updSloss;
  }


  /**
   * Convert soil loss / sediment deposition from tons/acre/year to inches/year
   * formula: [ (Z tons/acre) x (2000 lbs/ton) x (1 acre / 43560 ft^2) / (100
   * lbs / ft^3) ] x (12 inches / ft) = inches of detachment or deposition per
   * year
   *
   * @param data to convert in t/ac/year
   * @return inches/year of soil loss
   */
  public static double convertUnit(double data) {
    double res = (data * 2000 * ((double) 1 / 43560) / 100) * 12;
    return res;
  }


  /**
   * Convert the date into a string that has the month name. Used in the report
   * formats.
   *
   * @param date string to reformat
   * @return new date formatted with name for month
   */
  public static String getAltDate(String date) {
    String adate;
    String[] monstrs
        = {
          "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };

    String mdate[] = date.split("-");
    int yr = Integer.parseInt(mdate[0]);
    int mon = Integer.parseInt(mdate[1]);
    int day = Integer.parseInt(mdate[2]);

    adate = String.format("%s %02d, %02d", monstrs[mon - 1], day, yr);
    return adate;
  }


  /**
   * Change data format from yyyy-mm-dd to mm-dd-yyyy
   *
   * @param date to change
   * @return string containing new date format.
   */
  public static String swapDate(String date) {
    String mdate[] = date.split("-");
    // switch from yyyy-mm-dd to mm-dd-yyyy
    return String.format("%s-%s-%s", mdate[1], mdate[2], mdate[0]);
  }


  /**
   * Get number of days difference between start and end dates as strings.
   *
   * @param endd ending date
   * @param startd starting dates
   * @return number of days between the two dates.
   */
  public static int daysDiff(String endd, String startd, int rotYears, int years) {
    int days = 0;
    String[] dparts = endd.split("-");
    int year = Integer.parseInt(dparts[0]);
    int mon = Integer.parseInt(dparts[1]);
    int day = Integer.parseInt(dparts[2]);

    GregorianCalendar gcend = new GregorianCalendar();
    gcend.set(GregorianCalendar.DAY_OF_MONTH, day);
    gcend.set(GregorianCalendar.MONTH, mon);
    gcend.set(GregorianCalendar.YEAR, 2001 + year);  // use 2001 as a non-leap year

    dparts = startd.split("-");
    year = Integer.parseInt(dparts[0]);
    mon = Integer.parseInt(dparts[1]);
    day = Integer.parseInt(dparts[2]);

    GregorianCalendar gcst = new GregorianCalendar();
    gcst.set(GregorianCalendar.DAY_OF_MONTH, day);
    gcst.set(GregorianCalendar.MONTH, mon);
    gcst.set(GregorianCalendar.YEAR, 2001 + year);  // use 2001 as a non-leap year

    if (gcend.getTime().getTime() > gcst.getTime().getTime()) {
      days = daysBetween(gcst.getTime(), gcend.getTime()) + 1;
    } else {
      if (gcend.getTime().getTime() == gcst.getTime().getTime()) {
        // same date, number of days is the length of rotation * 365
        days = 365 * rotYears;
      } else {
        // wrapped around, need to get two sets of days
        // first one is start of run to current end date
        GregorianCalendar gctmp = new GregorianCalendar();
        gctmp.set(GregorianCalendar.DAY_OF_MONTH, 1);
        gctmp.set(GregorianCalendar.MONTH, 0);
        gctmp.set(GregorianCalendar.YEAR, 2001);  // use 2001 as a non-leap year
        days = daysBetween(gctmp.getTime(), gcend.getTime()) + 1;

        // second part is starting date until end of rotation
        gctmp.set(GregorianCalendar.DAY_OF_MONTH, 31);
        gctmp.set(GregorianCalendar.MONTH, 11);

        gctmp.set(GregorianCalendar.YEAR, 2001 + (years - 1));

        days = days + daysBetween(gcst.getTime(), gctmp.getTime()) + 1;
      }
    }

    if (days == 0) {
      if (endd.equals(startd)) {
        days = 365 * year;
      }
    }
    return days;
  }


  /**
   * Get number of days between two dates.
   *
   * @param d1 starting date
   * @param d2 ending date
   * @return number of days between
   */
  public static int daysBetween(Date d1, Date d2) {
    return (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
  }

  public static  JSONObject doStats(Double[] items) throws Exception {
    double min = 9999;
    double max = -9999;
    double mean, median, stddev, coefvar;
    double total, numsquared, diff;

    total = 0.0;

    for (int i = 0; i < items.length; i++) {
      if (items[i] < min) {
        min = items[i];
      }
      if (items[i] > max) {
        max = items[i];
      }
      total += items[i];
    }
    mean = total / items.length;
    numsquared = 0;
    for (int i = 0; i < items.length; i++) {
      diff = items[i] - mean;
      numsquared += (diff * diff);
    }
    stddev = numsquared / (double) items.length;
    stddev = Math.sqrt(stddev);
    if (mean == 0.0) {
      coefvar = 0;
    } else {
      coefvar = stddev / mean;
    }
    Arrays.sort(items);
    if ((items.length % 2) == 0) {
      // even number
      int right = items.length / 2;
      int left = (items.length / 2) - 1;
      median = (items[left] + items[right]) / 2;
    } else {
      int mid = items.length / 2;
      median = items[mid];
    }

    JSONObject obj = new JSONObject();
    obj.put("min", min);
    obj.put("max", max);
    obj.put("mean", mean);
    obj.put("median", median);
    obj.put("stddev", stddev);
    obj.put("coefvar", coefvar);

    return obj;
  }
}