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 **************************************************************************************************/