Package com.bbn.openmap.layer.shape

Source Code of com.bbn.openmap.layer.shape.SpatialIndex$MemoryIndex

// **********************************************************************
//
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/shape/SpatialIndex.java,v $
// $RCSfile: SpatialIndex.java,v $
// $Revision: 1.6.2.8 $
// $Date: 2008/10/10 00:35:11 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.layer.shape;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.ImageIcon;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.dataAccess.shape.DbfHandler;
import com.bbn.openmap.dataAccess.shape.EsriGraphicFactory;
import com.bbn.openmap.io.BinaryBufferedFile;
import com.bbn.openmap.io.BinaryFile;
import com.bbn.openmap.io.FormatException;
import com.bbn.openmap.omGraphics.DrawingAttributes;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.proj.coords.GeoCoordTransformation;
import com.bbn.openmap.util.Debug;

/**
* A Spatial Index is a variation on a Shape Index, adding the bounding box of
* the shape to the index.
* <p>
* The file has a 100 byte header identical to a Shape Index followed by <i>n
* </i> records.
* <p>
* The record layout of the spatial index is as follows:
* <p>
* <TABLE BORDER COLS=5 WIDTH="100%" >
* <TR>
* <TD ALIGN=CENTER><b><i>Position </i> </b></TD>
* <TD ALIGN=CENTER><b><i>Field </i> </b></TD>
* <TD ALIGN=CENTER><b><i>Value </i> </b></TD>
* <TD ALIGN=CENTER><b><i>Type </i> </b></TD>
* <TD ALIGN=CENTER><b><i>Byte Order </i> </b></TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 0</TD>
* <TD ALIGN=CENTER>Offset</TD>
* <TD ALIGN=CENTER>Offset</TD>
* <TD ALIGN=CENTER>Integer</TD>
* <TD ALIGN=CENTER>Big</TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 4</TD>
* <TD ALIGN=CENTER>Content Length</TD>
* <TD ALIGN=CENTER>Content Length</TD>
* <TD ALIGN=CENTER>Integer</TD>
* <TD ALIGN=CENTER>Big</TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 8</TD>
* <TD ALIGN=CENTER>Bounding Box</TD>
* <TD ALIGN=CENTER>Xmin</TD>
* <TD ALIGN=CENTER>Double</TD>
* <TD ALIGN=CENTER>Little</TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 16</TD>
* <TD ALIGN=CENTER>Bounding Box</TD>
* <TD ALIGN=CENTER>Ymin</TD>
* <TD ALIGN=CENTER>Double</TD>
* <TD ALIGN=CENTER>Little</TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 24</TD>
* <TD ALIGN=CENTER>Bounding Box</TD>
* <TD ALIGN=CENTER>Xmax</TD>
* <TD ALIGN=CENTER>Double</TD>
* <TD ALIGN=CENTER>Little</TD>
* </TR>
* <TR>
* <TD ALIGN=CENTER>Byte 32</TD>
* <TD ALIGN=CENTER>Bounding Box</TD>
* <TD ALIGN=CENTER>Ymax</TD>
* <TD ALIGN=CENTER>Double</TD>
* <TD ALIGN=CENTER>Little</TD>
* </TR>
* </TABLE>
*
* <H2>Usage</H2>
* <DT>java com.bbn.openmap.layer.shape.SpatialIndex -d file.ssx </DT>
* <DD><i>Dumps spatial index information, excluding bounding boxes to stdout.
* Useful for comparing to a shape index. </i></DD>
* <p>
* <DT>java com.bbn.openmap.layer.shape.SpatialIndex -d -b file.ssx </DT>
* <DD><i>Dumps spatial index information including bounding boxes to stdout.
* </i></DD>
* <p>
* <DT>java com.bbn.openmap.layer.shape.SpatialIndex -c file.shp</DT>
* <DD><i>Creates spatial index <code>file.ssx</code> from shape file
* <code>file.shp</code>. </i></DD>
* <p>
*
* <H2>Notes</H2>
* When reading the Shape file, the content length is the length of the record's
* contents, exclusive of the record header (8 bytes). So the size that we need
* to read in from the Shape file is actually denoted as ((contentLength * 2) +
* 8). This converts from 16bit units to 8 bit bytes and adds the 8 bytes for
* the record header.
*
* <H2>To Do</H2>
* <UL>
* <LI>index arcs</LI>
* <LI>index multipoints</LI>
* </UL>
*
* @author Tom Mitchell <tmitchell@bbn.com>
* @version $Revision: 1.6.2.8 $ $Date: 2008/10/10 00:35:11 $
* @see ShapeIndex
*/
public class SpatialIndex extends ShapeUtils {

    /** Size of a shape file header in bytes. */
    public final static int SHAPE_FILE_HEADER_LENGTH = 100;

    /** Size of a shape file record header in bytes. */
    public final static int SHAPE_RECORD_HEADER_LENGTH = 8;

    /** Size of the spatial index header in bytes. */
    public final static int SPATIAL_INDEX_HEADER_LENGTH = 100;

    /** Size of the spatial index record in bytes. */
    public final static int SPATIAL_INDEX_RECORD_LENGTH = 40;

    /** Default size for shape record buffer. */
    public final static int DEFAULT_SHAPE_RECORD_SIZE = 50000;

    /** The shape file. */
    protected BinaryBufferedFile shp;

    /**
     * The handler for dbf file information.
     */
    protected DbfHandler dbf;

    /** The icon to use for point objects. */
    protected ImageIcon pointIcon;

    /** The bounds of all the shapes in the shape file. */
    protected ESRIBoundingBox bounds = null;

    /**
     * The file name for the shape file, for opening/reopening.
     */
    protected String shpFileName;

    /**
     * A cached list of the SpatialIndex file entries, for repeated reference.
     */
    protected List entries;

