/* * Copyright (c) 2013 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.io.jdbc.postgresql; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.function.Supplier; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.WKTReader2; import org.geotools.referencing.CRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.postgis.PGgeometry; import org.postgresql.PGConnection; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import eu.esdihumboldt.hale.common.instance.geometry.DefaultGeometryProperty; import eu.esdihumboldt.hale.common.instance.geometry.impl.CodeDefinition; import eu.esdihumboldt.hale.common.instance.geometry.impl.WKTDefinition; import eu.esdihumboldt.hale.common.schema.geometry.CRSDefinition; import eu.esdihumboldt.hale.common.schema.geometry.GeometryProperty; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.model.constraint.type.GeometryMetadata; import eu.esdihumboldt.hale.common.schema.model.impl.DefaultTypeDefinition; import eu.esdihumboldt.hale.io.jdbc.GeometryAdvisor; import schemacrawler.schema.BaseColumn; import schemacrawler.schema.ColumnDataType; /** * Geometry advisor for PostGIS. * * @author Simon Templer, Dominik Reuter */ public class PostGISGeometries implements GeometryAdvisor<PGConnection> { @Override public boolean isFixedType(ColumnDataType columnType) { /* * Concrete information on geometry type and SRS is not stored in the * column but as metadata in the database. Therefore every column has to * be configured on its own. */ return false; } @Override public Class<? extends Geometry> configureGeometryColumnType(PGConnection connection, BaseColumn<?> column, DefaultTypeDefinition type) { Connection con = (Connection) connection; String columnValueName = column.getParent().getName(); String geometryType = null; try { Statement stmt = con.createStatement(); // Get the srid, dimension and geometry type ResultSet rs = stmt.executeQuery( "SELECT srid,type,coord_dimension FROM geometry_columns WHERE f_table_name = " + "'" + columnValueName + "'"); if (rs.next()) { geometryType = rs.getString("type"); String dimension = rs.getString("coord_dimension"); // Get the epsg code for the srid String srid = rs.getString("srid"); ResultSet r = stmt.executeQuery( "SELECT auth_srid, auth_name, srtext FROM spatial_ref_sys WHERE srid = " + srid); if (r.next()) { // Create Constraint to save the informations GeometryMetadata columnTypeConstraint = new GeometryMetadata( r.getString("auth_srid"), Integer.parseInt(dimension), r.getString("srtext"), r.getString("auth_name")); type.setConstraint(columnTypeConstraint); } } else { // XXX what if no SRID information is present? is that a case // that may still be valid? } } catch (SQLException e) { e.printStackTrace(); } // In this case we have no geometry column information if (geometryType == null) { // use geometry even if no geometry column is present describing it return Geometry.class; } // return the geometryType if (geometryType.equalsIgnoreCase("MultiPolygon")) { return MultiPolygon.class; } else if (geometryType.equalsIgnoreCase("MultiPoint")) { return MultiPoint.class; } else if (geometryType.equalsIgnoreCase("MultiLineString")) { return MultiLineString.class; } // TODO: shouldn't this be LineString instead? else if (geometryType.equalsIgnoreCase("LinearRing")) { return LinearRing.class; } else if (geometryType.equalsIgnoreCase("Point")) { return Point.class; } else if (geometryType.equalsIgnoreCase("Polygon")) { return Polygon.class; } else { return Geometry.class; } } @Override public Object convertGeometry(GeometryProperty<?> geom, TypeDefinition columnType, PGConnection pgconn) throws Exception { PGgeometry pGeometry = null; // Transform from sourceCRS to targetCRS GeometryMetadata columnTypeMetadata = columnType.getConstraint(GeometryMetadata.class); // transform CoordinateReferenceSystem targetCRS = null; String authName = columnTypeMetadata.getAuthName(); if (authName != null && authName.equals("EPSG")) { // PostGIS assumes lon/lat targetCRS = CRS.decode(authName + ":" + columnTypeMetadata.getSrs(), true); } else { String wkt = columnTypeMetadata.getSrsText(); if (wkt != null && !wkt.isEmpty()) { targetCRS = CRS.parseWKT(wkt); } } Geometry targetGeometry; if (targetCRS != null) { MathTransform transform = CRS.findMathTransform(geom.getCRSDefinition().getCRS(), targetCRS); targetGeometry = JTS.transform(geom.getGeometry(), transform); } else { targetGeometry = geom.getGeometry(); } // Convert the jts Geometry to postgis PGgeometry and set the SRSID pGeometry = new PGgeometry(targetGeometry.toText()); try { pGeometry.getGeometry().setSrid(Integer.parseInt(columnTypeMetadata.getSrs())); } catch (Exception e) { // ignore } return pGeometry; } /** * * @see eu.esdihumboldt.hale.io.jdbc.GeometryAdvisor#convertToInstanceGeometry(java.lang.Object, * eu.esdihumboldt.hale.common.schema.model.TypeDefinition, * java.lang.Object, java.util.function.Supplier) */ @Override public GeometryProperty<?> convertToInstanceGeometry(Object geom, TypeDefinition columnType, PGConnection connection, Supplier<CRSDefinition> crsProvider) throws Exception { if (geom instanceof PGgeometry) { PGgeometry pgeom = (PGgeometry) geom; // conversion to JTS via WKT // TODO use better conversion (p4b?) WKTReader2 reader = new WKTReader2(); String value = pgeom.getGeometry().toString(); if (value.startsWith(PGgeometry.SRIDPREFIX) && value.indexOf(';') >= 0) { value = value.substring(value.indexOf(';') + 1); } Geometry jtsGeom = reader.read(value); // determine CRS GeometryMetadata columnTypeMetadata = columnType.getConstraint(GeometryMetadata.class); CRSDefinition crsDef = null; String authName = columnTypeMetadata.getAuthName(); if (authName != null && authName.equals("EPSG")) { // PostGIS assumes lon/lat order crsDef = new CodeDefinition(authName + ":" + columnTypeMetadata.getSrs(), true); } else { String wkt = columnTypeMetadata.getSrsText(); if (wkt != null) { crsDef = new WKTDefinition(wkt, null); } } return new DefaultGeometryProperty<Geometry>(crsDef, jtsGeom); } throw new IllegalArgumentException("Only conversion of PGgeometry supported"); } }