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.renderer.shape;
017:
018: import java.util.logging.Level;
019: import java.util.logging.Logger;
020:
021: import org.geotools.filter.IllegalFilterException;
022: import org.geotools.filter.visitor.DuplicatingFilterVisitor;
023: import org.geotools.geometry.jts.JTS;
024: import org.geotools.geometry.jts.ReferencedEnvelope;
025: import org.geotools.referencing.CRS;
026: import org.geotools.referencing.ReferencingFactoryFinder;
027: import org.geotools.referencing.operation.matrix.Matrix2;
028: import org.geotools.util.logging.Logging;
029: import org.opengis.filter.expression.Literal;
030: import org.opengis.filter.expression.PropertyName;
031: import org.opengis.filter.spatial.BBOX;
032: import org.opengis.filter.spatial.Beyond;
033: import org.opengis.filter.spatial.DWithin;
034: import org.opengis.geometry.MismatchedDimensionException;
035: import org.opengis.referencing.FactoryException;
036: import org.opengis.referencing.crs.CoordinateReferenceSystem;
037: import org.opengis.referencing.operation.MathTransform;
038: import org.opengis.referencing.operation.TransformException;
039:
040: import com.vividsolutions.jts.geom.Envelope;
041: import com.vividsolutions.jts.geom.Geometry;
042:
043: /**
044: * Transforms all GeometryExpressions with the provided transform.
045: * <p>
046: * extraData may be a filterfactory2
047: * </p>
048: * @author Jesse
049: */
050: public class FilterTransformer extends DuplicatingFilterVisitor {
051: static final Logger LOGGER = Logging
052: .getLogger(FilterTransformer.class.getName());
053:
054: MathTransform mt;
055: CoordinateReferenceSystem fromCRS;
056: CoordinateReferenceSystem toCRS;
057:
058: public FilterTransformer(final MathTransform mt) {
059: this .mt = mt;
060: }
061:
062: /**
063: * Alternate constructor, takes the source CRS, the destination CRS, and an affine transform to
064: * be concatenated to the geographic transfromation. This contructor allows for accurate envelope
065: * transformations when the data set contains extreme points such as the poles or the Greenwitch
066: * antimeridian.
067: * @see ReferencedEnvelope#transform(CoordinateReferenceSystem, boolean)
068: * @param fromCRS
069: * @param toCRS
070: * @param affineTransform
071: * @throws FactoryException
072: */
073: public FilterTransformer(final CoordinateReferenceSystem fromCRS,
074: final CoordinateReferenceSystem toCRS,
075: final MathTransform affineTransform)
076: throws FactoryException {
077: this .fromCRS = fromCRS;
078: this .toCRS = toCRS;
079:
080: try {
081: mt = CRS.findMathTransform(fromCRS, toCRS);
082: } catch (Exception e) {
083: mt = null;
084: }
085:
086: if (mt == null) {
087: mt = affineTransform;
088: } else {
089: mt = ReferencingFactoryFinder.getMathTransformFactory(null)
090: .createConcatenatedTransform(mt, affineTransform);
091: }
092:
093: // if everything else failed, use the identity transform
094: if (mt == null)
095: mt = ReferencingFactoryFinder.getMathTransformFactory(null)
096: .createAffineTransform(new Matrix2(1, 0, 0, 1));
097: }
098:
099: public Object visit(BBOX filter, Object extraData) {
100: String propertyName = filter.getPropertyName();
101: double[] coords = new double[4];
102: coords[0] = filter.getMinX();
103: coords[1] = filter.getMinY();
104: coords[2] = filter.getMaxX();
105: coords[3] = filter.getMaxY();
106: String srs = filter.getSRS();
107:
108: double[] dest = new double[4];
109: try {
110: mt.transform(coords, 0, dest, 0, 2);
111: } catch (TransformException e) {
112: throw new RuntimeException(e);
113: }
114:
115: return getFactory(extraData).bbox(propertyName, dest[0],
116: dest[1], dest[2], dest[3], srs);
117:
118: }
119:
120: public Object visit(Beyond filter, Object extraData) {
121: // given a distance filter the best bet for transformation is to turn it into a intersects/disjoint
122: // filter against a buffered geometry (affine tx and reprojection can turn the initial reference geometry
123: // into... anything, and the distance would not make sense anymore.
124: double distance = filter.getDistance();
125: if (filter.getExpression1() instanceof Literal) {
126: Literal transformed = bufferTransformGeometry(
127: (Literal) filter.getExpression1(), distance,
128: extraData);
129: return getFactory(extraData).disjoint(transformed,
130: filter.getExpression2());
131: } else if (filter.getExpression2() instanceof Literal) {
132: Literal transformed = bufferTransformGeometry(
133: (Literal) filter.getExpression2(), distance,
134: extraData);
135: return getFactory(extraData).disjoint(
136: filter.getExpression1(), transformed);
137: } else {
138: LOGGER
139: .log(
140: Level.WARNING,
141: "Could not transform this filter because "
142: + "it does not use a geometry literal: {0}.\n"
143: + "The resulting of filtering will be most likely wrong",
144: new Object[] { filter });
145: return filter;
146: }
147: }
148:
149: public Object visit(DWithin filter, Object extraData) {
150: double distance = filter.getDistance();
151: if (filter.getExpression1() instanceof Literal) {
152: Literal transformed = bufferTransformGeometry(
153: (Literal) filter.getExpression1(), distance,
154: extraData);
155: return getFactory(extraData).intersects(transformed,
156: filter.getExpression2());
157: } else if (filter.getExpression2() instanceof Literal) {
158: Literal transformed = bufferTransformGeometry(
159: (Literal) filter.getExpression2(), distance,
160: extraData);
161: return getFactory(extraData).intersects(
162: filter.getExpression1(), transformed);
163: } else {
164: LOGGER
165: .log(
166: Level.WARNING,
167: "Could not transform this filter because "
168: + "it does not use a geometry literal: {0}.\n"
169: + "The resulting of filtering will be most likely wrong",
170: new Object[] { filter });
171: return filter;
172: }
173: }
174:
175: /**
176: * Given a geometry literal, it buffers it with the provided distance, and then
177: * transforms it with the given reprojection/affine transform we're applying.
178: * Used to transform distance filters
179: * @param geom
180: * @param distance
181: * @return
182: */
183: private Literal bufferTransformGeometry(Literal geomLiteral,
184: double distance, Object extraData) {
185: try {
186: Geometry geometry = (Geometry) geomLiteral.evaluate(null,
187: Geometry.class);
188: Geometry buffered = geometry.buffer(distance);
189: Geometry transformed = JTS.transform(buffered, mt);
190: return getFactory(extraData).literal(transformed);
191: } catch (Exception e) {
192: throw new RuntimeException(e);
193: }
194: }
195:
196: public Object visit(Literal expression, Object extraData) {
197: Object value = expression.getValue();
198: try {
199: if (value instanceof com.vividsolutions.jts.geom.Geometry) {
200: return getFactory(extraData)
201: .literal(
202: JTS
203: .transform(
204: (com.vividsolutions.jts.geom.Geometry) value,
205: mt));
206: }
207: if (value instanceof Envelope) {
208: ReferencedEnvelope start = new ReferencedEnvelope(
209: (Envelope) value, toCRS);
210: return getFactory(extraData).literal(
211: JTS.transform((Envelope) start, mt));
212: }
213: } catch (MismatchedDimensionException e) {
214: throw new RuntimeException(e);
215: } catch (IllegalFilterException e) {
216: throw new RuntimeException(e);
217: } catch (TransformException e) {
218: throw new RuntimeException(e);
219: }
220: return super.visit(expression, extraData);
221: }
222: }
|