    /**
     * A factory object to use to create OMGraphics from the shp file.
     */
    EsriGraphicFactory factory = new EsriGraphicFactory();

    /**
     * Opens a spatial index file for reading based on the location of the
     * provided shp file.
     *
     * @param ssxFilename the name of the spatial index file
     * @exception IOException if something goes wrong opening the file
     */
    public SpatialIndex(String shpFilename) throws IOException {
        this.shpFileName = shpFilename;
        if (Debug.debugging("spatialindex")) {
            Debug.output("SpatialIndex(" + shpFilename + ");");
        }
    }

    /**
     * Opens a spatial index file and it's associated shape file.
     *
     * @param ssxFilename the name of the spatial index file
     * @param shpFilename the name of the shape file
     * @exception IOException if something goes wrong opening the files
     * @deprecated ssx file is figured based on the shp file path
     */
    public SpatialIndex(String ssxFilename, String shpFilename)
            throws IOException {
        this(shpFilename);
    }

    /**
     * Figures out the ssx file name from the shp file name.
     *
     * @param shpFileName
     * @return
     */
    public static String ssx(String shpFileName) {
        String ret = null;
        if (shpFileName != null) {
            ret = shpFileName.substring(0, shpFileName.indexOf(".shp"))
                    + ".ssx";
        }
        return ret;
    }

    /**
     * Figures out the dbf file name from the shp file name.
     *
     * @param shpFileName
     * @return
     */
    public static String dbf(String shpFileName) {
        String ret = null;
        if (shpFileName != null) {
            ret = shpFileName.substring(0, shpFileName.indexOf(".shp"))
                    + ".dbf";
        }
        return ret;
    }

    /**
     * Get the box boundary containing all the shapes.
     */
    public ESRIBoundingBox getBounds() {
        if (bounds == null) {
            try {
                locateRecords(-180, -90, 180, 90);
            } catch (IOException ioe) {
                bounds = null;
            } catch (FormatException fe) {
                bounds = null;
            }
        }
        return bounds;
    }

    /**
     * Reset the bounds so they will be recalculated the next time a file is
     * read.
     */
    public void resetBounds() {
        bounds = null;
    }

    /**
     * Creates a record instance from the shape file data. Calls the appropriate
     * record constructor based on the shapeType, and passes the buffer and
     * offset to that constructor.
     *
     * @param shapeType the shape file's shape type, enumerated in
     *        <code>ShapeUtils</code>
     * @param b the buffer pointing to the raw record data
     * @param off the offset of the data starting point in the buffer
     * @exception IOException if something goes wrong reading the file
     * @see ShapeUtils
     */
    public ESRIRecord makeESRIRecord(int shapeType, byte[] b, int off)
            throws IOException {
        switch (shapeType) {
        case SHAPE_TYPE_NULL:
            return null;
        case SHAPE_TYPE_POINT:
            // return new ESRIPointRecord(b, off);
            return new ESRIPointRecord(b, off, pointIcon);
        case SHAPE_TYPE_POLYGON:
        case SHAPE_TYPE_ARC:
            // case SHAPE_TYPE_POLYLINE:
            return new ESRIPolygonRecord(b, off);
        case SHAPE_TYPE_MULTIPOINT:
            Debug.output("SpatialIndex.makeESRIRecord: Arc NYI");
            return null;
            // return new ESRIMultipointRecord(b, off);
        default:
            return null;
        }
    }

