001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/shpapi/MainFile.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.IOException;
041    import java.io.RandomAccessFile;
042    
043    import org.deegree.model.spatialschema.ByteUtils;
044    
045    /**
046     * Class representing an ESRI Shape File.
047     * <p>
048     * Uses class ByteUtils modified from the original package com.bbn.openmap.layer.shape <br>
049     * Copyright (C) 1998 BBN Corporation 10 Moulton St. Cambridge, MA 02138 <br>
050     * 
051     * @version 16.08.2000
052     * @author Andreas Poth
053     * 
054     */
055    public class MainFile {
056    
057        /*
058         * A buffer for current record's header.
059         */
060        protected byte[] recHdr = new byte[ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
061    
062        private byte[] envRecBuf = new byte[36];
063    
064        private byte[] dataRecBuf = new byte[36];
065    
066        /*
067         * instance variables
068         */
069        private FileHeader fh;
070    
071        private IndexFile shx;
072    
073        /*
074         * file suffixes for shp
075         */
076        private static final String _shp = ".shp";
077    
078        /*
079         * references to the main file
080         */
081        private RandomAccessFile raf;
082    
083        /**
084         * Construct a MainFile from a file name.
085         * 
086         * @param url
087         * @throws IOException
088         */
089        public MainFile( String url ) throws IOException {
090    
091            // creates raf
092            raf = new RandomAccessFile( url + _shp, "r" );
093    
094            fh = new FileHeader( raf );
095    
096            shx = new IndexFile( url );
097    
098        }
099    
100        /**
101         * Construct a MainFile from a file name.
102         * 
103         * @param url
104         * @param rwflag
105         * @throws IOException
106         */
107        public MainFile( String url, String rwflag ) throws IOException {
108    
109            // delet file if it exists
110            File file = new File( url + _shp );
111    
112            if ( rwflag.indexOf( 'w' ) > -1 && file.exists() )
113                file.delete();
114            file = null;
115    
116            // creates raf
117            raf = new RandomAccessFile( url + _shp, rwflag );
118    
119            fh = new FileHeader( raf, rwflag.indexOf( 'w' ) > -1 );
120    
121            shx = new IndexFile( url, rwflag );
122    
123        }
124    
125        /**
126         *
127         */
128        public void close() {
129            try {
130                raf.close();
131            } catch ( Exception ex ) {
132                ex.printStackTrace();
133            }
134            try {
135                shx.close();
136            } catch ( Exception ex ) {
137                ex.printStackTrace();
138            }
139        }
140    
141        /**
142         * method: getFileMBR()<BR>
143         * returns the minimum bounding rectangle of geometries<BR>
144         * within the shape-file
145         * 
146         * @return the minimum bounding rectangle of geometries<BR>
147         */
148        public SHPEnvelope getFileMBR() {
149    
150            return fh.getFileMBR();
151    
152        }
153    
154        /**
155         * method: getRecordNum()<BR>
156         * returns the number of record with in a shape-file<BR>
157         * 
158         * @return the number of record with in a shape-file<BR>
159         */
160        public int getRecordNum() {
161    
162            return shx.getRecordNum();
163    
164        }
165    
166        /**
167         * method: getRecordMBR(int RecNo)<BR>
168         * returns the minimum bound rectangle of RecNo's Geometrie of the shape-file<BR>
169         * 
170         * @param RecNo
171         * @return the minimum bound rectangle of RecNo's Geometrie of the shape-file<BR>
172         * @throws IOException
173         */
174        public SHPEnvelope getRecordMBR( int RecNo )
175                                throws IOException {
176    
177            SHPEnvelope recordMBR = null;
178    
179            // index in IndexArray (see IndexFile)
180            int iaIndex = RecNo - 1;
181    
182            int off = shx.getRecordOffset( iaIndex );
183    
184            // off holds the offset of the shape-record in 16-bit words (= 2 byte)
185            // multiply with 2 gets number of bytes to seek
186            long rafPos = off * 2;
187    
188            // fetch shape record
189            raf.seek( rafPos + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
190    
191            if ( raf.read( envRecBuf, 0, envRecBuf.length ) != -1 ) {
192    
193                int shpType = ByteUtils.readLEInt( envRecBuf, 0 );
194    
195                // only for PolyLines, Polygons and MultiPoints minimum bounding rectangles are defined
196                if ( shpType == ShapeConst.SHAPE_TYPE_POLYLINE || shpType == ShapeConst.SHAPE_TYPE_POLYGON
197                     || shpType == ShapeConst.SHAPE_TYPE_MULTIPOINT ) {
198                    recordMBR = new SHPEnvelope( envRecBuf );
199                } // end if shpType
200            } // end if result
201    
202            return recordMBR;
203        }
204    
205        /**
206         * method: getByRecNo (int RecNo)<BR>
207         * retruns a ShapeRecord-Geometry by RecorcNumber<BR>
208         * 
209         * @param RecNo
210         * @return a ShapeRecord-Geometry by RecorcNumber<BR>
211         * @throws IOException
212         */
213        public SHPGeometry getByRecNo( int RecNo )
214                                throws IOException {
215    
216            SHPGeometry shpGeom = null;
217    
218            // index in IndexArray (see IndexFile)
219            int iaIndex = RecNo - 1;
220    
221            int off = shx.getRecordOffset( iaIndex );
222    
223            // calculate length from 16-bit words (= 2 bytes) to lenght in bytes
224            int len = shx.getRecordLength( iaIndex ) * 2;
225    
226            // off holds the offset of the shape-record in 16-bit words (= 2 byte)
227            // multiply with 2 gets number of bytes to seek
228            long rafPos = off * 2;
229    
230            // fetch record header
231            raf.seek( rafPos );
232    
233            // fetch shape record
234            raf.seek( rafPos + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
235    
236            if ( len > dataRecBuf.length ) {
237                dataRecBuf = new byte[len];
238            }
239    
240            if ( raf.read( dataRecBuf, 0, len ) != -1 ) {
241    
242                int shpType = ByteUtils.readLEInt( dataRecBuf, 0 );
243    
244                // create a geometry out of record buffer with shapetype
245                if ( shpType == ShapeConst.SHAPE_TYPE_POINT ) {
246                    shpGeom = new SHPPoint( dataRecBuf, 4 );
247                } else if ( shpType == ShapeConst.SHAPE_TYPE_MULTIPOINT ) {
248                    shpGeom = new SHPMultiPoint( dataRecBuf );
249                } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYLINE ) {
250                    shpGeom = new SHPPolyLine( dataRecBuf );
251                } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGON ) {
252                    shpGeom = new SHPPolygon( dataRecBuf );
253                } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGONZ ) {
254                    shpGeom = new SHPPolygon3D( dataRecBuf );
255                }
256    
257            } // end if result
258    
259            return shpGeom;
260    
261        }
262    
263        /**
264         * method: getShapeType(int RecNo)<BR>
265         * returns the minimum bound rectangle of RecNo's Geometrie of the shape-file<BR>
266         * 
267         * @param RecNo
268         * @return the minimum bound rectangle of RecNo's Geometrie of the shape-file<BR>
269         * @throws IOException
270         */
271        public int getShapeTypeByRecNo( int RecNo )
272                                throws IOException {
273    
274            int shpType = -1;
275    
276            // index in IndexArray (see IndexFile)
277            int iaIndex = RecNo - 1;
278    
279            int off = shx.getRecordOffset( iaIndex );
280    
281            // calculate length from 16-bit words (= 2 bytes) to length in bytes
282            int len = 4;// shx.getRecordLength( iaIndex ) * 2;
283    
284            // off holds the offset of the shape-record in 16-bit words (= 2 byte)
285            // multiply with 2 gets number of bytes to seek
286            long rafPos = off * 2;
287    
288            // fetch shape record
289            raf.seek( rafPos + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
290    
291            byte[] recBuf_ = new byte[len];
292    
293            if ( raf.read( recBuf_, 0, len ) != -1 ) {
294                shpType = ByteUtils.readLEInt( recBuf_, 0 );
295            } // end if result
296    
297            return shpType;
298        }
299    
300        /**
301         * method: public void write(byte[] bytearray)<BR>
302         * appends a bytearray to the shape file<BR>
303         * 
304         * @param bytearray
305         * @param record
306         * @param mbr
307         * @throws IOException
308         */
309        public void write( byte[] bytearray, IndexRecord record, SHPEnvelope mbr )
310                                throws IOException {
311            raf.seek( record.offset * 2 );
312            raf.write( bytearray );
313            shx.appendRecord( record, mbr );
314        }
315    
316        /**
317         * method: public void writeHeader(int filelength, byte shptype, SHPEnvelope mbr)<BR>
318         * writes a header to the shape and index file<BR>
319         * 
320         * @param filelength
321         * @param shptype
322         * @param mbr
323         * @throws IOException
324         */
325        public void writeHeader( int filelength, byte shptype, SHPEnvelope mbr )
326                                throws IOException {
327            fh.writeHeader( filelength, shptype, mbr );
328            shx.writeHeader( shptype, mbr );
329        }
330    
331    }