001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package org.geotools.data.geometryless;
018:
019: import java.io.IOException;
020: import java.sql.ResultSet;
021: import java.sql.SQLException;
022: import java.util.logging.Logger;
023:
024: import javax.sql.DataSource;
025:
026: import org.geotools.data.DataSourceException;
027: import org.geotools.data.FeatureReader;
028: import org.geotools.data.FeatureWriter;
029: import org.geotools.data.Transaction;
030: import org.geotools.data.geometryless.attributeio.BBOXAttributeIO;
031: import org.geotools.data.jdbc.ConnectionPool;
032: import org.geotools.data.jdbc.JDBCFeatureWriter;
033: import org.geotools.data.jdbc.QueryData;
034: import org.geotools.data.jdbc.SQLBuilder;
035: import org.geotools.data.jdbc.attributeio.AttributeIO;
036: import org.geotools.feature.AttributeType;
037: import org.geotools.feature.AttributeTypeFactory;
038: import org.opengis.filter.Filter; //import org.geotools.filter.SQLEncoder;
039: import org.geotools.data.geometryless.filter.SQLEncoderBBOX;
040:
041: import com.vividsolutions.jts.geom.Polygon;
042:
043: /**
044: * An implementation of the GeoTools Data Store API for a generic non-spatial database platform.
045: *
046: * This specialisation uses X,Y (lat/lon) database colums to hold Envelope (BBOX) geometries
047: *
048: * the constructor is used to pass metadata from datastore to SQLEncoder class
049: *
050: * <br>
051: * Please see {@link org.geotools.data.jdbc.JDBCDataStore class JDBCDataStore} and
052: * {@link org.geotools.data.DataStore interface DataStore} for DataStore usage details.
053: * @author Rob Atkinson Social Change Online
054: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/geometryless/src/main/java/org/geotools/data/geometryless/BBOXDataStore.java $
055: */
056:
057: public class BBOXDataStore extends
058: org.geotools.data.geometryless.JDBCDataStore {
059: /** The logger for the mysql module. */
060: private static final Logger LOGGER = org.geotools.util.logging.Logging
061: .getLogger("org.geotools.data.geometryless");
062:
063: private String XMinColumnName, YMinColumnName = null;
064: private String XMaxColumnName, YMaxColumnName = null;
065: private String geomName = null;
066:
067: public BBOXDataStore(DataSource connectionPool) throws IOException {
068: super (connectionPool);
069: }
070:
071: /**
072: * Constructor for BBOXDataStore where the database schema name is provided.
073: * @param connectionPool a MySQL {@link org.geotools.data.jdbc.ConnectionPool ConnectionPool}
074: * @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.
075: * @throws IOException if the database cannot be properly accessed
076: */
077: public BBOXDataStore(DataSource connectionPool,
078: String databaseSchemaName, String namespace, String xmin,
079: String ymin, String xmax, String ymax, String geomName)
080: throws IOException {
081: super (connectionPool, databaseSchemaName, namespace);
082: this .XMinColumnName = xmin;
083: this .YMinColumnName = ymin;
084: this .XMaxColumnName = xmax;
085: this .YMaxColumnName = ymax;
086: this .geomName = geomName;
087: }
088:
089: /**
090: * Utility method for getting a FeatureWriter for modifying existing features,
091: * using no feature filtering and auto-committing. Not used for adding new
092: * features.
093: * @param typeName the feature type name (the table name)
094: * @return a FeatureWriter for modifying existing features
095: * @throws IOException if the database cannot be properly accessed
096: */
097: public FeatureWriter getFeatureWriter(String typeName)
098: throws IOException {
099: return getFeatureWriter(typeName, Filter.INCLUDE,
100: Transaction.AUTO_COMMIT);
101: }
102:
103: /**
104: * Utility method for getting a FeatureWriter for adding new features, using
105: * auto-committing. Not used for modifying existing features.
106: * @param typeName the feature type name (the table name)
107: * @return a FeatureWriter for adding new features
108: * @throws IOException if the database cannot be properly accessed
109: */
110: public FeatureWriter getFeatureWriterAppend(String typeName)
111: throws IOException {
112: return getFeatureWriterAppend(typeName, Transaction.AUTO_COMMIT);
113: }
114:
115: /**
116: * Constructs an AttributeType from a row in a ResultSet. The ResultSet
117: * contains the information retrieved by a call to getColumns() on the
118: * DatabaseMetaData object. This information can be used to construct an
119: * Attribute Type.
120: *
121: * <p>
122: * In addition to standard SQL types, this method identifies MySQL 4.1's geometric
123: * datatypes and creates attribute types accordingly. This happens when the
124: * datatype, identified by column 5 of the ResultSet parameter, is equal to
125: * java.sql.Types.OTHER. If a Types.OTHER ends up not being geometric, this
126: * method simply calls the parent class's buildAttributeType method to do something
127: * with it.
128: * </p>
129: *
130: * <p>
131: * Note: Overriding methods must never move the current row pointer in the
132: * result set.
133: * </p>
134: *
135: * @param rs The ResultSet containing the result of a
136: * DatabaseMetaData.getColumns call.
137: *
138: * @return The AttributeType built from the ResultSet.
139: *
140: * @throws SQLException If an error occurs processing the ResultSet.
141: * @throws DataSourceException Provided for overriding classes to wrap
142: * exceptions caused by other operations they may perform to
143: * determine additional types. This will only be thrown by the
144: * default implementation if a type is present that is not present
145: * in the TYPE_MAPPINGS.
146: */
147: protected AttributeType buildAttributeType(ResultSet rs)
148: throws IOException {
149: final int COLUMN_NAME = 4;
150: final int DATA_TYPE = 5;
151: final int TYPE_NAME = 6;
152:
153: try {
154: int dataType = rs.getInt(DATA_TYPE);
155: String colName = rs.getString(COLUMN_NAME);
156: LOGGER.fine("dataType: " + dataType + " "
157: + rs.getString(TYPE_NAME) + " " + colName);
158: Class type = (Class) TYPE_MAPPINGS
159: .get(new Integer(dataType));
160:
161: //This should be improved - first should probably check for
162: //presence of both the x and y columns, only create the geometry
163: //if both are found, instead of just ignoring the y - right now
164: //the y could just not exist. And then if either do not exist
165: //an exception should be thrown.
166: //Also, currently the name of the geometry is hard coded -
167: //do we want it to be user configurable? ch
168: if (colName.equals(XMinColumnName)) {
169: //do type checking here, during config, not during reading.
170: if (Number.class.isAssignableFrom(type)) {
171: return AttributeTypeFactory.newAttributeType(
172: geomName, Polygon.class);
173: } else {
174: String excMesg = "Specified MIN X column of "
175: + colName + " of type: " + type
176: + ", can not be used as BBOX element";
177: throw new DataSourceException(excMesg);
178: }
179:
180: } else if (colName.equals(YMinColumnName)) {
181: if (Number.class.isAssignableFrom(type)) {
182: return null;
183: } else {
184: String excMesg = "Specified Y column of " + colName
185: + " of type: " + type
186: + ", can not be used as as BBOX element";
187: throw new DataSourceException(excMesg);
188: }
189: } else if (colName.equals(XMaxColumnName)) {
190: if (Number.class.isAssignableFrom(type)) {
191: return null;
192: } else {
193: String excMesg = "Specified X column of " + colName
194: + " of type: " + type
195: + ", can not be used as as BBOX element";
196: throw new DataSourceException(excMesg);
197: }
198: } else if (colName.equals(YMaxColumnName)) {
199: if (Number.class.isAssignableFrom(type)) {
200: return null;
201: } else {
202: String excMesg = "Specified Y column of " + colName
203: + " of type: " + type
204: + ", can not be used as as BBOX element";
205: throw new DataSourceException(excMesg);
206: }
207: } else {
208: return super .buildAttributeType(rs);
209: }
210: } catch (SQLException e) {
211: throw new IOException("SQL exception occurred: "
212: + e.getMessage());
213: }
214: }
215:
216: public SQLBuilder getSqlBuilder(String typeName) throws IOException {
217:
218: SQLEncoderBBOX encoder = new SQLEncoderBBOX(XMinColumnName,
219: YMinColumnName, XMaxColumnName, YMaxColumnName);
220: encoder.setFIDMapper(getFIDMapper(typeName));
221: return new BBOXSQLBuilder(encoder, XMinColumnName,
222: YMinColumnName, XMaxColumnName, YMaxColumnName);
223: }
224:
225: /**
226: * @see org.geotools.data.jdbc.JDBCDataStore#getGeometryAttributeIO(org.geotools.feature.AttributeType)
227: */
228: protected AttributeIO getGeometryAttributeIO(AttributeType type,
229: QueryData queryData) {
230: return new BBOXAttributeIO();
231: }
232:
233: protected JDBCFeatureWriter createFeatureWriter(
234: FeatureReader reader, QueryData queryData)
235: throws IOException {
236: LOGGER.fine("returning jdbc feature writer");
237:
238: return new GeometrylessFeatureWriter(reader, queryData);
239: }
240:
241: }
|