001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/IndexFile.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    
045    package org.deegree.io.shpapi;
046    
047    import java.io.File;
048    import java.io.FileOutputStream;
049    import java.io.IOException;
050    import java.io.RandomAccessFile;
051    import java.util.ArrayList;
052    
053    import org.deegree.model.spatialschema.ByteUtils;
054    
055    /**
056     * Class representing an ESRI Shape File.
057     * <p>
058     * Uses class ShapeUtils modified from the original package com.bbn.openmap.layer.shape <br>
059     * Copyright (C) 1998 BBN Corporation 10 Moulton St. Cambridge, MA 02138 <br>
060     * 
061     * 
062     * @version 31.07.2000
063     * @author Andreas Poth
064     */
065    
066    public class IndexFile {
067    
068        private static final String _shx = ".shx";
069    
070        private RandomAccessFile raf;
071    
072        /**
073         * The length of an index record. (8 byte)
074         */
075        private static final int INDEX_RECORD_LENGTH = 8;
076    
077        /**
078         * array which holds the content of .shx-file:
079         */
080        private IndexRecord[] indexArray = null;
081    
082        /**
083         * IndexFileHeader is equal to ShapeFileHeader
084         */
085        private FileHeader fh;
086    
087        /**
088         * minimum bounding rectangle of the shape-file
089         */
090        private SHPEnvelope fileMBR;
091    
092        /**
093         * number of Records in .shp, .shx., .dbf has to be identical
094         */
095        private int RecordNum;
096    
097        /**
098         * file position offset
099         */
100        private long offset;
101    
102        /**
103         * length of the indexfile
104         */
105        private int filelength = 0;
106    
107        /**
108         * Construct a IndexFile from a file name.
109         */
110        public IndexFile( String url ) throws IOException {
111    
112            /*
113             * creates raf
114             */
115            raf = new RandomAccessFile( url + _shx, "r" );
116    
117            /*
118             * construct Header as ShapeFileHeader
119             */
120            fh = new FileHeader( raf );
121    
122            fileMBR = fh.getFileMBR();
123    
124            /*
125             * construct indexArray
126             */
127            setIndexArray();
128    
129        }
130    
131        /**
132         * Construct a IndexFile from a file name.
133         */
134        public IndexFile( String url, String rwflag ) throws IOException {
135    
136            // delete file if it exists
137            File file = new File( url + _shx );
138            if ( rwflag.indexOf( 'w' ) > -1 && file.exists() ) {
139                file.delete();
140                FileOutputStream os = new FileOutputStream( file, false );
141                os.close();
142            }
143            file = null;
144            raf = new RandomAccessFile( url + _shx, rwflag );
145            // if the 2nd arg is true an empty header will be
146            // written by FileHeader
147            fh = new FileHeader( raf, rwflag.indexOf( 'w' ) > -1 );
148            fileMBR = fh.getFileMBR();
149            offset = raf.length();
150    
151            if ( offset < 100 ) {
152                offset = ShapeConst.SHAPE_FILE_HEADER_LENGTH;
153            }
154            setIndexArray();
155        }
156    
157        public void close() {
158            try {
159                raf.close();
160            } catch ( Exception ex ) {
161                ex.printStackTrace();
162            }
163    
164        }
165    
166        /**
167         * method: writeHeader(int filelength, byte shptype,SHPEnvelope mbr) <BR>
168         * Writes a header into the index file. <BR>
169         */
170        public void writeHeader( int shptype, SHPEnvelope mbr )
171                                throws IOException {
172    
173            byte[] header = new byte[ShapeConst.SHAPE_FILE_HEADER_LENGTH];
174    
175            ByteUtils.writeBEInt( header, 0, ShapeConst.SHAPE_FILE_CODE );
176            ByteUtils.writeBEInt( header, 24, filelength );
177            ByteUtils.writeLEInt( header, 28, ShapeConst.SHAPE_FILE_VERSION );
178            ByteUtils.writeLEInt( header, 32, shptype );
179            ShapeUtils.writeBox( header, 36, mbr );
180    
181            raf.seek( 0 );
182            raf.write( header, 0, ShapeConst.SHAPE_FILE_HEADER_LENGTH );
183        }
184    
185        /**
186         * method: getFileMBR() <BR>
187         * returns the minimum bounding rectangle of the shape-file <BR>
188         */
189        public SHPEnvelope getFileMBR() {
190    
191            return fileMBR;
192    
193        }
194    
195        /**
196         * method: setIndexArray() <BR>
197         * local constructor for local field indexArray <BR>
198         */
199        private void setIndexArray()
200                                throws IOException {
201    
202            byte[] recBuf = new byte[INDEX_RECORD_LENGTH];
203            long rafPos = ShapeConst.SHAPE_FILE_HEADER_LENGTH;
204            int iaIndex = 0;
205            ArrayList<IndexRecord> indexArrayVector = new ArrayList<IndexRecord>( 10000 );
206    
207            raf.seek( rafPos );
208            // loop over index records, until EOF
209            while ( raf.read( recBuf, 0, INDEX_RECORD_LENGTH ) != -1 ) {
210                IndexRecord ir = new IndexRecord( recBuf );
211    
212                // set ArrayVector item as index record
213                indexArrayVector.add( ir );
214    
215                // array index adjustment
216                ++iaIndex;
217    
218                // filepos adjustment
219                rafPos = rafPos + INDEX_RECORD_LENGTH;
220                raf.seek( rafPos );
221            } // end of while
222    
223            // iaIndex holds Record Number
224            RecordNum = iaIndex;
225    
226            // copy vector into indexArray
227            indexArray = indexArrayVector.toArray( new IndexRecord[RecordNum] );
228        }
229    
230        /**
231         * method: getIndexArray() <BR>
232         * clones local field indexArray <BR>
233         */
234        public IndexRecord[] getIndexArray() {
235            return indexArray;
236        }
237    
238        /**
239         * method: getRecordNum() <BR>
240         * function to get number of Records <BR>
241         */
242        public int getRecordNum() {
243            return RecordNum;
244        }
245    
246        /**
247         * methode: getRecordOffset (int RecNo) <BR>
248         * function to get Record offset by Record number <BR>
249         */
250        public int getRecordOffset( int RecNo ) {
251            if ( RecNo >= 0 ) {
252                return indexArray[RecNo].offset;
253            }
254            return -1;
255        }
256    
257        /**
258         * method: getRecordLength (int RecNo) <BR>
259         * function to get Record Length by Record number <BR>
260         */
261        public int getRecordLength( int RecNo ) {
262            if ( RecNo >= 0 ) {
263                return indexArray[RecNo].length;
264            }
265            return -1;
266        }
267    
268        /**
269         * method: getIndexRecord (int RecNo) <BR>
270         * function to get Index Record by Record number <BR>
271         */
272        public IndexRecord getIndexRecord( int RecNo ) {
273            IndexRecord ir = new IndexRecord();
274            if ( RecNo >= 0 ) {
275                return ir = indexArray[RecNo];
276            }
277            return ir;
278        }
279    
280        /**
281         * appends an index record to the indexfile
282         */
283        public void appendRecord( IndexRecord record, SHPEnvelope mbr )
284                                throws IOException {
285            offset = raf.length();
286            raf.seek( offset );
287            raf.write( record.writeIndexRecord() );
288            offset = offset + INDEX_RECORD_LENGTH;
289            // actualize mbr
290            if ( fileMBR.west > mbr.west ) {
291                fileMBR.west = mbr.west;
292            }
293            if ( fileMBR.east < mbr.east ) {
294                fileMBR.east = mbr.east;
295            }
296            if ( fileMBR.south > mbr.south ) {
297                fileMBR.south = mbr.south;
298            }
299            if ( fileMBR.north < mbr.north ) {
300                fileMBR.north = mbr.north;
301            }
302            raf.seek( 36 );
303            raf.write( fileMBR.writeLESHPEnvelope() );
304    
305            // actualize file length
306            filelength = (int) offset / 2;
307        }
308    }