V1_0.java [src/java/m/svap/svap08_svapweather] 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 m.svap.svap08_svapweather;
import csip.Config;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Resource;
import gisobjects.GISObject;
import gisobjects.GISObjectException;
import gisobjects.GISObjectFactory;
import gisobjects.db.GISEngineFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Iterator;
import javax.ws.rs.Path;
import csip.annotations.Description;
import csip.annotations.Name;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.geotools.referencing.CRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import svap.utils.ClimateStation;
import svap.utils.DBResources;
import static svap.utils.DBResources.CRDB;
import static svap.utils.DBResources.NRTDB;
import svap.utils.HUC12;
import svap.utils.NRT_DB;
import svap.utils.NRT_DB.County;
import svap.utils.SVAPUtils;
import static svap.utils.SVAPUtils.convertKMtoMiles;
import svap.utils.StationDataService;
import svap.utils.StationMetaDataService;
/**
*
* @author Robert Streetman
* @author Shaun Case
*/
@Name("SVAP-08 Get Weather Conditions for Field Assessment.")
@Description("This service returns average daily values for temperature, minimum temperature, maximum temperature, and precipitation within a specified period from the date of stream reach assessment, default set to 5 days: the date of assessment and previous 4 days")
@Path("m/svap/svapweather/1.0")
@Resource(from = DBResources.class)
public class V1_0 extends ModelDataService {
private static final String URL_ACIS_STNMETA = "http://data.rcc-acis.org/StnMeta";
private static final String URL_ACIS_STNDATA = "http://data.rcc-acis.org/StnData";
private static final String URL_ERAMS_STATIONS = "http://csip.engr.colostate.edu:8083/csip-climate/d/acis/1.0";
private static final String URL_ERAMS_HUC_EXTENT = "https://csip.erams.com/csip-huc/d/huc/extent/1.0";
private static final String URL_ERAMS_HUC_GEOMETRY = "https://csip.erams.com/csip-huc/d/huc/geometry/1.0";
private static final String ACIS = "ACIS";
private static final String ERAMS = "ERAMS";
private static final int DEFAULT_PAST_DAYS = 5;
private LocalDate eDate, sDate;
private GISObject locationPoint;
private ClimateStation finalStation;
private static String stations_source;
private static String url_acis_stnmeta;
private static String url_acis_stndata;
private static String url_erams_stations;
private static String url_erams_huc;
private boolean foundInHUC, foundInCounty, foundInState = false;
private boolean printAll;
private ArrayList<ClimateStation> allStations = new ArrayList<>();
static {
// Setting the system-wide default at startup time
//System.setProperty("org.geotools.referencing.forceXY", "true");
stations_source = Config.getString("service.svap.svapweather.stations.source", ACIS).toUpperCase();
switch (stations_source) {
case ACIS:
url_acis_stnmeta = Config.getString("service.svap.svapweather.acis.stnmeta", URL_ACIS_STNMETA);
url_acis_stnmeta = Config.getString("service.svap.svapweather.acis.stndata", URL_ACIS_STNDATA);
break;
case ERAMS:
url_erams_stations = Config.getString("service.svap.svapweather.erams.climate.stations", URL_ERAMS_STATIONS);
url_erams_huc = Config.getString("service.svap.svapweather.erams.huc.extent", URL_ERAMS_HUC_EXTENT);
url_erams_huc = Config.getString("service.svap.svapweather.erams.huc.geometry", URL_ERAMS_HUC_GEOMETRY);
break;
}
}
@Override
protected void preProcess() throws ServiceException, JSONException, IOException, GISObjectException, SQLException {
JSONObject location;
sDate = SVAPUtils.getDate(parameter().getString("date"), DEFAULT_PAST_DAYS);
eDate = SVAPUtils.getDate(parameter().getString("date"), 0);
location = parameter().getJSON("location");
locationPoint = GISObjectFactory.createGISObject(location,
GISEngineFactory.createGISEngine(resources().getJDBC(CRDB)), 4326);
printAll = parameter().getBoolean("show_all_stations", false);
}
@Override
protected void doProcess() throws ServiceException, FactoryException, TransformException {
finalStation = getStationData(findClosestStation(getHUC12Boundary()));
}
@Override
protected void postProcess() throws ServiceException, JSONException, FactoryException {
JSONArray tOutput = new JSONArray();
finalStation.setNonOutputColumns(null);
finalStation.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(
ClimateStation.STATION_ID, ClimateStation.STATION_NAME,
ClimateStation.STATION_DISTANCE, ClimateStation.AVG_TEMP,
ClimateStation.AVG_LOW_TEMP, ClimateStation.AVG_HI_TEMP,
ClimateStation.AVG_PCPN)));
finalStation.toJSON(tOutput);
for (int i = 0; i < tOutput.length(); i++) {
// changed result to name the station from 'putResult(tOutput.get(i));' to
results().put("station_" + i, tOutput.get(i));
}
results().put("station_proximity_type", ((foundInHUC) ? "Within HUC-12 boundary" : ((foundInCounty) ? "Within county boundary" : "Within state boundary")), "Identifies the potential applicability of the station's data to the actual watershed represented by the input location point. Possible locations are HUC-12, county, and state boundary, respectively representing lesser applicability.");
if (printAll && (allStations.size() > 0)) {
JSONArray stations = new JSONArray();
SortedMap sortedStations = sortStations(allStations);
Iterator itr = sortedStations.values().iterator();
while (itr.hasNext()) {
ClimateStation tStation = (ClimateStation) itr.next();
tStation.setNonOutputColumns(null);
tStation.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(
ClimateStation.STATION_ID, ClimateStation.STATION_NAME,
ClimateStation.STATION_DISTANCE)));
JSONArray stationArray = new JSONArray();
tStation.toJSON(stationArray);
stations.put(stationArray);
}
if (stations.length() > 0) {
results().put("all_station_list", stations);
}
}
}
////////////////////////////////
//
// Private member functions
//
////////////////////////////////
private HUC12 getHUC12Boundary() throws ServiceException {
HUC12 huc12 = null;
try {
huc12 = new HUC12();
huc12.setOutFields("HUC12, NAME");
huc12.setFormat("geojson");
// Assumes that the input geometry was a point. However, if not, that is okay also.
// The embedded function getLongitude() and getLatitude() in GISObject will
// return the centroid of all other geometry shapes, as well, by default.
huc12.fillHUC12Data(locationPoint.getLongitude() + ", " + locationPoint.getLatitude());
} catch (GISObjectException | JSONException | URISyntaxException | IOException | ServiceException ex) {
throw new ServiceException("Cannot get HUC12 boundary: " + ex.getMessage(), ex);
}
return huc12;
}
private ClimateStation findClosestStation(HUC12 huc12) throws ServiceException, FactoryException, TransformException {
ArrayList<ClimateStation> stationList;
ClimateStation ret_val = null;
if (null != huc12) {
String coord = huc12.getEnvelopeList();
StationMetaDataService stationService = new StationMetaDataService();
stationList = stationService.searchBBox(coord, sDate.toString(), eDate.toString());
if (stationList.size() > 0) {
ret_val = traverseFindNearest(stationList);
foundInHUC = true;
} else {
// We want to keep trying to find stations, but now outside of the associated HUC-12 boundary.
// For some states, there can be very few climate stations, and they may be almost 100 miles away.
// 1. Check county
// 2. If no county stations, check State.
try (Connection conn = resources().getJDBC(NRTDB);) {
NRT_DB nrtDb = new NRT_DB(conn);
County countyData = nrtDb.findCounty(locationPoint);
stationList = stationService.searchCounty(countyData.st_cnty_code, sDate.toString(), eDate.toString());
if (stationList.size() > 0) {
foundInCounty = true;
ret_val = traverseFindNearest(stationList);
} else {
stationList = stationService.searchState(countyData.st_abbr, sDate.toString(), eDate.toString());
if (stationList.size() > 0) {
foundInState = true;
ret_val = traverseFindNearest(stationList);
} else {
throw new ServiceException("Could not find climate stations within the HUC-12, County, nor State boundaries, for the specified location, and date range.");
}
}
} catch (SQLException ex) {
throw new ServiceException("Did not find any climate stations within the bounding box containing the HUC-12 boundary which contained the location specified. Failed in attempt to locate state and county information to search for more possible stations: " + ex.getMessage(), ex);
}
}
if (printAll) {
allStations.addAll(stationList);
}
} else {
throw new ServiceException("No HUC-12 boundary was passed to the findClosestStation() function.");
}
return ret_val;
}
private SortedMap sortStations(ArrayList<ClimateStation> stationList) throws FactoryException, ServiceException {
SortedMap distanceMap = new TreeMap();
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
for (ClimateStation station : stationList) {
//double distance = convertKMtoMiles(JTS.orthodromicDistance(locationPoint.getGeometry().getCoordinate(), station.coordinates().getGeometry().getCoordinate(), sourceCRS)/1000.0);
double distance = Double.NaN;
try {
distance = convertKMtoMiles(locationPoint.distanceTo(station.coordinates()) / 1000.0);
} catch (GISObjectException ex) {
throw new ServiceException("Cannot calculate distance to climate station, " + station.name() + ", for the specified location point: " + ex.getMessage(), ex);
}
station.distance(distance);
distanceMap.put(distance, station);
}
return distanceMap;
}
private ClimateStation traverseFindNearest(ArrayList<ClimateStation> stationList) throws ServiceException, FactoryException, TransformException {
ClimateStation ret_val = null;
if (null != stationList) {
if (stationList.size() > 1) {
SortedMap distanceMap = sortStations(stationList);
if (distanceMap.size() > 0) {
// Get closest station
ret_val = (ClimateStation) distanceMap.get(distanceMap.firstKey());
} else {
throw new ServiceException("Could not sort the station list data. Sorting function failed to return any data.");
}
} else {
ret_val = stationList.get(0);
}
}
if (null == ret_val) {
throw new ServiceException("No nearby climate station could be found for this HUC-12 boundary.");
}
return ret_val;
}
private ClimateStation getStationData(ClimateStation station) throws ServiceException {
if (null != station) {
StationDataService stationData = new StationDataService(station, sDate.toString(), 5);
stationData.getStationData();
} else {
throw new ServiceException("No climate station was passed to the getStationData() function.");
}
return station;
}
}