/** * H2GIS is a library that brings spatial support to the H2 Database Engine * <http://www.h2database.com>. H2GIS is developed by CNRS * <http://www.cnrs.fr/>. * * This code is part of the H2GIS project. H2GIS is free software; * you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation; * version 3.0 of the License. * * H2GIS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details <http://www.gnu.org/licenses/>. * * * For more information, please consult: <http://www.h2gis.org/> * or contact directly: info_at_h2gis.org */ package org.h2gis.functions.io.shp.internal; import com.vividsolutions.jts.geom.Geometry; import org.h2gis.functions.io.utility.ReadBufferManager; import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; /** * The general use of this class is: <CODE><PRE> * * FileChannel in = new FileInputStream("thefile.dbf").getChannel(); * ShapefileReader r = new ShapefileReader( in ) while (r.hasNext()) { Geometry * shape = (Geometry) r.nextRecord().shape() // do stuff } r.close(); * * </PRE></CODE> You don't have to immediately ask for the shape from the record. The * record will contain the bounds of the shape and will only read the shape when * the shape() method is called. This ShapefileReader.Record is the same object * every time, so if you need data from the Record, be sure to copy it. * * @author jamesm * @author aaime * @author Ian Schneider * @source $URL: * http://svn.geotools.org/geotools/tags/2.3.1/plugin/shapefile/src/org/geotools/data/shapefile/shp/ShapefileReader.java $ */ public class ShapefileReader { private ShapeHandler handler; private ShapefileHeader header; private FileChannel channel; private ReadBufferManager buffer; private ShapeType fileShapeType = ShapeType.UNDEFINED; /** * Creates a new instance of ShapeFile. * * @param channel * The ReadableByteChannel this reader will use. * @throws java.io.IOException * If problems arise. * @throws ShapefileException * If for some reason the file contains invalid records. */ public ShapefileReader(FileChannel channel) throws IOException, ShapefileException { this.channel = channel; init(); } // convenience to peak at a header /** * A short cut for reading the header from the given channel. * * @param channel * The channel to read from. * @throws java.io.IOException * If problems arise. * @return A ShapefileHeader object. */ public static ShapefileHeader readHeader(ReadableByteChannel channel) throws IOException { ByteBuffer buffer = ByteBuffer.allocateDirect(100); if (channel.read(buffer) != 100) { throw new EOFException("Premature end of header"); } buffer.flip(); ShapefileHeader header = new ShapefileHeader(); header.read(buffer); return header; } private void init() throws IOException, ShapefileException { header = readHeader(channel); fileShapeType = header.getShapeType(); handler = fileShapeType.getShapeHandler(); // recordHeader = ByteBuffer.allocateDirect(8); // recordHeader.order(ByteOrder.BIG_ENDIAN); if (handler == null) { throw new IOException("Unsuported shape type:" + fileShapeType); } buffer = new ReadBufferManager(channel); } /** * Get the header. Its parsed in the constructor. * * @return The header that is associated with this file. */ public ShapefileHeader getHeader() { return header; } // do important cleanup stuff. // Closes channel ! /** * Clean up any resources. Closes the channel. * * @throws java.io.IOException * If errors occur while closing the channel. */ public void close() throws IOException { if (channel != null && channel.isOpen()) { channel.close(); } channel = null; header = null; } /** * Fetch the next record information. * * @param offset * @throws java.io.IOException * @return The record instance associated with this reader. */ public Geometry geomAt(int offset) throws IOException { // need to update position buffer.position(offset); // record header buffer.skip(8); // shape record is all little endian buffer.order(ByteOrder.LITTLE_ENDIAN); // read the type, handlers don't need it ShapeType recordType = ShapeType.forID(buffer.getInt()); // this usually happens if the handler logic is bunk, // but bad files could exist as well... if (recordType != ShapeType.NULL && recordType != fileShapeType) { throw new IllegalStateException("ShapeType changed illegally from " + fileShapeType + " to " + recordType); } return handler.read(buffer, recordType); } /** * @param handler * The handler to set. */ public void setHandler(ShapeHandler handler) { this.handler = handler; } }