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 }