V1_0.java [src/java/m/svap/svap03_svaplanduse] 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-2017, 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.svap.svap03_svaplanduse;

import csip.Config;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Description;
import csip.annotations.Name;
import csip.utils.JSONUtils;
import gisobjects.GISObjectException;
import gisobjects.raster.GISRaster;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.HashMap;
import javax.ws.rs.Path;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import svap.utils.HUC12;
import svap.utils.NLCD_Clip;

/**
 * SVAP-03: Get Land Use for Watershed Description
 *
 * This service accepts a GeoJSON lat/long, finds the intersecting HUC-12
 * boundary and sends it to the CSIP-NLCD clip service, then parses the results.
 *
 * @author Robert Streetman
 * @author Shaun Case
 * @version 1.0
 */
@Name("SVAP-03: Get Land Use for Watershed Description")
@Description("This service gets the NLCD land use data for the HUC-12 and converts it to NRCS land use percentages")
@Path("m/svap/svaplanduse/1.0")

public class V1_0 extends ModelDataService {

  private final String NLCD_CLIP_URL = "https://csip.erams.com/csip-nlcd/m/clip/1.0";
  public static final String USGS_HUC_URL = "https://services.nationalmap.gov/arcgis/rest/services/wbd/MapServer/7/query";

  private static final HashMap<Integer, String> landuseMapNLCD;
  private static final HashMap<Integer, String> landuseMapNRCS;

  private HashMap<Integer, Integer> histogramNRCS;
  private HashMap<Double, Integer> histogramNLCD;

  private JSONObject assessmentLocation;
  private String huc12Number, huc12Name;
  private int assessmentId;
  private double binCount = 0, binCountNRCS = 0;


  static {
    landuseMapNLCD = new HashMap();
    landuseMapNLCD.put(11, "Water;Water");
    landuseMapNLCD.put(12, "Water;Water");
    landuseMapNLCD.put(21, "Developed Land;Developed");
    landuseMapNLCD.put(22, "Developed Land;Developed");
    landuseMapNLCD.put(23, "Developed Land;Developed");
    landuseMapNLCD.put(24, "Developed Land;Developed");
    landuseMapNLCD.put(31, "Other Rural Land;Barren");
    landuseMapNLCD.put(41, "Forest;Forest");
    landuseMapNLCD.put(42, "Forest;Forest");
    landuseMapNLCD.put(43, "Forest;Forest");
    landuseMapNLCD.put(51, "Range;Shrubland");
    landuseMapNLCD.put(52, "Range;Shrubland");
    landuseMapNLCD.put(71, "Range;Herbaceous");
    landuseMapNLCD.put(72, "Range;Herbaceous");
    landuseMapNLCD.put(73, "Range;Herbaceous");
    landuseMapNLCD.put(74, "Range;Herbaceous");
    landuseMapNLCD.put(81, "Pasture;Planted/Cultivated");
    landuseMapNLCD.put(82, "Crop;Planted/Cultivated");
    landuseMapNLCD.put(90, "Other Rural;Wetlands");
    landuseMapNLCD.put(95, "Other Rural;Wetlands");

    landuseMapNRCS = new HashMap();
    landuseMapNRCS.put(0, "KW;Undetermined");
    landuseMapNRCS.put(1, "CR;Crop");
    landuseMapNRCS.put(2, "FO;Forest");
    landuseMapNRCS.put(3, "RA;Range");
    landuseMapNRCS.put(4, "PA;Pasture");
    landuseMapNRCS.put(5, "PR;Protected");
    landuseMapNRCS.put(6, "HQ;Farmstead");
    landuseMapNRCS.put(7, "UR;Developed Land");
    landuseMapNRCS.put(8, "WA;Water");
    landuseMapNRCS.put(9, "OR;Other Rural Land");
    landuseMapNRCS.put(10, "AL;Associated Land Ag");
  }


  @Override
  protected void preProcess() throws ServiceException {
    //TODO:  Change this to a GISObject...
    assessmentLocation = parameter().getJSON("assessment_location");
    assessmentId = parameter().getInt("assessment_id");
  }


  @Override
  protected void doProcess() throws ServiceException, SQLException, GISObjectException, JSONException, IOException, URISyntaxException, Exception {
    HUC12 huc12 = getHUC12Data();

    if (huc12.getDataLength() > 0) {
      huc12Number = huc12.getPropertyValue(0, "HUC12");
      huc12Name = huc12.getPropertyValue(0, "NAME");
      if (null != huc12Number) {
        if (null != huc12Name) {
          translateRasterHistogram(getNLCDRaster(huc12.toJSON()));
        } else {
          throw new ServiceException("USGS WBD service [HUC_12] returned no value for the HUC_12 name.");
        }
      } else {
        throw new ServiceException("USGS WBD service [HUC_12] returned no value for the HUC_12 Id number.");
      }
    } else {
      throw new ServiceException("Invalid return data from USGS WBD service [HUC_12].  Returned data length is zero.");
    }
  }


