V1_21.java [src/java/d/soils] Revision: 1e3f2b36363f0eeca9f70d1b10adc5e6aaf69e99  Date: Tue Nov 06 16:10:17 MST 2018
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package d.soils;

import c.GIS_DB;
import c.SqlGIS;
import com.vividsolutions.jts.geom.Geometry;
import java.util.Collection;
import java.util.logging.Level;
import csip.ServiceException;
import java.io.IOException;
import javax.ws.rs.*;
import org.apache.commons.io.IOUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import csip.ModelDataService;
import csip.annotations.*;
import csip.annotations.Resource;
import static csip.annotations.ResourceType.JDBC;
import static csip.annotations.State.RELEASED;
import csip.utils.JSONUtils;
import java.sql.SQLException;
import java.util.Map;
import org.codehaus.jettison.json.JSONException;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;

import static d.soils.V1_21.DB;

/**
 * REST Web Service. Front end callable.
 *
 * @author wlloyd, od
 */
@Name("Soils")
@Description("IET soils")
@VersionInfo("1.2")
@State(RELEASED)
@Path("d/soils/1.2")
@Resource(type = JDBC, file = "${conservation_resources.db}", id = DB)

public class V1_21 extends ModelDataService {

    JSONArray outputObj;
    JSONObject aoa_input_geometry;
    Geometry aoa_geometry;

    static final String DB = "csip.erosion.sqlsvr";


    @Override
    protected void preProcess() throws ServiceException {
        Map<String, JSONObject> inputParams = getParamMap();
        aoa_input_geometry = inputParams.get("AoI");
        if (null == aoa_input_geometry) {
            throw new ServiceException("No 'AoI' item was found in the JSON input.  Please specify an 'AoI' .");
        }
    }