    /**
     * Locates records in the shape file that intersect with the given
     * rectangle. The spatial index is searched for intersections and the
     * appropriate records are read from the shape file.
     *
     * @param xmin the smaller of the x coordinates
     * @param ymin the smaller of the y coordinates
     * @param xmax the larger of the x coordinates
     * @param ymax the larger of the y coordinates
     * @return an array of records that intersect the given rectangle
     * @exception IOException if something goes wrong reading the files
     */
    public ESRIRecord[] locateRecords(double xmin, double ymin, double xmax,
                                      double ymax) throws IOException,
            FormatException {

        boolean gatherBounds = false;

        if (bounds == null) {
            bounds = new ESRIBoundingBox();
            gatherBounds = true;
        }

        if (Debug.debugging("spatialindex")) {
            Debug.output("locateRecords:");
            Debug.output("\txmin: " + xmin + "; ymin: " + ymin);
            Debug.output("\txmax: " + xmax + "; ymax: " + ymax);
        }

        byte ixRecord[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];
        int recNum = 0;
        Vector v = new Vector();
        int sRecordSize = DEFAULT_SHAPE_RECORD_SIZE;
        byte sRecord[] = new byte[sRecordSize];

        if (shpFileName == null) {
            return null;
        }

        BinaryBufferedFile ssx = new BinaryBufferedFile(ssx(shpFileName));
        if (shp == null) {
            shp = new BinaryBufferedFile(shpFileName);
        }

        // Need to figure out what the shape type is...
        ssx.seek(32);

        // int shapeType = readLEInt(ssx);
        // /
        ssx.byteOrder(false);
        int shapeType = ssx.readInteger();
        // /
        ssx.seek(100); // skip the file header

        while (true) {
            int result = ssx.read(ixRecord, 0, SPATIAL_INDEX_RECORD_LENGTH);
            // if (result == -1) {
            if (result <= 0) {
                break;// EOF
            } else {
                recNum++;
                double xmin2 = readLEDouble(ixRecord, 8);
                double ymin2 = readLEDouble(ixRecord, 16);
                double xmax2 = readLEDouble(ixRecord, 24);
                double ymax2 = readLEDouble(ixRecord, 32);
                if (Debug.debugging("spatialindexdetail")) {
                    Debug.output("Looking at rec num " + recNum);
                    Debug.output("  " + xmin2 + ", " + ymin2 + "\n  " + xmax2
                            + ", " + ymax2);
                }

                if (gatherBounds) {
                    bounds.addPoint(xmin2, ymin2);
                    bounds.addPoint(xmax2, ymax2);
                }

                if (intersects(xmin,
                        ymin,
                        xmax,
                        ymax,
                        xmin2,
                        ymin2,
                        xmax2,
                        ymax2)) {

                    int offset = readBEInt(ixRecord, 0);
                    int byteOffset = offset * 2;
                    int contentLength = readBEInt(ixRecord, 4);
                    int recordSize = (contentLength * 2) + 8;
                    // System.out.print(".");
                    // System.out.flush();

                    if (recordSize < 0) {
                        Debug.error("SpatialIndex: supposed to read record size of "
                                + recordSize);
                        break;
                    }

                    if (recordSize > sRecordSize) {
                        sRecordSize = recordSize;
                        if (Debug.debugging("spatialindexdetail")) {
                            Debug.output("Shapefile SpatialIndex record size: "
                                    + sRecordSize);
                        }
                        sRecord = new byte[sRecordSize];
                    }

                    if (Debug.debugging("spatialindex")) {
                        Debug.output("going to shp byteOffset = " + byteOffset
                                + " for record size = " + recordSize
                                + ", offset = " + offset + ", shape type = "
                                + shapeType);
                    }

                    try {
                        shp.seek(byteOffset);
                        int nBytes = shp.read(sRecord, 0, recordSize);
                        if (nBytes < recordSize) {
                            Debug.error("Shapefile SpatialIndex expected "
                                    + recordSize + " bytes, but got " + nBytes
                                    + " bytes instead.");
                        }

                        ESRIRecord record = makeESRIRecord(shapeType,
                                sRecord,
                                0);
                        v.addElement(record);
                    } catch (IOException ioe) {
                        Debug.error("SpatialIndex.locateRecords: IOException. ");
                        ioe.printStackTrace();
                        break;
                    }
                }
            }
        }

        if (Debug.debugging("spatialindex")) {
            Debug.output("Processed " + recNum + " records");
            Debug.output("Selected " + v.size() + " records");
        }
        int nRecords = v.size();

        ssx.close();
        shp.close();
        shp = null;
        ESRIRecord result[] = new ESRIRecord[nRecords];
        v.copyInto(result);
        return result;

    }

    /**
     * The factory is used to filter and create OMGraphics from a shape file.
     * This accessor is provided in order to allow you to modify the data
     * projection it uses, or the line type.
     *
     * @return
     */
    public EsriGraphicFactory getFactory() {
        if (factory == null) {
            factory = new EsriGraphicFactory();
        }
        return factory;
    }

    public void setFactory(EsriGraphicFactory factory) {
        this.factory = factory;
    }

    /**
     * Locates OMGraphics in the shape file that intersect with the given
     * rectangle. The spatial index is searched for intersections and the
     * appropriate OMGraphics are created from the shape file.
     *
     * @param xmin the smaller of the x coordinates
     * @param ymin the smaller of the y coordinates
     * @param xmax the larger of the x coordinates
     * @param ymax the larger of the y coordinates
     * @param list OMGraphicList to add OMGraphics to and return, if null one
     *        will be created.
     * @param drawingAttributes DrawingAttributes to set on the OMGraphics.
     * @param mapProj the Map Projection for the OMGraphics so they can be
     *        generated right after creation.
     * @param dataProj for preprojected data, a coordinate translator for the
     *        data's projection to use to translate the coordinates to decimal
     *        degree lat/lon. Can be null to leave the coordinates untouched.
     * @return an OMGraphicList containing OMGraphics that intersect the given
     *         rectangle
     * @exception IOException if something goes wrong reading the files
     */
    public OMGraphicList getOMGraphics(double xmin, double ymin, double xmax,
                                       double ymax, OMGraphicList list,
                                       DrawingAttributes drawingAttributes,
                                       Projection mapProj,
                                       GeoCoordTransformation dataProj)
            throws IOException, FormatException {

        if (Debug.debugging("spatialindex")) {
            Debug.output("locateRecords:");
            Debug.output("\txmin: " + xmin + "; ymin: " + ymin);
            Debug.output("\txmax: " + xmax + "; ymax: " + ymax);
        }

        if (list == null) {
            list = new OMGraphicList();
        }

        if (shp == null) {
            shp = new BinaryBufferedFile(shpFileName);
        }

        if (shp == null) {
            return list;
        }

        EsriGraphicFactory.ReadByteTracker byteTracker = new EsriGraphicFactory.ReadByteTracker();
        EsriGraphicFactory factory = getFactory();
        factory.setDataCoordTransformation(dataProj);

        OMGraphicList labels = new OMGraphicList();
        list.add(labels);

        for (Iterator it = entryIterator(dataProj); it.hasNext();) {
            Entry entry = (Entry) it.next();

            if (entry.intersects(xmin, ymin, xmax, ymax)) {

                try {

                    OMGraphic omg = (OMGraphic) factory.makeEsriGraphicFromRecord(entry.getByteOffset(),
                            shp,
                            drawingAttributes,
                            pointIcon,
                            byteTracker);

                    if (omg != null) {

                        if (dbf != null) {
                            omg = dbf.evaluate(omg, labels, mapProj);

                            if (omg == null) {
                                // Failed dbf test, should be ignored.
                                continue;
                            }
                        }

                        if (mapProj != null) {
                            omg.generate(mapProj);
                        }
                        list.add(omg);
                    }

                } catch (IOException ioe) {
                    Debug.error("SpatialIndex.locateRecords: IOException. ");
                    ioe.printStackTrace();
                    break;
                }
            }
        }

        if (shp != null) {
            shp.close();
            shp = null;
        }

        if (dbf != null) {
            dbf.close();
        }

        return list;

    }

