/* * 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.renderer.shape; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.filter.IllegalFilterException; import org.geotools.filter.visitor.DuplicatingFilterVisitor; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; import org.geotools.referencing.operation.matrix.Matrix2; import org.geotools.util.logging.Logging; import org.opengis.filter.FilterFactory2; import org.opengis.filter.expression.Literal; import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.Beyond; import org.opengis.filter.spatial.DWithin; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; /** * Transforms all GeometryExpressions with the provided transform. * <p> * extraData may be a filterfactory2 * </p> * @author Jesse * * * @source $URL$ */ public class FilterTransformer extends DuplicatingFilterVisitor { static final Logger LOGGER = Logging.getLogger(FilterTransformer.class); MathTransform mt; CoordinateReferenceSystem fromCRS; CoordinateReferenceSystem toCRS; public FilterTransformer(final MathTransform mt) { this.mt = mt; } public FilterTransformer(final FilterFactory2 ff, final MathTransform mt) { super(ff); this.mt = mt; } /** * Alternate constructor, takes the source CRS, the destination CRS, and an affine transform to * be concatenated to the geographic transfromation. This contructor allows for accurate envelope * transformations when the data set contains extreme points such as the poles or the Greenwitch * antimeridian. * @see ReferencedEnvelope#transform(CoordinateReferenceSystem, boolean) * @param fromCRS * @param toCRS * @param affineTransform * @throws FactoryException */ public FilterTransformer(final CoordinateReferenceSystem fromCRS, final CoordinateReferenceSystem toCRS, final MathTransform affineTransform) throws FactoryException { this.fromCRS = fromCRS; this.toCRS = toCRS; try { mt = CRS.findMathTransform(fromCRS, toCRS); } catch (Exception e) { mt = null; } if (mt == null) { mt = affineTransform; } else { mt = ReferencingFactoryFinder.getMathTransformFactory(null).createConcatenatedTransform( mt, affineTransform); } // if everything else failed, use the identity transform if(mt == null) mt = ReferencingFactoryFinder.getMathTransformFactory(null).createAffineTransform( new Matrix2(1,0,0,1)); } public Object visit(BBOX filter, Object extraData) { String propertyName=filter.getPropertyName(); double [] coords= new double[4]; coords[0]=filter.getMinX(); coords[1]=filter.getMinY(); coords[2]=filter.getMaxX(); coords[3]=filter.getMaxY(); String srs=filter.getSRS(); double[] dest=new double[4]; try { mt.transform(coords, 0, dest, 0, 2); } catch (TransformException e) { throw new RuntimeException(e); } return getFactory(extraData).bbox(propertyName, dest[0], dest[1], dest[2], dest[3], srs); } @Override public Object visit(Beyond filter, Object extraData) { // given a distance filter the best bet for transformation is to turn it into a intersects/disjoint // filter against a buffered geometry (affine tx and reprojection can turn the initial reference geometry // into... anything, and the distance would not make sense anymore. double distance = filter.getDistance(); if(filter.getExpression1() instanceof Literal) { Literal transformed = bufferTransformGeometry((Literal) filter.getExpression1(), distance, extraData); return getFactory(extraData).disjoint(transformed, filter.getExpression2()); } else if(filter.getExpression2() instanceof Literal) { Literal transformed = bufferTransformGeometry((Literal) filter.getExpression2(), distance, extraData); return getFactory(extraData).disjoint(filter.getExpression1(), transformed); } else { LOGGER.log(Level.WARNING, "Could not transform this filter because " + "it does not use a geometry literal: {0}.\n" + "The resulting of filtering will be most likely wrong", new Object[] {filter}); return filter; } } @Override public Object visit(DWithin filter, Object extraData) { double distance = filter.getDistance(); if(filter.getExpression1() instanceof Literal) { Literal transformed = bufferTransformGeometry((Literal) filter.getExpression1(), distance, extraData); return getFactory(extraData).intersects(transformed, filter.getExpression2()); } else if(filter.getExpression2() instanceof Literal) { Literal transformed = bufferTransformGeometry((Literal) filter.getExpression2(), distance, extraData); return getFactory(extraData).intersects(filter.getExpression1(), transformed); } else { LOGGER.log(Level.WARNING, "Could not transform this filter because " + "it does not use a geometry literal: {0}.\n" + "The resulting of filtering will be most likely wrong", new Object[] {filter}); return filter; } } /** * Given a geometry literal, it buffers it with the provided distance, and then * transforms it with the given reprojection/affine transform we're applying. * Used to transform distance filters * @param geom * @param distance * @return */ private Literal bufferTransformGeometry(Literal geomLiteral, double distance, Object extraData) { try { Geometry geometry = geomLiteral.evaluate(null, Geometry.class); Geometry buffered = geometry.buffer(distance); Geometry transformed = JTS.transform(buffered, mt); return getFactory(extraData).literal(transformed); } catch(Exception e) { throw new RuntimeException(e); } } public Object visit(Literal expression, Object extraData) { Object value = expression.getValue(); try { if( value instanceof com.vividsolutions.jts.geom.Geometry ){ return getFactory(extraData).literal(JTS.transform((com.vividsolutions.jts.geom.Geometry)value, mt)); } if( value instanceof Envelope ){ ReferencedEnvelope start = new ReferencedEnvelope((Envelope)value, toCRS); return getFactory(extraData).literal(JTS.transform((Envelope) start, mt)); } } catch (MismatchedDimensionException e) { throw new RuntimeException(e); } catch (IllegalFilterException e) { throw new RuntimeException(e); } catch (TransformException e) { throw new RuntimeException(e); } return super.visit(expression, extraData); } }