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 }