    /**
     * Retrieves all OMGraphics in the shape file.
     *
     * @param retList OMGraphicList to add OMGraphics to and return, if null one
     *        will be created.
     * @param drawingAttributes DrawingAttributes to set on the OMGraphics.
     * @param mapProj the Map Projection for the OMGraphics so they can be
     *        generated right after creation. This will also be used by the
     *        DbfHandler, to determine if some OMGraphics should not be returned
     *        based on attribute settings.
     * @param dataProj for preprojected data, a coordinate translator for the
     *        data's projection to use to translate the coordinates to decimal
     *        degree lat/lon. Can be null to leave the coordinates untouched.
     * @return an OMGraphicList containing OMGraphics that intersect the given
     *         rectangle
     * @exception IOException if something goes wrong reading the files
     */
    public OMGraphicList getAllOMGraphics(OMGraphicList retList,
                                          DrawingAttributes drawingAttributes,
                                          Projection mapProj,
                                          GeoCoordTransformation dataProj)
            throws IOException, FormatException {

        if (retList == null) {
            retList = new OMGraphicList();
        }

        if (shp == null) {
            shp = new BinaryBufferedFile(shpFileName);
        }

        if (shp == null) {
            return retList;
        }

        EsriGraphicFactory factory = getFactory();
        factory.setDataCoordTransformation(dataProj);
        factory.getEsriGraphics(shp,
                drawingAttributes,
                pointIcon,
                mapProj,
                retList);

        shp.close();

        return retList;
    }

    /**
     * Takes the contents of the list and evaluates them against the information
     * contained in the DbfHandler set in this SpatialIndex class.
     *
     * @param retList the list of OMGraphics to evaluate.
     * @param mapProj the current map projection to be used by the DbfHandler to
     *        determine if some OMGraphics should be visible.
     * @return OMGraphicList containing OMGraphics modified/passing evaluations
     *         rules in the DbfHandler.
     */
    public OMGraphicList evaluateDbf(OMGraphicList retList, Projection mapProj) {

        if (dbf != null) {
            OMGraphicList labels = new OMGraphicList();
            retList.add(labels);

            OMGraphicList testList = new OMGraphicList();
            for (Iterator it = retList.iterator(); it.hasNext();) {
                OMGraphic omg = (OMGraphic) it.next();

                if (omg != null) {

                    omg = dbf.evaluate(omg, labels, mapProj);

                    if (mapProj != null) {
                        omg.generate(mapProj);
                    }
                    testList.add(omg);
                }
            }
            retList = testList;
        }

        return retList;
    }

    /**
     * Evaluates the OMGraphic against the DbfHandler rules.
     *
     * @param omg the OMGraphic to evaluate.
     * @param labels for DbfHandler label rules. Assumes that you are managing
     *        display of the labels list.
     * @param mapProj for DbfHandler scale rules.
     * @return OMGraphic if it passes the rules.
     */
    public OMGraphic evaluate(OMGraphic omg, OMGraphicList labels,
                              Projection mapProj) {
        if (dbf != null) {
            omg = dbf.evaluate(omg, labels, mapProj);
        }

        return omg;
    }

    /**
     * Skips the BinaryFile for the shp data to the offset and reads the record
     * data there, creating an OMGraphic from that data.
     *
     * @param byteOffset, usually gotten from an Entry object.
     * @param drawingAttributes
     * @return
     * @throws IOException
     * @throws FormatException
     */
    public OMGraphic getOMGraphicAtOffset(int byteOffset,
                                          DrawingAttributes drawingAttributes)
            throws IOException, FormatException {
        return (OMGraphic) getFactory().makeEsriGraphicFromRecord(byteOffset,
                shp,
                drawingAttributes,
                pointIcon,
                new EsriGraphicFactory.ReadByteTracker());
    }

    /**
     * Provides an iterator over the SpatialIndex entries.
     *
     * @return
     * @throws IOException
     * @throws FormatException
     */
    public Iterator entryIterator() throws IOException, FormatException {
        return entryIterator(null);
    }

    /**
     * Provides an iterator over the SpatialIndex entries.
     *
     * @param dataTransform GeoCoordTransform for pre-projected data.
     * @return
     * @throws IOException
     * @throws FormatException
     */
    public Iterator entryIterator(GeoCoordTransformation dataTransform)
            throws IOException, FormatException {
        if (entries == null) {
            boolean gatherBounds = false;
            if (bounds == null) {
                bounds = new ESRIBoundingBox();
                gatherBounds = true;
            }

            entries = readIndexFile(gatherBounds ? bounds : null, dataTransform);
        }

        return entries.iterator();
    }

    /**
     *
     * @param bounds if not null, add min/max values to them.
     * @return
     * @throws IOException
     * @throws FormatException
     */
    protected List readIndexFile(ESRIBoundingBox bounds) throws IOException,
            FormatException {
        return readIndexFile(bounds, null);
    }

