/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.filter.spatial; import org.geotools.filter.GeometryFilterImpl; import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Literal; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.prep.PreparedGeometry; import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; /** * A base class for GeometryFilters that will use PreparedGeometries when the Expression * is a Literal Expression. * * <p> * This class determines when the expressions are set which expressions are literals. The protected * field {@link #literals} contains the {@link Literals} enumerated value that indicates if: * </p> * <ul> * <li><strong>NEITHER</strong> ({@link Literals#NEITHER) of the expressions are literal and is a JTS Geometry (also non-null)</li> * <li><strong>BOTH</strong> ({@link Literals#BOTH}) expressions are literals and is a JTS Geometry (also non-null)</li> * <li>the <strong>LEFT</strong> ({@link Literals#LEFT}) expression (Expression1) is a literal and is a JTS Geometry (also non-null)</li> * <li>or the <strong>RIGHT</strong> ({@link Literals#RIGHT}) expression (Expression2) is a literal and is a JTS Geometry (also non-null)</li> * </ul> * <p> * If <strong>BOTH</strong> of the expressions are literals then a cached value is generated by calling {@link #basicEvaluate(Geometry, Geometry)}. * </p> * <p> * The method {@link #basicEvaluate(Geometry, Geometry)} is required to be implemented so that a cached value can be generated in the case * that both expressions are literals * </p> * <hr/><br> * Example usage for intersects filter: * <pre><code> public boolean evaluate(Object feature) { if (feature instanceof SimpleFeature && !validate((SimpleFeature) feature)) { // we could not obtain a geometry for both left and right hand sides // so default to false return false; } Geometry left; Geometry right; switch (literals) { case BOTH: return cacheValue; case RIGHT: { return rightPreppedGeom.intersects(getLeftGeometry(feature)); } case LEFT: { return leftPreppedGeom.intersects(getRightGeometry(feature)); } default: { left = getLeftGeometry(feature); right = getRightGeometry(feature); return basicEvaluate(left, right); } } } protected final boolean basicEvaluate(Geometry left, Geometry right) { Envelope envLeft = left.getEnvelopeInternal(); Envelope envRight = right.getEnvelopeInternal(); return envRight.intersects(envLeft) && left.intersects(right); } </code></pre> * </p> * * @author jesse * * * @source $URL$ */ public abstract class AbstractPreparedGeometryFilter extends GeometryFilterImpl { /** * Constant that identifies which expressions are Literal and JTS Geometries * * @author jesse */ protected enum Literals { /** * Neither Expression are {@link Literal}s */ NEITHER, /** * Expression2 the "right" geometry is a {@link Literal} */ RIGHT, /** * Expression1 the "left" geometry is a {@link Literal} */ LEFT, /** * Both expressions are {@link Literal} expressions */ BOTH; private static Literals calculate(Expression expression1, Expression expression2) { boolean left = expression1 instanceof Literal && ((Literal)expression1).getValue() instanceof Geometry; boolean right = expression2 instanceof Literal && ((Literal)expression2).getValue() instanceof Geometry; if (left && right) { return BOTH; } if (left) { return LEFT; } if (right) { return RIGHT; } return NEITHER; } } private final PreparedGeometryFactory pGeomFac; /** * Indicates which expressions are {@link Literal}s */ protected Literals literals; /** * The PreparedGeometry for the left Geometry. Null if the left geometry is not a * {@link Literal} */ protected PreparedGeometry leftPreppedGeom; /** * The PreparedGeometry for the right Geometry. Null if the right geometry is not a * {@link Literal} */ protected PreparedGeometry rightPreppedGeom; /** * If both expressions are literals the value will never change. In that * case this field is that calculated value. It is false otherwise. */ protected boolean cacheValue; protected AbstractPreparedGeometryFilter(FilterFactory factory, Expression e1, Expression e2) { super(factory, e1, e2); pGeomFac = new PreparedGeometryFactory(); if( e1!=null ) setExpression1(e1); if( e2!=null ) setExpression2(e2); } private void prepare() { if( expression1==null || expression2==null ){ // filter not yet fully configured so wait return; } literals = Literals.calculate(expression1, expression2); switch (literals) { case BOTH: { Geometry left = (Geometry) ((Literal) expression1).getValue(); Geometry right = (Geometry) ((Literal) expression2).getValue(); cacheValue = basicEvaluate(left, right); leftPreppedGeom = rightPreppedGeom = null; break; } case LEFT: { Geometry left = (Geometry) ((Literal) expression1).getValue(); leftPreppedGeom = pGeomFac.create(left); rightPreppedGeom = null; cacheValue = false; break; } case RIGHT: { Geometry right = (Geometry) ((Literal) expression2).getValue(); rightPreppedGeom = pGeomFac.create(right); leftPreppedGeom = null; cacheValue = false; break; } default: { leftPreppedGeom = rightPreppedGeom = null; cacheValue = false; } } } @Override public void setExpression1(Expression expression) { super.setExpression1(expression); prepare(); } @Override public void setExpression2(Expression expression) { super.setExpression2(expression); prepare(); } /** * Performs the calculation on the two geometries. This is used to calculate the cached value * in the case that both geometries are Literals. But in practice it is useful to extract this functionality * into its own method. * * @param left the geometry on the left of the equations (the geometry obtained from evaluating Expression1) * @param right the geometry on the right of the equations (the geometry obtained from evaluating Expression2) * @return true if the filter evaluates to true for the two geometries */ protected abstract boolean basicEvaluate(Geometry left, Geometry right); }