/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.data.geometryless; import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; import java.util.logging.Logger; import javax.sql.DataSource; import org.geotools.data.DataSourceException; import org.geotools.data.FeatureReader; import org.geotools.data.FeatureWriter; import org.geotools.data.Transaction; import org.geotools.data.geometryless.attributeio.BBOXAttributeIO; import org.geotools.data.geometryless.filter.SQLEncoderBBOX; import org.geotools.data.jdbc.JDBCFeatureWriter; import org.geotools.data.jdbc.QueryData; import org.geotools.data.jdbc.SQLBuilder; import org.geotools.data.jdbc.attributeio.AttributeIO; import org.geotools.feature.AttributeTypeBuilder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.filter.Filter; import com.vividsolutions.jts.geom.Polygon; /** * An implementation of the GeoTools Data Store API for a generic non-spatial database platform. * * This specialisation uses X,Y (lat/lon) database colums to hold Envelope (BBOX) geometries * * the constructor is used to pass metadata from datastore to SQLEncoder class * * <br> * Please see {@link org.geotools.data.jdbc.JDBCDataStore class JDBCDataStore} and * {@link org.geotools.data.DataStore interface DataStore} for DataStore usage details. * @author Rob Atkinson Social Change Online * * @source $URL$ */ public class BBOXDataStore extends org.geotools.data.geometryless.JDBCDataStore { /** The logger for the mysql module. */ private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.geometryless"); private String XMinColumnName,YMinColumnName = null; private String XMaxColumnName,YMaxColumnName = null; private String geomName = null; public BBOXDataStore(DataSource connectionPool) throws IOException { super(connectionPool); } /** * Constructor for BBOXDataStore where the database schema name is provided. * @param connectionPool a MySQL {@link org.geotools.data.jdbc.ConnectionPool ConnectionPool} * @param databaseSchemaName the database schema. Can be null. See the comments for the parameter schemaPattern in {@link java.sql.DatabaseMetaData#getTables(String, String, String, String[]) DatabaseMetaData.getTables}, because databaseSchemaName behaves in the same way. * @throws IOException if the database cannot be properly accessed */ public BBOXDataStore(DataSource connectionPool, String databaseSchemaName, String namespace , String xmin, String ymin,String xmax,String ymax , String geomName) throws IOException { super(connectionPool, databaseSchemaName, namespace); this.XMinColumnName = xmin; this.YMinColumnName = ymin; this.XMaxColumnName = xmax; this.YMaxColumnName = ymax; this.geomName = geomName; } /** * Utility method for getting a FeatureWriter for modifying existing features, * using no feature filtering and auto-committing. Not used for adding new * features. * @param typeName the feature type name (the table name) * @return a FeatureWriter for modifying existing features * @throws IOException if the database cannot be properly accessed */ public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName) throws IOException { return getFeatureWriter(typeName, Filter.INCLUDE, Transaction.AUTO_COMMIT); } /** * Utility method for getting a FeatureWriter for adding new features, using * auto-committing. Not used for modifying existing features. * @param typeName the feature type name (the table name) * @return a FeatureWriter for adding new features * @throws IOException if the database cannot be properly accessed */ public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriterAppend(String typeName) throws IOException { return getFeatureWriterAppend(typeName, Transaction.AUTO_COMMIT); } /** * Constructs an AttributeType from a row in a ResultSet. The ResultSet * contains the information retrieved by a call to getColumns() on the * DatabaseMetaData object. This information can be used to construct an * Attribute Type. * * <p> * In addition to standard SQL types, this method identifies MySQL 4.1's geometric * datatypes and creates attribute types accordingly. This happens when the * datatype, identified by column 5 of the ResultSet parameter, is equal to * java.sql.Types.OTHER. If a Types.OTHER ends up not being geometric, this * method simply calls the parent class's buildAttributeType method to do something * with it. * </p> * * <p> * Note: Overriding methods must never move the current row pointer in the * result set. * </p> * * @param rs The ResultSet containing the result of a * DatabaseMetaData.getColumns call. * * @return The AttributeType built from the ResultSet. * * @throws SQLException If an error occurs processing the ResultSet. * @throws DataSourceException Provided for overriding classes to wrap * exceptions caused by other operations they may perform to * determine additional types. This will only be thrown by the * default implementation if a type is present that is not present * in the TYPE_MAPPINGS. */ protected AttributeDescriptor buildAttributeType(ResultSet rs) throws IOException { final int COLUMN_NAME = 4; final int DATA_TYPE = 5; final int TYPE_NAME = 6; try { int dataType = rs.getInt(DATA_TYPE); String colName = rs.getString(COLUMN_NAME); LOGGER.fine("dataType: " + dataType + " " + rs.getString(TYPE_NAME) + " " + colName ); Class type = (Class) TYPE_MAPPINGS.get(new Integer(dataType)); //This should be improved - first should probably check for //presence of both the x and y columns, only create the geometry //if both are found, instead of just ignoring the y - right now //the y could just not exist. And then if either do not exist //an exception should be thrown. //Also, currently the name of the geometry is hard coded - //do we want it to be user configurable? ch if (colName.equals(XMinColumnName)) { //do type checking here, during config, not during reading. if (Number.class.isAssignableFrom(type)) { return new AttributeTypeBuilder().binding(Polygon.class) .buildDescriptor(geomName); } else { String excMesg = "Specified MIN X column of " + colName + " of type: " + type + ", can not be used as BBOX element"; throw new DataSourceException(excMesg); } } else if (colName.equals(YMinColumnName)) { if (Number.class.isAssignableFrom(type)) { return null; } else { String excMesg = "Specified Y column of " + colName + " of type: " + type + ", can not be used as as BBOX element"; throw new DataSourceException(excMesg); } } else if (colName.equals(XMaxColumnName)) { if (Number.class.isAssignableFrom(type)) { return null; } else { String excMesg = "Specified X column of " + colName + " of type: " + type + ", can not be used as as BBOX element"; throw new DataSourceException(excMesg); } }else if (colName.equals(YMaxColumnName)) { if (Number.class.isAssignableFrom(type)) { return null; } else { String excMesg = "Specified Y column of " + colName + " of type: " + type + ", can not be used as as BBOX element"; throw new DataSourceException(excMesg); } } else { return super.buildAttributeType(rs); } } catch (SQLException e) { throw new IOException("SQL exception occurred: " + e.getMessage()); } } public SQLBuilder getSqlBuilder(String typeName) throws IOException { SQLEncoderBBOX encoder = new SQLEncoderBBOX(XMinColumnName,YMinColumnName,XMaxColumnName,YMaxColumnName); encoder.setFIDMapper(getFIDMapper(typeName)); return new BBOXSQLBuilder(encoder, XMinColumnName,YMinColumnName, XMaxColumnName,YMaxColumnName); } /** * @see org.geotools.data.jdbc.JDBCDataStore#getGeometryAttributeIO(org.geotools.feature.AttributeType) */ protected AttributeIO getGeometryAttributeIO(AttributeDescriptor type, QueryData queryData) { return new BBOXAttributeIO(); } protected JDBCFeatureWriter createFeatureWriter(FeatureReader <SimpleFeatureType, SimpleFeature> reader, QueryData queryData) throws IOException { LOGGER.fine("returning jdbc feature writer"); return new GeometrylessFeatureWriter(reader, queryData); } }