    /**
     *
     * @param bounds if not null, add min/max values to them.
     * @param dataTransform GeoCoordTransform for pre-projected data.
     * @return
     * @throws IOException
     * @throws FormatException
     */
    protected List readIndexFile(ESRIBoundingBox bounds,
                                 GeoCoordTransformation dataTransform)
            throws IOException, FormatException {
        entries = new ArrayList();

        byte ixRecord[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];

        if (shpFileName == null) {
            return entries;
        }

        String ssxFileName = ssx(shpFileName);

        if (!BinaryBufferedFile.exists(ssxFileName)) {
            // If we got this far without an ssx existing, then we should just
            // create one in memory.
            entries = SpatialIndex.MemoryIndex.create(shpFileName);
            return entries;
        }

        BinaryBufferedFile ssx = new BinaryBufferedFile(ssxFileName);

        ssx.byteOrder(false);
        ssx.seek(100); // skip the file header

        LatLonPoint llp = null;
        if (dataTransform != null) {
            llp = new LatLonPoint();
        }

        while (true) {
            int result = ssx.read(ixRecord, 0, SPATIAL_INDEX_RECORD_LENGTH);
            if (result <= 0) {
                break;// EOF
            } else {
                double xmin = readLEDouble(ixRecord, 8);
                double ymin = readLEDouble(ixRecord, 16);
                double xmax = readLEDouble(ixRecord, 24);
                double ymax = readLEDouble(ixRecord, 32);
                int byteOffset = readBEInt(ixRecord, 0) * 2;

                if (dataTransform != null) {
                    dataTransform.inverse(xmin, ymin, llp);
                    xmin = llp.getLongitude();
                    ymin = llp.getLatitude();
                    dataTransform.inverse(xmax, ymax, llp);
                    xmax = llp.getLongitude();
                    ymax = llp.getLatitude();
                }

                if (Debug.debugging("spatialindexdetail")) {
                    Debug.output("  " + xmin + ", " + ymin + "\n  " + xmax
                            + ", " + ymax);
                }

                Entry entry = new Entry(xmin, ymin, xmax, ymax, byteOffset);
                entries.add(entry);

                if (bounds != null) {
                    bounds.addPoint(xmin, ymin);
                    bounds.addPoint(xmax, ymax);
                }
            }
        }

        ssx.close();

        return entries;

    }

    /**
     * Determines if two rectangles intersect. Actually, this method determines
     * if two rectangles don't intersect, and then returns a negation of that
     * result. But the bottom line is the same.
     *
     * @param xmin1 the small x of rectangle 1
     * @param ymin1 the small y of rectangle 1
     * @param xmax1 the big x of rectangle 1
     * @param ymax1 the big y of rectangle 1
     * @param xmin2 the small x of rectangle 2
     * @param ymin2 the small y of rectangle 2
     * @param xmax2 the big x of rectangle 2
     * @param ymax2 the big y of rectangle 2
     * @return <code>true</code> if the rectangles intersect,
     *         <code>false</code> if they do not
     */
    protected static final boolean intersects(double xmin1, double ymin1,
                                              double xmax1, double ymax1,
                                              double xmin2, double ymin2,
                                              double xmax2, double ymax2) {
        return !((xmax1 <= xmin2) || (ymax1 <= ymin2) || (xmin1 >= xmax2) || (ymin1 >= ymax2));
    }

    /**
     * Displays the contents of this index.
     *
     * @param showBounds true to show bounding box, false to skip it
     * @exception IOException if something goes wrong reading the file
     */
    public void dumpIndex(boolean showBounds) throws IOException {
        byte ixRecord[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];
        int recNum = 0;

        if (shpFileName == null) {
            return;
        }

        BinaryBufferedFile ssx = new BinaryBufferedFile(ssx(shpFileName));
        ssx.seek(100); // skip the file header
        while (true) {
            int result = ssx.read(ixRecord, 0, SPATIAL_INDEX_RECORD_LENGTH);
            // if (result == -1) {
            if (result <= 0) {
                // Debug.output("Processed " + recNum + " records");
                break;// EOF
            } else {
                recNum++;
                int offset = readBEInt(ixRecord, 0);
                int length = readBEInt(ixRecord, 4);
                Debug.output("Record "
                        + recNum
                        + ": "
                        + offset
                        + ", "
                        + length
                        + (showBounds ? ("; " + readLEDouble(ixRecord, 8)
                                + ", " + readLEDouble(ixRecord, 16) + ", "
                                + readLEDouble(ixRecord, 24) + ", " + readLEDouble(ixRecord,
                                32))
                                : ""));
            }
        }
        ssx.close();
    }

    /**
     * Prints a usage statement describing how to use this class from the
     * command line.
     *
     * @param out The output stream to use for output
     */
    public static void printUsage(PrintStream out) {
        String className = SpatialIndex.class.getName();

        out.println("Usage:");
        out.println();
        out.println("java " + className + " -c file.shp");
        out.println("Creates spatial index <file.ssx> from "
                + "shape file <file.shp>.");
        out.println();
        out.println("java " + className + " -d file.ssx");
        out.println("Dumps spatial index information, excluding "
                + "bounding boxes to stdout.  Useful for "
                + "comparing to a shape index.");
        out.println();
        out.println("java " + className + " -d -b file.ssx");
        out.println("Dumps spatial index information including "
                + "bounding boxes to stdout.");
        out.println();
    }

    /**
     * Locate file 'fileName' in classpath, if it is not an absolute file name.
     *
     * @return absolute name of the file as a string if found, null otherwise.
     */
    public static String locateFile(String name) {
        File file = new File(name);
        if (file.exists()) {
            return name;
        } else {
            java.net.URL url = ClassLoader.getSystemResource(name);

            // OK, now we want to look around for the file, in the
            // classpaths, and as a resource. It may be a file in
            // a classpath, available for direct access.
            if (url != null) {
                String newname = url.getFile();
                file = new File(newname);
                if (file.exists()) {
                    return newname;
                }
            }
        }
        return null;
    }

