⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shapefilereader.java

📁 shape file read and write
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, Geotools Project Managment Committee (PMC)
 *    (C) 2002, Centre for Computational Geography
 *
 *    This library 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; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library 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.
 */
package org.geotools.data.shapefile.shp;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;

import org.geotools.data.DataSourceException;
import org.geotools.data.shapefile.FileReader;
import org.geotools.data.shapefile.ShpFileType;
import org.geotools.data.shapefile.ShpFiles;
import org.geotools.data.shapefile.StreamLogging;
import org.geotools.resources.NIOUtilities;

/**
 * The general use of this class is: <CODE><PRE>
 * 
 * FileChannel in = new FileInputStream(&quot;thefile.dbf&quot;).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/trunk/gt/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/shp/ShapefileReader.java $
 */
public class ShapefileReader implements FileReader {

    /**
     * The reader returns only one Record instance in its lifetime. The record
     * contains the current record information.
     */
    public final class Record {
        int length;

        int number = 0;

        int offset; // Relative to the whole file

        int start = 0; // Relative to the current loaded buffer

        /** The minimum X value. */
        public double minX;

        /** The minimum Y value. */
        public double minY;

        /** The maximum X value. */
        public double maxX;

        /** The maximum Y value. */
        public double maxY;

        ShapeType type;

        int end = 0; // Relative to the whole file

        Object shape = null;

        /** Fetch the shape stored in this record. */
        public Object shape() {
            if (shape == null) {
                buffer.position(start);
                buffer.order(ByteOrder.LITTLE_ENDIAN);
                shape = handler.read(buffer, type);
            }
            return shape;
        }

        public int offset() {
            return offset;
        }

        /** A summary of the record. */
        public String toString() {
            return "Record " + number + " length " + length + " bounds " + minX
                    + "," + minY + " " + maxX + "," + maxY;
        }
    }

    private ShapeHandler handler;

    private ShapefileHeader header;

    private ReadableByteChannel channel;

    ByteBuffer buffer;

    private ShapeType fileShapeType = ShapeType.UNDEFINED;

    private ByteBuffer headerTransfer;

    private final Record record = new Record();

    private final boolean randomAccessEnabled;

    private boolean useMemoryMappedBuffer;

    private long currentOffset = 0L;
    private StreamLogging streamLogger = new StreamLogging("Shapefile Reader");

    /**
     * Creates a new instance of ShapeFile.
     * 
     * @param shapefileFiles
     *                The ReadableByteChannel this reader will use.
     * @param strict
     *                True to make the header parsing throw Exceptions if the
     *                version or magic number are incorrect.
     * @throws IOException
     *                 If problems arise.
     * @throws ShapefileException
     *                 If for some reason the file contains invalid records.
     */
    public ShapefileReader(ShpFiles shapefileFiles, boolean strict,
            boolean useMemoryMapped) throws IOException, ShapefileException {
        this.channel = shapefileFiles.getReadChannel(ShpFileType.SHP, this);
        this.useMemoryMappedBuffer = useMemoryMapped;
        streamLogger.open();
        randomAccessEnabled = channel instanceof FileChannel;
        init(strict);
    }

    // convenience to peak at a header
    /**
     * A short cut for reading the header from the given channel.
     * 
     * @param channel
     *                The channel to read from.
     * @param strict
     *                True to make the header parsing throw Exceptions if the
     *                version or magic number are incorrect.
     * @throws IOException
     *                 If problems arise.
     * @return A ShapefileHeader object.
     */
    public static ShapefileHeader readHeader(ReadableByteChannel channel,
            boolean strict) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(100);
        if (fill(buffer, channel) == -1) {
            throw new EOFException("Premature end of header");
        }
        buffer.flip();
        ShapefileHeader header = new ShapefileHeader();
        header.read(buffer, strict);
        NIOUtilities.clean(buffer);
        return header;
    }

    // ensure the capacity of the buffer is of size by doubling the original
    // capacity until it is big enough
    // this may be naiive and result in out of MemoryError as implemented...
    public static ByteBuffer ensureCapacity(ByteBuffer buffer, int size,
            boolean useMemoryMappedBuffer) {
        // This sucks if you accidentally pass is a MemoryMappedBuffer of size
        // 80M
        // like I did while messing around, within moments I had 1 gig of
        // swap...
        if (buffer.isReadOnly() || useMemoryMappedBuffer) {
            return buffer;
        }

        int limit = buffer.limit();
        while (limit < size) {
            limit *= 2;
        }
        if (limit != buffer.limit()) {
            // if (record.ready) {
            buffer = ByteBuffer.allocateDirect(limit);
            // }
            // else {
            // throw new IllegalArgumentException("next before hasNext");
            // }
        }
        return buffer;
    }

    // for filling a ReadableByteChannel
    public static int fill(ByteBuffer buffer, ReadableByteChannel channel)
            throws IOException {
        int r = buffer.remaining();
        // channel reads return -1 when EOF or other error
        // because they a non-blocking reads, 0 is a valid return value!!
        while (buffer.remaining() > 0 && r != -1) {
            r = channel.read(buffer);
        }
        if (r == -1) {
            buffer.limit(buffer.position());
        }
        return r;
    }

    private void init(boolean strict) throws IOException, ShapefileException {
        header = readHeader(channel, strict);
        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);
        }

        if (channel instanceof FileChannel && useMemoryMappedBuffer) {
            FileChannel fc = (FileChannel) channel;
            buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
            buffer.position(100);
            this.currentOffset = 0;
        } else {
            // force useMemoryMappedBuffer to false
            this.useMemoryMappedBuffer = false;
            // start with 8K buffer
            buffer = ByteBuffer.allocateDirect(8 * 1024);
            fill(buffer, channel);
            buffer.flip();
            this.currentOffset = 100;
        }

        headerTransfer = ByteBuffer.allocate(8);
        headerTransfer.order(ByteOrder.BIG_ENDIAN);

        // make sure the record end is set now...
        record.end = this.toFileOffset(buffer.position());
    }

    /**
     * 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 IOException
     *                 If errors occur while closing the channel.
     */
    public void close() throws IOException {
        if (channel.isOpen()) {
            channel.close();
            streamLogger.close();
        }
        if (buffer instanceof MappedByteBuffer) {
            NIOUtilities.clean(buffer);
        }
        channel = null;
        header = null;
    }

    public boolean supportsRandomAccess() {
        return randomAccessEnabled;
    }

    /**
     * If there exists another record. Currently checks the stream for the
     * presence of 8 more bytes, the length of a record. If this is true and the
     * record indicates the next logical record number, there exists more
     * records.
     * 
     * @throws IOException
     * @return True if has next record, false otherwise.
     */
    public boolean hasNext() throws IOException {
        return this.hasNext(true);
    }

    /**
     * If there exists another record. Currently checks the stream for the
     * presence of 8 more bytes, the length of a record. If this is true and the
     * record indicates the next logical record number (if checkRecord == true),
     * there exists more records.
     * 
     * @param checkRecno
     *                If true then record number is checked
     * @throws IOException
     * @return True if has next record, false otherwise.
     */
    private boolean hasNext(boolean checkRecno) throws IOException {
        // mark current position
        int position = buffer.position();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -