001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/io/shpapi/shape_new/ShapeFileReader.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    package org.deegree.io.shpapi.shape_new;
037    
038    import java.io.BufferedInputStream;
039    import java.io.File;
040    import java.io.FileInputStream;
041    import java.io.IOException;
042    import java.io.InputStream;
043    import java.util.LinkedList;
044    
045    import org.deegree.framework.log.ILogger;
046    import org.deegree.framework.log.LoggerFactory;
047    import org.deegree.io.dbaseapi.DBaseFile;
048    import org.deegree.model.crs.CoordinateSystem;
049    import org.deegree.model.spatialschema.ByteUtils;
050    
051    /**
052     * <code>ShapeFileReader</code> is a class to read shapefiles.
053     * 
054     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
055     * @author last edited by: $Author: aschmitz $
056     * 
057     * @version $Revision: 19488 $, $Date: 2009-09-03 15:05:04 +0200 (Do, 03. Sep 2009) $
058     */
059    public class ShapeFileReader {
060    
061        private static final ILogger LOG = LoggerFactory.getLogger( ShapeFileReader.class );
062    
063        private String baseName;
064    
065        private CoordinateSystem defaultCRS;
066    
067        private int shapeType;
068    
069        private ShapeEnvelope envelope;
070    
071        private int length;
072    
073        /**
074         * Does not read it yet - just initializes the object.
075         * 
076         * @param baseName
077         */
078        public ShapeFileReader( String baseName ) {
079            if ( baseName.toLowerCase().endsWith( ".shp" ) ) {
080                baseName = baseName.substring( 0, baseName.length() - 4 );
081            }
082            this.baseName = baseName;
083        }
084    
085        /**
086         * Does not read it yet - just initializes the object.
087         * 
088         * @param baseName
089         * @param defaultCRS
090         *            CoordinateSystem for the shape file
091         */
092        public ShapeFileReader( String baseName, CoordinateSystem defaultCRS ) {
093            this( baseName );
094            this.defaultCRS = defaultCRS;
095        }
096    
097        private void readHeader( InputStream in )
098                                throws IOException {
099            byte[] header = new byte[100];
100    
101            if ( in.read( header ) != 100 ) {
102                LOG.logError( "Header is too small, unexpected things might happen!" );
103                return;
104            }
105    
106            int fileType = ByteUtils.readBEInt( header, 0 );
107            if ( fileType != ShapeFile.FILETYPE ) {
108                LOG.logWarning( "File type is wrong, unexpected things might happen, continuing anyway..." );
109            }
110    
111            length = ByteUtils.readBEInt( header, 24 ) * 2; // 16 bit words...
112    
113            int version = ByteUtils.readLEInt( header, 28 );
114            if ( version != ShapeFile.VERSION ) {
115                LOG.logWarning( "File version is wrong, continuing in the hope of compatibility..." );
116            }
117    
118            shapeType = ByteUtils.readLEInt( header, 32 );
119    
120            envelope = new ShapeEnvelope( false, false );
121            envelope.read( header, 36 );
122    
123            // it shouldn't hurt to write these values as doubles default to 0.0 anyway
124            double zmin = ByteUtils.readLEDouble( header, 68 );
125            double zmax = ByteUtils.readLEDouble( header, 76 );
126            double mmin = ByteUtils.readLEDouble( header, 84 );
127            double mmax = ByteUtils.readLEDouble( header, 92 );
128    
129            switch ( shapeType ) {
130            case ShapeFile.NULL:
131            case ShapeFile.POINT:
132            case ShapeFile.POLYLINE:
133            case ShapeFile.POLYGON:
134            case ShapeFile.MULTIPOINT:
135                break;
136    
137            case ShapeFile.POINTM:
138            case ShapeFile.POLYLINEM:
139            case ShapeFile.POLYGONM:
140            case ShapeFile.MULTIPOINTM:
141                envelope.extend( mmin, mmax );
142                break;
143    
144            case ShapeFile.POINTZ:
145            case ShapeFile.POLYLINEZ:
146            case ShapeFile.POLYGONZ:
147            case ShapeFile.MULTIPOINTZ:
148            case ShapeFile.MULTIPATCH:
149                envelope.extend( zmin, zmax, mmin, mmax );
150    
151            }
152        }
153    
154        private LinkedList<Shape> readShapes( InputStream in, boolean strict )
155                                throws IOException {
156            LinkedList<Shape> shapes = new LinkedList<Shape>();
157    
158            int offset = 0;
159            byte[] bytes = new byte[length - 100];
160            // read the whole file at once, this makes the "parsing" pretty fast
161            in.read( bytes );
162    
163            while ( offset < bytes.length ) {
164                if ( shapes.size() % 10000 == 0 ) {
165                    System.out.print( shapes.size() + " shapes read.\r" );
166                }
167    
168                // ByteUtils.readBEInt( hdr, 0 ); // just ignore the record number
169                int len = ByteUtils.readBEInt( bytes, offset + 4 ) * 2;
170                offset += 8;
171    
172                if ( strict ) {
173                    Shape s = null;
174                    switch ( shapeType ) {
175                    case ShapeFile.NULL:
176                        break;
177                    case ShapeFile.POINT:
178                        s = new ShapePoint( false, false, defaultCRS );
179                        break;
180                    case ShapeFile.POINTM:
181                        s = new ShapePoint( false, true, defaultCRS );
182                        break;
183                    case ShapeFile.POINTZ:
184                        s = new ShapePoint( true, false, defaultCRS );
185                        break;
186                    case ShapeFile.POLYLINE:
187                        s = new ShapePolyline( false, false, defaultCRS );
188                        break;
189                    case ShapeFile.POLYLINEM:
190                        s = new ShapePolyline( false, true, defaultCRS );
191                        break;
192                    case ShapeFile.POLYLINEZ:
193                        s = new ShapePolyline( true, false, defaultCRS );
194                        break;
195                    case ShapeFile.POLYGON:
196                        s = new ShapePolygon( false, false, defaultCRS );
197                        break;
198                    case ShapeFile.POLYGONM:
199                        s = new ShapePolygon( false, true, defaultCRS );
200                        break;
201                    case ShapeFile.POLYGONZ:
202                        s = new ShapePolygon( true, false, defaultCRS );
203                        break;
204                    case ShapeFile.MULTIPOINT:
205                        s = new ShapeMultiPoint( false, false, defaultCRS );
206                        break;
207                    case ShapeFile.MULTIPOINTM:
208                        s = new ShapeMultiPoint( false, true, defaultCRS );
209                        break;
210                    case ShapeFile.MULTIPOINTZ:
211                        s = new ShapeMultiPoint( true, false, defaultCRS );
212                        break;
213                    case ShapeFile.MULTIPATCH:
214                        s = new ShapeMultiPatch( len, defaultCRS );
215                        break;
216                    }
217    
218                    LOG.logDebug( "Reading shape type " + s.getClass().getSimpleName() );
219    
220                    int alen = s.read( bytes, offset ) - offset;
221    
222                    if ( len != alen ) {
223                        LOG.logWarning( "Length is supposedly " + len + ", actual read length was " + alen );
224                        // broken files that omit the M-section and that add the record length to the
225                        // length header:
226                        offset += len - 8;
227                    } else {
228                        offset += len;
229                    }
230    
231                    shapes.add( s );
232    
233                } else {
234                    // TODO
235                }
236            }
237    
238            LOG.logInfo( "Read " + shapes.size() + " shapes in total." );
239    
240            return shapes;
241        }
242    
243        /**
244         * @return the shape file contents.
245         * @throws IOException
246         */
247        public ShapeFile read()
248                                throws IOException {
249            File mainFile = new File( baseName + ".shp" );
250            BufferedInputStream mainIn = new BufferedInputStream( new FileInputStream( mainFile ) );
251            readHeader( mainIn );
252    
253            LinkedList<Shape> shapes = readShapes( mainIn, true );
254    
255            DBaseFile dbf = new DBaseFile( baseName );
256    
257            return new ShapeFile( shapes, envelope, dbf, baseName );
258        }
259    
260        /**
261         * @return the dbase file
262         * @throws IOException
263         */
264        public DBaseFile getTables()
265                                throws IOException {
266            return new DBaseFile( baseName );
267        }
268    
269        /**
270         * @return the number of shapes stored in this shape file.
271         * @throws IOException
272         */
273        public int getShapeCount()
274                                throws IOException {
275            File file = new File( baseName + ".shx" );
276            BufferedInputStream in = new BufferedInputStream( new FileInputStream( file ) );
277            readHeader( in );
278            return ( length - 100 ) / 8;
279        }
280    
281        /**
282         * @return the type of the shape file's contents.
283         * @throws IOException
284         */
285        public int getShapeType()
286                                throws IOException {
287            File file = new File( baseName + ".shx" );
288            BufferedInputStream in = new BufferedInputStream( new FileInputStream( file ) );
289            readHeader( in );
290            return shapeType;
291        }
292    
293    }