    /**
     * Create a SpatialIndex object with just a shape file name. If the shape
     * file is local, this method will attempt to build the spatial index file
     * and place it next to the shape file.
     */
    public static SpatialIndex locateAndSetShapeData(String shapeFileName) {
        SpatialIndex spi = null;

        if (shapeFileName == null) {
            return null;
        }

        int appendixIndex = shapeFileName.indexOf(".shp");

        if (Debug.debugging("shape")) {
            Debug.output("SpatialIndex: created with just the shape file "
                    + shapeFileName);
        }

        if (appendixIndex != -1) {

            if (BinaryFile.exists(shapeFileName)) {
                // OK, the shape files exists - now look for spatial
                // index file next to it.
                String spatialIndexFileName = ssx(shapeFileName);

                // Now, see if the spatialIndexFileName exists, and if
                // not, create it.
                if (Debug.debugging("shape")) {
                    Debug.output("Trying to locate spatial index file "
                            + spatialIndexFileName);
                }

                if (!BinaryFile.exists(spatialIndexFileName)) {
                    // OK, the spatial index doesn't exist, but if the
                    // shape file is local, we have a shot at creating
                    // it.
                    String newShapeFileName = locateFile(shapeFileName);

                    if (newShapeFileName != null) {
                        // It's Local!!
                        Debug.output("Creating spatial index file: "
                                + spatialIndexFileName);

                        // SpatialIndex.FileIndex.create(newShapeFileName);
                    }
                }

                try {
                    spi = new SpatialIndex(shapeFileName);

                } catch (java.io.IOException ioe) {
                    Debug.error(ioe.getMessage());
                    ioe.printStackTrace(Debug.getErrorStream());
                    spi = null;
                }
            } else {
                Debug.error("SpatialIndex: Couldn't locate shape file "
                        + shapeFileName);
            }

        } else {
            if (Debug.debugging("shape")) {
                Debug.output("SpatialIndex: file " + shapeFileName
                        + " doesn't look like a shape file");
            }
        }

        return spi;
    }

    /**
     * The driver for the command line interface. Reads the command line
     * arguments and executes appropriate calls.
     * <p>
     * See the file documentation for usage.
     *
     * @param argv the command line arguments
     * @exception IOException if something goes wrong reading or writing the
     *            file
     */
    public static void main(String argv[]) throws IOException {
        int argc = argv.length;

        if (argc == 0) {
            // No arguments, give the user some help
            printUsage(System.out);
            System.exit(0);
        }

        if (argv[0].equals("-d")) {
            if (argc == 2) {
                String name = argv[1];
                SpatialIndex si = new SpatialIndex(name);
                si.dumpIndex(false);
            } else if ((argc == 3) && (argv[1].equals("-b"))) {
                String name = argv[2];
                SpatialIndex si = new SpatialIndex(name);
                si.dumpIndex(true);
            } else {
                printUsage(System.err);
                System.exit(1);
            }
        } else if ((argc == 3) && argv[0].equals("-c")) {
            String shapeFile = argv[1];
            SpatialIndex.FileIndex.create(shapeFile);
        } else {
            printUsage(System.err);
            System.exit(1);
        }
    }

    /**
     * Set the icon to use for point objects, in general.
     *
     * @param ii ImageIcon to use for icon.
     */
    public synchronized void setPointIcon(ImageIcon ii) {
        pointIcon = ii;
    }

