GridmetV1_1.java [src/java/d/weather] Revision: default  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 d.weather;

import csip.Config;
import csip.ModelDataService;
import csip.api.client.ModelDataServiceCall;
import csip.api.server.ServiceException;
import csip.annotations.Description;
import csip.annotations.Name;
import csip.annotations.Resource;
//import csip.utils.CheckPointing;
import gisobjects.GISObject;
import gisobjects.GISObjectFactory;
import gisobjects.db.GISEngineFactory;
import java.io.PrintWriter;
import java.sql.Connection;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ws.rs.Path;
import m.ghg.ApplicationResources;
import static m.ghg.ApplicationResources.GISDB_SQLSVR;
import static m.ghg.ApplicationResources.GRIDMET_URL_KEY;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

@Name("DayCent Weather")
@Description("Provide Daycent weather input.")
@Path("d/weather/gridmet/1.1")
@Resource(from = ApplicationResources.class)
@Resource(from = soils.db.DBResources.class)
public class GridmetV1_1 extends ModelDataService {

  // Request JSON name defines
  static final String DEFAULT_DAYCENT_CLIMATE_FILE = "dayCentWeather.IN";
  static final String WEATHER_FILE_KEY = "weather_file";
  static final String WEATHER_DURATION_KEY = "weather_duration";
  static final String STARTING_YEAR_KEY = "starting_year";

  int weatherDuration;
  int startingYear;
  int endingYear;
  JSONObject aoa_geometry;

  static final String gridmetURL = Config.getString(GRIDMET_URL_KEY);


  @Override
  protected void preProcess() throws ServiceException {
    if (gridmetURL == null) {
      throw new ServiceException("gridmet service url not set!");
    }

    aoa_geometry = parameter().getJSON(soils.AoA.AOA_GEOMETRY);
    weatherDuration = parameter().getInt(WEATHER_DURATION_KEY, 50);
    startingYear = parameter().getInt(STARTING_YEAR_KEY, 2000);
    endingYear = startingYear + weatherDuration;
  }


  @Override
  protected void doProcess() throws Exception {

    double[] latlon = {Double.NaN, Double.NaN};

    //  Get centroid of shape, if it is not a point already.  (If it's a point, the centroid is the point)
    try (Connection conn = resources().getJDBC(GISDB_SQLSVR)) {
      GISObject shape = GISObjectFactory.createGISObject(aoa_geometry, GISEngineFactory.createGISEngine(conn));

      //  getLat/Lon functions get point value or the centroid of the shape.
      latlon[0] = shape.getLatitude();
      latlon[1] = shape.getLongitude();
    }

    if (Double.isNaN(latlon[0]) || Double.isNaN(latlon[1])) {
      throw new ServiceException("Could not get the latitude and longitude of the AoI");
    }

    getClimateData(gridmetURL, latlon[0], latlon[1], weatherDuration);

    toDaycentClimateInput();
//    cp.check("conv");
//    System.out.println(cp);
  }


  @Override
  protected void postProcess() throws Exception {
    results().put(workspace().getFile(DEFAULT_DAYCENT_CLIMATE_FILE), "DayCent weather file");
  }


  void getClimateData(String url, double lat, double lon, int years) throws Exception {
    ModelDataServiceCall r = new ModelDataServiceCall()
        .put("units", "metric")
        .put("start_date", startingYear + "-01-01")
        .put("end_date", endingYear + "-12-31")
        //  .put("duration", years)
        .put("input_zone_features", aoa_geometry)
        //        .put("duration", years)
        .put("input_zone_features", new JSONObject(
            "{  'type': 'FeatureCollection',"
            + "   'features': [{"
            + "      'type': 'Feature',"
            + "      'properties': {"
            + "        'name': 'loc',"
            + "        'gid': 1"
            + "       },"
            + "      'geometry': {"
            + "        'type': 'Point',"
            + "        'coordinates': ["
            + "          " + lon + "," + lat
            + "         ]"
            + "       }"
            + "   }]"
            + "}"))
        .withDefaultLogger()
        .url(url)
        .call();

    if (r.serviceFinished()) {
      r.download("1.json", workspace().getFile("loc.json"));
    } else {
      throw new ServiceException("Gridmet service error: " + r.getError());
    }
  }


  void toDaycentClimateInput() throws Exception {
    JSONArray a = new JSONArray(workspace().readString("loc.json"));
    Integer doy_count = 1;
    Integer oldyy = Integer.MIN_VALUE;
    String prevTmax = "";
    String prevTmin = "";
    try (PrintWriter w = new PrintWriter(workspace().getFile(DEFAULT_DAYCENT_CLIMATE_FILE))) {
      for (int i = 0; i < a.length(); i++) {
        JSONArray row = a.getJSONArray(i);
        String[] date = row.get(0).toString().split("-");
        int dd = Integer.parseInt(date[2]);
        int mm = Integer.parseInt(date[1]);
        int yy = Integer.parseInt(date[0]);
        if (yy > oldyy) {
          doy_count = 1;
          oldyy = yy;
        }
        String doy = doy_count.toString();
        doy_count++;
        String tmax = row.get(10).toString();
        String tmin = row.get(9).toString();
        if (Double.parseDouble(tmax) < Double.parseDouble(tmin)) {
          if (prevTmax.isEmpty() || prevTmin.isEmpty()) {
            String msg = "Need at least one iteration to initialize previous step tmax and tmin";
            throw new NullPointerException(msg);
          }
          tmax = prevTmax;
          tmin = prevTmin;
        } else {
          prevTmax = tmax;
          prevTmin = tmin;
        }
        Double precip = Double.parseDouble(row.get(3).toString()) / 10; // precip in cm
        Double rad = Double.parseDouble(row.get(7).toString()) / 0.484583; // W m-2 to Langleys/day
        Double relhum = 0.5 * (Double.parseDouble(row.get(4).toString()) + Double.parseDouble(row.get(5).toString()));
        if (relhum > 100.0) {
          relhum = 100.0;
        }
        Double wind = Double.parseDouble(row.get(12).toString()) * 2.237; // m/s to mph
        w.println(dd + " "
            + mm + " "
            + yy + " "
            + doy + " "
            + tmax + " "
            + tmin + " "
            + space4format(precip) + " "
            + space4format(rad) + " "
            + space4format(relhum) + " "
            + space4format(wind));
      }
    }

  }


  private String space4format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.00");
    return String.format("%1$-4s", df1.format(value));
  }

}