    @Override
    protected void doProcess() throws Exception {
        GIS_DB db = new SqlGIS(getResourceJDBC(DB));
        try {
            LOG.log(Level.INFO, "phacil soils input obj dump");
            outputObj = new JSONArray(getParam().toString());

            Collection<JSONObject> coll = getParamMap().values();
            for (JSONObject jsonobj : coll) {
                LOG.log(Level.INFO, "json obj=" + jsonobj.toString());
                JSONObject aoiobj = jsonobj.optJSONObject("value");
                JSONArray features = aoiobj.optJSONArray("features");

                if (LOG.isLoggable(Level.INFO)) {
                    LOG.log(Level.INFO, "****THE FEATURES IS");
                    LOG.log(Level.INFO, "features=" + features.toString());
                }

                // get polygon translation
                String sPolygon = geometryToText(aoiobj);
                LOG.log(Level.INFO, "features obj=" + features.toString());
                for (int i = 0; i < features.length(); i++) {
                    JSONObject poly = features.getJSONObject(i);
                    LOG.log(Level.INFO, "poly obj #" + i + poly.toString());
                    JSONObject geometry = poly.optJSONObject("geometry");
                    LOG.log(Level.INFO, "geometry obj=" + geometry.toString());
                    JSONArray coords = geometry.optJSONArray("coordinates");
                    LOG.log(Level.INFO, "coords obj=" + coords.toString());
                    JSONArray aCoord = new JSONArray();
                    for (int j = 0; j < coords.length(); j++) {
                        JSONArray shape = coords.getJSONArray(j);
                        LOG.log(Level.INFO, "shape obj=" + shape.toString());
                        JSONArray aPoly = new JSONArray();
                        String newPolygon = "POLYGON((";
                        double firstLong = 0.0;
                        for (int k = 0; k < shape.length(); k++) {
                            JSONArray point = shape.getJSONArray(k);
                            if (firstLong == 0) {
                                firstLong = point.getDouble(0);
                            }
                            if (LOG.isLoggable(Level.INFO)) {
                                LOG.log(Level.INFO, "point obj=" + point.toString());
                                LOG.log(Level.INFO, "point 0 value=" + point.getDouble(0));
                                LOG.log(Level.INFO, "point 1 value=" + point.getDouble(1));
                            }
                            if (k > 0) {
                                newPolygon += ",";
                            }
                            newPolygon += point.getDouble(0) + " " + point.getDouble(1);
                        }
                        newPolygon += "))";
                        if (LOG.isLoggable(Level.INFO)) {
                            LOG.log(Level.INFO, "Polygon of interest=" + newPolygon);
                            LOG.log(Level.INFO, "Query Rusle2 Soils coverage");
                        }

                        // if we were able to get a polygon from the GeometryToText call, use it instead
                        // otherwise use what was generated above...
                        if ((sPolygon != null) && (sPolygon.length() > 0)) {
                            newPolygon = sPolygon;
                        }

                        Collection<GIS_DB.Soil> soils = db.findSoilsForPolygonWithSand(newPolygon, firstLong);

                        for (GIS_DB.Soil soilObj : soils) {
                            LOG.log(Level.INFO, "\n\n\n\n\n\n\n\n\nSAND TOTAL VALUE=" + soilObj.sandtotalR);
//                            String sSoil = "R2 SOIL INTERSECTION: cokey:" + soilObj.coKey;
//                            sSoil += ", mukey:" + soilObj.muKey;
//                            sSoil += " ,cofips:" + soilObj.coFips;
//                            sSoil += " ,compname:" + soilObj.compName;
//                            sSoil += " ,kffact:" + soilObj.kffact;
//                            sSoil += " ,tfact:" + soilObj.tfact + " " + soilObj.tfactUnits;
//                            sSoil += " ,muname:" + soilObj.muName;
//                            sSoil += " ,musym:" + soilObj.muSym;
//                            sSoil += " ,perwater:" + soilObj.perWater + " " + soilObj.perWaterUnits;
//                            sSoil += " ,perwind:" + soilObj.perWind + " " + soilObj.perWindUnits;
//                            sSoil += " ,r2name:" + soilObj.r2Name;
//                            sSoil += " ,r2path:" + soilObj.r2Path;
//                            sSoil += " ,lengthR:" + soilObj.lengthR + " " + soilObj.lengthRUnits;
//                            sSoil += " ,slopeR:" + soilObj.slopeR + " " + soilObj.slopeRUnits;
//                            sSoil += " ,sandtotalR:" + soilObj.sandtotalR + " " + soilObj.sandtotalRUnits;
//                            sSoil += " ,%AOI:" + soilObj.percentAoi;
//                            sSoil += " ,sizeaoi:" + soilObj.sizeAoi + " " + soilObj.sizeAoiUnits;
                            //LOG.log(Level.INFO, sSoil);
                            JSONArray aSoil = new JSONArray();
                            aSoil.put(JSONUtils.data("ssurgo_cokey", soilObj.coKey));
                            aSoil.put(JSONUtils.data("ssurgo_mukey", soilObj.muKey));
                            aSoil.put(JSONUtils.data("ssurgo_areasymbol", soilObj.coFips));
                            aSoil.put(JSONUtils.data("ssurgo_compname", soilObj.compName));
                            aSoil.put(JSONUtils.data("ssurgo_kffact", soilObj.kffact));
                            aSoil.put(JSONUtils.data("ssurgo_tfact", soilObj.tfact, soilObj.tfactUnits));
                            aSoil.put(JSONUtils.data("ssurgo_muname", soilObj.muName));
                            aSoil.put(JSONUtils.data("ssurgo_musym", soilObj.muSym));
                            aSoil.put(JSONUtils.data("per_water", soilObj.perWater, soilObj.perWaterUnits));
                            aSoil.put(JSONUtils.data("per_wind", soilObj.perWind, soilObj.perWindUnits));
                            aSoil.put(JSONUtils.data("r2_r2name", soilObj.r2Name));
                            aSoil.put(JSONUtils.data("r2_r2path", soilObj.r2Path));
                            aSoil.put(JSONUtils.data("length_r", soilObj.lengthR, soilObj.lengthRUnits));
                            aSoil.put(JSONUtils.data("ssurgo_slopeR", soilObj.slopeR, soilObj.slopeRUnits));
                            aSoil.put(JSONUtils.data("ssurgo_sandtotalR", soilObj.sandtotalR, soilObj.sandtotalRUnits));
                            aSoil.put(JSONUtils.data("percent_of_aoi", soilObj.percentAoi, soilObj.percentAoiUnits));
                            aSoil.put(JSONUtils.data("acres_in_aoi", soilObj.sizeAoi, soilObj.sizeAoiUnits));
                            aPoly.put(aSoil);
                        }
                        LOG.log(Level.INFO, "Query WEPS Soils coverage");
                        Collection<GIS_DB.Soil> soilsW = db.findSoilsForPolygonWeps(newPolygon, firstLong);
                        for (GIS_DB.Soil soilObj : soilsW) {
//                            String sSoil = "WEPS SOIL INTERSECTION: cokey:" + soilObj.coKey;
//                            sSoil += ", mukey:" + soilObj.muKey;
//                            sSoil += " ,cofips:" + soilObj.coFips;
//                            sSoil += " ,compname:" + soilObj.compName;
//                            sSoil += " ,kffact:" + soilObj.kffact;
//                            sSoil += " ,tfact:" + soilObj.tfact + " " + soilObj.tfactUnits;
//                            sSoil += " ,muname:" + soilObj.muName;
//                            sSoil += " ,musym:" + soilObj.muSym;
//                            sSoil += " ,perwater:" + soilObj.perWater + " " + soilObj.perWaterUnits;
//                            sSoil += " ,perwind:" + soilObj.perWind + " " + soilObj.perWindUnits;
//                            sSoil += " ,wepsname:" + soilObj.wepsName;
//                            sSoil += " ,wepspath:" + soilObj.wepsPath;
//                            sSoil += " ,lengthR:" + soilObj.lengthR + " " + soilObj.lengthRUnits;
//                            sSoil += " ,slopeR:" + soilObj.slopeR + " " + soilObj.slopeRUnits;
//                            sSoil += " ,%AOI:" + soilObj.percentAoi;
//                            sSoil += " ,sizeaoi:" + soilObj.sizeAoi + " " + soilObj.sizeAoiUnits;
                            //LOG.log(Level.INFO, sSoil);
                            JSONArray aSoil = new JSONArray();
                            aSoil.put(JSONUtils.data("ssurgo_cokey", soilObj.coKey));
                            aSoil.put(JSONUtils.data("ssurgo_mukey", soilObj.muKey));
                            aSoil.put(JSONUtils.data("ssurgo_areasymbol", soilObj.coFips));
                            aSoil.put(JSONUtils.data("ssurgo_compname", soilObj.compName));
                            aSoil.put(JSONUtils.data("ssurgo_kffact", soilObj.kffact));
                            aSoil.put(JSONUtils.data("ssurgo_tfact", soilObj.tfact, soilObj.tfactUnits));
                            aSoil.put(JSONUtils.data("ssurgo_muname", soilObj.muName));
                            aSoil.put(JSONUtils.data("ssurgo_musym", soilObj.muSym));
                            aSoil.put(JSONUtils.data("per_water", soilObj.perWater, soilObj.perWaterUnits));
                            aSoil.put(JSONUtils.data("per_wind", soilObj.perWind, soilObj.perWindUnits));
                            aSoil.put(JSONUtils.data("weps_name", soilObj.wepsName));
                            aSoil.put(JSONUtils.data("weps_path", soilObj.wepsPath));
                            aSoil.put(JSONUtils.data("length_r", soilObj.lengthR, soilObj.lengthRUnits));
                            aSoil.put(JSONUtils.data("ssurgo_slopeR", soilObj.slopeR, soilObj.slopeRUnits));
//                                aSoil.put(JSONUtils.data("ssurgo_sandtotalR", soilObj.sandtotalR, soilObj.sandtotalRUnits));
                            aSoil.put(JSONUtils.data("percent_of_aoi", soilObj.percentAoi, soilObj.percentAoiUnits));
                            aSoil.put(JSONUtils.data("acres_in_aoi", soilObj.sizeAoi, soilObj.sizeAoiUnits));
                            // Check if soil is already in aPoly - if so add weps path/name otherwise append
                            addWepsSoil(aPoly, aSoil, soilObj);
                        }
                        aCoord.put(aPoly);
                    }
                    outputObj.put(aCoord);
                    // aCoord is now returned with putResults
                }
            }
        } catch (JSONException | SQLException jse) {
            throw new ServiceException("Error executing soil serivce ", jse);
        } finally {
            db.close();
        }
    }