  @Override
  protected void postProcess() throws ServiceException, JSONException {
    results().put("assessment_id", assessmentId, "Assessment identifier.");
    results().put("huc_12_id", huc12Number, "HUC12 identifier.");
    results().put("huc_name", huc12Name, "HUC12 name.");

    if (histogramNLCD != null && binCount > 0) {
      JSONArray histArr = new JSONArray();

      for (int key : histogramNRCS.keySet()) {
        binCountNRCS += histogramNRCS.get(key);
        String[] nrcsString = landuseMapNRCS.get(key).split(";");
        JSONArray nlcdLanduseArr = new JSONArray();

        nlcdLanduseArr.put(JSONUtils.dataDesc("land_use_id", key, "NRCS land use id."));
        nlcdLanduseArr.put(JSONUtils.dataDesc("land_use_code", nrcsString[0], "NRCS land use code."));
        nlcdLanduseArr.put(JSONUtils.dataDesc("land_use_name", nrcsString[1], "NRCS land use name."));
        nlcdLanduseArr.put(JSONUtils.dataDesc("land_use_bin_count", histogramNRCS.get(key), "NRCS land use pixel count."));
        nlcdLanduseArr.put(JSONUtils.data("land_use_pct", ((double) histogramNRCS.get(key) / binCount) * 100.0, "NRCS land use percentage.", "Percent"));
        histArr.put(JSONUtils.data("land_use_nrcs", nlcdLanduseArr, "NRCS land use summary."));
      }

      histArr.put(JSONUtils.data("land_use_total_percent", (((double) binCountNRCS / binCount) * 100.0), "Total percentage of NRCS land use", "Percent"));
      histArr.put(JSONUtils.data("land_use_total_pixels", binCountNRCS, "Total number of pixels of valid land use."));
      histArr.put(JSONUtils.data("land_use_total_bins", binCount, "Total number of pixels in all bins (even non-valid)"));

      results().put("huc12_histogram", histArr, "HUC-12 histogram of land use.");
    }
  }


  protected GISRaster getNLCDRaster(JSONObject geometry) throws ServiceException, Exception {
    NLCD_Clip nlcdService = new NLCD_Clip(geometry, Config.getString("service.erams.nlcd_clip.uri", NLCD_CLIP_URL), new JSONObject(metainfo().toString()));
    nlcdService.call();
    return nlcdService.getRaster();
  }


  protected void translateRasterHistogram(GISRaster raster) throws GISObjectException {
    histogramNLCD = new HashMap();
    histogramNLCD.putAll(raster.createRasterHistogram()); 

    histogramNRCS = new HashMap();

    //Loop through all land use ids in NLCD clip service histogram
    histogramNLCD.forEach((key, val) -> {
      int k = key.intValue();
      binCount += val;

      //Map to NLCD histogram if valid id
      if (landuseMapNLCD.containsKey(k)) {
        //Has this NRCS land use been initialized?
        if (histogramNRCS.containsKey(nrcsLandUseId(k))) {//Y
          int newBinSum = val + histogramNRCS.get(nrcsLandUseId(k));
          histogramNRCS.replace(nrcsLandUseId(k), newBinSum);
        } else {//N
          histogramNRCS.put(nrcsLandUseId(k), val);
        }
      }
    });
  }


  public HUC12 getHUC12Data() throws JSONException, URISyntaxException, IOException, ServiceException, GISObjectException {
    String longitude = assessmentLocation.getJSONArray("coordinates").get(0).toString();
    String latitude = assessmentLocation.getJSONArray("coordinates").get(1).toString();
    HUC12 huc12 = new HUC12();
    huc12.setOutFields("HUC12, NAME");
    huc12.setFormat("geojson");
    huc12.fillHUC12Data(longitude + ", " + latitude);
    return huc12;
  }


  private int nrcsLandUseId(int nlcd) {
    int nrt = -1;

    switch (nlcd) {
      case 11:
      case 12:
        nrt = 8;
        break;

      case 21:
      case 22:
      case 23:
      case 24:
        nrt = 7;
        break;

      case 31:
      case 90:
      case 95:
        nrt = 9;
        break;

      case 41:
      case 42:
      case 43:
        nrt = 2;
        break;

      case 51:
      case 52:
      case 71:
      case 72:
      case 73:
      case 74:
        nrt = 3;
        break;

      case 81:
        nrt = 4;
        break;

      case 82:
        nrt = 1;
        break;
    }

    return nrt;
  }
}