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.Map;
023: import java.util.logging.Logger;
024:
025: import javax.sql.DataSource;
026:
027: import org.geotools.data.DataSourceException;
028: import org.geotools.data.FeatureReader;
029: import org.geotools.data.FeatureWriter;
030: import org.geotools.data.Transaction;
031: import org.geotools.data.geometryless.attributeio.PointXYAttributeIO;
032: import org.geotools.data.jdbc.ConnectionPool;
033: import org.geotools.data.jdbc.JDBCFeatureWriter;
034: import org.geotools.data.jdbc.QueryData;
035: import org.geotools.data.jdbc.SQLBuilder;
036: import org.geotools.data.jdbc.attributeio.AttributeIO;
037: import org.geotools.data.sql.BypassSqlFeatureTypeHandler;
038: import org.geotools.data.sql.BypassSqlSQLBuilder;
039: import org.geotools.feature.AttributeType;
040: import org.geotools.feature.AttributeTypeFactory;
041: import org.geotools.filter.UnaliasSQLEncoder;
042: import org.opengis.filter.Filter; // import org.geotools.data.jdbc.FilterToSQL;
043: // import org.geotools.filter.SQLEncoder;
044: import org.geotools.data.geometryless.filter.SQLEncoderLocationsXY;
045:
046: import com.vividsolutions.jts.geom.Point;
047:
048: /**
049: * An implementation of the GeoTools Data Store API for a generic non-spatial
050: * database platform.
051: *
052: * This specialisation uses X,Y (lat/lon) database colums to hold point
053: * geometries
054: *
055: * the constructor is used to pass metadata from datastore to SQLEncoder class
056: *
057: * <br>
058: * Please see {@link org.geotools.data.jdbc.JDBCDataStore class JDBCDataStore}
059: * and {@link org.geotools.data.DataStore interface DataStore} for DataStore
060: * usage details.
061: *
062: * @author Rob Atkinson rob@socialchange.net.au
063: * @source $URL:
064: * http://svn.geotools.org/geotools/trunk/gt/modules/unsupported/geometryless/src/main/java/org/geotools/data/geometryless/LocationsXYDataStore.java $
065: */
066:
067: public class LocationsXYDataStore extends
068: org.geotools.data.geometryless.JDBCDataStore {
069: /** The logger for the mysql module. */
070: private static final Logger LOGGER = org.geotools.util.logging.Logging
071: .getLogger("org.geotools.data.geometryless");
072:
073: private String XCoordColumnName = null;
074:
075: private String YCoordColumnName = null;
076:
077: private String geomName = null;
078:
079: public LocationsXYDataStore(DataSource connectionPool)
080: throws IOException {
081: super (connectionPool);
082: }
083:
084: /**
085: * Constructor for LocationsXYDataStore where the database schema name is
086: * provided.
087: *
088: * @param connectionPool
089: * a {@link org.geotools.data.jdbc.ConnectionPool ConnectionPool}
090: * @param databaseSchemaName
091: * the database schema. Can be null. See the comments for the
092: * parameter schemaPattern in
093: * {@link java.sql.DatabaseMetaData#getTables(String, String, String, String[]) DatabaseMetaData.getTables},
094: * because databaseSchemaName behaves in the same way.
095: * @throws IOException
096: * if the database cannot be properly accessed
097: */
098: public LocationsXYDataStore(DataSource connectionPool,
099: String databaseSchemaName, String namespace, String x,
100: String y, String geomName) throws IOException {
101: // databaseSchemaName can be null
102: super (connectionPool, databaseSchemaName, namespace);
103: this .XCoordColumnName = x;
104: this .YCoordColumnName = y;
105: this .geomName = geomName;
106: }
107:
108: /**
109: * Utility method for getting a FeatureWriter for modifying existing
110: * features, using no feature filtering and auto-committing. Not used for
111: * adding new features.
112: *
113: * @param typeName
114: * the feature type name (the table name)
115: * @return a FeatureWriter for modifying existing features
116: * @throws IOException
117: * if the database cannot be properly accessed
118: */
119: public FeatureWriter getFeatureWriter(String typeName)
120: throws IOException {
121: return getFeatureWriter(typeName, Filter.INCLUDE,
122: Transaction.AUTO_COMMIT);
123: }
124:
125: /**
126: * Utility method for getting a FeatureWriter for adding new features, using
127: * auto-committing. Not used for modifying existing features.
128: *
129: * @param typeName
130: * the feature type name (the table name)
131: * @return a FeatureWriter for adding new features
132: * @throws IOException
133: * if the database cannot be properly accessed
134: */
135: public FeatureWriter getFeatureWriterAppend(String typeName)
136: throws IOException {
137: return getFeatureWriterAppend(typeName, Transaction.AUTO_COMMIT);
138: }
139:
140: /**
141: * Constructs an AttributeType from a row in a ResultSet. The ResultSet
142: * contains the information retrieved by a call to getColumns() on the
143: * DatabaseMetaData object. This information can be used to construct an
144: * Attribute Type.
145: *
146: * <p>
147: * This simply returns the constructed geometry when the column is the first
148: * (X)
149: * </p>
150: *
151: * <p>
152: * Note: Overriding methods must never move the current row pointer in the
153: * result set.
154: * </p>
155: *
156: * @param rs
157: * The ResultSet containing the result of a
158: * DatabaseMetaData.getColumns call.
159: *
160: * @return The AttributeType built from the ResultSet.
161: *
162: * @throws SQLException
163: * If an error occurs processing the ResultSet.
164: * @throws DataSourceException
165: * Provided for overriding classes to wrap exceptions caused by
166: * other operations they may perform to determine additional
167: * types. This will only be thrown by the default implementation
168: * if a type is present that is not present in the
169: * TYPE_MAPPINGS.
170: */
171: protected AttributeType buildAttributeType(ResultSet rs)
172: throws IOException {
173: final int COLUMN_NAME = 4;
174: final int DATA_TYPE = 5;
175: final int TYPE_NAME = 6;
176:
177: try {
178: int dataType = rs.getInt(DATA_TYPE);
179: String colName = rs.getString(COLUMN_NAME);
180: LOGGER.fine("dataType: " + dataType + " "
181: + rs.getString(TYPE_NAME) + " " + colName);
182: Class type = (Class) TYPE_MAPPINGS
183: .get(new Integer(dataType));
184:
185: // This should be improved - first should probably check for
186: // presence of both the x and y columns, only create the geometry
187: // if both are found, instead of just ignoring the y - right now
188: // the y could just not exist. And then if either do not exist
189: // an exception should be thrown.
190: // Also, currently the name of the geometry is hard coded -
191: // do we want it to be user configurable? ch
192: if (colName.equals(XCoordColumnName)) {
193: // do type checking here, during config, not during reading.
194: if (Number.class.isAssignableFrom(type)) {
195: return AttributeTypeFactory.newAttributeType(
196: geomName, Point.class);
197: } else {
198: String excMesg = "Specified X column of " + colName
199: + " of type: " + type
200: + ", can not be used as x point";
201: throw new DataSourceException(excMesg);
202: }
203:
204: } else if (colName.equals(YCoordColumnName)) {
205: if (Number.class.isAssignableFrom(type)) {
206: return null;
207: } else {
208: String excMesg = "Specified X column of " + colName
209: + " of type: " + type
210: + ", can not be used as x point";
211: throw new DataSourceException(excMesg);
212: }
213: } else {
214: return super .buildAttributeType(rs);
215: }
216: } catch (SQLException e) {
217: throw new IOException("SQL exception occurred: "
218: + e.getMessage());
219: }
220: }
221:
222: // public SQLBuilder getSqlBuilder(String typeName) throws IOException {
223: //
224: // SQLEncoderLocationsXY encoder = new
225: // SQLEncoderLocationsXY(XCoordColumnName,YCoordColumnName);
226: // encoder.setFIDMapper(getFIDMapper(typeName));
227: // return new LocationsXYSQLBuilder(encoder, XCoordColumnName,
228: // YCoordColumnName);
229: // }
230:
231: public SQLBuilder getSqlBuilder(String typeName) throws IOException {
232: String xCol = XCoordColumnName;
233: String yCol = YCoordColumnName;
234: BypassSqlFeatureTypeHandler ftHanlder = (BypassSqlFeatureTypeHandler) super .typeHandler;
235: if (ftHanlder.isView(typeName)) {
236: String sqlQeury = ftHanlder.getQuery(typeName);
237: Map aliases = BypassSqlSQLBuilder.parseAliases(sqlQeury);
238: xCol = (String) aliases.get(xCol);
239: yCol = (String) aliases.get(yCol);
240: }
241:
242: SQLEncoderLocationsXY encoder = new SQLEncoderLocationsXY(xCol,
243: yCol);
244: encoder.setFeatureType(ftHanlder.getSchema(typeName));
245: encoder.setFIDMapper(getFIDMapper(typeName));
246:
247: return new LocationsXYSQLBuilder(encoder, geomName,
248: XCoordColumnName, YCoordColumnName, ftHanlder);
249: }
250:
251: /**
252: * @see org.geotools.data.jdbc.JDBCDataStore#getGeometryAttributeIO(org.geotools.feature.AttributeType)
253: */
254: protected AttributeIO getGeometryAttributeIO(AttributeType type,
255: QueryData queryData) {
256: return new PointXYAttributeIO();
257: }
258:
259: protected JDBCFeatureWriter createFeatureWriter(
260: FeatureReader reader, QueryData queryData)
261: throws IOException {
262: LOGGER.fine("returning jdbc feature writer");
263:
264: return new GeometrylessFeatureWriter(reader, queryData);
265: }
266:
267: }
|