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.caching.util;
017:
018: import com.vividsolutions.jts.geom.Envelope;
019: import com.vividsolutions.jts.geom.Geometry;
020:
021: import org.geotools.filter.FilterFactoryImpl;
022:
023: import org.opengis.filter.And;
024: import org.opengis.filter.ExcludeFilter;
025: import org.opengis.filter.Filter;
026: import org.opengis.filter.FilterVisitor;
027: import org.opengis.filter.Id;
028: import org.opengis.filter.IncludeFilter;
029: import org.opengis.filter.Not;
030: import org.opengis.filter.Or;
031: import org.opengis.filter.PropertyIsBetween;
032: import org.opengis.filter.PropertyIsEqualTo;
033: import org.opengis.filter.PropertyIsGreaterThan;
034: import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
035: import org.opengis.filter.PropertyIsLessThan;
036: import org.opengis.filter.PropertyIsLessThanOrEqualTo;
037: import org.opengis.filter.PropertyIsLike;
038: import org.opengis.filter.PropertyIsNotEqualTo;
039: import org.opengis.filter.PropertyIsNull;
040: import org.opengis.filter.expression.Literal;
041: import org.opengis.filter.spatial.BBOX;
042: import org.opengis.filter.spatial.Beyond;
043: import org.opengis.filter.spatial.BinarySpatialOperator;
044: import org.opengis.filter.spatial.Contains;
045: import org.opengis.filter.spatial.Crosses;
046: import org.opengis.filter.spatial.DWithin;
047: import org.opengis.filter.spatial.Disjoint;
048: import org.opengis.filter.spatial.Equals;
049: import org.opengis.filter.spatial.Intersects;
050: import org.opengis.filter.spatial.Overlaps;
051: import org.opengis.filter.spatial.Touches;
052: import org.opengis.filter.spatial.Within;
053:
054: import java.util.ArrayList;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Stack;
058:
059: /** The purpose of this class is to split any Filter into two filters :
060: * <ol><ul> a SpatialRestriction
061: * <ul> and an OtherAttributesRestriction
062: * <ol>
063: * so we have :
064: * OriginalFilter = SpatialRestriction && OtherAttributeRestriction
065: *
066: * SpatialRestriction may actually be a rough approximation of OtherAttributeRestriction
067: *
068: * @author Christophe Rousson, SoC 2007, CRG-ULAVAL
069: *
070: */
071: public class FilterSplitter implements FilterVisitor {
072: private Stack envelopes = new Stack();
073: private Stack otherRestrictions = new Stack();
074: private String geom = null;
075: private String srs = null;
076:
077: //private Stack notEnvelopes = new Stack() ;
078: public Object visit(ExcludeFilter arg0, Object arg1) {
079: // TODO Auto-generated method stub
080: return null;
081: }
082:
083: public Object visit(IncludeFilter arg0, Object arg1) {
084: // TODO Auto-generated method stub
085: return null;
086: }
087:
088: public Object visit(And f, Object arg1) {
089: int envSize = envelopes.size();
090: int othSize = otherRestrictions.size();
091:
092: for (Iterator it = f.getChildren().iterator(); it.hasNext();) {
093: Filter child = (Filter) it.next();
094: child.accept(this , arg1);
095: }
096:
097: if (envelopes.size() >= (envSize + 2)) {
098: Envelope e = (Envelope) envelopes.pop();
099:
100: for (int i = envelopes.size(); i > envSize; i--) {
101: e = e.intersection((Envelope) envelopes.pop());
102: }
103:
104: envelopes.push(e);
105: }
106:
107: if (otherRestrictions.size() >= (othSize + 2)) {
108: List pops = new ArrayList();
109:
110: for (int i = otherRestrictions.size(); i > othSize; i--) {
111: pops.add((Filter) otherRestrictions.pop());
112: }
113:
114: otherRestrictions.push(new FilterFactoryImpl().and(pops));
115: }
116:
117: return null;
118: }
119:
120: public Object visit(Id f, Object arg1) {
121: otherRestrictions.push(f);
122:
123: return null;
124: }
125:
126: public Object visit(Not f, Object arg1) {
127: otherRestrictions.push(f);
128:
129: return null;
130: }
131:
132: public Object visit(Or f, Object arg1) {
133: int envSize = envelopes.size();
134: int othSize = otherRestrictions.size();
135:
136: for (Iterator it = f.getChildren().iterator(); it.hasNext();) {
137: Filter child = (Filter) it.next();
138: child.accept(this , arg1);
139: }
140:
141: if (envelopes.size() > (envSize + 1)) {
142: Envelope e = (Envelope) envelopes.pop();
143:
144: for (int i = envelopes.size(); i > envSize; i--) {
145: e.expandToInclude((Envelope) envelopes.pop());
146: }
147:
148: envelopes.push(e);
149: } else if (envelopes.size() == (envSize + 1)) {
150: // the trick is we cannot separate this filter in the form of SpatialRestriction && OtherRestriction
151: // so we add this part to OtherRestriction
152: envelopes.pop();
153: }
154:
155: // in all case, we'll need original filter as computed SpatialRestriction is a rough approximation
156: multiplePop(otherRestrictions, othSize);
157: otherRestrictions.push(f);
158:
159: return null;
160: }
161:
162: public Object visit(PropertyIsBetween f, Object arg1) {
163: otherRestrictions.push(f);
164:
165: return null;
166: }
167:
168: public Object visit(PropertyIsEqualTo f, Object arg1) {
169: otherRestrictions.push(f);
170:
171: return null;
172: }
173:
174: public Object visit(PropertyIsNotEqualTo f, Object arg1) {
175: otherRestrictions.push(f);
176:
177: return null;
178: }
179:
180: public Object visit(PropertyIsGreaterThan f, Object arg1) {
181: otherRestrictions.push(f);
182:
183: return null;
184: }
185:
186: public Object visit(PropertyIsGreaterThanOrEqualTo f, Object arg1) {
187: otherRestrictions.push(f);
188:
189: return null;
190: }
191:
192: public Object visit(PropertyIsLessThan f, Object arg1) {
193: otherRestrictions.push(f);
194:
195: return null;
196: }
197:
198: public Object visit(PropertyIsLessThanOrEqualTo f, Object arg1) {
199: otherRestrictions.push(f);
200:
201: return null;
202: }
203:
204: public Object visit(PropertyIsLike f, Object arg1) {
205: otherRestrictions.push(f);
206:
207: return null;
208: }
209:
210: public Object visit(PropertyIsNull f, Object arg1) {
211: otherRestrictions.push(f);
212:
213: return null;
214: }
215:
216: public Object visit(BBOX f, Object arg1) {
217: if (geom == null) {
218: geom = f.getPropertyName();
219: srs = f.getSRS();
220: } else if ((geom != f.getPropertyName()) || (srs != f.getSRS())) {
221: throw new UnsupportedOperationException(
222: "This splitter can not be used against a filter where different BBOX filters refer to different Geometry attributes.");
223: }
224:
225: Envelope e = new Envelope(f.getMinX(), f.getMaxX(),
226: f.getMinY(), f.getMaxY());
227: envelopes.push(e);
228:
229: return null;
230: }
231:
232: public Object visit(Beyond f, Object arg1) {
233: // we don't know how to handle this geometric restriction as a BBox
234: // so we treat this as an attribute filter
235: otherRestrictions.push(f);
236:
237: return null;
238: }
239:
240: public Object visit(Contains f, Object arg1) {
241: // we don't know how to handle this geometric restriction as a BBox
242: // so we treat this as an attribute filter
243: otherRestrictions.push(f);
244:
245: return null;
246: }
247:
248: public Object visit(Crosses f, Object arg1) {
249: // we don't know how to handle this geometric restriction as a BBox
250: // so we treat this as an attribute filter
251: otherRestrictions.push(f);
252:
253: return null;
254: }
255:
256: public Object visit(Disjoint f, Object arg1) {
257: // we don't know how to handle this geometric restriction as a BBox
258: // so we treat this as an attribute filter
259: otherRestrictions.push(f);
260:
261: return null;
262: }
263:
264: public Object visit(DWithin f, Object arg1) {
265: // we don't know how to handle this geometric restriction as a BBox
266: // so we treat this as an attribute filter
267: otherRestrictions.push(f);
268:
269: return null;
270: }
271:
272: public Object visit(Equals f, Object arg1) {
273: // we don't know how to handle this geometric restriction as a BBox
274: // so we treat this as an attribute filter
275: otherRestrictions.push(f);
276:
277: return null;
278: }
279:
280: protected void traverse(BinarySpatialOperator f) {
281: if (f.getExpression1() instanceof Literal) {
282: Literal l = (Literal) f.getExpression1();
283: Geometry g = (Geometry) l.getValue();
284: envelopes.push(g.getEnvelopeInternal());
285: } else if (f.getExpression2() instanceof Literal) {
286: Literal l = (Literal) f.getExpression2();
287: Geometry g = (Geometry) l.getValue();
288: envelopes.push(g.getEnvelopeInternal());
289: }
290:
291: otherRestrictions.push(f);
292: }
293:
294: public Object visit(Intersects f, Object arg1) {
295: traverse(f);
296:
297: return null;
298: }
299:
300: public Object visit(Overlaps f, Object arg1) {
301: traverse(f);
302:
303: return null;
304: }
305:
306: public Object visit(Touches f, Object arg1) {
307: traverse(f);
308:
309: return null;
310: }
311:
312: public Object visit(Within f, Object arg1) {
313: traverse(f);
314:
315: return null;
316: }
317:
318: public Object visitNullFilter(Object arg0) {
319: // TODO Auto-generated method stub
320: return null;
321: }
322:
323: public Envelope getEnvelope() {
324: assert (envelopes.size() < 2);
325:
326: if (envelopes.isEmpty()) {
327: return null;
328: } else {
329: return (Envelope) envelopes.peek();
330: }
331: }
332:
333: public Filter getSpatialRestriction() {
334: Envelope e = getEnvelope();
335:
336: if (e == null) {
337: return Filter.INCLUDE;
338: } else {
339: return new FilterFactoryImpl().bbox(geom, e.getMinX(), e
340: .getMinY(), e.getMaxX(), e.getMaxY(), srs);
341: }
342: }
343:
344: public Filter getOtherRestriction() {
345: if (otherRestrictions.isEmpty()) {
346: return Filter.INCLUDE;
347: } else if (otherRestrictions.size() == 1) {
348: return (Filter) otherRestrictions.peek();
349: } else {
350: return new FilterFactoryImpl().and(otherRestrictions
351: .subList(0, otherRestrictions.size() - 1));
352: }
353: }
354:
355: private void multiplePop(Stack s, int downsize) {
356: for (int i = s.size(); i > downsize; i--) {
357: s.pop();
358: }
359: }
360: }
|