001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/ShapeFile.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 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: apoth $
094 *
095 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 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. must not be empty.
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 * @param fc
709 * FeatureCollection. must not be null or empty.
710 * @throws Exception
711 */
712 public void writeShape( FeatureCollection fc )
713 throws Exception {
714
715 int nbyte = 0;
716 int geotype = -1;
717 byte shptype = -1;
718 int typ_ = getGeometryType( fc, 0 );
719 byte[] bytearray = null;
720 IndexRecord record = null;
721 SHPEnvelope mbr = null;
722 // mbr of the whole shape file
723 SHPEnvelope shpmbr = new SHPEnvelope();
724
725 // Set the Offset to the end of the fileHeader
726 int offset = ShapeConst.SHAPE_FILE_HEADER_LENGTH;
727
728 // initialize the dbasefile associated with the shapefile
729 initDBaseFile( fc );
730
731 // loop throug the Geometries of the feature collection anf write them
732 // to a bytearray
733 for ( int i = 0; i < getGeometryCount( fc ); i++ ) {
734 if ( i % 1000 == 0 ) {
735 System.gc();
736 }
737 // write i'th features properties to a ArrayList
738 PropertyType[] ftp = fc.getFeature( 0 ).getFeatureType().getProperties();
739 ArrayList<Object> vec = new ArrayList<Object>( ftp.length );
740 for ( int j = 0; j < ftp.length; j++ ) {
741 if ( ftp[j].getType() == Types.GEOMETRY )
742 continue;
743 FeatureProperty fp = fc.getFeature( i ).getDefaultProperty( ftp[j].getName() );
744 Object obj = null;
745 if ( fp != null ) {
746 obj = fp.getValue();
747 }
748
749 if ( obj instanceof Object[] ) {
750 obj = ( (Object[]) obj )[0];
751 }
752
753 if ( ( ftp[j].getType() == Types.INTEGER ) || ( ftp[j].getType() == Types.BIGINT )
754 || ( ftp[j].getType() == Types.SMALLINT ) || ( ftp[j].getType() == Types.CHAR )
755 || ( ftp[j].getType() == Types.FLOAT ) || ( ftp[j].getType() == Types.DOUBLE )
756 || ( ftp[j].getType() == Types.NUMERIC ) || ( ftp[j].getType() == Types.VARCHAR )
757 || ( ftp[j].getType() == Types.DATE ) ) {
758 vec.add( obj );
759 }
760
761 }
762
763 // write the ArrayList (properties) to the dbase file
764 try {
765 dbf.setRecord( vec );
766 } catch ( DBaseException db ) {
767 db.printStackTrace();
768 throw new Exception( db.toString() );
769 }
770
771 // Get Geometry Type of i'th feature
772 geotype = getGeometryType( fc, i );
773
774 if ( geotype < 0 ) {
775 continue;
776 }
777
778 if ( ( typ_ == 0 ) || ( typ_ == 3 ) ) {
779 if ( ( geotype != 0 ) && ( geotype != 3 ) ) {
780 throw new Exception( "Not a homogen FeatureCollection" );
781 }
782 }
783
784 if ( ( typ_ == 1 ) || ( typ_ == 4 ) ) {
785 if ( ( geotype != 1 ) && ( geotype != 4 ) ) {
786 throw new Exception( "Not a homogen FeatureCollection" );
787 }
788 }
789
790 if ( ( typ_ == 2 ) || ( typ_ == 5 ) ) {
791 if ( ( geotype != 2 ) && ( geotype != 5 ) ) {
792 throw new Exception( "Not a homogen FeatureCollection" );
793 }
794 }
795
796 // get wks geometrie for feature (i) and write it to a file
797 if ( geotype == 0 ) {
798 // Geometrie Type = Point
799 Point wks = (Point) getFeatureAsGeometry( fc, i );
800 SHPPoint shppoint = new SHPPoint( wks.getPosition() );
801 nbyte = shppoint.size();
802 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
803 shppoint.writeSHPPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
804 mbr = new SHPEnvelope( shppoint, shppoint );
805
806 if ( i == 0 ) {
807 shpmbr = mbr;
808 }
809
810 shptype = 1;
811 } else if ( geotype == 1 ) {
812 // Geometrie Type = LineString
813 Curve[] wks = new Curve[1];
814 wks[0] = (Curve) getFeatureAsGeometry( fc, i );
815
816 SHPPolyLine shppolyline = new SHPPolyLine( wks );
817 nbyte = shppolyline.size();
818 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
819 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
820 mbr = shppolyline.getEnvelope();
821
822 if ( i == 0 ) {
823 shpmbr = mbr;
824 }
825
826 shptype = 3;
827 } else if ( geotype == 2 ) {
828 // Geometrie Type = Polygon
829 Surface[] wks = new Surface[1];
830 wks[0] = (Surface) getFeatureAsGeometry( fc, i );
831 validateOrientation( wks );
832
833 SHPPolygon shppolygon = new SHPPolygon( wks );
834 nbyte = shppolygon.size();
835 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
836 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
837 mbr = shppolygon.getEnvelope();
838
839 if ( i == 0 ) {
840 shpmbr = mbr;
841 }
842
843 shptype = 5;
844 } else if ( geotype == 3 ) {
845 // Geometrie Type = MultiPoint
846 MultiPoint wks = (MultiPoint) getFeatureAsGeometry( fc, i );
847 SHPMultiPoint shpmultipoint = new SHPMultiPoint( wks );
848 nbyte = shpmultipoint.size();
849 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
850 shpmultipoint.writeSHPMultiPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
851 mbr = shpmultipoint.getEnvelope();
852 shptype = 8;
853 } else if ( geotype == 4 ) {
854 // Geometrie Type = MultiLineString
855 MultiCurve wks = (MultiCurve) getFeatureAsGeometry( fc, i );
856 SHPPolyLine shppolyline = new SHPPolyLine( wks.getAllCurves() );
857 nbyte = shppolyline.size();
858 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
859 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
860 mbr = shppolyline.getEnvelope();
861
862 if ( i == 0 ) {
863 shpmbr = mbr;
864 }
865
866 shptype = 3;
867 } else if ( geotype == 5 ) {
868 // Geometrie Type = MultiPolygon
869 MultiSurface wks = (MultiSurface) getFeatureAsGeometry( fc, i );
870 SHPPolygon shppolygon = new SHPPolygon( wks.getAllSurfaces() );
871 nbyte = shppolygon.size();
872 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH];
873 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
874 mbr = shppolygon.getEnvelope();
875
876 if ( i == 0 ) {
877 shpmbr = mbr;
878 }
879
880 shptype = 5;
881 }
882
883 // write bytearray to the shape file
884 record = new IndexRecord( offset / 2, nbyte / 2 );
885
886 // write recordheader to the bytearray
887 ByteUtils.writeBEInt( bytearray, 0, i );
888 ByteUtils.writeBEInt( bytearray, 4, nbyte / 2 );
889
890 // write record (bytearray) including recordheader to the shape file
891 shp.write( bytearray, record, mbr );
892
893 // actualise shape file minimum boundary rectangle
894 if ( mbr.west < shpmbr.west ) {
895 shpmbr.west = mbr.west;
896 }
897
898 if ( mbr.east > shpmbr.east ) {
899 shpmbr.east = mbr.east;
900 }
901
902 if ( mbr.south < shpmbr.south ) {
903 shpmbr.south = mbr.south;
904 }
905
906 if ( mbr.north > shpmbr.north ) {
907 shpmbr.north = mbr.north;
908 }
909
910 // icrement offset for pointing at the end of the file
911 offset += ( nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH );
912
913 bytearray = null;
914 }
915
916 dbf.writeAllToFile();
917
918 // write shape header
919 shp.writeHeader( offset, shptype, shpmbr );
920
921 }
922
923 private void validateOrientation( Surface[] wks )
924 throws GeometryException {
925 com.vividsolutions.jts.geom.Geometry jts = JTSAdapter.export( wks[0] );
926 CGAlgorithms.isCCW( jts.getCoordinates() );
927 if ( CGAlgorithms.isCCW( jts.getCoordinates() ) ) {
928 Position[] pos = wks[0].getSurfaceBoundary().getExteriorRing().getPositions();
929 Position[] pos2 = new Position[pos.length];
930 for ( int j = 0; j < pos2.length; j++ ) {
931 pos2[j] = pos[pos.length - j - 1];
932 }
933 Position[][] iPos = null;
934 if ( wks[0].getSurfaceBoundary().getInteriorRings() != null ) {
935 Ring[] rings = wks[0].getSurfaceBoundary().getInteriorRings();
936 iPos = new Position[rings.length][];
937 for ( int j = 0; j < rings.length; j++ ) {
938 pos = rings[j].getPositions();
939 iPos[j] = new Position[pos.length];
940 for ( int k = 0; k < pos.length; k++ ) {
941 iPos[j][k] = pos[pos.length - k - 1];
942 }
943 }
944 }
945 wks[0] = GeometryFactory.createSurface( pos2, iPos, new SurfaceInterpolationImpl(),
946 wks[0].getCoordinateSystem() );
947 }
948 }
949 }