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.filter;
018:
019: import java.io.IOException;
020: import java.util.logging.Logger;
021:
022: import com.vividsolutions.jts.geom.Envelope;
023: import com.vividsolutions.jts.geom.Geometry;
024:
025: import org.geotools.data.DataSourceException;
026: import org.geotools.data.jdbc.FilterToSQL;
027: import org.opengis.filter.ExcludeFilter;
028: import org.opengis.filter.FilterVisitor;
029: import org.opengis.filter.Id;
030: import org.opengis.filter.IncludeFilter;
031: import org.opengis.filter.PropertyIsBetween;
032: import org.opengis.filter.PropertyIsLike;
033: import org.opengis.filter.PropertyIsNull;
034: import org.opengis.filter.expression.Expression;
035: import org.opengis.filter.expression.Literal;
036: import org.opengis.filter.expression.PropertyName; // import org.geotools.filter.LiteralExpression;
037: import org.opengis.filter.spatial.BBOX;
038: import org.opengis.filter.spatial.BinarySpatialOperator;
039:
040: import org.geotools.filter.FilterCapabilities;
041: import org.geotools.filter.UnaliasSQLEncoder;
042:
043: /**
044: * Encodes a filter into a SQL WHERE statement for generic SQL. This class adds
045: * the ability to turn geometry filters into sql statements if they are based on
046: * x,y (longitude/latitude) column pairs..
047: *
048: * @author Rob Atkinson , SCO
049: * @author Debasish Sahu, debasish.sahu@rmsi.com
050: *
051: * @source $URL:
052: * http://svn.geotools.org/geotools/trunk/gt/modules/unsupported/geometryless/src/main/java/org/geotools/data/geometryless/filter/SQLEncoderLocationsXY.java $
053: */
054: public class SQLEncoderLocationsXY extends UnaliasSQLEncoder {
055: /** Standard java logger */
056: private static Logger LOGGER = org.geotools.util.logging.Logging
057: .getLogger("org.geotools.filter");
058:
059: /**
060: * The srid of the schema, so the bbox conforms. Could be better to have it
061: * in the bbox filter itself, but this works for now.
062: */
063: private int srid;
064:
065: // names of sql addressable columns containing numerical coordinates
066: private String xcolumn = null;
067:
068: private String ycolumn = null;
069:
070: /** The geometry attribute to use if none is specified. */
071: private String defaultGeom;
072:
073: /**
074: * Empty constructor TODO: rethink empty constructor, as BBOXes _need_ an
075: * SRID, must make client set it somehow. Maybe detect when encode is
076: * called?
077: */
078: public SQLEncoderLocationsXY(String xcolumn, String ycolumn) {
079: capabilities = createFilterCapabilities();
080: this .xcolumn = xcolumn;
081: this .ycolumn = ycolumn;
082:
083: setSqlNameEscape("");
084: }
085:
086: public SQLEncoderLocationsXY(int srid) {
087: this .srid = srid;
088: }
089:
090: /**
091: * @see org.geotools.filter.SQLEncoder#createFilterCapabilities()
092: */
093: protected FilterCapabilities createFilterCapabilities() {
094: FilterCapabilities capabilities = new FilterCapabilities();
095:
096: capabilities.addAll(FilterCapabilities.LOGICAL_OPENGIS);
097: capabilities
098: .addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS);
099: capabilities.addType(PropertyIsNull.class);
100: capabilities.addType(PropertyIsBetween.class);
101: capabilities.addType(Id.class);
102: capabilities.addType(IncludeFilter.class);
103: capabilities.addType(ExcludeFilter.class);
104: capabilities.addType(PropertyIsLike.class);
105:
106: capabilities.addType(BBOX.class);
107:
108: return capabilities;
109: }
110:
111: /**
112: * Sets a spatial reference system ESPG number, so that the geometry can be
113: * properly encoded for postgis. If geotools starts actually creating
114: * geometries with valid srids then this method will no longer be needed.
115: *
116: * @param srid
117: * the integer code for the EPSG spatial reference system.
118: */
119: public void setSRID(int srid) {
120: this .srid = srid;
121: }
122:
123: /**
124: * Sets the default geometry, so that filters with null for one of their
125: * expressions can assume that the default geometry is intended.
126: *
127: * @param name
128: * the name of the default geometry Attribute.
129: *
130: * @task REVISIT: pass in a featureType so that geometries can figure out
131: * their own default geometry?
132: */
133: public void setDefaultGeometry(String name) {
134: // Do we really want clients to be using malformed filters?
135: // I mean, this is a useful method for unit tests, but shouldn't
136: // fully formed filters usually be used? Though I guess adding
137: // the option wouldn't hurt. -ch
138: this .defaultGeom = name;
139: }
140:
141: /**
142: * Turns a geometry filter into the postgis sql bbox statement.
143: *
144: * @param filter
145: * the geometry filter to be encoded.
146: *
147: * @throws RuntimeException
148: * for IO exception (need a better error)
149: */
150: public Object visitBinarySpatialOperator(
151: BinarySpatialOperator filter, Object extraData)
152: throws RuntimeException {
153:
154: if (filter instanceof BBOX) {
155: Expression left = (Expression) filter.getExpression1();
156: Expression right = (Expression) filter.getExpression2();
157:
158: PropertyName propertyExpr;
159: Literal geomLiteralExpr;
160:
161: // left and right have to be valid expressions
162: try {
163: if (left instanceof PropertyName
164: && right instanceof Literal) {
165: propertyExpr = (PropertyName) left;
166: geomLiteralExpr = (Literal) right;
167: } else if (right instanceof PropertyName
168: && left instanceof Literal) {
169: propertyExpr = (PropertyName) right;
170: geomLiteralExpr = (Literal) left;
171: } else {
172: String err = "LocationsXY currently supports one geometry and one "
173: + "attribute expr. You gave: "
174: + left
175: + ", " + right;
176: throw new DataSourceException(err);
177: }
178:
179: visitLiteralGeometry(geomLiteralExpr);
180:
181: } catch (java.io.IOException ioe) {
182: LOGGER.warning("Unable to export filter" + ioe);
183: }
184: } else {
185: LOGGER
186: .warning("exporting unknown filter type, only bbox supported");
187: throw new RuntimeException(
188: "Only BBox is currently supported");
189: }
190:
191: return extraData;
192: }
193:
194: /**
195: * Checks to see if the literal is a geometry, and encodes it if it is, if
196: * not just sends to the parent class.
197: *
198: * @param expression
199: * the expression to visit and encode.
200: *
201: * @throws IOException
202: * for IO exception (need a better error)
203: */
204: public void visitLiteralGeometry(Literal expression)
205: throws IOException {
206: Geometry bbox = (Geometry) expression.getValue();
207: Envelope e = bbox.getEnvelopeInternal();
208: double x1 = e.getMinX();
209: double x2 = e.getMaxX();
210: double y1 = e.getMinY();
211: double y2 = e.getMaxY();
212: out.write("( " + xcolumn + " < " + x2 + " and " + xcolumn
213: + " > " + x1 + " and " + ycolumn + " < " + y2 + " and "
214: + ycolumn + " > " + y1 + " )");
215: }
216: }
|