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