
org.jgrasstools.dbs.compat.ASpatialDb Maven / Gradle / Ivy
/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.jgrasstools.dbs.compat;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.jgrasstools.dbs.spatialite.QueryResult;
import org.jgrasstools.dbs.spatialite.RasterCoverage;
import org.jgrasstools.dbs.spatialite.SpatialiteGeometryColumns;
import org.jgrasstools.dbs.spatialite.SpatialiteGeometryType;
import org.jgrasstools.dbs.spatialite.SpatialiteTableNames;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBReader;
/**
* Abstract spatial db class.
*
* @author Andrea Antonello (www.hydrologis.com)
*
*/
public abstract class ASpatialDb extends ADb implements AutoCloseable {
public static String PK_UID = "PK_UID";
public static String defaultGeomFieldName = "the_geom";
/**
* Open the connection to a database.
*
* Make sure the connection object is created here.
*
* @param dbPath
* the database path. If null
, an in-memory db is
* created.
* @return true
if the database did already exist.
* @throws Exception
*/
public abstract boolean open( String dbPath ) throws Exception;
/**
* Create Spatial Metadata initialize SPATIAL_REF_SYS and GEOMETRY_COLUMNS.
*
*
* If the optional argument mode is not specified then any possible ESPG
* SRID definition will be inserted into the spatial_ref_sys table.
*
*
* If the mode arg 'WGS84' (alias 'WGS84_ONLY') is specified, then only
* WGS84-related EPSG SRIDs will be inserted
*
*
* If the mode arg 'NONE' (alias 'EMPTY') is specified, no EPSG SRID will be
* inserted at all
*
*
* @param options
* optional tweaks.
* @throws Exception
*/
public abstract void initSpatialMetadata( String options ) throws Exception;
/**
* Adds a geometry column to a table.
*
* @param tableName
* the table name.
* @param geomColName
* the geometry column name.
* @param geomType
* the geometry type (ex. LINESTRING);
* @param epsg
* the optional epsg code (default is 4326);
* @param avoidIndex if true
, the index is not created.
* @throws Exception
*/
public void addGeometryXYColumnAndIndex( String tableName, String geomColName, String geomType, String epsg,
boolean avoidIndex ) throws Exception {
String epsgStr = "4326";
if (epsg != null) {
epsgStr = epsg;
}
String geomTypeStr = "LINESTRING";
if (geomType != null) {
geomTypeStr = geomType;
}
if (geomColName == null) {
geomColName = defaultGeomFieldName;
}
try (IJGTStatement stmt = mConn.createStatement()) {
String sql = "SELECT AddGeometryColumn('" + tableName + "','" + geomColName + "', " + epsgStr + ", '" + geomTypeStr
+ "', 'XY')";
stmt.execute(sql);
if (!avoidIndex) {
sql = "SELECT CreateSpatialIndex('" + tableName + "', '" + geomColName + "');";
stmt.execute(sql);
}
}
}
/**
* Adds a geometry column to a table.
*
* @param tableName
* the table name.
* @param geomColName
* the geometry column name.
* @param geomType
* the geometry type (ex. LINESTRING);
* @param epsg
* the optional epsg code (default is 4326);
* @throws Exception
*/
public void addGeometryXYColumnAndIndex( String tableName, String geomColName, String geomType, String epsg )
throws Exception {
addGeometryXYColumnAndIndex(tableName, geomColName, geomType, epsg, false);
}
/**
* Insert a geometry into a table.
*
* @param tableName
* the table to use.
* @param geometry
* the geometry to insert.
* @param epsg
* the optional epsg.
* @throws Exception
*/
public void insertGeometry( String tableName, Geometry geometry, String epsg ) throws Exception {
String epsgStr = "4326";
if (epsg == null) {
epsgStr = epsg;
}
SpatialiteGeometryColumns gc = getGeometryColumnsForTable(tableName);
String sql = "INSERT INTO " + tableName + " (" + gc.f_geometry_column + ") VALUES (GeomFromText(?, " + epsgStr + "))";
try (IJGTPreparedStatement pStmt = mConn.prepareStatement(sql)) {
pStmt.setString(1, geometry.toText());
pStmt.executeUpdate();
}
}
/**
* Get the list of available raster coverages.
*
* @param doOrder
* if true
, the names are ordered.
* @return the list of raster coverages.
* @throws Exception
*/
public List getRasterCoverages( boolean doOrder ) throws Exception {
List rasterCoverages = new ArrayList();
String orderBy = " ORDER BY name";
if (!doOrder) {
orderBy = "";
}
String sql = "SELECT " + RasterCoverage.COVERAGE_NAME + ", " + RasterCoverage.TITLE + ", " + RasterCoverage.SRID + ", "
+ RasterCoverage.COMPRESSION + ", " + RasterCoverage.EXTENT_MINX + ", " + RasterCoverage.EXTENT_MINY + ", "
+ RasterCoverage.EXTENT_MAXX + ", " + RasterCoverage.EXTENT_MAXY + " FROM " + RasterCoverage.TABLENAME + orderBy;
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
while( rs.next() ) {
RasterCoverage rc = new RasterCoverage();
int i = 1;
rc.coverage_name = rs.getString(i++);
rc.title = rs.getString(i++);
rc.srid = rs.getInt(i++);
rc.compression = rs.getString(i++);
rc.extent_minx = rs.getDouble(i++);
rc.extent_miny = rs.getDouble(i++);
rc.extent_maxx = rs.getDouble(i++);
rc.extent_maxy = rs.getDouble(i++);
rasterCoverages.add(rc);
}
return rasterCoverages;
}
}
/**
* Get the list of available tables, mapped by type.
*
*
* Supported types are:
*
* - {@value SpatialiteTableNames#INTERNALDATA}
* - {@value SpatialiteTableNames#METADATA}
* - {@value SpatialiteTableNames#SPATIALINDEX}
* - {@value SpatialiteTableNames#STYLE}
* - {@value SpatialiteTableNames#USERDATA}
*
*
*
*
*
* @param doOrder
* @return the map of tables sorted by aggregated type:
* @throws Exception
*/
public HashMap> getTablesMap( boolean doOrder ) throws Exception {
List tableNames = getTables(doOrder);
HashMap> tablesMap = SpatialiteTableNames.getTablesSorted(tableNames, doOrder);
return tablesMap;
}
/**
* Get the geometry column definition for a given table.
*
* @param tableName
* the table to check.
* @return the {@link SpatialiteGeometryColumns column info}.
* @throws Exception
*/
public SpatialiteGeometryColumns getGeometryColumnsForTable( String tableName ) throws Exception {
String attachedStr = "";
if (tableName.indexOf('.') != -1) {
// if the tablename contains a dot, then it comes from an attached
// database
// get the database name
String[] split = tableName.split("\\.");
attachedStr = split[0] + ".";
tableName = split[1];
// logger.debug(MessageFormat.format("Considering attached database:
// {0}", attachedStr));
}
String sql = "select " + SpatialiteGeometryColumns.F_TABLE_NAME + ", " //
+ SpatialiteGeometryColumns.F_GEOMETRY_COLUMN + ", " //
+ SpatialiteGeometryColumns.GEOMETRY_TYPE + "," //
+ SpatialiteGeometryColumns.COORD_DIMENSION + ", " //
+ SpatialiteGeometryColumns.SRID + ", " //
+ SpatialiteGeometryColumns.SPATIAL_INDEX_ENABLED + " from " //
+ attachedStr + SpatialiteGeometryColumns.TABLENAME + " where " + SpatialiteGeometryColumns.F_TABLE_NAME + "='"
+ tableName + "'";
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
if (rs.next()) {
SpatialiteGeometryColumns gc = new SpatialiteGeometryColumns();
gc.f_table_name = rs.getString(1);
gc.f_geometry_column = rs.getString(2);
gc.geometry_type = rs.getInt(3);
gc.coord_dimension = rs.getInt(4);
gc.srid = rs.getInt(5);
gc.spatial_index_enabled = rs.getInt(6);
return gc;
}
return null;
}
}
/**
* Checks if a table is spatial.
*
* @param tableName
* the table to check.
* @return true
if a geometry column is present.
* @throws Exception
*/
public boolean isTableSpatial( String tableName ) throws Exception {
SpatialiteGeometryColumns geometryColumns = getGeometryColumnsForTable(tableName);
return geometryColumns != null;
}
/**
* Get the table records map with geometry in the given envelope.
*
*
* If the table is not geometric, the geom is set to null.
*
* @param tableName
* the table name.
* @param envelope
* the envelope to check.
* @param limit
* if > 0 a limit is set.
* @param alsoPK_UID
* if true
, also the PK_UID column is considered.
* @return the list of found records.
* @throws SQLException
* @throws ParseException
*/
public QueryResult getTableRecordsMapIn( String tableName, Envelope envelope, boolean alsoPK_UID, int limit,
int reprojectSrid ) throws Exception {
QueryResult queryResult = new QueryResult();
SpatialiteGeometryColumns gCol = null;
try {
gCol = getGeometryColumnsForTable(tableName);
} catch (Exception e) {
// ignore
}
boolean hasGeom = gCol != null;
List tableColumnsInfo = getTableColumns(tableName);
List tableColumns = new ArrayList<>();
for( String[] info : tableColumnsInfo ) {
tableColumns.add(info[0]);
}
if (hasGeom) {
tableColumns.remove(gCol.f_geometry_column);
}
if (!alsoPK_UID)
tableColumns.remove(PK_UID);
String sql = "SELECT ";
if (hasGeom) {
if (reprojectSrid == -1 || reprojectSrid == gCol.srid) {
sql += "ST_AsBinary(" + gCol.f_geometry_column + ") AS " + gCol.f_geometry_column;
} else {
sql += "ST_AsBinary(ST_Transform(" + gCol.f_geometry_column + "," + reprojectSrid + ")) AS "
+ gCol.f_geometry_column;
}
}
for( int i = 0; i < tableColumns.size(); i++ ) {
if (hasGeom || i != 0)
sql += ",";
sql += tableColumns.get(i);
}
sql += " FROM " + tableName;
if (envelope != null) {
double x1 = envelope.getMinX();
double y1 = envelope.getMinY();
double x2 = envelope.getMaxX();
double y2 = envelope.getMaxY();
sql += " WHERE "; //
sql += getSpatialindexBBoxWherePiece(tableName, null, x1, y1, x2, y2);
}
if (limit > 0) {
sql += " LIMIT " + limit;
}
WKBReader wkbReader = new WKBReader();
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
IJGTResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for( int i = 1; i <= columnCount; i++ ) {
String columnName = rsmd.getColumnName(i);
queryResult.names.add(columnName);
String columnTypeName = rsmd.getColumnTypeName(i);
queryResult.types.add(columnTypeName);
if (hasGeom && columnName.equals(gCol.f_geometry_column)) {
queryResult.geometryIndex = i - 1;
}
}
while( rs.next() ) {
int i = 1;
Object[] rec = new Object[columnCount];
if (hasGeom) {
byte[] geomBytes = rs.getBytes(i);
Geometry geometry = wkbReader.read(geomBytes);
rec[i - 1] = geometry;
i++;
}
for( int j = i; j <= columnCount; j++ ) {
Object object = rs.getObject(j);
rec[j - 1] = object;
}
queryResult.data.add(rec);
}
return queryResult;
}
}
/**
* Execute a query from raw sql.
*
* @param sql
* the sql to run.
* @param limit
* a limit, ignored if < 1
* @return the resulting records.
* @throws Exception
*/
public QueryResult getTableRecordsMapFromRawSql( String sql, int limit ) throws Exception {
QueryResult queryResult = new QueryResult();
WKBReader wkbReader = new WKBReader();
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
IJGTResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
int geometryIndex = -1;
for( int i = 1; i <= columnCount; i++ ) {
int columnType = rsmd.getColumnType(i);
String columnName = rsmd.getColumnName(i);
queryResult.names.add(columnName);
String columnTypeName = rsmd.getColumnTypeName(i);
queryResult.types.add(columnTypeName);
if (columnTypeName.equals("BLOB") && SpatialiteGeometryType.forValue(columnType) != null) {
geometryIndex = i;
queryResult.geometryIndex = i - 1;
}
}
int count = 0;
while( rs.next() ) {
Object[] rec = new Object[columnCount];
for( int j = 1; j <= columnCount; j++ ) {
if (j == geometryIndex) {
byte[] geomBytes = rs.getBytes(j);
try {
Geometry geometry = wkbReader.read(geomBytes);
rec[j - 1] = geometry;
} catch (Exception e) {
// ignore this, it could be missing ST_AsBinary() in
// the sql
}
} else {
Object object = rs.getObject(j);
rec[j - 1] = object;
}
}
queryResult.data.add(rec);
if (limit > 0 && ++count > (limit - 1)) {
break;
}
}
return queryResult;
}
}
/**
* Execute a query from raw sql and put the result in a csv file.
*
* @param sql
* the sql to run.
* @param csvFile
* the output file.
* @param doHeader
* if true
, the header is written.
* @param separator
* the separator (if null, ";" is used).
* @throws Exception
*/
public void runRawSqlToCsv( String sql, File csvFile, boolean doHeader, String separator ) throws Exception {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(csvFile))) {
WKBReader wkbReader = new WKBReader();
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
IJGTResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
int geometryIndex = -1;
for( int i = 1; i <= columnCount; i++ ) {
if (i > 1) {
bw.write(separator);
}
int columnType = rsmd.getColumnType(i);
String columnTypeName = rsmd.getColumnTypeName(i);
String columnName = rsmd.getColumnName(i);
bw.write(columnName);
if (columnTypeName.equals("BLOB") && SpatialiteGeometryType.forValue(columnType) != null) {
geometryIndex = i;
}
}
bw.write("\n");
while( rs.next() ) {
for( int j = 1; j <= columnCount; j++ ) {
if (j > 1) {
bw.write(separator);
}
byte[] geomBytes = null;
if (j == geometryIndex) {
geomBytes = rs.getBytes(j);
}
if (geomBytes != null) {
try {
Geometry geometry = wkbReader.read(geomBytes);
bw.write(geometry.toText());
} catch (Exception e) {
// write it as it comes
Object object = rs.getObject(j);
if (object != null) {
bw.write(object.toString());
} else {
bw.write("");
}
}
} else {
Object object = rs.getObject(j);
if (object != null) {
bw.write(object.toString());
} else {
bw.write("");
}
}
}
bw.write("\n");
}
}
}
}
/**
* Get the geometries of a table inside a given envelope.
*
* @param tableName
* the table name.
* @param envelope
* the envelope to check.
* @return The list of geometries intersecting the envelope.
* @throws Exception
*/
public List getGeometriesIn( String tableName, Envelope envelope ) throws Exception {
List geoms = new ArrayList();
SpatialiteGeometryColumns gCol = getGeometryColumnsForTable(tableName);
String sql = "SELECT ST_AsBinary(" + gCol.f_geometry_column + ") FROM " + tableName;
if (envelope != null) {
double x1 = envelope.getMinX();
double y1 = envelope.getMinY();
double x2 = envelope.getMaxX();
double y2 = envelope.getMaxY();
sql += " WHERE " + getSpatialindexBBoxWherePiece(tableName, null, x1, y1, x2, y2);
}
WKBReader wkbReader = new WKBReader();
try (IJGTStatement stmt = mConn.createStatement(); IJGTResultSet rs = stmt.executeQuery(sql)) {
while( rs.next() ) {
byte[] geomBytes = rs.getBytes(1);
Geometry geometry = wkbReader.read(geomBytes);
geoms.add(geometry);
}
return geoms;
}
}
/**
* Get the where cause of a Spatialindex based BBOX query.
*
* @param tableName
* the name of the table.
* @param x1
* west bound.
* @param y1
* south bound.
* @param x2
* east bound.
* @param y2
* north bound.
* @return the sql piece.
* @throws Exception
*/
public String getSpatialindexBBoxWherePiece( String tableName, String alias, double x1, double y1, double x2, double y2 )
throws Exception {
String rowid = "";
if (alias == null) {
alias = "";
rowid = tableName + ".ROWID";
} else {
rowid = alias + ".ROWID";
alias = alias + ".";
}
SpatialiteGeometryColumns gCol = getGeometryColumnsForTable(tableName);
if (tableName.indexOf('.') != -1) {
// if the tablename contains a dot, then it comes from an attached
// database
tableName = "DB=" + tableName;
}
String sql = "ST_Intersects(" + alias + gCol.f_geometry_column + ", BuildMbr(" + x1 + ", " + y1 + ", " + x2 + ", " + y2
+ ")) = 1 AND " + rowid + " IN ( SELECT ROWID FROM SpatialIndex WHERE "//
+ "f_table_name = '" + tableName + "' AND " //
+ "search_frame = BuildMbr(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + "))";
return sql;
}
/**
* Get the where query piece based on a geometry intersection.
*
* @param tableName
* the table to query.
* @param alias
* optinal alias.
* @param geometry
* the geometry to intersect.
* @return the query piece.
* @throws Exception
*/
public String getSpatialindexGeometryWherePiece( String tableName, String alias, Geometry geometry ) throws Exception {
String rowid = "";
if (alias == null) {
alias = "";
rowid = tableName + ".ROWID";
} else {
rowid = alias + ".ROWID";
alias = alias + ".";
}
Envelope envelope = geometry.getEnvelopeInternal();
double x1 = envelope.getMinX();
double x2 = envelope.getMaxX();
double y1 = envelope.getMinY();
double y2 = envelope.getMaxY();
SpatialiteGeometryColumns gCol = getGeometryColumnsForTable(tableName);
if (tableName.indexOf('.') != -1) {
// if the tablename contains a dot, then it comes from an attached
// database
tableName = "DB=" + tableName;
}
String sql = "ST_Intersects(" + alias + gCol.f_geometry_column + ", " + "GeomFromText('" + geometry.toText() + "')"
+ ") = 1 AND " + rowid + " IN ( SELECT ROWID FROM SpatialIndex WHERE "//
+ "f_table_name = '" + tableName + "' AND " //
+ "search_frame = BuildMbr(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + "))";
return sql;
}
/**
* Get the bounds of a table.
*
* @param tableName
* the table to query.
* @return the {@link Envelope} of the table.
* @throws Exception
*/
public abstract Envelope getTableBounds( String tableName ) throws Exception;
protected abstract void logWarn( String message );
protected abstract void logInfo( String message );
protected abstract void logDebug( String message );
}