    /**
     * Get the icon used for general point objects.
     *
     * @return ImageIcon, null if not set.
     */
    public synchronized ImageIcon getPointIcon() {
        return pointIcon;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.bbn.openmap.io.Closable#close(boolean)
     */
    public boolean close(boolean done) {
        try {
            if (shp != null) {
                shp.close();
            }

            return true;
        } catch (IOException ioe) {

        }

        return false;
    }

    /**
     * The spatial information for each shp entry is held in one of these.
     *
     * @author dietrick
     */
    public static class Entry {

        double xMin;
        double yMin;
        double xMax;
        double yMax;
        int byteOffset;

        public Entry(double xMin, double yMin, double xMax, double yMax,
                int byteOffset) {
            this.xMin = xMin;
            this.yMin = yMin;
            this.xMax = xMax;
            this.yMax = yMax;
            this.byteOffset = byteOffset;
        }

        public boolean intersects(double xmin, double ymin, double xmax,
                                  double ymax) {
            return SpatialIndex.intersects(xmin,
                    ymin,
                    xmax,
                    ymax,
                    xMin,
                    yMin,
                    xMax,
                    yMax);
        }

        public int getByteOffset() {
            return byteOffset;
        }

        public void addToBounds(ESRIBoundingBox bounds) {
            bounds.addPoint(xMin, yMin);
            bounds.addPoint(xMax, yMax);
        }

    }

    public DbfHandler getDbf() {
        return dbf;
    }

    public void setDbf(DbfHandler dbf) {
        this.dbf = dbf;
    }

    public static class FileIndex {

        protected FileIndex() {}

        public static void create(String shpFile) {
            FileIndex fi = new FileIndex();
            fi.createIndex(shpFile);
        }

        /**
         * Writes the spatial index for a polygon shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param os the spatial index file output stream
         */
        protected void indexPolygons(InputStream is, long ptr, OutputStream os) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            byte outBuf[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];
            int result;
            int shapeType;
            int nRecords = 0;
            int recLengthWords, recLengthBytes /* , recNumber */;
            long recOffset;
            int recBufSize = 100000;
            byte recBuf[] = new byte[recBufSize];
            ESRIBoundingBox polyBounds;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Shapefile SpatialIndex Found "
                                    + nRecords + " records");
                            Debug.output("Shapefile SpatialIndex recBufSize = "
                                    + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        // Null shapes are allowed in any shape file, at any
                        // time.
                        shapeType = readLEInt(recBuf, 0);
                        if (shapeType != SHAPE_TYPE_NULL) {
                            polyBounds = readBox(recBuf, 4);
                        } else {
                            polyBounds = new ESRIBoundingBox();
                        }
                        ptr += recLengthBytes + 8;

                        writeBEInt(outBuf, 0, (int) (recOffset / 2));
                        writeBEInt(outBuf, 4, recLengthWords);
                        writeLEDouble(outBuf, 8, polyBounds.min.x);
                        writeLEDouble(outBuf, 16, polyBounds.min.y);
                        writeLEDouble(outBuf, 24, polyBounds.max.x);
                        writeLEDouble(outBuf, 32, polyBounds.max.y);
                        os.write(outBuf, 0, SPATIAL_INDEX_RECORD_LENGTH);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Writes the spatial index for a point shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param os the spatial index file output stream
         */
        protected void indexPoints(InputStream is, long ptr, OutputStream os) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            byte outBuf[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];
            int result;
            int nRecords = 0;
            int recLengthWords, recLengthBytes/* , recNumber */;
            long recOffset;
            int shapeType;
            int recBufSize = 20;
            byte recBuf[] = new byte[recBufSize];
            double x = 0;
            double y = 0;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Found " + nRecords + " records");
                            Debug.output("recBufSize = " + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        // Null shapes are allowed in any shape file, at any
                        // time.
                        shapeType = readLEInt(recBuf, 0);
                        if (shapeType != SHAPE_TYPE_NULL) {
                            x = readLEDouble(recBuf, 4);
                            y = readLEDouble(recBuf, 12);
                        }
                        ptr += recLengthBytes + 8;

                        writeBEInt(outBuf, 0, (int) (recOffset / 2));
                        writeBEInt(outBuf, 4, recLengthWords);
                        writeLEDouble(outBuf, 8, x);
                        writeLEDouble(outBuf, 16, y);
                        writeLEDouble(outBuf, 24, x);
                        writeLEDouble(outBuf, 32, y);
                        os.write(outBuf, 0, SPATIAL_INDEX_RECORD_LENGTH);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Writes the spatial index for a null shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param os the spatial index file output stream
         */
        protected void indexNulls(InputStream is, long ptr, OutputStream os) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            byte outBuf[] = new byte[SPATIAL_INDEX_RECORD_LENGTH];
            int result;
            int nRecords = 0;
            int recLengthWords, recLengthBytes/* , recNumber */;
            long recOffset;
            int recBufSize = 20;
            byte recBuf[] = new byte[recBufSize];
            double x;
            double y;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Found " + nRecords + " records");
                            Debug.output("recBufSize = " + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        x = 0;
                        y = 0;
                        ptr += recLengthBytes + 8;

                        writeBEInt(outBuf, 0, (int) (recOffset / 2));
                        writeBEInt(outBuf, 4, recLengthWords);
                        writeLEDouble(outBuf, 8, x);
                        writeLEDouble(outBuf, 16, y);
                        writeLEDouble(outBuf, 24, x);
                        writeLEDouble(outBuf, 32, y);
                        os.write(outBuf, 0, SPATIAL_INDEX_RECORD_LENGTH);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Creates a spatial index for a shape file. Reads the records from the
         * shape file, writing appropriate index records to the spatial index
         * file.
         *
         * @param inFile the shape file or spatial index file, the method will
         *        figure it out based on the file name extension.
         */
        public void createIndex(String inFile) {
            String ssxFile = null;
            String shpFile = null;
            if (inFile.endsWith(".shp")) {
                shpFile = inFile;
                ssxFile = ssx(shpFile);
            } else if (inFile.endsWith(".ssx")) {
                ssxFile = inFile;
                shpFile = ssxFile.substring(0, ssxFile.indexOf(".ssx"))
                        + ".shp";
            } else {
                return;
            }

            byte fileHeader[] = new byte[SHAPE_FILE_HEADER_LENGTH];
            FileInputStream shp = null;
            FileOutputStream ssx = null;
            int shapeType;
            try {
                shp = new FileInputStream(shpFile);
                ssx = new FileOutputStream(ssxFile);
                shp.read(fileHeader, 0, SHAPE_FILE_HEADER_LENGTH);
                ssx.write(fileHeader, 0, SHAPE_FILE_HEADER_LENGTH);
                shapeType = readLEInt(fileHeader, 32);
                switch (shapeType) {
                case SHAPE_TYPE_NULL:
                    indexNulls(shp, SHAPE_FILE_HEADER_LENGTH, ssx);
                    break;
                case SHAPE_TYPE_POINT:
                case SHAPE_TYPE_POINTZ:
                case SHAPE_TYPE_POINTM:
                    indexPoints(shp, SHAPE_FILE_HEADER_LENGTH, ssx);
                    break;
                case SHAPE_TYPE_MULTIPOINT:
                case SHAPE_TYPE_MULTIPOINTZ:
                case SHAPE_TYPE_MULTIPOINTM:
                    // case SHAPE_TYPE_ARC:
                case SHAPE_TYPE_POLYLINE:
                case SHAPE_TYPE_POLYLINEZ:
                case SHAPE_TYPE_POLYLINEM:
                case SHAPE_TYPE_POLYGON:
                case SHAPE_TYPE_POLYGONZ:
                case SHAPE_TYPE_POLYGONM:
                    indexPolygons(shp, SHAPE_FILE_HEADER_LENGTH, ssx);
                    break;
                default:
                    Debug.error("Shapefile SpatialIndex.createIndex:  Unknown shape type: "
                            + shapeType);
                }

            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    shp.close();
                    ssx.close();
                } catch (java.io.IOException e) {
                }
            }
        }
    }

    public static class MemoryIndex {

        protected MemoryIndex() {}

        public static List create(String shpFile) {
            MemoryIndex mi = new MemoryIndex();
            return mi.createIndex(shpFile);
        }

        /**
         * Writes the spatial index for a polygon shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param entries a List of Entries to add to
         */
        protected void indexPolygons(InputStream is, long ptr, List entries) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            int result;
            int shapeType;
            int nRecords = 0;
            int recLengthWords, recLengthBytes /* , recNumber */;
            long recOffset;
            int recBufSize = 100000;
            byte recBuf[] = new byte[recBufSize];
            ESRIBoundingBox polyBounds;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Shapefile SpatialIndex Found "
                                    + nRecords + " records");
                            Debug.output("Shapefile SpatialIndex recBufSize = "
                                    + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        // Null shapes are allowed in any shape file, at any
                        // time.
                        shapeType = readLEInt(recBuf, 0);
                        if (shapeType != SHAPE_TYPE_NULL) {
                            polyBounds = readBox(recBuf, 4);
                        } else {
                            polyBounds = new ESRIBoundingBox();
                        }
                        ptr += recLengthBytes + 8;

                        Entry entry = new Entry(polyBounds.min.x, polyBounds.min.y, polyBounds.max.x, polyBounds.max.y, (int) recOffset);
                        entries.add(entry);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Writes the spatial index for a point shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param entries a List of Entries to add to
         */
        protected void indexPoints(InputStream is, long ptr, List entries) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            int result;
            int nRecords = 0;
            int recLengthWords, recLengthBytes/* , recNumber */;
            long recOffset;
            int shapeType;
            int recBufSize = 20;
            byte recBuf[] = new byte[recBufSize];
            double x = 0;
            double y = 0;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Found " + nRecords + " records");
                            Debug.output("recBufSize = " + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        // Null shapes are allowed in any shape file, at any
                        // time.
                        shapeType = readLEInt(recBuf, 0);
                        if (shapeType != SHAPE_TYPE_NULL) {
                            x = readLEDouble(recBuf, 4);
                            y = readLEDouble(recBuf, 12);
                        }
                        ptr += recLengthBytes + 8;

                        Entry entry = new Entry(x, y, x, y, (int) recOffset);
                        entries.add(entry);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Writes the spatial index for a null shape file.
         *
         * @param is the shape file input stream
         * @param ptr the current position in the file
         * @param entries a List of Entries to add to
         */
        protected void indexNulls(InputStream is, long ptr, List entries) {
            boolean moreRecords = true;
            byte rHdr[] = new byte[SHAPE_RECORD_HEADER_LENGTH];
            int result;
            int nRecords = 0;
            int recLengthWords, recLengthBytes/* , recNumber */;
            long recOffset;
            int recBufSize = 20;
            byte recBuf[] = new byte[recBufSize];
            double x;
            double y;

            try {
                while (moreRecords) {
                    result = is.read(rHdr, 0, SHAPE_RECORD_HEADER_LENGTH);
                    if (result < 0) {
                        moreRecords = false;
                        if (Debug.debugging("shape")) {
                            Debug.output("Found " + nRecords + " records");
                            Debug.output("recBufSize = " + recBufSize);
                        }
                    } else {
                        nRecords++;
                        recOffset = ptr;
                        /* recNumber = */readBEInt(rHdr, 0);
                        recLengthWords = readBEInt(rHdr, 4);
                        recLengthBytes = recLengthWords * 2;

                        if (recLengthBytes > recBufSize) {
                            if (Debug.debugging("shape")) {
                                Debug.output("Shapefile SpatialIndex increasing recBufSize to "
                                        + recLengthBytes);
                            }
                            recBufSize = recLengthBytes;
                            recBuf = new byte[recBufSize];
                        }

                        result = is.read(recBuf, 0, recLengthBytes);
                        x = 0;
                        y = 0;
                        ptr += recLengthBytes + 8;

                        Entry entry = new Entry(x, y, x, y, (int) recOffset);
                        entries.add(entry);
                    }
                }
            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (java.io.IOException e) {
                }
            }
        }

        /**
         * Creates a spatial index for a shape file. Reads the records from the
         * shape file, writing appropriate index records to the spatial index
         * file.
         *
         * @param inFile the shape file.
         */
        public List createIndex(String inFile) {
            String shpFile = null;
            List entries = new ArrayList();
            if (inFile.endsWith(".shp")) {
                shpFile = inFile;
            } else {
                return entries;
            }

            byte fileHeader[] = new byte[SHAPE_FILE_HEADER_LENGTH];
            FileInputStream shp = null;
            int shapeType;
            try {
                shp = new FileInputStream(shpFile);
                shp.read(fileHeader, 0, SHAPE_FILE_HEADER_LENGTH);
                shapeType = readLEInt(fileHeader, 32);
                switch (shapeType) {
                case SHAPE_TYPE_NULL:
                    indexNulls(shp, SHAPE_FILE_HEADER_LENGTH, entries);
                    break;
                case SHAPE_TYPE_POINT:
                case SHAPE_TYPE_POINTZ:
                case SHAPE_TYPE_POINTM:
                    indexPoints(shp, SHAPE_FILE_HEADER_LENGTH, entries);
                    break;
                case SHAPE_TYPE_MULTIPOINT:
                case SHAPE_TYPE_MULTIPOINTZ:
                case SHAPE_TYPE_MULTIPOINTM:
                    // case SHAPE_TYPE_ARC:
                case SHAPE_TYPE_POLYLINE:
                case SHAPE_TYPE_POLYLINEZ:
                case SHAPE_TYPE_POLYLINEM:
                case SHAPE_TYPE_POLYGON:
                case SHAPE_TYPE_POLYGONZ:
                case SHAPE_TYPE_POLYGONM:
                    indexPolygons(shp, SHAPE_FILE_HEADER_LENGTH, entries);
                    break;
                default:
                    Debug.error("Shapefile SpatialIndex.createIndex:  Unknown shape type: "
                            + shapeType);
                }

            } catch (java.io.IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    shp.close();
                } catch (java.io.IOException e) {
                }
            }

            return entries;
        }
    }
}
TOP

Related Classes of com.bbn.openmap.layer.shape.SpatialIndex$MemoryIndex

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.