1 | package org.geotools.data.collection;
|
---|
2 |
|
---|
3 | import java.awt.RenderingHints.Key;
|
---|
4 | import java.io.IOException;
|
---|
5 | import java.util.ArrayList;
|
---|
6 | import java.util.Arrays;
|
---|
7 | import java.util.Collections;
|
---|
8 | import java.util.Comparator;
|
---|
9 | import java.util.HashSet;
|
---|
10 | import java.util.List;
|
---|
11 | import java.util.Set;
|
---|
12 |
|
---|
13 | import org.geotools.data.DataAccess;
|
---|
14 | import org.geotools.data.DataUtilities;
|
---|
15 | import org.geotools.data.FeatureListener;
|
---|
16 | import org.geotools.data.Query;
|
---|
17 | import org.geotools.data.QueryCapabilities;
|
---|
18 | import org.geotools.data.simple.SimpleFeatureCollection;
|
---|
19 | import org.geotools.data.simple.SimpleFeatureSource;
|
---|
20 | import org.geotools.data.store.EmptyFeatureCollection;
|
---|
21 | import org.geotools.data.store.ReTypingFeatureCollection;
|
---|
22 | import org.geotools.data.store.ReprojectingFeatureCollection;
|
---|
23 | import org.geotools.feature.collection.DecoratingSimpleFeatureCollection;
|
---|
24 | import org.geotools.feature.collection.FilteringSimpleFeatureCollection;
|
---|
25 | import org.geotools.feature.collection.MaxSimpleFeatureCollection;
|
---|
26 | import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
|
---|
27 | import org.geotools.geometry.jts.ReferencedEnvelope;
|
---|
28 | import org.opengis.feature.simple.SimpleFeature;
|
---|
29 | import org.opengis.feature.simple.SimpleFeatureType;
|
---|
30 | import org.opengis.feature.type.Name;
|
---|
31 | import org.opengis.filter.Filter;
|
---|
32 | import org.opengis.filter.sort.SortBy;
|
---|
33 |
|
---|
34 | /**
|
---|
35 | * This is a "port" of ContentFeatureSource to work with an iterator.
|
---|
36 | * <p>
|
---|
37 | * To use this class please "wrap" CollectionFeatureSource around your choice of FeatureCollection.
|
---|
38 | *
|
---|
39 | * <pre>
|
---|
40 | * SimpleFeatureCollection collection = new ListFeatureCollection(schema);
|
---|
41 | * collection.add(feature1);
|
---|
42 | * collection.add(feature2);
|
---|
43 | * FeatureSource source = new CollectionFeatureSource(collection);
|
---|
44 | * </pre>
|
---|
45 | * <p>
|
---|
46 | * Note to implementors: If you are performing "real I/O" please use ContentFeatureSource as it
|
---|
47 | * provides support for IOException.
|
---|
48 | *
|
---|
49 | * @author Jody
|
---|
50 | *
|
---|
51 | * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/build/maven/javadoc/../../../modules/library/main/src/main/java/org/geotools/data/collection/CollectionFeatureSource.java $
|
---|
52 | */
|
---|
53 | public class CollectionFeatureSource implements SimpleFeatureSource {
|
---|
54 | protected SimpleFeatureCollection collection;
|
---|
55 |
|
---|
56 | /**
|
---|
57 | * observers
|
---|
58 | */
|
---|
59 | protected List<FeatureListener> listeners = null;
|
---|
60 |
|
---|
61 | private QueryCapabilities capabilities;
|
---|
62 |
|
---|
63 | private Set<Key> hints;
|
---|
64 |
|
---|
65 | public CollectionFeatureSource(SimpleFeatureCollection collection) {
|
---|
66 | this.collection = collection;
|
---|
67 | }
|
---|
68 |
|
---|
69 | public SimpleFeatureType getSchema() {
|
---|
70 | return collection.getSchema();
|
---|
71 | }
|
---|
72 |
|
---|
73 | public synchronized void addFeatureListener(FeatureListener listener) {
|
---|
74 | if (listeners == null) {
|
---|
75 | listeners = Collections.synchronizedList(new ArrayList<FeatureListener>());
|
---|
76 | }
|
---|
77 | listeners.add(listener);
|
---|
78 | }
|
---|
79 |
|
---|
80 | public synchronized void removeFeatureListener(FeatureListener listener) {
|
---|
81 | if (listeners == null) {
|
---|
82 | return;
|
---|
83 | }
|
---|
84 | listeners.remove(listener);
|
---|
85 | }
|
---|
86 |
|
---|
87 | public ReferencedEnvelope getBounds() throws IOException {
|
---|
88 | return collection.getBounds();
|
---|
89 | }
|
---|
90 |
|
---|
91 | public ReferencedEnvelope getBounds(Query query) throws IOException {
|
---|
92 | return getFeatures(query).getBounds();
|
---|
93 | }
|
---|
94 |
|
---|
95 | public int getCount(Query query) throws IOException {
|
---|
96 | return getFeatures(query).size();
|
---|
97 | }
|
---|
98 |
|
---|
99 | public DataAccess<SimpleFeatureType, SimpleFeature> getDataStore() {
|
---|
100 | throw new UnsupportedOperationException("CollectionFeatureSource is an inmemory wrapper");
|
---|
101 | }
|
---|
102 |
|
---|
103 | public Name getName() {
|
---|
104 | return collection.getSchema().getName();
|
---|
105 | }
|
---|
106 |
|
---|
107 | public synchronized QueryCapabilities getQueryCapabilities() {
|
---|
108 | if (capabilities == null) {
|
---|
109 | capabilities = new QueryCapabilities() {
|
---|
110 | public boolean isOffsetSupported() {
|
---|
111 | return true;
|
---|
112 | }
|
---|
113 |
|
---|
114 | public boolean isReliableFIDSupported() {
|
---|
115 | return true;
|
---|
116 | }
|
---|
117 |
|
---|
118 | public boolean supportsSorting(org.opengis.filter.sort.SortBy[] sortAttributes) {
|
---|
119 | return true;
|
---|
120 | }
|
---|
121 | };
|
---|
122 | }
|
---|
123 | return capabilities;
|
---|
124 | }
|
---|
125 |
|
---|
126 | public synchronized Set<Key> getSupportedHints() {
|
---|
127 | if (hints == null) {
|
---|
128 | Set<Key> supports = new HashSet<Key>();
|
---|
129 | // supports.add( Hints.FEATURE_DETACHED );
|
---|
130 | hints = Collections.unmodifiableSet(supports);
|
---|
131 | }
|
---|
132 | return hints;
|
---|
133 | }
|
---|
134 |
|
---|
135 | @Override
|
---|
136 | public String toString() {
|
---|
137 | StringBuilder buf = new StringBuilder();
|
---|
138 | buf.append("CollectionFeatureSource:");
|
---|
139 | buf.append(collection);
|
---|
140 | return buf.toString();
|
---|
141 | }
|
---|
142 |
|
---|
143 | //
|
---|
144 | // GET FEATURES
|
---|
145 | // This forms the heart of the CollectionFeatureSource implementation
|
---|
146 | // Use: DataUtilities.mixQueries(this.query, query, "subCollection" ) as needed
|
---|
147 | //
|
---|
148 | public SimpleFeatureCollection getFeatures() throws IOException {
|
---|
149 | return getFeatures( Query.ALL );
|
---|
150 | }
|
---|
151 |
|
---|
152 | public SimpleFeatureCollection getFeatures(Filter filter) {
|
---|
153 | Query query = new Query(getSchema().getTypeName(), filter);
|
---|
154 | return getFeatures(query);
|
---|
155 | }
|
---|
156 |
|
---|
157 | public SimpleFeatureCollection getFeatures(Query query) {
|
---|
158 | query = DataUtilities.resolvePropertyNames(query, getSchema());
|
---|
159 | final int offset = query.getStartIndex() != null ? query.getStartIndex() : 0;
|
---|
160 | if (offset > 0 & query.getSortBy() == null) {
|
---|
161 | if (!getQueryCapabilities().supportsSorting(query.getSortBy())){
|
---|
162 | throw new IllegalStateException("Feature source does not support this sorting "
|
---|
163 | + "so there is no way a stable paging (offset/limit) can be performed");
|
---|
164 | }
|
---|
165 | Query copy = new Query(query);
|
---|
166 | copy.setSortBy(new SortBy[] { SortBy.NATURAL_ORDER });
|
---|
167 | query = copy;
|
---|
168 | }
|
---|
169 | SimpleFeatureCollection features = collection;
|
---|
170 | // step one: filter
|
---|
171 | if( query.getFilter() != null && query.getFilter().equals(Filter.EXCLUDE)){
|
---|
172 | return new EmptyFeatureCollection( getSchema() );
|
---|
173 | }
|
---|
174 | if (query.getFilter() != null && query.getFilter() != Filter.INCLUDE) {
|
---|
175 | features = new FilteringSimpleFeatureCollection(features, query.getFilter());
|
---|
176 | }
|
---|
177 | // step two: reproject
|
---|
178 | if (query.getCoordinateSystemReproject() != null) {
|
---|
179 | features = new ReprojectingFeatureCollection(features, query
|
---|
180 | .getCoordinateSystemReproject());
|
---|
181 | }
|
---|
182 | // step two sort! (note this makes a sorted copy)
|
---|
183 | if (query.getSortBy() != null && query.getSortBy().length != 0) {
|
---|
184 | SimpleFeature array[] = features.toArray(new SimpleFeature[features.size()]);
|
---|
185 | // Arrays sort is stable (not resorting equal elements)
|
---|
186 | for (SortBy sortBy : query.getSortBy()) {
|
---|
187 | Comparator<SimpleFeature> comparator = DataUtilities.sortComparator(sortBy);
|
---|
188 | Arrays.sort(array, comparator);
|
---|
189 | }
|
---|
190 | ArrayList<SimpleFeature> list = new ArrayList<SimpleFeature>(Arrays.asList(array));
|
---|
191 | features = new ListFeatureCollection(getSchema(), list);
|
---|
192 | }
|
---|
193 |
|
---|
194 | // step three skip to start and return max number of fetaures
|
---|
195 | if (offset > 0 || !query.isMaxFeaturesUnlimited()) {
|
---|
196 | long max = Long.MAX_VALUE;
|
---|
197 | if (!query.isMaxFeaturesUnlimited()) {
|
---|
198 | max = query.getMaxFeatures();
|
---|
199 | }
|
---|
200 | features = new MaxSimpleFeatureCollection(features, offset, max);
|
---|
201 | }
|
---|
202 |
|
---|
203 | // step four - retyping
|
---|
204 | // (It would be nice to do this earlier so as to not have all the baggage
|
---|
205 | // of unneeded attributes)
|
---|
206 | if (query.getPropertyNames() != Query.ALL_NAMES) {
|
---|
207 | // rebuild the type and wrap the reader
|
---|
208 | SimpleFeatureType schema = features.getSchema();
|
---|
209 | SimpleFeatureType target = SimpleFeatureTypeBuilder.retype(schema, query
|
---|
210 | .getPropertyNames());
|
---|
211 |
|
---|
212 | // do an equals check because we may have needlessly retyped (that is,
|
---|
213 | // the subclass might be able to only partially retype)
|
---|
214 | if (!target.equals(schema)) {
|
---|
215 | features = new ReTypingFeatureCollection(features, target);
|
---|
216 | }
|
---|
217 | }
|
---|
218 | // Wrap up the results in a method that allows subCollection
|
---|
219 | return new SubCollection( query, features );
|
---|
220 | }
|
---|
221 |
|
---|
222 | /**
|
---|
223 | * SubCollection for CollectionFeatureSource.
|
---|
224 | * <p>
|
---|
225 | * Will route any calls refining the feature collection back to CollectionFeatureSource. This is
|
---|
226 | * based on the success of ContentFeatureCollection.
|
---|
227 | * </p>
|
---|
228 | *
|
---|
229 | * @author Jody
|
---|
230 | */
|
---|
231 | protected class SubCollection extends DecoratingSimpleFeatureCollection {
|
---|
232 | private Query query;
|
---|
233 | protected SubCollection(Query query, SimpleFeatureCollection features) {
|
---|
234 | super(features);
|
---|
235 | this.query = query;
|
---|
236 | }
|
---|
237 | public SimpleFeatureCollection subCollection(Filter filter) {
|
---|
238 | Query q = new Query(getSchema().getTypeName(), filter);
|
---|
239 |
|
---|
240 | Query subQuery = DataUtilities.mixQueries(query, q, q.getHandle() );
|
---|
241 | return CollectionFeatureSource.this.getFeatures( subQuery );
|
---|
242 | }
|
---|
243 | }
|
---|
244 | }
|
---|