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