/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-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.geometryless.filter; import java.io.IOException; import java.util.logging.Logger; import org.geotools.data.DataSourceException; import org.geotools.data.jdbc.FilterToSQL; import org.geotools.filter.FilterCapabilities; import org.opengis.filter.ExcludeFilter; import org.opengis.filter.Id; import org.opengis.filter.IncludeFilter; import org.opengis.filter.PropertyIsBetween; import org.opengis.filter.PropertyIsLike; import org.opengis.filter.PropertyIsNull; 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 com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; /** * Encodes a filter into a SQL WHERE statement for generic SQL. This class adds * the ability to turn geometry filters into sql statements if they are * based on xmin,ymin, xmax, ymax (longitude/latitude) columns .. * * @author Rob Atkinson , SCO * * @source $URL$ */ public class SQLEncoderBBOX extends FilterToSQL { /** Standard java logger */ private static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.filter"); /** * The srid of the schema, so the bbox conforms. Could be better to have * it in the bbox filter itself, but this works for now. */ private int srid; // names of sql addressable columns containing numerical coordinates private String XMinColumnName,YMinColumnName = null; private String XMaxColumnName,YMaxColumnName = null; private String geomName = null; /** The geometry attribute to use if none is specified. */ private String defaultGeom; /** * Empty constructor TODO: rethink empty constructor, as BBOXes _need_ an * SRID, must make client set it somehow. Maybe detect when encode is * called? */ public SQLEncoderBBOX(String minx, String miny, String maxx, String maxy) { capabilities = createFilterCapabilities(); this.XMinColumnName = minx; this.YMinColumnName = miny; this.XMaxColumnName = maxx; this.YMaxColumnName = maxy; this.geomName = geomName; setSqlNameEscape(""); } public SQLEncoderBBOX(int srid) { this.srid = srid; } /** * @see org.geotools.filter.SQLEncoder#createFilterCapabilities() */ protected FilterCapabilities createFilterCapabilities() { FilterCapabilities capabilities = new FilterCapabilities(); capabilities.addAll(FilterCapabilities.LOGICAL_OPENGIS); capabilities.addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS); capabilities.addType(PropertyIsNull.class); capabilities.addType(PropertyIsBetween.class); capabilities.addType(Id.class); capabilities.addType(IncludeFilter.class); capabilities.addType(ExcludeFilter.class); capabilities.addType(PropertyIsLike.class); capabilities.addType(BBOX.class); return capabilities; } /** * Sets a spatial reference system ESPG number, so that the geometry can be * properly encoded for postgis. If geotools starts actually creating * geometries with valid srids then this method will no longer be needed. * * @param srid the integer code for the EPSG spatial reference system. */ public void setSRID(int srid) { this.srid = srid; } /** * Sets the default geometry, so that filters with null for one of their * expressions can assume that the default geometry is intended. * * @param name the name of the default geometry Attribute. * * @task REVISIT: pass in a featureType so that geometries can figure out * their own default geometry? */ public void setDefaultGeometry(String name) { //Do we really want clients to be using malformed filters? //I mean, this is a useful method for unit tests, but shouldn't //fully formed filters usually be used? Though I guess adding //the option wouldn't hurt. -ch this.defaultGeom = name; } /** * Turns a geometry filter into the postgis sql bbox statement. * * @param filter the geometry filter to be encoded. * * @throws RuntimeException for IO exception (need a better error) */ public Object visitBinarySpatialOperator(BinarySpatialOperator filter, Object extraData) throws RuntimeException { if (filter instanceof BBOX) { Expression left = (Expression) filter.getExpression1(); Expression right = (Expression) filter.getExpression2(); PropertyName propertyExpr; Literal geomLiteralExpr; // left and right have to be valid expressions try { if (left instanceof PropertyName && right instanceof Literal) { propertyExpr = (PropertyName)left; geomLiteralExpr = (Literal)right; } else if (right instanceof PropertyName && left instanceof Literal) { propertyExpr = (PropertyName) right; geomLiteralExpr = (Literal) left; } else { String err = "LocationsXY currently supports one geometry and one " + "attribute expr. You gave: " + left + ", " + right; throw new DataSourceException(err); } visitLiteralGeometry(geomLiteralExpr); } catch (java.io.IOException ioe) { LOGGER.warning("Unable to export filter" + ioe); } } else { LOGGER.warning("exporting unknown filter type, only bbox supported"); throw new RuntimeException("Only BBox is currently supported"); } return extraData; } /** * Checks to see if the literal is a geometry, and encodes it if it is, if * not just sends to the parent class. * * @param expression the expression to visit and encode. * * @throws IOException for IO exception (need a better error) */ public void visitLiteralGeometry(Literal expression) throws IOException { Geometry bbox = (Geometry) expression.getValue(); Envelope e = bbox.getEnvelopeInternal(); double x1 = e.getMinX(); double x2 = e.getMaxX(); double y1 = e.getMinY(); double y2 = e.getMaxY(); out.write( "( " + XMinColumnName + " < " + x2 + " and " + XMaxColumnName + " > " + x1 + " and " + YMinColumnName + " < " + y2 + " and " + YMaxColumnName + " > " + y1 + " )" ); } }