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.caching.impl;
017:
018: import com.vividsolutions.jts.geom.Envelope;
019:
020: import org.geotools.caching.DataCache;
021: import org.geotools.caching.FeatureIndex;
022: import org.geotools.caching.InternalStore;
023:
024: import org.geotools.data.DataStore;
025: import org.geotools.data.DefaultQuery;
026: import org.geotools.data.FeatureListener;
027: import org.geotools.data.FeatureSource;
028: import org.geotools.data.Query;
029: import org.geotools.data.view.DefaultView;
030:
031: import org.geotools.feature.DefaultFeatureCollection;
032: import org.geotools.feature.Feature;
033: import org.geotools.feature.FeatureCollection;
034: import org.geotools.feature.FeatureType;
035: import org.geotools.feature.SchemaException;
036:
037: import org.geotools.filter.spatial.BBOXImpl;
038:
039: import org.geotools.index.Data;
040: import org.geotools.index.DataDefinition;
041: import org.geotools.index.LockTimeoutException;
042: import org.geotools.index.TreeException;
043: import org.geotools.index.rtree.PageStore;
044: import org.geotools.index.rtree.RTree;
045: import org.geotools.index.rtree.memory.MemoryPageStore;
046:
047: import org.opengis.filter.Filter;
048:
049: import java.io.IOException;
050:
051: import java.util.ArrayList;
052: import java.util.Collection;
053: import java.util.Iterator;
054: import java.util.List;
055:
056: /** An implementation of FeatureIndex, that stores every thing needed in memory.
057: *
058: * @task handle size limit properly (currently, we do nothing).
059: *
060: * @author Christophe Rousson, SoC 2007, CRG-ULAVAL
061: *
062: */
063: public class MemoryFeatureIndex implements FeatureIndex {
064: private static final DataDefinition df = createDataDefinition();
065: private final DataCache parent;
066: private RTree tree = createTree();
067: private final InternalStore internalStore;
068: private final int capacity;
069: private int indexCount = 0;
070: private final FeatureType type;
071: private Query currentQuery = Query.ALL;
072:
073: /** Creates a new index that can store features of given type.
074: *
075: * @param type FeatureType of features this index will store
076: * @param capacity maximum number of features we can store.
077: */
078: public MemoryFeatureIndex(DataCache parent, FeatureType type,
079: int capacity) {
080: this .parent = parent;
081: this .internalStore = new SimpleHashMapInternalStore();
082: this .capacity = capacity;
083: this .type = type;
084: }
085:
086: /* (non-Javadoc)
087: * @see org.geotools.caching.FeatureIndex#add(org.geotools.feature.Feature)
088: */
089: public void add(Feature f) {
090: if (internalStore.contains(f)) {
091: return;
092: }
093:
094: Data d = new Data(df);
095:
096: try {
097: d.addValue(f.getID());
098: tree.insert(f.getBounds(), d);
099: internalStore.put(f);
100: indexCount++;
101: } catch (TreeException e) {
102: // TODO Auto-generated catch block
103: e.printStackTrace();
104: } catch (LockTimeoutException e) {
105: e.printStackTrace();
106: }
107: }
108:
109: /* (non-Javadoc)
110: * @see org.geotools.caching.FeatureIndex#clear()
111: */
112: public void clear() {
113: try {
114: tree.close();
115: } catch (TreeException e) {
116: // TODO Auto-generated catch block
117: e.printStackTrace();
118: }
119:
120: tree = createTree();
121: internalStore.clear();
122: }
123:
124: public void flush() {
125: // TODO Auto-generated method stub
126: }
127:
128: /* (non-Javadoc)
129: * @see org.geotools.caching.FeatureIndex#get(java.lang.String)
130: */
131: public Feature get(String featureID) {
132: /*Filter f = FilterFactoryFinder.createFilterFactory().createFidFilter(featureID) ;
133: try {
134: FeatureSource fs = internalStore.getView(new DefaultQuery(type.getTypeName(), f)) ;
135: FeatureCollection fc = fs.getFeatures() ;
136: if (fc.isEmpty())
137: // TODO throw appropriate exception, so we can handle the case
138: return null ;
139: FeatureIterator i = fc.features() ;
140: Feature ret = i.next() ;
141: fc.close(i) ;
142: return ret ;
143: } catch (IOException e) {
144: // TODO Auto-generated catch block
145: e.printStackTrace();
146: } catch (SchemaException e) {
147: // TODO Auto-generated catch block
148: e.printStackTrace();
149: }
150: return null ;*/
151: Feature f = (Feature) internalStore.get(featureID);
152:
153: // TODO test if we get null, and do something ...
154: return f;
155: }
156:
157: /* (non-Javadoc)
158: * @see org.geotools.caching.FeatureIndex#getFeatures(org.geotools.data.Query)
159: */
160: public FeatureCollection getFeatures(Query q) throws IOException {
161: Filter f = q.getFilter();
162:
163: return getFeatures(f);
164: }
165:
166: /** Preselects features from the spatial index.
167: * If query is not a BBox query, returns all features in the cache.
168: *
169: * @param q a Query
170: * @return a collection of features within or intersecting query bounds.
171: */
172: private Collection getCandidates(Filter f) {
173: if (f instanceof BBOXImpl) {
174: List candidates = new ArrayList();
175: BBOXImpl bb = (BBOXImpl) f;
176: Envelope env = new Envelope(bb.getMinX(), bb.getMaxX(), bb
177: .getMinY(), bb.getMaxY());
178:
179: try {
180: List results = tree.search(env);
181:
182: for (Iterator r = results.iterator(); r.hasNext();) {
183: Data d = (Data) r.next();
184: String fid = (String) d.getValue(0);
185: candidates.add(internalStore.get(fid));
186: }
187:
188: return candidates;
189: } catch (TreeException e) {
190: // TODO Auto-generated catch block
191: e.printStackTrace();
192: } catch (LockTimeoutException e) {
193: // TODO Auto-generated catch block
194: e.printStackTrace();
195: }
196: }
197:
198: return internalStore.getAll();
199: }
200:
201: /* (non-Javadoc)
202: * @see org.geotools.caching.FeatureIndex#remove(java.lang.String)
203: */
204: public void remove(String featureID) {
205: Envelope env = ((Feature) internalStore.get(featureID))
206: .getBounds();
207:
208: try {
209: tree.delete(env);
210: } catch (TreeException e) {
211: // TODO Auto-generated catch block
212: e.printStackTrace();
213: } catch (LockTimeoutException e) {
214: // TODO Auto-generated catch block
215: e.printStackTrace();
216: }
217:
218: internalStore.remove(featureID);
219: indexCount--;
220: }
221:
222: /* (non-Javadoc)
223: * @see org.geotools.caching.FeatureIndex#getView(org.geotools.data.Query)
224: */
225: public FeatureSource getView(Query q) throws SchemaException {
226: return new DefaultView(this , q);
227: }
228:
229: /** R-tree to keep envelopes of stored features.
230: *
231: * @return a R-tree, memory mapped.
232: */
233: private static RTree createTree() {
234: try {
235: PageStore ps = new MemoryPageStore(df, 8, 4,
236: PageStore.SPLIT_QUADRATIC);
237: RTree tree = new RTree(ps);
238:
239: return tree;
240: } catch (TreeException e) {
241: throw (RuntimeException) new RuntimeException()
242: .initCause(e);
243: }
244: }
245:
246: /** Data definition of data we feed into the R-tree.
247: * What we store is the ID of features (we assume ID are less than 256 chars).
248: * @return data definition
249: */
250: private static DataDefinition createDataDefinition() {
251: DataDefinition df = new DataDefinition("US-ASCII");
252: df.addField(256);
253:
254: return df;
255: }
256:
257: /* (non-Javadoc)
258: * @see org.geotools.data.FeatureSource#addFeatureListener(org.geotools.data.FeatureListener)
259: */
260: public void addFeatureListener(FeatureListener arg0) {
261: // TODO Auto-generated method stub
262: }
263:
264: /* (non-Javadoc)
265: * @see org.geotools.data.FeatureSource#getBounds()
266: */
267: public Envelope getBounds() throws IOException {
268: /*try {
269: return tree.getBounds();
270: } catch (TreeException e) {
271: throw (IOException) new IOException().initCause(e);
272: }*/
273: return parent.getFeatureSource(type.getTypeName()).getBounds();
274: }
275:
276: /* (non-Javadoc)
277: * @see org.geotools.data.FeatureSource#getBounds(org.geotools.data.Query)
278: */
279: public Envelope getBounds(Query q) throws IOException {
280: FeatureCollection fc = this .getFeatures(q);
281:
282: return fc.getBounds();
283: }
284:
285: /* (non-Javadoc)
286: * @see org.geotools.data.FeatureSource#getCount(org.geotools.data.Query)
287: */
288: public int getCount(Query q) throws IOException {
289: Query qprime = adapt(q);
290:
291: try {
292: this .parent.getView(qprime);
293: } catch (SchemaException e) {
294: throw (IOException) new IOException().initCause(e);
295: }
296:
297: FeatureCollection fc = this .getFeatures(qprime);
298:
299: return fc.size();
300: }
301:
302: /* (non-Javadoc)
303: * @see org.geotools.data.FeatureSource#getDataStore()
304: */
305: public DataStore getDataStore() {
306: // TODO Auto-generated method stub
307: return this .parent;
308: }
309:
310: /* (non-Javadoc)
311: * @see org.geotools.data.FeatureSource#getFeatures()
312: */
313: public FeatureCollection getFeatures() throws IOException {
314: // TODO Auto-generated method stub
315: return this .getFeatures(Query.ALL);
316: }
317:
318: /* (non-Javadoc)
319: * @see org.geotools.data.FeatureSource#getFeatures(org.opengis.filter.Filter)
320: */
321: public FeatureCollection getFeatures(Filter f) throws IOException {
322: FeatureCollection fc = new DefaultFeatureCollection(null, type);
323: boolean refine = (f instanceof BBOXImpl);
324:
325: for (Iterator i = getCandidates(f).iterator(); i.hasNext();) {
326: Feature next = (Feature) i.next();
327:
328: if (refine || f.evaluate(next)) {
329: fc.add(next);
330: }
331: }
332:
333: return fc;
334: }
335:
336: /* (non-Javadoc)
337: * @see org.geotools.data.FeatureSource#getSchema()
338: */
339: public FeatureType getSchema() {
340: // TODO Auto-generated method stub
341: return type;
342: }
343:
344: /* (non-Javadoc)
345: * @see org.geotools.data.FeatureSource#removeFeatureListener(org.geotools.data.FeatureListener)
346: */
347: public void removeFeatureListener(FeatureListener arg0) {
348: // TODO Auto-generated method stub
349: }
350:
351: private Query adapt(Query q) {
352: if (q.getTypeName() == null) {
353: DefaultQuery r = new DefaultQuery(q);
354: r.setTypeName(this.type.getTypeName());
355:
356: return r;
357: } else {
358: return q;
359: }
360: }
361: }
|