/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-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.data.ogr;
import org.geotools.filter.FilterCapabilities;
import org.geotools.filter.visitor.PostPreProcessFilterSplittingVisitor;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.And;
import org.opengis.filter.Filter;
import org.opengis.filter.Or;
import org.opengis.filter.PropertyIsBetween;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import com.vividsolutions.jts.geom.Geometry;
/**
* Helper which translates the GeoTools filters into the filter bits that OGR understands
*
* @author Andrea Aime - GeoSolutions
*/
class OGRFilterTranslator {
static final FilterCapabilities ATTRIBUTE_FILTER_CAPABILITIES;
static final FilterCapabilities GEOMETRY_FILTER_CAPABILITIES;
static final FilterCapabilities STRICT_GEOMETRY_FILTER_CAPABILITIES;
static final FilterCapabilities EXTENDED_FILTER_CAPABILITIES;
static {
// attribute filters, these we can encode fully
ATTRIBUTE_FILTER_CAPABILITIES = new FilterCapabilities();
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsBetween.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsNotEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsGreaterThan.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsLessThan.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsGreaterThanOrEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsLessThanOrEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsLessThanOrEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(Or.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(And.class);
// we cannot encode these full, but have the capabilities extract
// any filter that can use geometric intersection as its base (bbox is the only
// one that we're sure to get support for)
GEOMETRY_FILTER_CAPABILITIES = new FilterCapabilities();
GEOMETRY_FILTER_CAPABILITIES.addType(BBOX.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Contains.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Crosses.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Equals.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Intersects.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Overlaps.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Touches.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Within.class);
// the geometry filters we can encode 1-1
STRICT_GEOMETRY_FILTER_CAPABILITIES = new FilterCapabilities();
STRICT_GEOMETRY_FILTER_CAPABILITIES.addType(BBOX.class);
// the extended caps, which work only assuming there is at most a single bbox filter
// in the filter to be encoded
EXTENDED_FILTER_CAPABILITIES = new FilterCapabilities();
EXTENDED_FILTER_CAPABILITIES.addAll(ATTRIBUTE_FILTER_CAPABILITIES);
EXTENDED_FILTER_CAPABILITIES.addAll(STRICT_GEOMETRY_FILTER_CAPABILITIES);
}
private SimpleFeatureType schema;
private Filter filter;
public OGRFilterTranslator(SimpleFeatureType schema, Filter filter) {
this.schema = schema;
SimplifyingFilterVisitor simplifier = new SimplifyingFilterVisitor();
this.filter = (Filter) filter.accept(simplifier, null);
}
/**
* Returns true if this filter can be fully encoded without requiring further post processing
*
* @return
*/
public boolean isFilterFullySupported() {
if (filter == Filter.INCLUDE || filter == Filter.EXCLUDE) {
return true;
}
// we can encode fully an attribute filter plus a bbox spatial filter
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(
ATTRIBUTE_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter postFilter = visitor.getFilterPost();
return postFilter == Filter.INCLUDE || postFilter instanceof BBOX;
}
/**
* Returns the post filter that could not be encoded
*
* @return
*/
public Filter getPostFilter() {
// see if the query has a single bbox filter (that's how much we're sure to be able to
// encode)
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(
STRICT_GEOMETRY_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter preFilter = visitor.getFilterPre();
if (preFilter == null || preFilter instanceof BBOX) {
// ok, then we can extract using the extended caps
visitor = new PostPreProcessFilterSplittingVisitor(EXTENDED_FILTER_CAPABILITIES,
schema, null);
filter.accept(visitor, null);
return visitor.getFilterPost();
} else {
// though luck, there is more than a single bbox filter
visitor = new PostPreProcessFilterSplittingVisitor(ATTRIBUTE_FILTER_CAPABILITIES,
schema, null);
filter.accept(visitor, null);
return visitor.getFilterPost();
}
}
/**
* Parses the Geotools filter and tries to extract an intersecting geometry that can be used as
* the OGR spatial filter
*
* @param schema
* @param filter
* @return
*/
public Geometry getSpatialFilter() {
// TODO: switch to the non deprecated splitter (that no one seems to be using)
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(
GEOMETRY_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter preFilter = visitor.getFilterPre();
if (preFilter instanceof BinarySpatialOperator) {
BinarySpatialOperator bso = ((BinarySpatialOperator) preFilter);
Expression geomExpression = null;
if (bso.getExpression1() instanceof PropertyName
&& bso.getExpression2() instanceof Literal) {
geomExpression = bso.getExpression2();
} else if (bso.getExpression1() instanceof Literal
&& bso.getExpression2() instanceof PropertyName) {
geomExpression = bso.getExpression1();
}
if (geomExpression != null) {
Geometry geom = geomExpression.evaluate(null, Geometry.class);
return geom;
}
}
return null;
}
/**
* Parses the GeoTools filter and tries to extract an SQL expression that can be used as the OGR
* attribute filter
*
* @param schema
* @param filter
* @return
*/
public String getAttributeFilter() {
// TODO: switch to the non deprecated splitter (that no one seems to be using)
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(
ATTRIBUTE_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter preFilter = visitor.getFilterPre();
if (preFilter != Filter.EXCLUDE && preFilter != Filter.INCLUDE) {
FilterToRestrictedWhere sqlConverter = new FilterToRestrictedWhere(schema);
preFilter.accept(sqlConverter, null);
return sqlConverter.getRestrictedWhere();
}
return null;
}
public Filter getFilter() {
return filter;
}
}