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 com.vividsolutions.jts.geom.Envelope;
019:
020: import org.geotools.data.shapefile.shp.IndexFile;
021: import org.geotools.index.quadtree.IndexStore;
022: import org.geotools.index.quadtree.Node;
023: import org.geotools.index.quadtree.QuadTree;
024: import org.geotools.index.quadtree.StoreException;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.nio.ByteBuffer;
030: import java.nio.ByteOrder;
031: import java.nio.channels.FileChannel;
032: import java.util.logging.Level;
033: import java.util.logging.Logger;
034:
035: /**
036: * DOCUMENT ME!
037: *
038: * @author Tommaso Nolli
039: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/shapefile/src/main/java/org/geotools/index/quadtree/fs/FileSystemIndexStore.java $
040: */
041: public class FileSystemIndexStore implements IndexStore {
042: private static final Logger LOGGER = org.geotools.util.logging.Logging
043: .getLogger("org.geotools.index.quadtree");
044: private File file;
045: private byte byteOrder;
046:
047: /**
048: * Constructor. The byte order defaults to NEW_MSB_ORDER
049: *
050: * @param file
051: */
052: public FileSystemIndexStore(File file) {
053: this .file = file;
054: this .byteOrder = IndexHeader.NEW_MSB_ORDER;
055: }
056:
057: /**
058: * Constructor
059: *
060: * @param file
061: * @param byteOrder
062: */
063: public FileSystemIndexStore(File file, byte byteOrder) {
064: this .file = file;
065: this .byteOrder = byteOrder;
066: }
067:
068: /**
069: * @see org.geotools.index.quadtree.IndexStore#store(org.geotools.index.quadtree.QuadTree)
070: */
071: public void store(QuadTree tree) throws StoreException {
072: // For efficiency, trim the tree
073: tree.trim();
074:
075: // Open the stream...
076: FileOutputStream fos = null;
077: FileChannel channel = null;
078:
079: try {
080: fos = new FileOutputStream(file);
081: channel = fos.getChannel();
082:
083: ByteBuffer buf = ByteBuffer.allocate(8);
084:
085: if (this .byteOrder > IndexHeader.NATIVE_ORDER) {
086: LOGGER.finest("Writing file header");
087:
088: IndexHeader header = new IndexHeader(byteOrder);
089: header.writeTo(buf);
090: buf.flip();
091: channel.write(buf);
092: }
093:
094: ByteOrder order = byteToOrder(this .byteOrder);
095:
096: buf.clear();
097: buf.order(order);
098:
099: buf.putInt(tree.getNumShapes());
100: buf.putInt(tree.getMaxDepth());
101: buf.flip();
102:
103: channel.write(buf);
104:
105: this .writeNode(tree.getRoot(), channel, order);
106: } catch (IOException e) {
107: throw new StoreException(e);
108: } finally {
109: try {
110: channel.close();
111: } catch (Exception e) {
112: }
113:
114: try {
115: fos.close();
116: } catch (Exception e) {
117: }
118: }
119: }
120:
121: /**
122: * Wites a tree node to the qix file
123: *
124: * @param node The node
125: * @param channel DOCUMENT ME!
126: * @param order byte order
127: *
128: * @throws IOException
129: * @throws StoreException DOCUMENT ME!
130: */
131: private void writeNode(Node node, FileChannel channel,
132: ByteOrder order) throws IOException, StoreException {
133: int offset = this .getSubNodeOffset(node);
134:
135: ByteBuffer buf = ByteBuffer.allocate((4 * 8) + (3 * 4)
136: + (node.getNumShapeIds() * 4));
137:
138: buf.order(order);
139: buf.putInt(offset);
140:
141: Envelope env = node.getBounds();
142: buf.putDouble(env.getMinX());
143: buf.putDouble(env.getMinY());
144: buf.putDouble(env.getMaxX());
145: buf.putDouble(env.getMaxY());
146:
147: buf.putInt(node.getNumShapeIds());
148:
149: for (int i = 0; i < node.getNumShapeIds(); i++) {
150: buf.putInt(node.getShapeId(i));
151: }
152:
153: buf.putInt(node.getNumSubNodes());
154: buf.flip();
155:
156: channel.write(buf);
157:
158: for (int i = 0; i < node.getNumSubNodes(); i++) {
159: this .writeNode(node.getSubNode(i), channel, order);
160: }
161: }
162:
163: /**
164: * Calculates the offset
165: *
166: * @param node
167: *
168: *
169: * @throws StoreException DOCUMENT ME!
170: */
171: private int getSubNodeOffset(Node node) throws StoreException {
172: int offset = 0;
173: Node tmp = null;
174:
175: for (int i = 0; i < node.getNumSubNodes(); i++) {
176: tmp = node.getSubNode(i);
177: offset += (4 * 8); // Envelope size
178: offset += ((tmp.getNumShapeIds() + 3) * 4); // Entries size + 3
179: offset += this .getSubNodeOffset(tmp);
180: }
181:
182: return offset;
183: }
184:
185: /**
186: * Loads a quadrtee stored in a '.qix' file. <b>WARNING:</b> The resulting
187: * quadtree will be immutable; if you perform an insert, an
188: * <code>UnsupportedOperationException</code> will be thrown.
189: *
190: * @see org.geotools.index.quadtree.IndexStore#load()
191: */
192: public QuadTree load(IndexFile indexfile) throws StoreException {
193: QuadTree tree = null;
194:
195: try {
196: if (LOGGER.isLoggable(Level.FINEST)) {
197: LOGGER.finest("Opening QuadTree "
198: + this .file.getCanonicalPath());
199: }
200:
201: final FileInputStream fis = new FileInputStream(file);
202: final FileChannel channel = fis.getChannel();
203:
204: IndexHeader header = new IndexHeader(channel);
205:
206: ByteOrder order = byteToOrder(header.getByteOrder());
207: ByteBuffer buf = ByteBuffer.allocate(8);
208: buf.order(order);
209: channel.read(buf);
210: buf.flip();
211:
212: tree = new QuadTree(buf.getInt(), buf.getInt(), indexfile) {
213: public void insert(int recno, Envelope bounds) {
214: throw new UnsupportedOperationException(
215: "File quadtrees are immutable");
216: }
217:
218: public boolean trim() {
219: return false;
220: }
221:
222: public void close() throws StoreException {
223: super .close();
224: try {
225: channel.close();
226: fis.close();
227: } catch (IOException e) {
228: throw new StoreException(e);
229: }
230: }
231: };
232:
233: tree.setRoot(FileSystemNode.readNode(0, null, channel,
234: order));
235:
236: LOGGER.finest("QuadTree opened");
237: } catch (IOException e) {
238: throw new StoreException(e);
239: }
240:
241: return tree;
242: }
243:
244: /**
245: * DOCUMENT ME!
246: *
247: * @param order
248: *
249: */
250: private static ByteOrder byteToOrder(byte order) {
251: ByteOrder ret = null;
252:
253: switch (order) {
254: case IndexHeader.NATIVE_ORDER:
255: ret = ByteOrder.nativeOrder();
256:
257: break;
258:
259: case IndexHeader.LSB_ORDER:
260: case IndexHeader.NEW_LSB_ORDER:
261: ret = ByteOrder.LITTLE_ENDIAN;
262:
263: break;
264:
265: case IndexHeader.MSB_ORDER:
266: case IndexHeader.NEW_MSB_ORDER:
267: ret = ByteOrder.BIG_ENDIAN;
268:
269: break;
270: }
271:
272: return ret;
273: }
274:
275: /**
276: * DOCUMENT ME!
277: *
278: * @return Returns the byteOrder.
279: */
280: public int getByteOrder() {
281: return this .byteOrder;
282: }
283:
284: /**
285: * DOCUMENT ME!
286: *
287: * @param byteOrder The byteOrder to set.
288: */
289: public void setByteOrder(byte byteOrder) {
290: this.byteOrder = byteOrder;
291: }
292: }
|