V1_0.java [src/java/m/crp/assessmenttool] Revision: 0a9cc7372019ee27d760bf3c92d33d84c06dfdd7  Date: Wed Dec 11 13:24:15 MST 2019
/*
 * $Id$
 *
 * This file is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-Service framework, API, and application suite.
 *
 * 2012-2019, 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 m.crp.assessmenttool;

import csip.Config;
import csip.ModelDataService;
import csip.PayloadParameter;
import csip.PayloadResults;
import csip.ServiceException;
import csip.annotations.Description;
import csip.annotations.Name;
import csip.annotations.Polling;
import csip.annotations.Resource;
import csip.utils.JSONUtils;
import crp.utils.DBResources;
import static crp.utils.DBResources.LOCAL_SQLSERVER;
import crp.utils.DEMSteepness;
import gisobjects.GISObject;
import gisobjects.GISObjectFactory;
import gisobjects.db.GISEngine;
import gisobjects.db.GISEngineFactory;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Map;
import javax.ws.rs.Path;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import crp.utils.Region;
import crp.utils.WEPP;
import crp.utils.WEPS;
import crp.utils.WEPSFallowRotation;
import crp.utils.WWESoilParams;
import crp.utils.WWESoilParams.SoilResult;
import data.interpretors.SlopeSteepness;
import data.interpretors.SlopeSteepness.SlopeData;

/**
 *
 * @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
 */
@Name("CRP Assessment")
@Description("This service consumes a JSON request containing a request identifier and CRP Offer geometry and returns sheet/rill water and wind erosion rates for a fallow management for up to the three most dominant soil components in a CRP Offer area, plus a weighted average for each erosion rate.")
@Path("m/crpassessment/1.0")
@Polling(first = 2000, next = 1000)
@Resource(from = DBResources.class)

public class V1_0 extends ModelDataService {

    public static final String SCENARIO_ID = "scenario_id";
    public static final String SCENARIO_GEOMETRY = "scenario_geometry";

    protected JSONObject scenario_geometry;
    protected String scenario_id;
    protected GISObject aoa_geometry;
    protected GISEngine gEngine;

    protected String regionURI = "http://csip.engr.colostate.edu:8083/csip-weps/m/region/2.0";
    protected String soilsURI = "http://csip.engr.colostate.edu:8083/csip-soils/d/wwesoilparams/2.0";
    protected String weppURI = "http://csip.engr.colostate.edu:8083/csip-wepp/m/wepp/3.1";
    protected String wepsURI = "http://csip.engr.colostate.edu:8083/csip-weps/m/weps/5.2";
    protected String demSteepnessURI = "NONE"; //"http://csip.engr.colostate.edu:8087/csip-watershed/m/average_slope/2.0";

    protected String outputFile;
    protected boolean streamOutputFile = false;

    protected double regionLength, regionWidth, regionOrientation;
    protected ArrayList<SoilResult> topThreeComponents;
    protected double latitude, longitude;
    protected JSONObject rotation;
    protected double waterErosionRate = 0.0;
    protected double windErosionRate = 0.0;
    protected SlopeSteepness slopes;

    @Override
    protected void preProcess() throws Exception {
        PayloadParameter params = parameter();

        soilsURI = Config.getString("crp.soils", soilsURI);
        regionURI = Config.getString("crp.region", regionURI);
        weppURI = Config.getString("crp.wepp", weppURI);
        wepsURI = Config.getString("crp.weps", wepsURI);
        demSteepnessURI = Config.getString("crp.dem.steepness", demSteepnessURI);

        if (params.has(SCENARIO_ID)) {
            scenario_id = params.getString(SCENARIO_ID);
        } else {
            throw new ServiceException("A required input 'scenario_id' was missing.  Please specify a scenario_id value.");
        }

        if (params.has(SCENARIO_GEOMETRY)) {
            scenario_geometry = params.getParamJSON(SCENARIO_GEOMETRY);

            try (Connection connection = resources().getJDBC(LOCAL_SQLSERVER);) {
                gEngine = GISEngineFactory.createGISEngine(connection);
                aoa_geometry = GISObjectFactory.createGISObject(scenario_geometry, gEngine);

                if (aoa_geometry.getType() != GISObject.GISObjectType.polygon
                        && aoa_geometry.getType() == GISObject.GISObjectType.multipolygon) {
                    throw new ServiceException("The input geometry must be a Polygon or a Multipolygon.");
                }

                //  Shortcut to getting the centroid of a shape...Built in to the GISObject source for getLat/Lon if shape is not a point.
                latitude = aoa_geometry.getLatitude();
                longitude = aoa_geometry.getLongitude();
            }

        } else {
            throw new ServiceException("A required input " + SCENARIO_GEOMETRY + " was missing.  Please specify a scenario_id value.");
        }

    }

