PGTools.java [src/java/m/rse/cfactor/utils] Revision: 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.rse.cfactor.utils;
import csip.Config;
import csip.ServiceException;
import csip.SessionLogger;
import csip.utils.Binaries;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import javax.sql.DataSource;
import java.util.ArrayList;
import static m.rse.cfactor.utils.Const.RSE_DEPLOYMENT_LINUX;
import org.apache.tomcat.jdbc.pool.PoolProperties;
/**
*
* @author LYaege
* @author Shaun Case
* @author Wes Lloyd
*/
public class PGTools {
private static ConcurrentHashMap<String, DataSource> dataSources = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, String> connectionStrings = new ConcurrentHashMap<>();
public static synchronized Connection getConnection(String className, SessionLogger logger) throws csip.ServiceException {
Connection ret_val = null;
boolean debugging = Config.getBoolean("debugging", false);
if (null != className) {
//Don't need to synchronize() around these calls because they are already in a synchronized member
if (!connectionStrings.containsKey(className)) {
logger.log(Level.INFO, "Using database source name: {0}", className);
String tConnectionString = Config.getString((debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db"), "error");
if ((null != tConnectionString) && !tConnectionString.equalsIgnoreCase("error")) {
logger.log(Level.INFO, "Using the connection string: {0}", tConnectionString);
connectionStrings.put(className, tConnectionString);
} else {
logger.log(Level.SEVERE, "No connection string found for the database source name of: {0}. The connection string should have been located under key: {1}. ", new Object[]{className, debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db"});
throw new csip.ServiceException("No connection configuration string found for the database source name of: " + className + ". The connection string should have been located under key: " + (debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db") + ". ");
}
}
if (connectionStrings.get(className) == null || connectionStrings.get(className).isEmpty()) {
if (logger != null) {
logger.log(Level.SEVERE, "Connection string not provided. Please configure the {0} configuration parameter.", (debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db"));
}
throw new csip.ServiceException("Unable to connect to RSE database. Please check the " + (debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db") + " configuration parameter");
}
try {
if (!dataSources.containsKey(className)) {
String tConnectionDriver = Config.getString(className + ".db.driver", "error");
if ((null != tConnectionDriver) && !tConnectionDriver.equalsIgnoreCase("error")) {
logger.log(Level.INFO, "Using the connection driver: {0}", tConnectionDriver);
createDataSource(className, tConnectionDriver, logger);
} else {
logger.log(Level.SEVERE, "Cannot find the connection driver configuration key for {0}.db.driver . Cannot proceed to making a database connection.", className);
throw new csip.ServiceException("Cannot find the connection driver configuration key for " + className + ".db.driver . Cannot proceed to making a database connection.");
}
}
ret_val = dataSources.get(className).getConnection();
} catch (SQLException ex) {
if (logger != null) {
logger.log(Level.SEVERE, null, ex);
}
throw new csip.ServiceException("Failed to connect to RSE database. Please check the " + className + " configuration parameter");
}
}
return ret_val;
}
//Doesn't 'need' to be synchronized because it should only be called by getConnection() which is synchronized,
// but just in case someone tries calling it elsewhere within this class at a later date...leave it synchronized.
private static synchronized void createDataSource(String className, String DriverClassName, SessionLogger logger) throws SQLException {
PoolProperties p = new PoolProperties();
p.setUrl(connectionStrings.get(className));
p.setDriverClassName(DriverClassName);
p.setDefaultAutoCommit(false);
p.setJmxEnabled(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(10);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;"
+ "org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer");
dataSources.put(className, new org.apache.tomcat.jdbc.pool.DataSource(p));
if (logger != null) {
logger.log(Level.INFO, "\nCreated datasource {0}", dataSources.get(className).toString());
}
}
public static class PolygonLatLon {
private ArrayList<PGTools.PolygonLatLon.PointLatLon> points;
private Boolean bValid;
public enum ESRIContourType {
outer, inner, unknown
};
protected ESRIContourType myRotation;
public PolygonLatLon() {
this.bValid = true;
this.points = new ArrayList<>();
}
public PolygonLatLon(ArrayList<PGTools.PolygonLatLon.PointLatLon> tPoints) {
if (null != tPoints) {
this.bValid = true;
this.points = new ArrayList<>(tPoints);
for (PGTools.PolygonLatLon.PointLatLon tPoint : tPoints) {
if (!tPoint.isValid()) {
this.bValid = false;
break;
}
}
}
}
public void add(PGTools.PolygonLatLon.PointLatLon tPoint) {
this.points.add(tPoint);
if (!tPoint.isValid()) {
this.bValid = false;
}
}
public void add(Double Lat, Double Lon) {
PGTools.PolygonLatLon.PointLatLon tPoint = new PGTools.PolygonLatLon.PointLatLon(Lat, Lon);
this.points.add(tPoint);
if (!tPoint.isValid()) {
this.bValid = false;
}
}
public String toWKT() {
String ret_val = "'POLYGON((";
int currentPoint = 0;
if (this.bValid) {
for (PGTools.PolygonLatLon.PointLatLon tPoint : this.points) {
if (currentPoint > 0) {
ret_val += ", ";
}
// Remember Lon is X and Lat is Y...
ret_val += tPoint.getLon().toString() + " " + tPoint.getLat().toString();
currentPoint++;
}
ret_val += "))'";
} else {
ret_val = "";
}
return ret_val;
}
public Boolean isValid() {
this.getRotation();
return this.bValid;
}
protected void swapRotation() {
ArrayList<PointLatLon> newPoints = new ArrayList<>();
int j;
for (j = this.points.size() - 1; j >= 0; j--) {
PointLatLon tPoint = new PointLatLon(this.points.get(j).getLat(), this.points.get(j).getLon());
newPoints.add(tPoint);
}
this.points.clear();
this.points = newPoints;
}
public Boolean makeESRIRotation(ESRIContourType contourType) {
Boolean ret_val = false;
if (this.isValid()) {
if (((contourType == ESRIContourType.outer) && (this.myRotation == ESRIContourType.inner))
|| ((contourType == ESRIContourType.inner) && (this.myRotation == ESRIContourType.outer))) {
this.swapRotation();
}
ret_val = true;
}
return ret_val;
}
protected void getRotation() {
double contour_area = 0.0;
int j;
PointLatLon a, b;
this.myRotation = ESRIContourType.unknown;
if (this.bValid) {
// Calculate cartesian area...test for positive or negative value. This tells the rotation of points CW versus CCW.
// NOTE: Don't use this method to calc "real" areas of Lat/Lon! This is just a fast way of finding out the order of points.
// Lat/Lon must be projected first to get actual area using this type of cross-product method.
if (this.points.size() > 2) {
for (j = 0; j < this.points.size() - 2; j++) {
a = this.points.get(j);
b = this.points.get(j + 1);
contour_area += a.getLon() * b.getLat() - b.getLon() * a.getLat();
}
a = this.points.get(j);
b = this.points.get(0);
contour_area += a.getLon() * b.getLat() - b.getLon() * a.getLat();
if (contour_area < 0) {
this.myRotation = ESRIContourType.inner; //CCW
} else {
this.myRotation = ESRIContourType.outer; //CW
}
}
}
}
public static class PointLatLon {
private Double Lat;
private Double Lon;
private Boolean bValid;
public PointLatLon(Double Lat, Double Lon) {
this.Lat = Lat;
this.Lon = Lon;
this.bValid = true;
if (Math.abs(Lon) > 180) {
this.bValid = false;
}
if (Math.abs(Lat) > 90) {
this.bValid = false;
}
}
public Boolean isValid() {
return this.bValid;
}
public Double getLat() {
return this.Lat;
}
public Double getLon() {
return this.Lon;
}
public void setLon(Double Lon) {
this.Lon = Lon;
if (Math.abs(Lon) > 180) {
this.bValid = false;
}
}
public void setLat(Double Lat) {
this.Lat = Lat;
if (Math.abs(Lat) > 90) {
this.bValid = false;
}
}
}
}
/**
* @author Shaun Case
* @param polygon
* @return
* @throws SQLException
*/
public static Centroid getCentroid(String polygon, SessionLogger logger) throws SQLException, ServiceException {
Centroid cent = new Centroid();
try (Connection c = PGTools.getConnection("cfactor", logger)) {
try (Statement s = c.createStatement()) {
String find_centroid = "select (" + polygon + ".STCentroid().STY) as lat, "
+ "(" + polygon + ".STCentroid().STX) as long;";
try (ResultSet r = s.executeQuery(find_centroid)) {
logger.info("query sql=" + find_centroid);
if (r.getMetaData().getColumnCount() != 2) {
logger.severe("invalid columns in getCentroid query");
throw new ServiceException("invalid columns in getCentroid query");
}
logger.info("getting centroid for polygon");
if (r.next()) {
cent.lat = r.getString(1);
cent.lon = r.getString(2);
}
}
}
}
return cent;
}
public static class Centroid {
public String lon = "0.0";
public String lat = "0.0";
}
/**
* @author Wes Lloyd
* @param slat
* @param slon
* @param workspacedirectory
* @return
* @throws SQLException
* @throws ServiceException
* @throws IOException Synchronized in an attempt to reduce random shell
* failures...
*/
public static synchronized double getCFactorRaster(String slat, String slon, File workspacedirectory, SessionLogger logger) throws SQLException, ServiceException, IOException {
double c_fact = Const.UNKNOWN_CFACTOR;
//File tiffFile = new File("/tmp/us_cvalues_topo2ras_masked.tif");
String gdal = Config.getString("gdal.deployment", RSE_DEPLOYMENT_LINUX);
String gdalwinpath = Config.getString("gdalwinpath", "C:\\Program Files\\GDAL\\");
String gdalbinpath = Config.getString("gdalbinpath", gdalwinpath);
String tifptr = "";
if (gdal.equals(Const.RSE_DEPLOYMENT_WINDOWS)) {
// Extract GDAL Library for Windows
//
Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tfw", gdalwinpath + "us_cvalues_topo2ras_masked.tfw").toString();
tifptr = Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tif", gdalwinpath + "us_cvalues_topo2ras_masked.tif").toString();
Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tif.aux.xml", gdalwinpath + "us_cvalues_topo2ras_masked.tif.aux.xml").toString();
logger.info("geotiff=" + tifptr);
// GDAL required DLLs & EXE
Binaries.unpackResourceAbsolute("/gdallocationinfo.exe", gdalwinpath + "gdallocationinfo.exe").toString();
Binaries.unpackResourceAbsolute("/gdal111.dll", gdalwinpath + "gdal111.dll").toString();
Binaries.unpackResourceAbsolute("/geos.dll", gdalwinpath + "geos.dll").toString();
Binaries.unpackResourceAbsolute("/geos_c.dll", gdalwinpath + "geos_c.dll").toString();
Binaries.unpackResourceAbsolute("/iconv.dll", gdalwinpath + "iconv.dll").toString();
Binaries.unpackResourceAbsolute("/libcurl.dll", gdalwinpath + "libcurl.dll").toString();
Binaries.unpackResourceAbsolute("/libeay32.dll", gdalwinpath + "libeay32.dll").toString();
Binaries.unpackResourceAbsolute("/libexpat.dll", gdalwinpath + "libexpat.dll").toString();
Binaries.unpackResourceAbsolute("/libmysql.dll", gdalwinpath + "libmysql.dll").toString();
Binaries.unpackResourceAbsolute("/libpq.dll", gdalwinpath + "libpq.dll").toString();
Binaries.unpackResourceAbsolute("/openjp2.dll", gdalwinpath + "openjp2.dll").toString();
Binaries.unpackResourceAbsolute("/proj.dll", gdalwinpath + "proj.dll").toString();
Binaries.unpackResourceAbsolute("/spatialite.dll", gdalwinpath + "spatialite.dll").toString();
Binaries.unpackResourceAbsolute("/ssleay32.dll", gdalwinpath + "ssleay32.dll").toString();
Binaries.unpackResourceAbsolute("/xerces-c_2_8.dll", gdalwinpath + "xerces-c_2_8.dll").toString();
Binaries.unpackResourceAbsolute("/zlib1.dll", gdalwinpath + "zlib1.dll").toString();
// Microsoft Visual C++ Redistributable Package 2010 - Service Pack 1 DLLs
Binaries.unpackResourceAbsolute("/atl100.dll", gdalwinpath + "atl100.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100chs.dll", gdalwinpath + "mfc100chs.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100cht.dll", gdalwinpath + "mfc100cht.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100deu.dll", gdalwinpath + "mfc100deu.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100.dll", gdalwinpath + "mfc100.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100enu.dll", gdalwinpath + "mfc100enu.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100esn.dll", gdalwinpath + "mfc100esn.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100fra.dll", gdalwinpath + "mfc100fra.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100ita.dll", gdalwinpath + "mfc100ita.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100jpn.dll", gdalwinpath + "mfc100jpn.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100kor.dll", gdalwinpath + "mfc100kor.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100rus.dll", gdalwinpath + "mfc100rus.dll").toString();
Binaries.unpackResourceAbsolute("/mfc100u.dll", gdalwinpath + "mfc100u.dll").toString();
Binaries.unpackResourceAbsolute("/mfcm100.dll", gdalwinpath + "mfcm100.dll").toString();
Binaries.unpackResourceAbsolute("/mfcm100u.dll", gdalwinpath + "mfcm100u.dll").toString();
Binaries.unpackResourceAbsolute("/msvcp100.dll", gdalwinpath + "msvcp100.dll").toString();
Binaries.unpackResourceAbsolute("/msvcr100.dll", gdalwinpath + "msvcr100.dll").toString();
Binaries.unpackResourceAbsolute("/propsys.dll", gdalwinpath + "propsys.dll").toString();
Binaries.unpackResourceAbsolute("/vcomp100.dll", gdalwinpath + "vcomp100.dll").toString();
} else {
//Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tfw", workspacedirectory + "/us_cvalues_topo2ras_masked.tfw").toString();
tifptr = workspacedirectory + "/us_cvalues_topo2ras_masked.tif"; //Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tif", workspacedirectory + "/us_cvalues_topo2ras_masked.tif").toString();
//Binaries.unpackResourceAbsolute("/us_cvalues_topo2ras_masked.tif.aux.xml", workspacedirectory + "/us_cvalues_topo2ras_masked.tif.aux.xml").toString();
logger.info("geotiff=" + tifptr);
}
// to do
// linux based WEPS assumes gdal is already installed
// optionally we might bundle the GDAL files for Linux here as well
//pc.exe = (gdal.equals(Const.RSE_DEPLOYMENT_WINDOWS) ? gdalwinpath : "") + "gdallocationinfo";
logger.info("gdal is equal to=" + gdal);
Execution e = new Execution(new File(gdalbinpath + "gdallocationinfo"));
e.setArguments(tifptr, slon, slat, "-wgs84");
e.setWorkingDirectory(workspacedirectory);
e.setLogger(logger);
StringWriter stdout = new StringWriter();
StringWriter stderr = new StringWriter();
e.redirectOutput(stdout);
e.redirectError(stderr);
int res = e.exec();
logger.info("RET=" + res);
logger.info("STDOUT=" + stdout.toString());
logger.info("STDERR=" + stderr.toString());
String output[] = stdout.toString().split("\\r?\\n");
// pc.exe = (gdal.equalsIgnoreCase(Const.RSE_DEPLOYMENT_WINDOWS) ? gdalwinpath : "") + "gdallocationinfo";
// logger.info(" the pc.exe is=" + pc.exe);
// pc.args = new String[]{tifptr, slon, slat, "-wgs84"};
// File workDir = workspacedirectory;
// String sessionWorkDir = workDir.toString();
// pc.working_dir = sessionWorkDir;
// pc.execute();
// logger.info("STDOUT=" + pc.stdout);
// logger.info("STDERR=" + pc.stderr);
// logger.info("working dir=" + pc.working_dir);
// logger.info("exe=" + pc.exe);
// String output[] = pc.stdout.split("\\r?\\n");
// cvalue appears on last line of output
//for (int i=0; i<output.length; i++)
// logger.info("i=" +i + " str=" + output[i]);
if (res == 0 && output.length > 0) {
String val[];
val = output[output.length - 1].split(" ");
//for (int i=0; i<val.length; i++)
// logger.info("i=" +i + " str=" + val[i]);
// c value is in the last position (col) of the last line
if ((null != val) && (val.length > 0) && !val[val.length - 1].isEmpty()) {
c_fact = Double.parseDouble(val[val.length - 1]);
} else {
logger.severe("gdallocationinfo returned no parseable output. Cannot proceed. ");
throw new csip.ServiceException("Cannot proceed with cfactor calculation; gdallocationinfo returned no parseable output: " + stdout.toString() + " err " + stderr.toString());
}
} else {
logger.severe("gdallocationinfo returned no output on stdout. Cannot proceed. ");
throw new csip.ServiceException("Cannot proceed with cfactor calculation: " + stderr.toString());
}
return c_fact;
}
}