001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-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.index.quadtree.fs;
017:
018: import java.io.IOException;
019: import java.nio.ByteBuffer;
020: import java.nio.ByteOrder;
021: import java.nio.IntBuffer;
022: import java.nio.channels.FileChannel;
023:
024: import org.geotools.index.quadtree.Node;
025: import org.geotools.index.quadtree.StoreException;
026:
027: import com.vividsolutions.jts.geom.Envelope;
028:
029: /**
030: * DOCUMENT ME!
031: *
032: * @author Tommaso Nolli
033: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/shapefile/src/main/java/org/geotools/index/quadtree/fs/FileSystemNode.java $
034: */
035: public class FileSystemNode extends Node {
036: private ScrollingBuffer buffer;
037: private ByteOrder order;
038: private int subNodeStartByte;
039: private int subNodesLength;
040: private int numSubNodes;
041:
042: /**
043: * DOCUMENT ME!
044: *
045: * @param bounds
046: * @param channel DOCUMENT ME!
047: * @param order DOCUMENT ME!
048: * @param startByte DOCUMENT ME!
049: * @param subNodesLength DOCUMENT ME!
050: */
051: FileSystemNode(Envelope bounds, int id, Node parent,
052: ScrollingBuffer buffer, int startByte, int subNodesLength) {
053: super (bounds, id, parent);
054: this .buffer = buffer;
055: this .subNodeStartByte = startByte;
056: this .subNodesLength = subNodesLength;
057: }
058:
059: public Node copy() throws IOException {
060: FileSystemNode copy = new FileSystemNode(getBounds(), id,
061: getParent(), buffer, subNodeStartByte, subNodesLength);
062: copy.numShapesId = numShapesId;
063: copy.shapesId = new int[numShapesId];
064: System.arraycopy(shapesId, 0, copy.shapesId, 0, numShapesId);
065: copy.numSubNodes = numSubNodes;
066: return copy;
067: }
068:
069: /**
070: * DOCUMENT ME!
071: *
072: * @return Returns the numSubNodes.
073: */
074: public int getNumSubNodes() {
075: return this .numSubNodes;
076: }
077:
078: /**
079: * DOCUMENT ME!
080: *
081: * @param numSubNodes The numSubNodes to set.
082: */
083: public void setNumSubNodes(int numSubNodes) {
084: this .numSubNodes = numSubNodes;
085: }
086:
087: /**
088: * DOCUMENT ME!
089: *
090: * @return Returns the subNodeStartByte.
091: */
092: public int getSubNodeStartByte() {
093: return this .subNodeStartByte;
094: }
095:
096: /**
097: * DOCUMENT ME!
098: *
099: * @return Returns the subNodesLength.
100: */
101: public int getSubNodesLength() {
102: return this .subNodesLength;
103: }
104:
105: /**
106: * @see org.geotools.index.quadtree.Node#getSubNode(int)
107: */
108: public Node getSubNode(int pos) throws StoreException {
109: if (this .subNodes.size() > pos) {
110: return super .getSubNode(pos);
111: }
112:
113: try {
114: FileSystemNode subNode = null;
115:
116: // Getting prec subNode...
117: int offset = this .subNodeStartByte;
118:
119: if (pos > 0) {
120: subNode = (FileSystemNode) getSubNode(pos - 1);
121: offset = subNode.getSubNodeStartByte()
122: + subNode.getSubNodesLength();
123: }
124:
125: buffer.goTo(offset);
126: for (int i = 0, ii = subNodes.size(); i < ((pos + 1) - ii); i++) {
127: subNode = readNode(pos, this , buffer);
128: this .addSubNode(subNode);
129: }
130: } catch (IOException e) {
131: throw new StoreException(e);
132: }
133:
134: return super .getSubNode(pos);
135: }
136:
137: /**
138: * DOCUMENT ME!
139: *
140: * @param channel
141: * @param order DOCUMENT ME!
142: *
143: *
144: * @throws IOException
145: */
146: public static FileSystemNode readNode(int id, Node parent,
147: FileChannel channel, ByteOrder order) throws IOException {
148: ScrollingBuffer buffer = new ScrollingBuffer(channel, order);
149: return readNode(id, parent, buffer);
150: }
151:
152: static FileSystemNode readNode(int id, Node parent,
153: ScrollingBuffer buf) throws IOException {
154: // offset
155: int offset = buf.getInt();
156: double x1;
157: double y1;
158: double x2;
159: double y2;
160:
161: // envelope
162: x1 = buf.getDouble();
163: y1 = buf.getDouble();
164: x2 = buf.getDouble();
165: y2 = buf.getDouble();
166: Envelope env = new Envelope(x1, x2, y1, y2);
167:
168: // shapes in this node
169: int numShapesId = buf.getInt();
170: int[] ids = new int[numShapesId];
171: buf.getIntArray(ids);
172: int numSubNodes = buf.getInt();
173:
174: // let's create the new node
175: FileSystemNode node = new FileSystemNode(env, id, parent, buf,
176: (int) buf.getPosition(), offset);
177: node.setShapesId(ids);
178: node.setNumSubNodes(numSubNodes);
179:
180: return node;
181: }
182:
183: /**
184: * A utility class to access file contents by using a single scrolling buffer reading
185: * file contents with a minimum of 8kb per access
186: */
187: private static class ScrollingBuffer {
188: FileChannel channel;
189: ByteOrder order;
190: ByteBuffer buffer;
191: /** the initial position of the buffer in the channel */
192: long bufferStart;
193:
194: public ScrollingBuffer(FileChannel channel, ByteOrder order)
195: throws IOException {
196: this .channel = channel;
197: this .order = order;
198: this .bufferStart = channel.position();
199:
200: // start with an 8kb buffer
201: this .buffer = ByteBuffer.allocateDirect(8 * 1024);
202: this .buffer.order(order);
203: channel.read(buffer);
204: buffer.flip();
205: }
206:
207: public int getInt() throws IOException {
208: if (buffer.remaining() < 4)
209: refillBuffer(4);
210: return buffer.getInt();
211: }
212:
213: public double getDouble() throws IOException {
214: if (buffer.remaining() < 8)
215: refillBuffer(8);
216: return buffer.getDouble();
217: }
218:
219: public void getIntArray(int[] array) throws IOException {
220: int size = array.length * 4;
221: if (buffer.remaining() < size)
222: refillBuffer(size);
223: // read the array using a view
224: IntBuffer intView = buffer.asIntBuffer();
225: intView.limit(array.length);
226: intView.get(array);
227: // don't forget to update the original buffer position, since the view is independent
228: buffer.position(buffer.position() + size);
229: }
230:
231: /**
232: *
233: * @param requiredSize
234: * @throws IOException
235: */
236: void refillBuffer(int requiredSize) throws IOException {
237: // compute the actual position up to we have read something
238: long currentPosition = bufferStart + buffer.position();
239: // if the buffer is not big enough enlarge it
240: if (buffer.capacity() < requiredSize) {
241: int size = buffer.capacity();
242: while (size < requiredSize)
243: size *= 2;
244: buffer = ByteBuffer.allocateDirect(size);
245: buffer.order(order);
246: }
247: readBuffer(currentPosition);
248: }
249:
250: private void readBuffer(long currentPosition)
251: throws IOException {
252: channel.position(currentPosition);
253: buffer.clear();
254: channel.read(buffer);
255: buffer.flip();
256: bufferStart = currentPosition;
257: }
258:
259: /**
260: * Jumps the buffer to the specified position in the file
261: * @param newPosition
262: * @throws IOException
263: */
264: public void goTo(long newPosition) throws IOException {
265: // if the new position is already in the buffer, just move the buffer position
266: // otherwise we have to reload it
267: if (newPosition >= bufferStart
268: && newPosition <= bufferStart + buffer.limit()) {
269: buffer.position((int) (newPosition - bufferStart));
270: } else {
271: readBuffer(newPosition);
272: }
273: }
274:
275: /**
276: * Returns the absolute position of the next byte that will be read
277: * @return
278: */
279: public long getPosition() {
280: return bufferStart + buffer.position();
281: }
282: }
283: }
|