001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, 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: package org.geotools.filter;
017:
018: import java.util.logging.Logger;
019:
020: import org.geotools.feature.Feature;
021: import org.opengis.filter.spatial.BBOX;
022: import org.opengis.filter.spatial.Beyond;
023: import org.opengis.filter.spatial.Contains;
024: import org.opengis.filter.spatial.Crosses;
025: import org.opengis.filter.spatial.Disjoint;
026: import org.opengis.filter.spatial.Equals;
027: import org.opengis.filter.spatial.Intersects;
028: import org.opengis.filter.spatial.Overlaps;
029: import org.opengis.filter.spatial.Touches;
030: import org.opengis.filter.spatial.Within;
031:
032: import com.vividsolutions.jts.geom.Geometry;
033:
034: /**
035: * Implements a geometry filter.
036: *
037: * <p>
038: * This filter implements a relationship - of some sort - between two geometry
039: * expressions. Note that this comparison does not attempt to restict its
040: * expressions to be meaningful. This means that it considers itself a valid
041: * filter as long as it contains two <b>geometry</b> sub-expressions. It is
042: * also slightly less restrictive than the OGC Filter specification because
043: * it does not require that one sub-expression be an geometry attribute and
044: * the other be a geometry literal.
045: * </p>
046: *
047: * <p>
048: * In other words, you may use this filter to compare two geometries in the
049: * same feature, such as: attributeA inside attributeB? You may also compare
050: * two literal geometries, although this is fairly meaningless, since it could
051: * be reduced (ie. it is always either true or false). This approach is very
052: * similar to that taken in the FilterCompare class.
053: * </p>
054: *
055: * @author Rob Hranac, TOPP
056: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/filter/GeometryFilterImpl.java $
057: * @version $Id: GeometryFilterImpl.java 27862 2007-11-12 19:51:19Z desruisseaux $
058: *
059: * @task REVISIT: make this class (and all filters) immutable, implement
060: * cloneable and return new filters when calling addLeftGeometry and
061: * addRightG Issues to think through: would be cleaner immutability to
062: * have constructor called with left and right Geometries, but this does
063: * not jive with SAX parsing, which is one of the biggest uses of
064: * filters. But the alternative is not incredibly efficient either, as
065: * there will be two filters that are just thrown away every time we
066: * make a full geometry filter. These issues extend to most filters, as
067: * just about all of them are mutable when creating them. Other issue
068: * is that lots of code will need to be changed for immutability.
069: * (comments by cholmes) - MUTABLE FACTORIES! Sax and immutability.
070: */
071: public abstract class GeometryFilterImpl extends
072: BinaryComparisonAbstract implements GeometryFilter {
073: /** Class logger */
074: private static final Logger LOGGER = org.geotools.util.logging.Logging
075: .getLogger("org.geotools.filter");
076:
077: protected GeometryFilterImpl(FilterFactory factory) {
078: super (factory);
079: }
080:
081: protected GeometryFilterImpl(FilterFactory factory,
082: org.opengis.filter.expression.Expression e1,
083: org.opengis.filter.expression.Expression e2) {
084: super (factory, e1, e2);
085: }
086:
087: /**
088: * Constructor with filter type.
089: *
090: * @param filterType The type of comparison.
091: *
092: * @throws IllegalFilterException Non-geometry type.
093: */
094: protected GeometryFilterImpl(short filterType)
095: throws IllegalFilterException {
096:
097: super (FilterFactoryFinder.createFilterFactory());
098:
099: if (isGeometryFilter(filterType)) {
100: this .filterType = filterType;
101: } else {
102: throw new IllegalFilterException(
103: "Attempted to create geometry "
104: + "filter with non-geometry type.");
105: }
106: }
107:
108: /**
109: * Adds the 'left' value to this filter.
110: *
111: * @param leftGeometry Expression for 'left' value.
112: *
113: * @throws IllegalFilterException Filter is not internally consistent.
114: *
115: * @deprecated use {@link #setExpression1(org.opengis.filter.expression.Expression)}
116: */
117: public final void addLeftGeometry(Expression leftGeometry)
118: throws IllegalFilterException {
119:
120: setExpression1(leftGeometry);
121: }
122:
123: public void setExpression1(
124: org.opengis.filter.expression.Expression expression) {
125: Expression leftGeometry = (Expression) expression;
126:
127: //Checks if this is geometry filter or not and handles appropriately
128: if (DefaultExpression.isGeometryExpression(leftGeometry
129: .getType())
130: || permissiveConstruction) {
131: super .setExpression1(leftGeometry);
132: } else {
133: throw new IllegalFilterException("Attempted to add (left)"
134: + " non-geometry expression"
135: + " to geometry filter.");
136: }
137:
138: }
139:
140: /**
141: * Adds the 'right' value to this filter.
142: *
143: * @param rightGeometry Expression for 'right' value.
144: *
145: * @throws IllegalFilterException Filter is not internally consistent.
146: *
147: * @deprecated use {@link #set}
148: *
149: */
150: public final void addRightGeometry(Expression rightGeometry)
151: throws IllegalFilterException {
152:
153: setExpression2(rightGeometry);
154: }
155:
156: public void setExpression2(
157: org.opengis.filter.expression.Expression expression) {
158: Expression rightGeometry = (Expression) expression;
159:
160: //Checks if this is math filter or not and handles appropriately
161: if (DefaultExpression.isGeometryExpression(rightGeometry
162: .getType())
163: || permissiveConstruction) {
164: super .setExpression2(rightGeometry);
165: } else {
166: throw new IllegalFilterException("Attempted to add (right)"
167: + " non-geometry"
168: + "expression to geometry filter.");
169: }
170: }
171:
172: /**
173: * Retrieves the expression on the left side of the comparison.
174: *
175: * @return the expression on the left.
176: * @deprecated use {@link org.opengis.filter.spatial.BinarySpatialOperator#getExpression1()}
177: */
178: public final Expression getLeftGeometry() {
179: return (Expression) getExpression1();
180: }
181:
182: /**
183: * Retrieves the expression on the right side of the comparison.
184: *
185: * @return the expression on the right.
186: * @deprecated use {@link org.opengis.filter.spatial.BinarySpatialOperator#getExpression2()}
187: */
188: public final Expression getRightGeometry() {
189: return (Expression) getExpression2();
190: }
191:
192: /**
193: * Subclass convenience method for returning left expression as a
194: * JTS geometry.
195: */
196: protected Geometry getLeftGeometry(Object feature) {
197: org.opengis.filter.expression.Expression leftGeometry = getExpression1();
198:
199: if (leftGeometry != null) {
200: Object obj = leftGeometry.evaluate(feature, Geometry.class);
201:
202: //LOGGER.finer("leftGeom = " + o.toString());
203: return (Geometry) obj;
204: } else if (feature instanceof Feature) {
205: return ((Feature) feature).getDefaultGeometry();
206: }
207: return null;
208: }
209:
210: /**
211: * Subclass convenience method for returning right expression as a
212: * JTS geometry.
213: */
214: protected Geometry getRightGeometry(Object feature) {
215: org.opengis.filter.expression.Expression rightGeometry = getExpression2();
216:
217: if (rightGeometry != null) {
218: return (Geometry) rightGeometry.evaluate(feature,
219: Geometry.class);
220: } else if (feature instanceof Feature) {
221: return ((Feature) feature).getDefaultGeometry();
222: }
223: return null;
224: }
225:
226: /**
227: * Subclass convenience method for validating the internals of the
228: */
229: protected boolean validate(Feature feature) {
230:
231: // Checks for error condition
232: Geometry right = getRightGeometry(feature);
233: Geometry left = getLeftGeometry(feature);
234:
235: // default behaviour: if the geometry that is to be filtered is not
236: // there we default to not returning anything
237: if (left == null)
238: return false;
239:
240: return true;
241: }
242:
243: /**
244: * Determines whether or not a given feature is 'inside' this filter.
245: *
246: * @param feature Specified feature to examine.
247: *
248: * @return Flag confirming whether or not this feature is inside filter.
249: */
250: public boolean evaluate(Feature feature) {
251: return evaluate((Object) feature);
252: }
253:
254: /**
255: * Return this filter as a string.
256: *
257: * @return String representation of this geometry filter.
258: */
259: public String toString() {
260: String operator = null;
261:
262: // Handles all normal geometry cases
263: if (filterType == GEOMETRY_EQUALS || this instanceof Equals) {
264: operator = " equals ";
265: } else if (filterType == GEOMETRY_DISJOINT
266: || this instanceof Disjoint) {
267: operator = " disjoint ";
268: } else if (filterType == GEOMETRY_INTERSECTS
269: || this instanceof Intersects) {
270: operator = " intersects ";
271: } else if (filterType == GEOMETRY_CROSSES
272: || this instanceof Crosses) {
273: operator = " crosses ";
274: } else if (filterType == GEOMETRY_WITHIN
275: || this instanceof Within) {
276: operator = " within ";
277: } else if (filterType == GEOMETRY_CONTAINS
278: || this instanceof Contains) {
279: operator = " contains ";
280: } else if (filterType == GEOMETRY_OVERLAPS
281: || this instanceof Overlaps) {
282: operator = " overlaps ";
283: } else if (filterType == GEOMETRY_BEYOND
284: || this instanceof Beyond) {
285: operator = " beyond ";
286: } else if (filterType == GEOMETRY_BBOX || this instanceof BBOX) {
287: operator = " bbox ";
288: } else if (filterType == GEOMETRY_TOUCHES
289: || this instanceof Touches) {
290: operator = " touches ";
291: } else {
292: operator = " UNKNOWN(class:" + this .getClass() + ") ";
293: }
294:
295: org.opengis.filter.expression.Expression leftGeometry = getExpression1();
296: org.opengis.filter.expression.Expression rightGeometry = getExpression2();
297:
298: if ((expression1 == null) && (rightGeometry == null)) {
299: return "[ " + "null" + operator + "null" + " ]";
300: } else if (leftGeometry == null) {
301: return "[ " + "null" + operator + rightGeometry.toString()
302: + " ]";
303: } else if (rightGeometry == null) {
304: return "[ " + leftGeometry.toString() + operator + "null"
305: + " ]";
306: }
307:
308: return "[ " + leftGeometry.toString() + operator
309: + rightGeometry.toString() + " ]";
310: }
311:
312: /**
313: * Compares this filter to the specified object. Returns true if the
314: * passed in object is the same as this filter. Checks to make sure the
315: * filter types are the same as well as the left and right geometries.
316: *
317: * @param obj - the object to compare this GeometryFilter against.
318: *
319: * @return true if specified object is equal to this filter; else false
320: */
321: public boolean equals(Object obj) {
322: if (obj instanceof GeometryFilterImpl) {
323: GeometryFilterImpl geomFilter = (GeometryFilterImpl) obj;
324: boolean isEqual = true;
325:
326: isEqual = geomFilter.getFilterType() == this .filterType;
327: LOGGER.finest("filter type match:" + isEqual + "; in:"
328: + geomFilter.getFilterType() + "; out:"
329: + this .filterType);
330: isEqual = (geomFilter.expression1 != null) ? (isEqual && geomFilter.expression1
331: .equals(this .expression1))
332: : (isEqual && (this .expression1 == null));
333: LOGGER.finest("left geom match:" + isEqual + "; in:"
334: + geomFilter.expression1 + "; out:"
335: + this .expression1);
336: isEqual = (geomFilter.expression2 != null) ? (isEqual && geomFilter.expression2
337: .equals(this .expression2))
338: : (isEqual && (this .expression2 == null));
339: LOGGER.finest("right geom match:" + isEqual + "; in:"
340: + geomFilter.expression2 + "; out:"
341: + this .expression2);
342:
343: return isEqual;
344: } else {
345: return false;
346: }
347: }
348:
349: /**
350: * Override of hashCode method.
351: *
352: * @return a hash code value for this geometry filter.
353: */
354: public int hashCode() {
355: org.opengis.filter.expression.Expression leftGeometry = getExpression1();
356: org.opengis.filter.expression.Expression rightGeometry = getExpression2();
357:
358: int result = 17;
359: result = (37 * result) + filterType;
360: result = (37 * result)
361: + ((leftGeometry == null) ? 0 : leftGeometry.hashCode());
362: result = (37 * result)
363: + ((rightGeometry == null) ? 0 : rightGeometry
364: .hashCode());
365:
366: return result;
367: }
368:
369: }
|