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