001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/shape_new/ShapeFileReader.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014    
015     This library is distributed in the hope that it will be useful,
016     but WITHOUT ANY WARRANTY; without even the implied warranty of
017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018     Lesser General Public License for more details.
019    
020     You should have received a copy of the GNU Lesser General Public
021     License along with this library; if not, write to the Free Software
022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact:
025    
026     Andreas Poth
027     lat/lon GmbH
028     Aennchenstr. 19
029     53177 Bonn
030     Germany
031     E-Mail: poth@lat-lon.de
032    
033     Prof. Dr. Klaus Greve
034     Department of Geography
035     University of Bonn
036     Meckenheimer Allee 166
037     53115 Bonn
038     Germany
039     E-Mail: greve@giub.uni-bonn.de
040    
041     ---------------------------------------------------------------------------*/
042    package org.deegree.io.shpapi.shape_new;
043    
044    import java.io.BufferedInputStream;
045    import java.io.File;
046    import java.io.FileInputStream;
047    import java.io.IOException;
048    import java.io.InputStream;
049    import java.util.LinkedList;
050    
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.io.dbaseapi.DBaseFile;
054    import org.deegree.model.spatialschema.ByteUtils;
055    
056    /**
057     * <code>ShapeFileReader</code> is a class to read shapefiles.
058     * 
059     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
060     * @author last edited by: $Author: apoth $
061     * 
062     * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
063     */
064    public class ShapeFileReader {
065    
066        private static final ILogger LOG = LoggerFactory.getLogger( ShapeFileReader.class );
067    
068        private String baseName;
069    
070        private int shapeType;
071    
072        private ShapeEnvelope envelope;
073    
074        private int length;
075    
076        /**
077         * Does not read it yet - just initializes the object.
078         * 
079         * @param baseName
080         */
081        public ShapeFileReader( String baseName ) {
082    
083            if( baseName.endsWith( ".shp" ) ){
084                baseName = baseName.substring( 0, baseName.length() - 4 );
085            }
086            this.baseName = baseName;
087        }
088    
089        private void readHeader( InputStream in )
090                                throws IOException {
091            byte[] header = new byte[100];
092    
093            if ( in.read( header ) != 100 ) {
094                LOG.logError( "Header is too small, unexpected things might happen!" );
095                return;
096            }
097    
098            int fileType = ByteUtils.readBEInt( header, 0 );
099            if ( fileType != ShapeFile.FILETYPE ) {
100                LOG.logWarning( "File type is wrong, unexpected things might happen, continuing anyway..." );
101            }
102    
103            length = ByteUtils.readBEInt( header, 24 ) * 2; // 16 bit words...
104    
105            int version = ByteUtils.readLEInt( header, 28 );
106            if ( version != ShapeFile.VERSION ) {
107                LOG.logWarning( "File version is wrong, continuing in the hope of compatibility..." );
108            }
109    
110            shapeType = ByteUtils.readLEInt( header, 32 );
111    
112            envelope = new ShapeEnvelope( false, false );
113            envelope.read( header, 36 );
114    
115            // it shouldn't hurt to write these values as doubles default to 0.0 anyway
116            double zmin = ByteUtils.readLEDouble( header, 68 );
117            double zmax = ByteUtils.readLEDouble( header, 76 );
118            double mmin = ByteUtils.readLEDouble( header, 84 );
119            double mmax = ByteUtils.readLEDouble( header, 92 );
120    
121            switch ( shapeType ) {
122            case ShapeFile.NULL:
123            case ShapeFile.POINT:
124            case ShapeFile.POLYLINE:
125            case ShapeFile.POLYGON:
126            case ShapeFile.MULTIPOINT:
127                break;
128    
129            case ShapeFile.POINTM:
130            case ShapeFile.POLYLINEM:
131            case ShapeFile.POLYGONM:
132            case ShapeFile.MULTIPOINTM:
133                envelope.extend( mmin, mmax );
134                break;
135    
136            case ShapeFile.POINTZ:
137            case ShapeFile.POLYLINEZ:
138            case ShapeFile.POLYGONZ:
139            case ShapeFile.MULTIPOINTZ:
140            case ShapeFile.MULTIPATCH:
141                envelope.extend( zmin, zmax, mmin, mmax );
142    
143            }
144        }
145    
146        private LinkedList<Shape> readShapes( InputStream in, boolean strict )
147                                throws IOException {
148            LinkedList<Shape> shapes = new LinkedList<Shape>();
149    
150            int offset = 0;
151            byte[] bytes = new byte[length - 100];
152            // read the whole file at once, this makes the "parsing" pretty fast
153            in.read( bytes );
154    
155            while ( offset < bytes.length ) {
156                if ( shapes.size() % 10000 == 0 ) {
157                    System.out.print( shapes.size() + " shapes read.\r" );
158                }
159    
160                // ByteUtils.readBEInt( hdr, 0 ); // just ignore the record number
161                int len = ByteUtils.readBEInt( bytes, offset + 4 ) * 2;
162                offset += 8;
163    
164                if ( strict ) {
165                    Shape s = null;
166                    switch ( shapeType ) {
167                    case ShapeFile.NULL:
168                        break;
169                    case ShapeFile.POINT:
170                        s = new ShapePoint( false, false );
171                        break;
172                    case ShapeFile.POINTM:
173                        s = new ShapePoint( false, true );
174                        break;
175                    case ShapeFile.POINTZ:
176                        s = new ShapePoint( true, false );
177                        break;
178                    case ShapeFile.POLYLINE:
179                        s = new ShapePolyline( false, false );
180                        break;
181                    case ShapeFile.POLYLINEM:
182                        s = new ShapePolyline( false, true );
183                        break;
184                    case ShapeFile.POLYLINEZ:
185                        s = new ShapePolyline( true, false );
186                        break;
187                    case ShapeFile.POLYGON:
188                        s = new ShapePolygon( false, false );
189                        break;
190                    case ShapeFile.POLYGONM:
191                        s = new ShapePolygon( false, true );
192                        break;
193                    case ShapeFile.POLYGONZ:
194                        s = new ShapePolygon( true, false );
195                        break;
196                    case ShapeFile.MULTIPOINT:
197                        s = new ShapeMultiPoint( false, false );
198                        break;
199                    case ShapeFile.MULTIPOINTM:
200                        s = new ShapeMultiPoint( false, true );
201                        break;
202                    case ShapeFile.MULTIPOINTZ:
203                        s = new ShapeMultiPoint( true, false );
204                        break;
205                    case ShapeFile.MULTIPATCH:
206                        s = new ShapeMultiPatch( len );
207                        break;
208                    }
209    
210                    LOG.logDebug( "Reading shape type " + s.getClass().getSimpleName() );
211    
212                    int alen = s.read( bytes, offset ) - offset;
213    
214                    if ( len != alen ) {
215                        LOG.logWarning( "Length is supposedly " + len + ", actual read length was "
216                                        + alen );
217                        // broken files that omit the M-section and that add the record length to the
218                        // length header:
219                        offset += len - 8;
220                    } else {
221                        offset += len;
222                    }
223    
224                    shapes.add( s );
225    
226                } else {
227                    // TODO
228                }
229            }
230    
231            LOG.logInfo( "Read " + shapes.size() + " shapes in total." );
232    
233            return shapes;
234        }
235    
236        /**
237         * @return the shape file contents.
238         * @throws IOException
239         */
240        public ShapeFile read()
241                                throws IOException {
242            File mainFile = new File( baseName + ".shp" );
243            BufferedInputStream mainIn = new BufferedInputStream( new FileInputStream( mainFile ) );
244            readHeader( mainIn );
245    
246            LinkedList<Shape> shapes = readShapes( mainIn, true );
247    
248            DBaseFile dbf = new DBaseFile( baseName );
249    
250            return new ShapeFile( shapes, envelope, dbf, baseName );
251        }
252    
253        /**
254         * @return the dbase file
255         * @throws IOException
256         */
257        public DBaseFile getTables() throws IOException {
258            return new DBaseFile( baseName );
259        }
260        
261        /**
262         * @return the number of shapes stored in this shape file.
263         * @throws IOException
264         */
265        public int getShapeCount() throws IOException {
266            File file = new File( baseName + ".shx" );
267            BufferedInputStream in = new BufferedInputStream( new FileInputStream( file ) );
268            readHeader( in );
269            return (length - 100) / 8;
270        }
271        
272        /**
273         * @return the type of the shape file's contents.
274         * @throws IOException
275         */
276        public int getShapeType() throws IOException {
277            File file = new File( baseName + ".shx" );
278            BufferedInputStream in = new BufferedInputStream( new FileInputStream( file ) );
279            readHeader( in );
280            return shapeType;
281        }
282        
283    }