001: package org.geotools.data.store;
002:
003: import java.io.IOException;
004: import java.util.ArrayList;
005: import java.util.Collection;
006: import java.util.Iterator;
007: import java.util.List;
008:
009: import org.geotools.data.FeatureReader;
010: import org.geotools.data.collection.DelegateFeatureReader;
011: import org.geotools.feature.CollectionListener;
012: import org.geotools.feature.Feature;
013: import org.geotools.feature.FeatureCollection;
014: import org.geotools.feature.FeatureIterator;
015: import org.geotools.feature.FeatureList;
016: import org.geotools.feature.FeatureType;
017: import org.geotools.feature.FeatureTypes;
018: import org.geotools.feature.IllegalAttributeException;
019: import org.geotools.feature.SchemaException;
020: import org.geotools.feature.collection.DelegateFeatureIterator;
021: import org.geotools.feature.visitor.FeatureVisitor;
022: import org.geotools.geometry.jts.GeometryCoordinateSequenceTransformer;
023: import org.geotools.geometry.jts.ReferencedEnvelope;
024: import org.geotools.referencing.CRS;
025: import org.geotools.util.ProgressListener;
026: import org.opengis.filter.Filter;
027: import org.opengis.filter.sort.SortBy;
028: import org.opengis.referencing.FactoryException;
029: import org.opengis.referencing.crs.CoordinateReferenceSystem;
030: import org.opengis.referencing.operation.MathTransform;
031:
032: import com.vividsolutions.jts.geom.Envelope;
033: import com.vividsolutions.jts.geom.Geometry;
034:
035: /**
036: * FeatureCollection decorator that reprojects the default geometry.
037: *
038: * @author Justin
039: */
040: public class ReprojectingFeatureCollection implements FeatureCollection {
041:
042: /**
043: * The decorated collection
044: */
045: FeatureCollection delegate;
046:
047: /**
048: * The transform to the target coordinate reference system
049: */
050: MathTransform transform;
051:
052: /**
053: * The schema of reprojected features
054: */
055: FeatureType schema;
056:
057: /**
058: * The feature type of the feature collection
059: */
060: FeatureType featureType;
061:
062: /**
063: * The target coordinate reference system
064: */
065: CoordinateReferenceSystem target;
066:
067: /**
068: * Transformer used to transform geometries;
069: */
070: GeometryCoordinateSequenceTransformer transformer;
071:
072: public ReprojectingFeatureCollection(FeatureCollection delegate,
073: CoordinateReferenceSystem target) {
074: this (delegate, delegate.getSchema().getDefaultGeometry()
075: .getCoordinateSystem(), target);
076: }
077:
078: public ReprojectingFeatureCollection(FeatureCollection delegate,
079: CoordinateReferenceSystem source,
080: CoordinateReferenceSystem target) {
081: this .delegate = delegate;
082: this .target = target;
083: FeatureType schema = delegate.getSchema();
084: this .schema = reType(schema, target);
085:
086: FeatureType featureType = delegate.getFeatureType();
087: this .featureType = reType(featureType, target);
088:
089: if (source == null) {
090: throw new NullPointerException("source crs");
091: }
092: if (target == null) {
093: throw new NullPointerException("destination crs");
094: }
095:
096: this .transform = transform(source, target);
097: transformer = new GeometryCoordinateSequenceTransformer();
098: }
099:
100: public void setTransformer(
101: GeometryCoordinateSequenceTransformer transformer) {
102: this .transformer = transformer;
103: }
104:
105: private MathTransform transform(CoordinateReferenceSystem source,
106: CoordinateReferenceSystem target) {
107: try {
108: return CRS.findMathTransform(source, target);
109: } catch (FactoryException e) {
110: throw new IllegalArgumentException(
111: "Could not create math transform");
112: }
113: }
114:
115: private FeatureType reType(FeatureType type,
116: CoordinateReferenceSystem target) {
117: try {
118: return FeatureTypes.transform(type, target);
119: } catch (SchemaException e) {
120: throw new IllegalArgumentException(
121: "Could not transform source schema");
122: }
123: }
124:
125: public FeatureReader reader() throws IOException {
126: return new DelegateFeatureReader(getSchema(), features());
127: }
128:
129: public FeatureIterator features() {
130: return new DelegateFeatureIterator(this , iterator());
131: }
132:
133: public void close(FeatureIterator close) {
134: close.close();
135: }
136:
137: public Iterator iterator() {
138: try {
139: return new ReprojectingIterator(delegate.iterator(),
140: transform, schema, transformer);
141: } catch (Exception e) {
142: throw new RuntimeException(e);
143: }
144: }
145:
146: public void close(Iterator close) {
147: Iterator iterator = ((ReprojectingIterator) close)
148: .getDelegate();
149: delegate.close(iterator);
150: }
151:
152: public void addListener(CollectionListener listener)
153: throws NullPointerException {
154: delegate.addListener(listener);
155: }
156:
157: public void removeListener(CollectionListener listener)
158: throws NullPointerException {
159: delegate.removeListener(listener);
160: }
161:
162: public FeatureType getFeatureType() {
163: return this .featureType;
164: }
165:
166: public FeatureType getSchema() {
167: return this .schema;
168: }
169:
170: public void accepts(FeatureVisitor visitor,
171: ProgressListener progress) throws IOException {
172: delegate.accepts(visitor, progress);
173: }
174:
175: public FeatureCollection subCollection(Filter filter) {
176: Filter unFilter = unFilter(filter);
177: return new ReprojectingFeatureCollection(delegate
178: .subCollection(unFilter), target);
179: // TODO: return new delegate.subCollection( filter ).reproject( target
180: // );
181: }
182:
183: /**
184: * Takes any literal geometry in the provided filter and backprojects it
185: *
186: * @param FilterFactory
187: * @param MathTransform
188: */
189: private Filter unFilter(Filter filter) {
190: // need: filterFactory
191: // need: inverse of our transform
192: // FilterVisitor fv = new ReprojectingFilterVisitor(ff, transform);
193: // filter.accept(fv, null);
194: // TODO: create FilterVisitor that backproject literal geometry
195: return filter;
196: }
197:
198: public FeatureList sort(SortBy order) {
199: // return new ReprojectingFeatureList( delegate.sort( order ), target );
200: throw new UnsupportedOperationException("Not yet");
201: }
202:
203: public void purge() {
204: delegate.purge();
205: }
206:
207: public int size() {
208: return delegate.size();
209: }
210:
211: public void clear() {
212: delegate.clear();
213: }
214:
215: public boolean isEmpty() {
216: return delegate.isEmpty();
217: }
218:
219: public Object[] toArray() {
220: return toArray(new Object[size()]);
221: }
222:
223: public Object[] toArray(Object[] a) {
224: List list = new ArrayList();
225: Iterator i = iterator();
226: try {
227: while (i.hasNext()) {
228: list.add(i.next());
229: }
230:
231: return list.toArray(a);
232: } finally {
233: close(i);
234: }
235: }
236:
237: public boolean add(Object o) {
238: // must back project any geometry attributes
239: throw new UnsupportedOperationException("Not yet");
240: // return delegate.add( o );
241: }
242:
243: public boolean contains(Object o) {
244: // must back project any geometry attributes
245: return delegate.add(o);
246: }
247:
248: public boolean remove(Object o) {
249: // must back project any geometry attributes
250: return delegate.remove(o);
251: }
252:
253: public boolean addAll(Collection c) {
254: // must back project any geometry attributes
255: return delegate.addAll(c);
256: }
257:
258: public boolean containsAll(Collection c) {
259: // must back project any geometry attributes
260: return delegate.containsAll(c);
261: }
262:
263: public boolean removeAll(Collection c) {
264: // must back project any geometry attributes
265: return delegate.removeAll(c);
266: }
267:
268: public boolean retainAll(Collection c) {
269: // must back project any geometry attributes
270: return delegate.retainAll(c);
271: }
272:
273: public String getID() {
274: return delegate.getID();
275: }
276:
277: public Object[] getAttributes(Object[] attributes) {
278: // must reproject any geometry attributes
279: return delegate.getAttributes(attributes);
280: }
281:
282: public Object getAttribute(String xPath) {
283: // must project any geometry attributes
284: return delegate.getAttribute(xPath);
285: }
286:
287: public Object getAttribute(int index) {
288: return delegate.getAttribute(index);
289: }
290:
291: public void setAttribute(int position, Object val)
292: throws IllegalAttributeException,
293: ArrayIndexOutOfBoundsException {
294: delegate.setAttribute(position, val);
295: }
296:
297: public int getNumberOfAttributes() {
298: return delegate.getNumberOfAttributes();
299: }
300:
301: public void setAttribute(String xPath, Object attribute)
302: throws IllegalAttributeException {
303: delegate.setAttribute(xPath, attribute);
304: }
305:
306: public Geometry getDefaultGeometry() {
307: return delegate.getDefaultGeometry();
308: }
309:
310: public void setDefaultGeometry(Geometry geometry)
311: throws IllegalAttributeException {
312: delegate.setDefaultGeometry(geometry);
313: }
314:
315: /**
316: * This method computes reprojected bounds the hard way, but computing them
317: * feature by feature. This method could be faster if computed the
318: * reprojected bounds by reprojecting the original feature bounds a Shape
319: * object, thus getting the true shape of the reprojected envelope, and then
320: * computing the minimum and maximum coordinates of that new shape. The
321: * result would not a true representation of the new bounds.
322: *
323: * @see org.geotools.data.FeatureResults#getBounds()
324: */
325: public ReferencedEnvelope getBounds() {
326: FeatureIterator r = features();
327: try {
328: Envelope newBBox = new Envelope();
329: Envelope internal;
330: Feature feature;
331:
332: while (r.hasNext()) {
333: feature = r.next();
334: final Geometry geom = feature.getDefaultGeometry();
335: if (geom != null) {
336: internal = geom.getEnvelopeInternal();
337: newBBox.expandToInclude(internal);
338: }
339: }
340: return ReferencedEnvelope.reference(newBBox);
341: } catch (Exception e) {
342: throw new RuntimeException(
343: "Exception occurred while computing reprojected bounds",
344: e);
345: } finally {
346: r.close();
347: }
348: }
349:
350: }
|