    @Override
    protected void doProcess() throws Exception {
        // Get Slope Steepness from DEM Service for this AoA.
        slopes = getDEMSteepness();
        double tSlope = Double.NaN;
        if (null != slopes) {
            if (!slopes.badSlopeData() && slopes.slopeDataMessages().isEmpty()) {
                SlopeData slope = slopes.slope("1");

                if (null != slope) {
                    tSlope = slope.mean();
                }
            }
        }

        //  Get region data
        Region regionCall = new Region(getMetainfo(), scenario_geometry, regionURI);
        regionCall.call();
        regionLength = regionCall.getLength();
        regionWidth = regionCall.getWidth();
        regionOrientation = regionCall.getOrientation();

        WWESoilParams soilsCall = new WWESoilParams(getMetainfo(), scenario_geometry, soilsURI);
        soilsCall.call();
        topThreeComponents = soilsCall.getTopThree();

        //TODO:  Currently rotations are the default fallow operation...final version of this service requires user to provide rotation.
        double totalArea = 0.0;
        for (SoilResult soilResult : topThreeComponents) {

            WEPP weppCall = new WEPP(getMetainfo(), latitude, longitude, soilResult.cokey,
                    soilResult.length, ((Double.isNaN(tSlope)) ? soilResult.slope_r : tSlope), getRotation((int) soilResult.length), weppURI);
            weppCall.call();
            soilResult.waterErosion = weppCall.waterErosion();

            WEPS wepsCall = new WEPS(getMetainfo(), latitude, longitude, regionLength,
                    regionWidth, regionOrientation, soilResult.cokey, getRotation(0), wepsURI);
            wepsCall.call();
            soilResult.windErosion = wepsCall.windErosion();

            totalArea += soilResult.area;
        }

        for (SoilResult soilResult : topThreeComponents) {
            waterErosionRate += soilResult.waterErosion * (soilResult.area / totalArea);
            windErosionRate += soilResult.windErosion * (soilResult.area / totalArea);
        }
    }

    @Override
    protected void postProcess() throws Exception {
        PayloadResults results = results();
        JSONArray soilsResult = new JSONArray();

        for (SoilResult soilResult : topThreeComponents) {
            JSONArray soilData = new JSONArray();
            soilData.put(JSONUtils.data("soilPtr", soilResult.cokey));
            soilData.put(JSONUtils.data("soilName", soilResult.soilName));
            soilData.put(JSONUtils.data("area_pct", soilResult.area_pct, "The percentage of the component of the mapunit intersection with the CRP Offer Area", "Percent"));
            soilData.put(JSONUtils.data("area", soilResult.area, "Soil Component Area (Acres) in the CRP Offer area", "Acres"));
            soilData.put(JSONUtils.data("WaterSoilLoss", soilResult.waterErosion, "Average Annual Soil Loss by Water", "ton/ac/yr"));
            soilData.put(JSONUtils.data("WindSoilLoss", soilResult.windErosion, "Average Annual Soil Loss by Wind", "ton/ac/yr"));
            soilsResult.put(soilData);
        }
        results.put("top_3_soils", soilsResult);
        results.put("WtAvgWaterSoilLoss", waterErosionRate, "Weighted Average Annual Soil Loss by Water", "ton/ac/yr");
        results.put("WtAvgWindSoilLoss", windErosionRate, "Weighted Average Annual Soil Loss by Wind", "ton/ac/yr");
        results.put("WtAvgTotalSoilLoss", waterErosionRate + windErosionRate, "Weighted Average Annual Soil Loss by Water and Wind", "ton/ac/yr");
    }

    //TODO finish when Jack is ready.
    protected JSONObject getRotation(int length) throws JSONException {
        return new WEPSFallowRotation(length).rotation();
    }

    protected SlopeSteepness getDEMSteepness() throws ServiceException {

        if (!demSteepnessURI.equals("NONE")) {
            DEMSteepness slopeCall = new DEMSteepness(getMetainfo(), scenario_geometry, demSteepnessURI);
            slopeCall.call();

            return slopeCall.slopes();

        }

        return null;
    }
}