001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/shpapi/shape_new/ShapeFileReader.java $
002 /*---------------- FILE HEADER ------------------------------------------
003 This file is part of deegree.
004 Copyright (C) 2001-2006 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: rbezema $
061 *
062 * @version $Revision: 7668 $, $Date: 2007-06-27 19:07:07 +0200 (Mi, 27 Jun 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 System.out.println( baseName );
086 }
087 this.baseName = baseName;
088 }
089
090 private void readHeader( InputStream in )
091 throws IOException {
092 byte[] header = new byte[100];
093
094 if ( in.read( header ) != 100 ) {
095 LOG.logError( "Header is too small, unexpected things might happen!" );
096 return;
097 }
098
099 int fileType = ByteUtils.readBEInt( header, 0 );
100 if ( fileType != ShapeFile.FILETYPE ) {
101 LOG.logWarning( "File type is wrong, unexpected things might happen, continuing anyway..." );
102 }
103
104 length = ByteUtils.readBEInt( header, 24 ) * 2; // 16 bit words...
105
106 int version = ByteUtils.readLEInt( header, 28 );
107 if ( version != ShapeFile.VERSION ) {
108 LOG.logWarning( "File version is wrong, continuing in the hope of compatibility..." );
109 }
110
111 shapeType = ByteUtils.readLEInt( header, 32 );
112
113 envelope = new ShapeEnvelope( false, false );
114 envelope.read( header, 36 );
115
116 // it shouldn't hurt to write these values as doubles default to 0.0 anyway
117 double zmin = ByteUtils.readLEDouble( header, 68 );
118 double zmax = ByteUtils.readLEDouble( header, 76 );
119 double mmin = ByteUtils.readLEDouble( header, 84 );
120 double mmax = ByteUtils.readLEDouble( header, 92 );
121
122 switch ( shapeType ) {
123 case ShapeFile.NULL:
124 case ShapeFile.POINT:
125 case ShapeFile.POLYLINE:
126 case ShapeFile.POLYGON:
127 case ShapeFile.MULTIPOINT:
128 break;
129
130 case ShapeFile.POINTM:
131 case ShapeFile.POLYLINEM:
132 case ShapeFile.POLYGONM:
133 case ShapeFile.MULTIPOINTM:
134 envelope.extend( mmin, mmax );
135 break;
136
137 case ShapeFile.POINTZ:
138 case ShapeFile.POLYLINEZ:
139 case ShapeFile.POLYGONZ:
140 case ShapeFile.MULTIPOINTZ:
141 case ShapeFile.MULTIPATCH:
142 envelope.extend( zmin, zmax, mmin, mmax );
143
144 }
145 }
146
147 private LinkedList<Shape> readShapes( InputStream in, boolean strict )
148 throws IOException {
149 LinkedList<Shape> shapes = new LinkedList<Shape>();
150
151 int offset = 0;
152 byte[] bytes = new byte[length - 100];
153 // read the whole file at once, this makes the "parsing" pretty fast
154 in.read( bytes );
155
156 while ( offset < bytes.length ) {
157 if ( shapes.size() % 10000 == 0 ) {
158 System.out.print( shapes.size() + " shapes read.\r" );
159 }
160
161 // ByteUtils.readBEInt( hdr, 0 ); // just ignore the record number
162 int len = ByteUtils.readBEInt( bytes, offset + 4 ) * 2;
163 offset += 8;
164
165 if ( strict ) {
166 Shape s = null;
167 switch ( shapeType ) {
168 case ShapeFile.NULL:
169 break;
170 case ShapeFile.POINT:
171 s = new ShapePoint( false, false );
172 break;
173 case ShapeFile.POINTM:
174 s = new ShapePoint( false, true );
175 break;
176 case ShapeFile.POINTZ:
177 s = new ShapePoint( true, false );
178 break;
179 case ShapeFile.POLYLINE:
180 s = new ShapePolyline( false, false );
181 break;
182 case ShapeFile.POLYLINEM:
183 s = new ShapePolyline( false, true );
184 break;
185 case ShapeFile.POLYLINEZ:
186 s = new ShapePolyline( true, false );
187 break;
188 case ShapeFile.POLYGON:
189 s = new ShapePolygon( false, false );
190 break;
191 case ShapeFile.POLYGONM:
192 s = new ShapePolygon( false, true );
193 break;
194 case ShapeFile.POLYGONZ:
195 s = new ShapePolygon( true, false );
196 break;
197 case ShapeFile.MULTIPOINT:
198 s = new ShapeMultiPoint( false, false );
199 break;
200 case ShapeFile.MULTIPOINTM:
201 s = new ShapeMultiPoint( false, true );
202 break;
203 case ShapeFile.MULTIPOINTZ:
204 s = new ShapeMultiPoint( true, false );
205 break;
206 case ShapeFile.MULTIPATCH:
207 s = new ShapeMultiPatch( len );
208 break;
209 }
210
211 LOG.logDebug( "Reading shape type " + s.getClass().getSimpleName() );
212
213 int alen = s.read( bytes, offset ) - offset;
214
215 if ( len != alen ) {
216 LOG.logWarning( "Length is supposedly " + len + ", actual read length was "
217 + alen );
218 // broken files that omit the M-section and that add the record length to the
219 // length header:
220 offset += len - 8;
221 } else {
222 offset += len;
223 }
224
225 shapes.add( s );
226
227 } else {
228 // TODO
229 }
230 }
231
232 LOG.logInfo( "Read " + shapes.size() + " shapes in total." );
233
234 return shapes;
235 }
236
237 /**
238 * @return the shape file contents.
239 * @throws IOException
240 */
241 public ShapeFile read()
242 throws IOException {
243 File mainFile = new File( baseName + ".shp" );
244 BufferedInputStream mainIn = new BufferedInputStream( new FileInputStream( mainFile ) );
245 readHeader( mainIn );
246
247 LinkedList<Shape> shapes = readShapes( mainIn, true );
248
249 DBaseFile dbf = new DBaseFile( baseName );
250
251 return new ShapeFile( shapes, envelope, dbf, baseName );
252 }
253
254 }
255
256 /***************************************************************************************************
257 * <code>
258 Changes to this class. What the people have been up to:
259
260 $Log$
261 Revision 1.4 2007/03/05 09:26:07 schmitz
262 Added support for broken files with incorrect length record headers that omit the M-section.
263
264 Revision 1.3 2007/02/27 13:03:40 schmitz
265 Fixed handling of Multipatch type.
266
267 Revision 1.2 2007/02/27 09:10:24 schmitz
268 Reading ignores length attribute in record header now.
269
270 Revision 1.1 2007/02/26 14:26:49 schmitz
271 Added a new implementation of the ShapeFile API that implements the Z and M variants of the datatypes as well.
272 Also added some basic tests for the API as well as a new version of the GML/Shape converters.
273
274 </code>
275 **************************************************************************************************/