    @Override
    protected void postProcess() throws Exception {
        for (int i = 0; i < outputObj.length(); i++) {
            putResult(outputObj.get(i));
        }
    }


    private void addWepsSoil(JSONArray aPolygon, JSONArray wepsSoil, GIS_DB.Soil soilObj) throws JSONException {
        String wepsCokey = JSONUtils.getValueByKey(wepsSoil, "ssurgo_cokey");
        String wepsaoi = JSONUtils.getValueByKey(wepsSoil, "acres_in_aoi");
        for (int i = 0; i < aPolygon.length(); i++) {
            JSONArray aSoil = aPolygon.getJSONArray(i);
            String cokey = JSONUtils.getValueByKey(aSoil, "ssurgo_cokey");
            String aoi = JSONUtils.getValueByKey(aSoil, "acres_in_aoi");
            if ((cokey.equals(wepsCokey)) && (aoi.equals(wepsaoi))) {
                // append weps attrs to existing soil
                aSoil.put(JSONUtils.data("weps_name", soilObj.wepsName));
                aSoil.put(JSONUtils.data("weps_path", soilObj.wepsPath));
                return;
            }
        }
        // add new WEPS soil results because its not there
        aPolygon.put(wepsSoil);
    }


    private String geometryToText(JSONObject features) throws JSONException {
        GeometryJSON geoJSON = new GeometryJSON();
        try {
            if (features.has("features")) {
                FeatureJSON featureJSON = new FeatureJSON();
                FeatureCollection inputFeatures = featureJSON.readFeatureCollection(features.toString());
                FeatureIterator tFeatures = inputFeatures.features();
                while (tFeatures.hasNext()) {
                    Feature tFeature = tFeatures.next();
                    String polygon = ((SimpleFeature) tFeature).getDefaultGeometry().toString();
                    if ((polygon != null) && (polygon.length() > 0)) {
                        return polygon;
                    }
                    aoa_geometry = (Geometry) ((SimpleFeature) tFeature).getAttribute("geometry");
                    //  Do something with this particular feature here...OR just grab the first one if only one was specified.
                    //  This particular code assumes only one was specified, but can easily be altered to send more than one
                    //  and do something with each....
                }
            } else {
                if (LOG.isLoggable(Level.INFO)) {
                    LOG.log(Level.INFO, "***NO FEATURES!!!");
                    LOG.log(Level.INFO, "THE AOA_INPUT_GEOMETRY=" + features.toString());
                }
                if (features.has("type")) {  //Try to uncover if this is a "Feature" or a geometry object                    
                    if (features.getString("type").equalsIgnoreCase("feature")) {
                        FeatureJSON featureJSON = new FeatureJSON();
                        Feature tFeature = featureJSON.readFeature(features.toString());
                        String polygon = ((SimpleFeature) tFeature).getAttribute("geometry").toString();
                        aoa_geometry = (Geometry) ((SimpleFeature) tFeature).getAttribute("geometry");
                        if (LOG.isLoggable(Level.INFO)) {
                            //  Here we could pull out the Feature's properies as well...
                            LOG.log(Level.INFO, "This Feature''s Id is: {0}", ((SimpleFeature) tFeature).getID());
                            LOG.log(Level.INFO, "This Feature''s name is: {0}", ((SimpleFeature) tFeature).getAttribute("name"));
                            //NOTE:  For some reason, property values that are arrays (as in the example JSON), don't get read, so watch out for this!
                            LOG.log(Level.INFO, "This Feature''s adjustment factor is: {0}", ((SimpleFeature) tFeature).getAttribute("AdjustmentFactor"));
                        }
                        return polygon;
                    } else {  //Try to read the GeoJSON geometry object
                        LOG.log(Level.INFO, "***TRY TO READ AS INPUT STREAM!");
                        aoa_geometry = geoJSON.read(IOUtils.toInputStream(features.toString()));
                        return aoa_geometry.toString();
                    }
                } else {
                    LOG.log(Level.INFO, "***GEOJSON is unparsable!!!");
                    throw new JSONException("Unable to parse GEOJSON");
                }
            }

            //  TODO:  Add your code here to do something with the geometry(ies) you read in.
            //         For instance:  See code lines below:  (NOTE:  The .toText() funciton of "Geometry" returns WKT..)
            //  Log the WKT of the input.
            LOG.log(Level.INFO, "***Last aoa_geometry value recieved was: {0}", aoa_geometry.toText());
            throw new JSONException("Unable to parse GEOJSON");
        } catch (IOException ioe) {
            LOG.log(Level.SEVERE, "$$$$$$$$$io exception converting geometry to text=" + ioe.toString());
        } catch (JSONException jse) {
            LOG.log(Level.SEVERE, "***************************************JSON EXCEPTION converting geometry to text=" + jse.toString());
            throw new JSONException("Unable to parse GEOJSON for soils service request");
        }
        return "";
    }
}