001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/shpapi/ShapeFile.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; 037 038 import java.io.ByteArrayInputStream; 039 import java.io.IOException; 040 import java.util.ArrayList; 041 import java.util.Enumeration; 042 import java.util.HashMap; 043 import java.util.Hashtable; 044 import java.util.Iterator; 045 import java.util.List; 046 import java.util.Map; 047 048 import org.deegree.datatypes.QualifiedName; 049 import org.deegree.datatypes.Types; 050 import org.deegree.io.dbaseapi.DBaseException; 051 import org.deegree.io.dbaseapi.DBaseFile; 052 import org.deegree.io.dbaseapi.DBaseIndex; 053 import org.deegree.io.dbaseapi.DBaseIndexException; 054 import org.deegree.io.dbaseapi.FieldDescriptor; 055 import org.deegree.io.rtree.HyperBoundingBox; 056 import org.deegree.io.rtree.HyperPoint; 057 import org.deegree.io.rtree.RTree; 058 import org.deegree.io.rtree.RTreeException; 059 import org.deegree.model.feature.Feature; 060 import org.deegree.model.feature.FeatureCollection; 061 import org.deegree.model.feature.FeatureFactory; 062 import org.deegree.model.feature.FeatureProperty; 063 import org.deegree.model.feature.schema.FeatureType; 064 import org.deegree.model.feature.schema.GeometryPropertyType; 065 import org.deegree.model.feature.schema.PropertyType; 066 import org.deegree.model.spatialschema.ByteUtils; 067 import org.deegree.model.spatialschema.Curve; 068 import org.deegree.model.spatialschema.Envelope; 069 import org.deegree.model.spatialschema.Geometry; 070 import org.deegree.model.spatialschema.GeometryException; 071 import org.deegree.model.spatialschema.GeometryFactory; 072 import org.deegree.model.spatialschema.JTSAdapter; 073 import org.deegree.model.spatialschema.MultiCurve; 074 import org.deegree.model.spatialschema.MultiPoint; 075 import org.deegree.model.spatialschema.MultiSurface; 076 import org.deegree.model.spatialschema.Point; 077 import org.deegree.model.spatialschema.Position; 078 import org.deegree.model.spatialschema.Ring; 079 import org.deegree.model.spatialschema.Surface; 080 import org.deegree.model.spatialschema.SurfaceInterpolationImpl; 081 082 import com.vividsolutions.jts.algorithm.CGAlgorithms; 083 084 /** 085 * Class representing an ESRI Shape File. 086 * 087 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 088 * @author last edited by: $Author: apoth $ 089 * 090 * @version $Revision: 25096 $, $Date: 2010-06-29 13:00:08 +0200 (Di, 29 Jun 2010) $ 091 */ 092 public class ShapeFile { 093 094 private DBaseFile dbf = null; 095 096 private SHP2WKS shpwks = new SHP2WKS(); 097 098 /* 099 * contains the dBase indexes 100 */ 101 private Hashtable<String, DBaseIndex> dBaseIndexes = new Hashtable<String, DBaseIndex>( 50 ); 102 103 /* 104 * aggregated Instance-variables 105 */ 106 private MainFile shp = null; 107 108 private RTree rti = null; 109 110 private String fileName = null; 111 112 /* 113 * indicates if a dBase-file is associated to the shape-file 114 */ 115 private boolean hasDBaseFile = true; 116 117 /* 118 * indicates if an R-tree index is associated to the shape-file 119 */ 120 private boolean hasRTreeIndex = true; 121 122 /** 123 * constructor: <BR> 124 * Construct a ShapeFile from a file name.<BR> 125 * 126 * @param fileName 127 * @throws IOException 128 */ 129 public ShapeFile( String fileName ) throws IOException { 130 this.fileName = fileName; 131 /* 132 * initialize the MainFile 133 */ 134 shp = new MainFile( fileName ); 135 136 /* 137 * initialize the DBaseFile 138 */ 139 try { 140 dbf = new DBaseFile( fileName ); 141 } catch ( IOException e ) { 142 hasDBaseFile = false; 143 } 144 145 /* 146 * initialize the RTreeIndex 147 */ 148 try { 149 rti = new RTree( fileName + ".rti" ); 150 } catch ( RTreeException e ) { 151 hasRTreeIndex = false; 152 } 153 154 if ( hasDBaseFile ) { 155 String[] s = null; 156 try { 157 s = getProperties(); 158 } catch ( Exception e ) { 159 e.printStackTrace(); 160 } 161 for ( int i = 0; i < s.length; i++ ) { 162 try { 163 dBaseIndexes.put( s[i], new DBaseIndex( fileName + "$" + s[i] ) ); 164 } catch ( IOException e ) { 165 // uh, oh 166 } 167 } 168 } 169 } 170 171 /** 172 * constructor: <BR> 173 * Construct a ShapeFile from a file name.<BR> 174 * 175 * @param url 176 * @param rwflag 177 * @throws IOException 178 */ 179 public ShapeFile( String url, String rwflag ) throws IOException { 180 this.fileName = url; 181 shp = new MainFile( url, rwflag ); 182 // TODO: initialize dbf, rti 183 hasDBaseFile = false; 184 hasRTreeIndex = false; 185 } 186 187 /** 188 * 189 */ 190 public void close() { 191 192 shp.close(); 193 dbf.close(); 194 195 if ( rti != null ) { 196 try { 197 rti.close(); 198 } catch ( Exception e ) { 199 // should never happen 200 e.printStackTrace(); 201 } 202 } 203 204 for ( Enumeration<DBaseIndex> e = dBaseIndexes.elements(); e.hasMoreElements(); ) { 205 DBaseIndex index = e.nextElement(); 206 207 try { 208 index.close(); 209 } catch ( Exception ex ) { 210 // uh, oh 211 } 212 } 213 } 214 215 /** 216 * Overrides the default feature type (which is generated from all columns in the dbase file) to allow customized 217 * naming and ordering of properties. 218 * 219 * @param ft 220 * @param ftMapping 221 */ 222 public void setFeatureType( FeatureType ft, Map<PropertyType, String> ftMapping ) { 223 dbf.setFeatureType( ft, ftMapping ); 224 } 225 226 /** 227 * returns true if a column is indexed 228 * 229 * @param column 230 * @return true if a column is indexed 231 */ 232 public boolean hasDBaseIndex( String column ) { 233 DBaseIndex index = dBaseIndexes.get( column ); 234 return index != null; 235 } 236 237 /** 238 * returns true if a dBase-file is associated to the shape-file<BR> 239 * 240 * @return true if a dBase-file is associated to the shape-file<BR> 241 */ 242 public boolean hasDBaseFile() { 243 return this.hasDBaseFile; 244 } 245 246 /** 247 * returns true if an R-tree index is associated to the shape-file<BR> 248 * 249 * @return true if an R-tree index is associated to the shape-file<BR> 250 */ 251 public boolean hasRTreeIndex() { 252 return this.hasRTreeIndex; 253 } 254 255 /** 256 * returns the number of records within a shape-file<BR> 257 * 258 * @return the number of records within a shape-file<BR> 259 */ 260 public int getRecordNum() { 261 return shp.getRecordNum(); 262 } 263 264 /** 265 * returns the minimum bounding rectangle of all geometries<BR> 266 * within the shape-file 267 * 268 * @return the minimum bounding rectangle of all geometries<BR> 269 * within the shape-file 270 */ 271 public Envelope getFileMBR() { 272 double xmin = shp.getFileMBR().west; 273 double xmax = shp.getFileMBR().east; 274 double ymin = shp.getFileMBR().south; 275 double ymax = shp.getFileMBR().north; 276 277 return GeometryFactory.createEnvelope( xmin, ymin, xmax, ymax, null ); 278 } 279 280 /** 281 * returns the minimum bound rectangle of RecNo'th Geometrie<BR> 282 * 283 * @param recNo 284 * @return the minimum bound rectangle of RecNo'th Geometrie<BR> 285 * @throws IOException 286 */ 287 public Envelope getMBRByRecNo( int recNo ) 288 throws IOException { 289 SHPEnvelope shpenv = shp.getRecordMBR( recNo ); 290 if ( shpenv != null ) { 291 double xmin = shpenv.west; 292 double xmax = shpenv.east; 293 double ymin = shpenv.south; 294 double ymax = shpenv.north; 295 296 return GeometryFactory.createEnvelope( xmin, ymin, xmax, ymax, null ); 297 } else { 298 return null; 299 } 300 } 301 302 /** 303 * Returns the given entry of the shape file as a {@link Feature} instance. 304 * <p> 305 * This contains the geometry as well as the attributes stored into the dbase file. The geometry property will use a 306 * default name (app:GEOM). 307 * 308 * @param RecNo 309 * @return the given entry of the shape file as a Feature instance 310 * @throws IOException 311 * @throws HasNoDBaseFileException 312 * @throws DBaseException 313 */ 314 public Feature getFeatureByRecNo( int RecNo ) 315 throws IOException, HasNoDBaseFileException, DBaseException { 316 317 if ( !hasDBaseFile ) { 318 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 319 } 320 321 // get feature (without geometry property) from DBaseFile 322 Feature feature = dbf.getFRow( RecNo ); 323 324 // exchange null geometries with real geometry 325 Geometry geo = getGeometryByRecNo( RecNo ); 326 GeometryPropertyType[] geoPTs = feature.getFeatureType().getGeometryProperties(); 327 for ( int i = 0; i < geoPTs.length; i++ ) { 328 FeatureProperty[] geoProp = feature.getProperties( geoPTs[i].getName() ); 329 for ( int j = 0; j < geoProp.length; j++ ) { 330 geoProp[j].setValue( geo ); 331 } 332 } 333 334 return feature; 335 } 336 337 /** 338 * returns RecNo'th Geometrie<BR> 339 * 340 * @param recNo 341 * @return RecNo'th Geometrie<BR> 342 * @throws IOException 343 */ 344 public Geometry getGeometryByRecNo( int recNo ) 345 throws IOException { 346 Geometry geom = null; 347 348 int shpType = getShapeTypeByRecNo( recNo ); 349 350 if ( shpType == ShapeConst.SHAPE_TYPE_POINT ) { 351 SHPPoint shppoint = (SHPPoint) shp.getByRecNo( recNo ); 352 geom = shpwks.transformPoint( null, shppoint ); 353 } else if ( shpType == ShapeConst.SHAPE_TYPE_MULTIPOINT ) { 354 SHPMultiPoint shpmultipoint = (SHPMultiPoint) shp.getByRecNo( recNo ); 355 Point[] points = shpwks.transformMultiPoint( null, shpmultipoint ); 356 if ( points != null ) { 357 MultiPoint mp = GeometryFactory.createMultiPoint( points ); 358 geom = mp; 359 } else { 360 geom = null; 361 } 362 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYLINE ) { 363 SHPPolyLine shppolyline = (SHPPolyLine) shp.getByRecNo( recNo ); 364 Curve[] curves = shpwks.transformPolyLine( null, shppolyline ); 365 if ( ( curves != null ) && ( curves.length > 1 ) ) { 366 // create multi curve 367 MultiCurve mc = GeometryFactory.createMultiCurve( curves ); 368 geom = mc; 369 } else if ( ( curves != null ) && ( curves.length == 1 ) ) { 370 // single curve 371 geom = curves[0]; 372 } else { 373 geom = null; 374 } 375 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGON ) { 376 SHPPolygon shppoly = (SHPPolygon) shp.getByRecNo( recNo ); 377 Surface[] polygons = shpwks.transformPolygon( null, shppoly ); 378 if ( ( polygons != null ) && ( polygons.length > 1 ) ) { 379 // create multi surface 380 MultiSurface ms = GeometryFactory.createMultiSurface( polygons ); 381 geom = ms; 382 } else if ( ( polygons != null ) && ( polygons.length == 1 ) ) { 383 geom = polygons[0]; 384 } else { 385 geom = null; 386 } 387 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGONZ ) { 388 389 SHPPolygon3D shppoly = (SHPPolygon3D) shp.getByRecNo( recNo ); 390 391 Surface[] polygons = shpwks.transformPolygon( null, shppoly ); 392 393 if ( ( polygons != null ) && ( polygons.length > 1 ) ) { 394 // create multi surface 395 MultiSurface ms = GeometryFactory.createMultiSurface( polygons ); 396 geom = ms; 397 } else if ( ( polygons != null ) && ( polygons.length == 1 ) ) { 398 geom = polygons[0]; 399 } else { 400 geom = null; 401 } 402 } 403 404 return geom; 405 } 406 407 /** 408 * returns the type of the RecNo'th Geometrie<BR> 409 * per definition a shape file contains onlay one shape type<BR> 410 * but null shapes are possible too!<BR> 411 * 412 * @param RecNo 413 * @return the type of the RecNo'th Geometrie<BR> 414 * per definition a shape file contains onlay one shape type<BR> 415 * but null shapes are possible too!<BR> 416 * @throws IOException 417 */ 418 public int getShapeTypeByRecNo( int RecNo ) 419 throws IOException { 420 return shp.getShapeTypeByRecNo( RecNo ); 421 } 422 423 /** 424 * returns a int array that containts all the record numbers that matches the search operation 425 * 426 * @param column 427 * @param value 428 * @return a int array that containts all the record numbers that matches the search operation 429 * @throws IOException 430 * @throws DBaseIndexException 431 */ 432 public int[] getGeoNumbersByAttribute( String column, Comparable<?> value ) 433 throws IOException, DBaseIndexException { 434 DBaseIndex index = dBaseIndexes.get( column ); 435 436 if ( index == null ) { 437 return null; 438 } 439 440 return index.search( value ); 441 } 442 443 /** 444 * returns a ArrayList that contains all geomeries of the shape file<BR> 445 * which mbr's are completly or partly within the rectangle r<BR> 446 * only Points, MultiPoints, PolyLines and Polygons are handled<BR> 447 * 448 * @param r 449 * @return a ArrayList that contains all geomeries of the shape file<BR> 450 * which mbr's are completly or partly within the rectangle r<BR> 451 * only Points, MultiPoints, PolyLines and Polygons are handled<BR> 452 * @throws IOException 453 */ 454 public int[] getGeoNumbersByRect( Envelope r ) 455 throws IOException { 456 SHPPoint geom = null; 457 int[] num = null; 458 int numRecs = getRecordNum(); 459 460 Envelope mbr = getFileMBR(); 461 462 if ( !mbr.intersects( r ) ) { 463 return null; 464 } 465 466 if ( hasRTreeIndex ) { 467 try { 468 // translate envelope (deegree) to bounding box (rtree) 469 HyperBoundingBox box = new HyperBoundingBox( new HyperPoint( r.getMin().getAsArray() ), 470 new HyperPoint( r.getMax().getAsArray() ) ); 471 Object[] iNumbers = rti.intersects( box ); 472 num = new int[iNumbers.length]; 473 474 for ( int i = 0; i < iNumbers.length; i++ ) 475 num[i] = ( (Integer) iNumbers[i] ).intValue(); 476 477 return num; 478 } catch ( Exception e ) { 479 e.printStackTrace(); 480 } 481 } 482 483 // for every geometry (record) within the shape file 484 // check if it's inside the search-rectangle r 485 List<Integer> numbers = new ArrayList<Integer>( 500 ); 486 for ( int i = 0; i < numRecs; i++ ) { 487 if ( getShapeTypeByRecNo( i + 1 ) == ShapeConst.SHAPE_TYPE_NULL ) { 488 // why is nothing done here? 489 } else if ( getShapeTypeByRecNo( i + 1 ) == ShapeConst.SHAPE_TYPE_POINT ) { 490 geom = (SHPPoint) shp.getByRecNo( i + 1 ); 491 492 // is the Point within the seach rectangle? 493 Position pos = GeometryFactory.createPosition( geom.x, geom.y ); 494 495 if ( r.contains( pos ) == true ) { 496 numbers.add( new Integer( i + 1 ) ); 497 } 498 } else { 499 // get minimum bounding rectangle of the i'th record 500 mbr = getMBRByRecNo( i + 1 ); 501 502 // is the i'th record a geometrie having a mbr 503 // (only for PolyLines, Polygons and MultiPoints mbrs are defined) 504 if ( mbr != null ) { 505 // if the tested rectangles are not disjunct the number of the 506 // actual record is added to the ArrayList 507 if ( mbr.intersects( r ) ) { 508 numbers.add( new Integer( i + 1 ) ); 509 } 510 } 511 } 512 } 513 514 if ( numbers.size() > 0 ) { 515 num = new int[numbers.size()]; 516 517 // put all numbers within numbers to an array 518 for ( int i = 0; i < numbers.size(); i++ ) { 519 num[i] = numbers.get( i ); 520 } 521 } 522 523 return num; 524 } // end of getGeoNumbersByRect 525 526 /** 527 * is a property unique? 528 * 529 * @param property 530 * @return if it is unique 531 */ 532 public boolean isUnique( String property ) { 533 DBaseIndex index = dBaseIndexes.get( property ); 534 535 if ( index == null ) { 536 return false; 537 } 538 539 return index.isUnique(); 540 } 541 542 /** 543 * returns the properties (column headers) of the dBase-file<BR> 544 * associated to the shape-file<BR> 545 * 546 * @return the properties (column headers) of the dBase-file<BR> 547 * associated to the shape-file<BR> 548 * @throws HasNoDBaseFileException 549 * @throws DBaseException 550 */ 551 public String[] getProperties() 552 throws HasNoDBaseFileException, DBaseException { 553 if ( !hasDBaseFile ) { 554 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 555 } 556 557 return dbf.getProperties(); 558 } 559 560 /** 561 * returns the datatype of each column of the database file<BR> 562 * associated to the shape-file<BR> 563 * 564 * @return the datatype of each column of the database file<BR> 565 * associated to the shape-file<BR> 566 * @throws HasNoDBaseFileException 567 * @throws DBaseException 568 */ 569 public String[] getDataTypes() 570 throws HasNoDBaseFileException, DBaseException { 571 if ( !hasDBaseFile ) { 572 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 573 } 574 575 return dbf.getDataTypes(); 576 } 577 578 /** 579 * 580 * 581 * @return the lengths 582 * 583 * @throws HasNoDBaseFileException 584 * @throws DBaseException 585 */ 586 public int[] getDataLengths() 587 throws HasNoDBaseFileException, DBaseException { 588 String[] properties = getProperties(); 589 int[] retval = new int[properties.length]; 590 591 for ( int i = 0; i < properties.length; i++ ) { 592 retval[i] = dbf.getDataLength( properties[i] ); 593 } 594 595 return retval; 596 } 597 598 /** 599 * returns the datatype of each column of the dBase associated<BR> 600 * to the shape-file specified by fields<BR> 601 * 602 * @param fields 603 * @return the datatype of each column of the dBase associated<BR> 604 * to the shape-file specified by fields<BR> 605 * @throws HasNoDBaseFileException 606 * @throws DBaseException 607 */ 608 public String[] getDataTypes( String[] fields ) 609 throws HasNoDBaseFileException, DBaseException { 610 if ( !hasDBaseFile ) { 611 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 612 } 613 614 return dbf.getDataTypes( fields ); 615 } 616 617 /** 618 * returns the number of geometries within a feature collection<BR> 619 * 620 * @param fc 621 * : featurecollection which is checked for the number geomtries<BR> 622 */ 623 private int getGeometryCount( FeatureCollection fc ) { 624 return fc.size(); 625 } 626 627 /** 628 * returns the type of the n'th feature in a featurecollection 629 * 630 * @param fc 631 * FeatureCollection. must not be empty. 632 * @param n 633 * number of the feature which should be examined starts with 0 634 */ 635 private int getGeometryType( FeatureCollection fc, int n ) { 636 Feature feature = null; 637 638 feature = fc.getFeature( n ); 639 640 Geometry[] g = feature.getGeometryPropertyValues(); 641 642 if ( ( g == null ) || ( g.length == 0 ) ) { 643 return -1; 644 } 645 646 if ( g[0] instanceof Point ) { 647 return 0; 648 } 649 650 if ( g[0] instanceof Curve ) { 651 return 1; 652 } 653 654 if ( g[0] instanceof Surface ) { 655 return 2; 656 } 657 658 if ( g[0] instanceof MultiPoint ) { 659 return 3; 660 } 661 662 if ( g[0] instanceof MultiCurve ) { 663 return 4; 664 } 665 666 if ( g[0] instanceof MultiSurface ) { 667 return 5; 668 } 669 670 return -1; 671 } 672 673 /** 674 * returns the n'th feature of a featurecollection as a Geometry<BR> 675 * 676 * @param fc 677 * : FeatureCollection<BR> 678 * @param n 679 * : number of the feature which should be returned<BR> 680 */ 681 private Geometry getFeatureAsGeometry( FeatureCollection fc, int n ) { 682 Feature feature = null; 683 684 feature = fc.getFeature( n ); 685 686 return feature.getGeometryPropertyValues()[0]; 687 } 688 689 /** 690 * Copies the feature properties of a feature. 691 * 692 * @param fc 693 * @param n 694 * the number of the feature to get the properties for 695 * @return the properties 696 */ 697 public FeatureProperty[] getFeatureProperties( FeatureCollection fc, int n ) { 698 Feature feature = fc.getFeature( n ); 699 700 PropertyType[] ftp = feature.getFeatureType().getProperties(); 701 FeatureProperty[] fp = new FeatureProperty[ftp.length]; 702 703 for ( int i = 0; i < ftp.length; i++ ) { 704 FeatureProperty fp_ = feature.getDefaultProperty( ftp[i].getName() ); 705 Object value = null; 706 if ( fp_ != null ) { 707 value = fp_.getValue(); 708 } 709 fp[i] = FeatureFactory.createFeatureProperty( ftp[i].getName(), value ); 710 } 711 712 return fp; 713 } 714 715 /** 716 */ 717 private void initDBaseFile( FeatureCollection fc ) 718 throws DBaseException { 719 FieldDescriptor[] fieldDesc = null; 720 721 // get feature properties 722 FeatureProperty[] pairs = getFeatureProperties( fc, 0 ); 723 724 // count regular fields 725 int cnt = 0; 726 FeatureType featT = fc.getFeature( 0 ).getFeatureType(); 727 PropertyType[] ftp = featT.getProperties(); 728 729 for ( int i = 0; i < pairs.length; i++ ) { 730 Object obj = pairs[i].getValue(); 731 732 if ( obj instanceof Object[] ) { 733 obj = ( (Object[]) obj )[0]; 734 } 735 PropertyType pt = featT.getProperty( pairs[i].getName() ); 736 if ( !( obj instanceof ByteArrayInputStream ) && !( obj instanceof Geometry ) && pt.getType() != Types.GEOMETRY ) { 737 cnt++; 738 } 739 } 740 741 // allocate memory for field descriptors 742 fieldDesc = new FieldDescriptor[cnt]; 743 744 // determine maximum field length for string columns 745 Iterator<Feature> iter = fc.iterator(); 746 Map<QualifiedName, Integer> fieldLength = new HashMap<QualifiedName, Integer>(); 747 while ( iter.hasNext() ) { 748 Feature feature = (Feature) iter.next(); 749 FeatureProperty[] fp = feature.getProperties(); 750 for ( FeatureProperty featureProperty : fp ) { 751 Object o = null; 752 if ( featureProperty != null && ( o = featureProperty.getValue() ) instanceof String ) { 753 int k = ( (String) o ).length(); 754 if ( fieldLength.get( featureProperty.getName() ) == null || k > fieldLength.get( featureProperty.getName() ) ) { 755 fieldLength.put( featureProperty.getName(), new Integer( k + 5) ); 756 } 757 } 758 } 759 760 } 761 // get properties names and types and create a FieldDescriptor 762 // for each properties except the geometry-property 763 cnt = 0; 764 for ( int i = 0; i < ftp.length; i++ ) { 765 int pos = ftp[i].getName().getLocalName().lastIndexOf( '.' ); 766 if ( pos < 0 ) { 767 pos = -1; 768 } 769 String s = ftp[i].getName().getLocalName().substring( pos + 1 ); 770 if ( ftp[i].getType() == Types.INTEGER ) { 771 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 20, (byte) 0 ); 772 } else if ( ftp[i].getType() == Types.BIGINT ) { 773 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 0 ); 774 } else if ( ftp[i].getType() == Types.SMALLINT ) { 775 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 4, (byte) 0 ); 776 } else if ( ftp[i].getType() == Types.CHAR ) { 777 fieldDesc[cnt++] = new FieldDescriptor( s, "C", (byte) 1, (byte) 0 ); 778 } else if ( ftp[i].getType() == Types.FLOAT ) { 779 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 10 ); 780 } else if ( ftp[i].getType() == Types.DOUBLE || ftp[i].getType() == Types.NUMERIC ) { 781 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 10 ); 782 } else if ( ftp[i].getType() == Types.DECIMAL ) { 783 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 10 ); 784 } else if ( ftp[i].getType() == Types.VARCHAR ) { 785 int len = 10; 786 if ( fieldLength.get( ftp[i].getName() ) != null ) { 787 len = fieldLength.get( ftp[i].getName() ).intValue()+2; 788 } 789 if ( len > 127 ) { 790 len = 127; 791 } 792 fieldDesc[cnt++] = new FieldDescriptor( s, "C", (byte)len, (byte) 0 ); 793 } else if ( ftp[i].getType() == Types.DATE ) { 794 fieldDesc[cnt++] = new FieldDescriptor( s, "D", (byte) 12, (byte) 0 ); 795 } else { 796 } 797 } 798 // initialize/create DBaseFile 799 try { 800 dbf = new DBaseFile( fileName, fieldDesc ); 801 } catch ( DBaseException e ) { 802 hasDBaseFile = false; 803 } 804 } 805 806 /** 807 * writes a OGC FeatureCollection to a ESRI shape file.<BR> 808 * all features in the collection must have the same properties.<BR> 809 * 810 * @param fc 811 * FeatureCollection. must not be null or empty. 812 * @throws Exception 813 */ 814 public void writeShape( FeatureCollection fc ) 815 throws Exception { 816 817 int nbyte = 0; 818 int geotype = -1; 819 byte shptype = -1; 820 int typ_ = getGeometryType( fc, 0 ); 821 byte[] bytearray = null; 822 IndexRecord record = null; 823 SHPEnvelope mbr = null; 824 // mbr of the whole shape file 825 SHPEnvelope shpmbr = new SHPEnvelope(); 826 827 // Set the Offset to the end of the fileHeader 828 int offset = ShapeConst.SHAPE_FILE_HEADER_LENGTH; 829 830 // initialize the dbasefile associated with the shapefile 831 initDBaseFile( fc ); 832 833 // loop throug the Geometries of the feature collection anf write them 834 // to a bytearray 835 for ( int i = 0; i < getGeometryCount( fc ); i++ ) { 836 if ( i % 1000 == 0 ) { 837 System.gc(); 838 } 839 // write i'th features properties to a ArrayList 840 PropertyType[] ftp = fc.getFeature( 0 ).getFeatureType().getProperties(); 841 ArrayList<Object> vec = new ArrayList<Object>( ftp.length ); 842 for ( int j = 0; j < ftp.length; j++ ) { 843 if ( ftp[j].getType() == Types.GEOMETRY ) 844 continue; 845 FeatureProperty fp = fc.getFeature( i ).getDefaultProperty( ftp[j].getName() ); 846 Object obj = null; 847 if ( fp != null ) { 848 obj = fp.getValue(); 849 } 850 851 if ( obj instanceof Object[] ) { 852 obj = ( (Object[]) obj )[0]; 853 } 854 855 if ( ( ftp[j].getType() == Types.INTEGER ) || ( ftp[j].getType() == Types.BIGINT ) 856 || ( ftp[j].getType() == Types.DECIMAL ) 857 || ( ftp[j].getType() == Types.SMALLINT ) || ( ftp[j].getType() == Types.CHAR ) 858 || ( ftp[j].getType() == Types.FLOAT ) || ( ftp[j].getType() == Types.DOUBLE ) 859 || ( ftp[j].getType() == Types.NUMERIC ) || ( ftp[j].getType() == Types.VARCHAR ) 860 || ( ftp[j].getType() == Types.DATE ) ) { 861 vec.add( obj ); 862 } 863 864 } 865 866 // write the ArrayList (properties) to the dbase file 867 try { 868 dbf.setRecord( vec ); 869 } catch ( DBaseException db ) { 870 db.printStackTrace(); 871 throw new Exception( db.toString() ); 872 } 873 874 // Get Geometry Type of i'th feature 875 geotype = getGeometryType( fc, i ); 876 877 if ( geotype < 0 ) { 878 continue; 879 } 880 881 if ( ( typ_ == 0 ) || ( typ_ == 3 ) ) { 882 if ( ( geotype != 0 ) && ( geotype != 3 ) ) { 883 throw new Exception( "Not a homogen FeatureCollection" ); 884 } 885 } 886 887 if ( ( typ_ == 1 ) || ( typ_ == 4 ) ) { 888 if ( ( geotype != 1 ) && ( geotype != 4 ) ) { 889 throw new Exception( "Not a homogen FeatureCollection" ); 890 } 891 } 892 893 if ( ( typ_ == 2 ) || ( typ_ == 5 ) ) { 894 if ( ( geotype != 2 ) && ( geotype != 5 ) ) { 895 throw new Exception( "Not a homogen FeatureCollection" ); 896 } 897 } 898 899 // get wks geometrie for feature (i) and write it to a file 900 if ( geotype == 0 ) { 901 // Geometrie Type = Point 902 Point wks = (Point) getFeatureAsGeometry( fc, i ); 903 SHPPoint shppoint = new SHPPoint( wks.getPosition() ); 904 nbyte = shppoint.size(); 905 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 906 shppoint.writeSHPPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 907 mbr = new SHPEnvelope( shppoint, shppoint ); 908 909 if ( i == 0 ) { 910 shpmbr = mbr; 911 } 912 913 shptype = 1; 914 } else if ( geotype == 1 ) { 915 // Geometrie Type = LineString 916 Curve[] wks = new Curve[1]; 917 wks[0] = (Curve) getFeatureAsGeometry( fc, i ); 918 919 SHPPolyLine shppolyline = new SHPPolyLine( wks ); 920 nbyte = shppolyline.size(); 921 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 922 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 923 mbr = shppolyline.getEnvelope(); 924 925 if ( i == 0 ) { 926 shpmbr = mbr; 927 } 928 929 shptype = 3; 930 } else if ( geotype == 2 ) { 931 // Geometrie Type = Polygon 932 Surface[] wks = new Surface[1]; 933 wks[0] = (Surface) getFeatureAsGeometry( fc, i ); 934 validateOrientation( wks ); 935 936 SHPPolygon shppolygon = new SHPPolygon( wks ); 937 nbyte = shppolygon.size(); 938 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 939 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 940 mbr = shppolygon.getEnvelope(); 941 942 if ( i == 0 ) { 943 shpmbr = mbr; 944 } 945 946 shptype = 5; 947 } else if ( geotype == 3 ) { 948 // Geometrie Type = MultiPoint 949 MultiPoint wks = (MultiPoint) getFeatureAsGeometry( fc, i ); 950 SHPMultiPoint shpmultipoint = new SHPMultiPoint( wks ); 951 nbyte = shpmultipoint.size(); 952 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 953 shpmultipoint.writeSHPMultiPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 954 mbr = shpmultipoint.getEnvelope(); 955 shptype = 8; 956 } else if ( geotype == 4 ) { 957 // Geometrie Type = MultiLineString 958 MultiCurve wks = (MultiCurve) getFeatureAsGeometry( fc, i ); 959 SHPPolyLine shppolyline = new SHPPolyLine( wks.getAllCurves() ); 960 nbyte = shppolyline.size(); 961 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 962 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 963 mbr = shppolyline.getEnvelope(); 964 965 if ( i == 0 ) { 966 shpmbr = mbr; 967 } 968 969 shptype = 3; 970 } else if ( geotype == 5 ) { 971 // Geometrie Type = MultiPolygon 972 MultiSurface wks = (MultiSurface) getFeatureAsGeometry( fc, i ); 973 SHPPolygon shppolygon = new SHPPolygon( wks.getAllSurfaces() ); 974 nbyte = shppolygon.size(); 975 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 976 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 977 mbr = shppolygon.getEnvelope(); 978 979 if ( i == 0 ) { 980 shpmbr = mbr; 981 } 982 983 shptype = 5; 984 } 985 986 // write bytearray to the shape file 987 record = new IndexRecord( offset / 2, nbyte / 2 ); 988 989 // write recordheader to the bytearray 990 ByteUtils.writeBEInt( bytearray, 0, i ); 991 ByteUtils.writeBEInt( bytearray, 4, nbyte / 2 ); 992 993 // write record (bytearray) including recordheader to the shape file 994 shp.write( bytearray, record, mbr ); 995 996 // actualise shape file minimum boundary rectangle 997 if ( mbr.west < shpmbr.west ) { 998 shpmbr.west = mbr.west; 999 } 1000 1001 if ( mbr.east > shpmbr.east ) { 1002 shpmbr.east = mbr.east; 1003 } 1004 1005 if ( mbr.south < shpmbr.south ) { 1006 shpmbr.south = mbr.south; 1007 } 1008 1009 if ( mbr.north > shpmbr.north ) { 1010 shpmbr.north = mbr.north; 1011 } 1012 1013 // icrement offset for pointing at the end of the file 1014 offset += ( nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 1015 1016 bytearray = null; 1017 } 1018 1019 dbf.writeAllToFile(); 1020 1021 // write shape header 1022 shp.writeHeader( offset, shptype, shpmbr ); 1023 1024 } 1025 1026 private void validateOrientation( Surface[] wks ) 1027 throws GeometryException { 1028 com.vividsolutions.jts.geom.Geometry jts = JTSAdapter.export( wks[0] ); 1029 CGAlgorithms.isCCW( jts.getCoordinates() ); 1030 if ( CGAlgorithms.isCCW( jts.getCoordinates() ) ) { 1031 Position[] pos = wks[0].getSurfaceBoundary().getExteriorRing().getPositions(); 1032 Position[] pos2 = new Position[pos.length]; 1033 for ( int j = 0; j < pos2.length; j++ ) { 1034 pos2[j] = pos[pos.length - j - 1]; 1035 } 1036 Position[][] iPos = null; 1037 if ( wks[0].getSurfaceBoundary().getInteriorRings() != null ) { 1038 Ring[] rings = wks[0].getSurfaceBoundary().getInteriorRings(); 1039 iPos = new Position[rings.length][]; 1040 for ( int j = 0; j < rings.length; j++ ) { 1041 pos = rings[j].getPositions(); 1042 iPos[j] = new Position[pos.length]; 1043 for ( int k = 0; k < pos.length; k++ ) { 1044 iPos[j][k] = pos[pos.length - k - 1]; 1045 } 1046 } 1047 } 1048 wks[0] = GeometryFactory.createSurface( pos2, iPos, new SurfaceInterpolationImpl(), 1049 wks[0].getCoordinateSystem() ); 1050 } 1051 } 1052 }