001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, 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: */
017: package org.geotools.renderer.shape;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.URL;
024: import java.nio.channels.Channels;
025: import java.nio.channels.ReadableByteChannel;
026: import java.util.Collection;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.logging.Level;
030:
031: import org.geotools.data.DataSourceException;
032: import org.geotools.data.shapefile.indexed.RecordNumberTracker;
033: import org.geotools.data.shapefile.shp.IndexFile;
034: import org.geotools.data.shapefile.shp.ShapefileReader;
035: import org.geotools.index.Data;
036: import org.geotools.index.LockTimeoutException;
037: import org.geotools.index.TreeException;
038: import org.geotools.index.quadtree.QuadTree;
039: import org.geotools.index.quadtree.StoreException;
040: import org.geotools.index.quadtree.fs.FileSystemIndexStore;
041: import org.geotools.index.rtree.RTree;
042: import org.geotools.index.rtree.fs.FileSystemPageStore;
043:
044: import com.vividsolutions.jts.geom.Envelope;
045:
046: /**
047: * Encapsulates index information for a layer in the MapContext. The associated layer can be
048: * obtained by
049: *
050: * @author jones
051: * @source $URL:
052: * http://svn.geotools.org/geotools/branches/2.2.x/ext/shaperenderer/src/org/geotools/renderer/shape/IndexInfo.java $
053: */
054: public class IndexInfo {
055: static final byte TREE_NONE = 0;
056: static final byte R_TREE = 1;
057: static final byte QUAD_TREE = 2;
058: final byte treeType;
059: final URL treeURL;
060: final URL shxURL;
061: private RTree rtree;
062: private QuadTree qtree;
063:
064: public IndexInfo(byte treeType, URL treeURL, URL shxURL) {
065: this .treeType = treeType;
066: this .treeURL = treeURL;
067: this .shxURL = shxURL;
068: }
069:
070: /**
071: * RTree query
072: *
073: * @param bbox
074: * @return
075: * @throws DataSourceException
076: * @throws IOException
077: */
078: List queryRTree(Envelope bbox) throws DataSourceException,
079: IOException {
080: List goodRecs = null;
081:
082: try {
083: if ((rtree != null) && !bbox.contains(rtree.getBounds())) {
084: goodRecs = rtree.search(bbox);
085: }
086: } catch (LockTimeoutException le) {
087: throw new DataSourceException("Error querying RTree", le);
088: } catch (TreeException re) {
089: throw new DataSourceException("Error querying RTree", re);
090: } finally {
091: try {
092: rtree.close();
093: } catch (Exception ee) {
094: }
095: }
096:
097: return goodRecs;
098: }
099:
100: /**
101: * QuadTree Query
102: *
103: * @param bbox
104: * @return
105: * @throws DataSourceException
106: * @throws IOException
107: * @throws TreeException DOCUMENT ME!
108: */
109: Collection queryQuadTree(Envelope bbox) throws DataSourceException,
110: IOException, TreeException {
111: Collection tmp = null;
112:
113: try {
114: // old code was checking the resulting collection wasn't empty and it that
115: // case it closed the qtree straight away. qtree gets closed anyways with
116: // this code path, but it's quite a bit faster because it avoid one disk access
117: // just to check the collection is not empty
118: if ((qtree != null)
119: && !bbox.contains(qtree.getRoot().getBounds()))
120: return qtree.search(bbox);
121: } catch (Exception e) {
122: ShapefileRenderer.LOGGER.warning(e.getLocalizedMessage());
123: }
124:
125: return null;
126: }
127:
128: /**
129: * Convenience method for opening an RTree index.
130: *
131: * @return A new RTree.
132: * @throws IOException If an error occurs during creation.
133: * @throws DataSourceException DOCUMENT ME!
134: */
135: RTree openRTree() throws IOException {
136: File file = new File(treeURL.getPath());
137:
138: RTree ret = null;
139:
140: try {
141: FileSystemPageStore fps = new FileSystemPageStore(file);
142: ret = new RTree(fps);
143: } catch (TreeException re) {
144: throw new DataSourceException("Error opening RTree", re);
145: }
146:
147: return ret;
148: }
149:
150: /**
151: * Convenience method for opening a QuadTree index.
152: *
153: * @return A new QuadTree
154: * @throws StoreException
155: */
156: QuadTree openQuadTree() throws StoreException {
157: File file = new File(treeURL.getPath());
158: FileSystemIndexStore store = new FileSystemIndexStore(file);
159:
160: try {
161: return store.load(openIndexFile());
162: } catch (IOException e) {
163: throw new StoreException(e);
164: }
165: }
166:
167: /**
168: * Convenience method for opening a ShapefileReader.
169: *
170: * @return An IndexFile
171: * @throws IOException
172: */
173: IndexFile openIndexFile() throws IOException {
174: ReadableByteChannel rbc = getReadChannel(shxURL);
175:
176: if (rbc == null) {
177: return null;
178: }
179:
180: // return new IndexFile(rbc, this.useMemoryMappedBuffer);
181: return new IndexFile(rbc, false);
182: }
183:
184: /**
185: * Obtain a ReadableByteChannel from the given URL. If the url protocol is file, a FileChannel
186: * will be returned. Otherwise a generic channel will be obtained from the urls input stream.
187: *
188: * @param url DOCUMENT ME!
189: * @return DOCUMENT ME!
190: * @throws IOException DOCUMENT ME!
191: */
192: private ReadableByteChannel getReadChannel(URL url)
193: throws IOException {
194: ReadableByteChannel channel = null;
195:
196: if (url.getProtocol().equals("file")) {
197: File file = new File(url.getFile());
198:
199: if (!file.exists() || !file.canRead()) {
200: throw new IOException(
201: "File either doesn't exist or is unreadable : "
202: + file);
203: }
204:
205: FileInputStream in = new FileInputStream(file);
206: channel = in.getChannel();
207: } else {
208: InputStream in = url.openConnection().getInputStream();
209: channel = Channels.newChannel(in);
210: }
211:
212: return channel;
213: }
214:
215: //
216: // public ShapefileReader.Record getNextRecord(ShapefileReader shpreader, Envelope bbox) throws
217: // Exception {
218: // if( treeType== IndexInfo.TREE_GRX || treeType== TREE_QIX){
219: //
220: // List goodRecs = null;
221: // try {
222: // goodRecs = queryTree(bbox);
223: // } catch (TreeException e) {
224: // throw new IOException("Error querying index: " +
225: // e.getMessage());
226: // }
227: // }
228: // ShapefileReader.Record record = shpreader
229: // .nextRecord();
230: // return record;
231: // }
232: private Collection queryTree(Envelope bbox) throws IOException,
233: TreeException {
234: if (treeType == IndexInfo.R_TREE) {
235: return queryRTree(bbox);
236: } else if (treeType == IndexInfo.QUAD_TREE) {
237: return queryQuadTree(bbox);
238: }
239:
240: // should not happen
241: return null;
242: }
243:
244: static class Reader implements RecordNumberTracker {
245: private ShapefileReader shp;
246: Iterator goodRecs;
247: private int recno = 1;
248: private Data next;
249: private IndexInfo info;
250:
251: public Reader(IndexInfo info, ShapefileReader reader,
252: Envelope bbox) throws IOException {
253: shp = reader;
254:
255: try {
256:
257: if (info.treeType == R_TREE) {
258: info.rtree = info.openRTree();
259: } else if (info.treeType == QUAD_TREE) {
260: info.qtree = info.openQuadTree();
261: }
262:
263: Collection queryTree = info.queryTree(bbox);
264: if (queryTree != null)
265: goodRecs = queryTree.iterator();
266: } catch (Exception e) {
267: ShapefileRenderer.LOGGER
268: .log(
269: Level.FINE,
270: "Exception occured attempting to use indexing:",
271: e);
272: goodRecs = null;
273: }
274:
275: this .info = info;
276: }
277:
278: public int getRecordNumber() {
279: return this .recno;
280: }
281:
282: public boolean hasNext() throws IOException {
283: if (this .goodRecs != null) {
284: if (next != null)
285: return true;
286: if (this .goodRecs.hasNext()) {
287:
288: next = (Data) goodRecs.next();
289: this .recno = ((Integer) next.getValue(0))
290: .intValue();
291: return true;
292: }
293: return false;
294: }
295:
296: return shp.hasNext();
297: }
298:
299: public ShapefileReader.Record next() throws IOException {
300: if (!hasNext())
301: throw new IndexOutOfBoundsException(
302: "No more features in reader");
303: if (this .goodRecs != null) {
304:
305: Long l = (Long) next.getValue(1);
306: ShapefileReader.Record record = shp.recordAt(l
307: .intValue());
308: next = null;
309: return record;
310: }
311: recno++;
312: return shp.nextRecord();
313: }
314:
315: public void close() throws IOException {
316: shp.close();
317: try {
318: if (info.qtree != null) {
319: info.qtree.close(goodRecs);
320: info.qtree.close();
321: }
322: } catch (StoreException e) {
323: // TODO Auto-generated catch block
324: e.printStackTrace();
325: }
326: }
327: }
328: }
|