001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/spatialschema/GeometryFactory.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     53115 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.model.spatialschema;
045    
046    import java.util.ArrayList;
047    
048    import org.deegree.framework.util.StringTools;
049    import org.deegree.model.crs.CoordinateSystem;
050    
051    /**
052     * Factory to create geometry instances.
053     * 
054     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
055     * @version $Revision: 7987 $, $Date: 2007-08-12 20:37:59 +0200 (So, 12 Aug 2007) $
056     * 
057     */
058    public final class GeometryFactory {
059    
060        private GeometryFactory() {
061            // Hidden default constructor.
062        }
063    
064        /**
065         * creates a Envelope object out from two corner coordinates
066         * 
067         * @param minx
068         *            lower x-axis coordinate
069         * @param miny
070         *            lower y-axis coordinate
071         * @param maxx
072         *            upper x-axis coordinate
073         * @param maxy
074         *            upper y-axis coordinate
075         * @param crs
076         *            The coordinate system
077         * @return an Envelope with given parameters
078         */
079        public static Envelope createEnvelope( double minx, double miny, double maxx, double maxy, CoordinateSystem crs ) {
080            Position min = createPosition( minx, miny );
081            Position max = createPosition( maxx, maxy );
082            return new EnvelopeImpl( min, max, crs );
083        }
084    
085        /**
086         * creates a Envelope object out from two corner coordinates
087         * 
088         * @param min
089         *            lower point
090         * @param max
091         *            upper point
092         * @param crs
093         *            The coordinate system
094         * @return an Envelope with given parameters
095         */
096        public static Envelope createEnvelope( Position min, Position max, CoordinateSystem crs ) {
097            return new EnvelopeImpl( min, max, crs );
098        }
099    
100        /**
101         * creates an Envelope from a comma seperated String; e.g.: 10,34,15,48
102         * 
103         * @param bbox
104         *            the boundingbox of the created Envelope
105         * @param crs
106         *            The coordinate system
107         * @return an Envelope with given parameters
108         */
109        public static Envelope createEnvelope( String bbox, CoordinateSystem crs ) {
110            double[] d = StringTools.toArrayDouble( bbox, "," );
111            return createEnvelope( d[0], d[1], d[2], d[3], crs );
112        }
113    
114        /**
115         * creates a Position from two coordinates.
116         * 
117         * @param x
118         *            coordinate on the x-axis
119         * @param y
120         *            coordinate on the y-axis
121         * @return a Position defining position x, y
122         */
123        public static Position createPosition( double x, double y ) {
124            return new PositionImpl( x, y );
125        }
126    
127        /**
128         * creates a Position from three coordinates.
129         * 
130         * @param x
131         *            coordinate on the x-axis
132         * @param y
133         *            coordinate on the y-axis
134         * @param z
135         *            coordinate on the z-axis
136         * @return a Position defining position x, y, z
137         */
138        public static Position createPosition( double x, double y, double z ) {
139            return new PositionImpl( new double[] { x, y, z } );
140        }
141    
142        /**
143         * creates a Position from an array of double.
144         * 
145         * @param p
146         *            list of points
147         * @return the Position defined by the array.
148         */
149        public static Position createPosition( double[] p ) {
150            return new PositionImpl( p );
151        }
152    
153        /**
154         * creates a Point from two coordinates.
155         * 
156         * @param x
157         *            x coordinate
158         * @param y
159         *            y coordinate
160         * @param crs
161         *            spatial reference system of the point geometry
162         * @return a Position defining position x, y in the given CRS
163         */
164        public static Point createPoint( double x, double y, CoordinateSystem crs ) {
165            return new PointImpl( x, y, crs );
166        }
167    
168        /**
169         * creates a Point from two coordinates.
170         * 
171         * @param x
172         *            x coordinate
173         * @param y
174         *            y coordinate
175         * @param z
176         *            coordinate on the z-axis
177         * @param crs
178         *            spatial reference system of the point geometry
179         * @return a Position defining position x, y, z in the given CRS
180         */
181        public static Point createPoint( double x, double y, double z, CoordinateSystem crs ) {
182            return new PointImpl( x, y, z, crs );
183        }
184    
185        /**
186         * creates a Point from a position.
187         * 
188         * @param position
189         *            position
190         * @param crs
191         *            spatial reference system of the point geometry
192         * @return the Position defined by the array in the given CRS
193         */
194        public static Point createPoint( Position position, CoordinateSystem crs ) {
195            return new PointImpl( position, crs );
196        }
197    
198        /**
199         * creates a Point from a wkb.
200         * 
201         * @param wkb
202         *            geometry in Well-Known Binary (WKB) format
203         * @param srs
204         *            spatial reference system of the geometry
205         * @return the Position defined by the WKB and the given CRS
206         * @throws GeometryException
207         *             if the wkb is not known or invalid
208         */
209        public static Point createPoint( byte[] wkb, CoordinateSystem srs )
210                                throws GeometryException {
211            int wkbType = -1;
212            double x = 0;
213            double y = 0;
214    
215            byte byteorder = wkb[0];
216    
217            if ( byteorder == 0 ) {
218                wkbType = ByteUtils.readBEInt( wkb, 1 );
219            } else {
220                wkbType = ByteUtils.readLEInt( wkb, 1 );
221            }
222    
223            if ( wkbType != 1 ) {
224                throw new GeometryException( "invalid byte stream" );
225            }
226    
227            if ( byteorder == 0 ) {
228                x = ByteUtils.readBEDouble( wkb, 5 );
229                y = ByteUtils.readBEDouble( wkb, 13 );
230            } else {
231                x = ByteUtils.readLEDouble( wkb, 5 );
232                y = ByteUtils.readLEDouble( wkb, 13 );
233            }
234    
235            return new PointImpl( x, y, srs );
236        }
237    
238        /**
239         * creates a CurveSegment from an array of points.
240         * 
241         * @param points
242         *            array of Point
243         * @param crs
244         *            CS_CoordinateSystem spatial reference system of the curve
245         * @return A curve defined by the given Points in the CRS.
246         * @throws GeometryException
247         *             if the point array is empty
248         */
249        public static CurveSegment createCurveSegment( Position[] points, CoordinateSystem crs )
250                                throws GeometryException {
251            return new LineStringImpl( points, crs );
252        }
253    
254        /**
255         * creates a Curve from an array of Positions.
256         * 
257         * @param positions
258         *            positions
259         * @param crs
260         *            spatial reference system of the geometry
261         * @return A curve defined by the given Points in the CRS.
262         * @throws GeometryException
263         *             if the point array is empty
264         */
265        public static Curve createCurve( Position[] positions, CoordinateSystem crs )
266                                throws GeometryException {
267            CurveSegment[] cs = new CurveSegment[1];
268            cs[0] = createCurveSegment( positions, crs );
269            return new CurveImpl( cs );
270        }
271    
272        /**
273         * creates a Curve from one curve segment.
274         * 
275         * @param segment
276         *            CurveSegments
277         * @return a new CurveSegment
278         * @throws GeometryException
279         *             if the segment is null
280         */
281        public static Curve createCurve( CurveSegment segment )
282                                throws GeometryException {
283            return new CurveImpl( new CurveSegment[] { segment } );
284        }
285    
286        /**
287         * creates a Curve from an array of curve segments.
288         * 
289         * @param segments
290         *            array of CurveSegments
291         * @return a new CurveSegment
292         * @throws GeometryException
293         *             if the segment is null or has no values
294         * 
295         */
296        public static Curve createCurve( CurveSegment[] segments )
297                                throws GeometryException {
298            return new CurveImpl( segments );
299        }
300    
301        /**
302         * creates a Curve from an array of curve segments.
303         * 
304         * @param segments
305         *            array of CurveSegments
306         * @param crs
307         * @return a new CurveSegment
308         * @throws GeometryException
309         *             if the segment is null or has no values
310         * 
311         */
312        public static Curve createCurve( CurveSegment[] segments, CoordinateSystem crs )
313                                throws GeometryException {
314            return new CurveImpl( segments, crs );
315        }
316    
317        /**
318         * creates a GM_Curve from an array of ordinates
319         * 
320         * TODO: If resources are available, think about good programming style.
321         * 
322         * @param ord
323         *            the ordinates
324         * @param dim
325         *            the dimension of the ordinates
326         * @param crs
327         *            the spatial reference system of the geometry
328         * 
329         * @return the Curve defined by the given parameters
330         * @throws GeometryException
331         *             if the ord array is empty
332         */
333        public static Curve createCurve( double[] ord, int dim, CoordinateSystem crs )
334                                throws GeometryException {
335            Position[] pos = new Position[ord.length / dim];
336            int i = 0;
337            while ( i < ord.length ) {
338                double[] o = new double[dim];
339                for ( int j = 0; j < dim; j++ ) {
340                    o[j] = ord[i++];
341                }
342                pos[i / dim - 1] = GeometryFactory.createPosition( o );
343            }
344            return GeometryFactory.createCurve( pos, crs );
345        }
346    
347        /**
348         * creates a SurfacePatch from array(s) of Position
349         * 
350         * @param exteriorRing
351         *            exterior ring of the patch
352         * @param interiorRings
353         *            interior rings of the patch
354         * @param si
355         *            SurfaceInterpolation
356         * @param crs
357         *            CS_CoordinateSystem spatial reference system of the surface patch
358         * @return a Surfacepatch defined by the given Parameters
359         * @throws GeometryException
360         */
361        public static SurfacePatch createSurfacePatch( Position[] exteriorRing, Position[][] interiorRings,
362                                                       SurfaceInterpolation si, CoordinateSystem crs )
363                                throws GeometryException {
364            return new PolygonImpl( si, exteriorRing, interiorRings, crs );
365        }
366    
367        /**
368         * 
369         * @param exteriorRing
370         * @param interiorRings
371         * @param crs
372         * @return
373         * @throws GeometryException
374         */
375        public static SurfacePatch createSurfacePatch( CurveSegment[] exteriorRing, CurveSegment[][] interiorRings,
376                                                       CoordinateSystem crs )
377                                throws GeometryException {
378            Ring eRing = new RingImpl( exteriorRing, crs, '+' );
379            Ring[] iRings = null;
380            if ( interiorRings != null ) {
381                iRings = new Ring[interiorRings.length];
382                for ( int i = 0; i < iRings.length; i++ ) {
383                    iRings[i] = new RingImpl( interiorRings[i], crs, '+' );
384                }
385            }
386            return new PolygonImpl( eRing, iRings, crs );
387        }
388    
389        /**
390         * 
391         * @param exteriorRing
392         * @param interiorRings
393         * @param crs
394         * @return
395         * @throws GeometryException
396         */
397        public static SurfacePatch createSurfacePatch( Curve exteriorRing, Curve[] interiorRings,
398                                                       CoordinateSystem crs )
399                                throws GeometryException {
400            CurveSegment[] e = exteriorRing.getCurveSegments();
401            CurveSegment[][] i = null;
402            if ( interiorRings != null ) {
403                i = new CurveSegment[interiorRings.length][];
404                for ( int j = 0; j < i.length; j++ ) {
405                    i[j] = interiorRings[j].getCurveSegments();
406                }
407            }
408            return createSurfacePatch( e, i, crs );
409        }
410    
411        /**
412         * creates a Curve from a wkb.
413         * 
414         * @param wkb
415         *            byte stream that contains the wkb information
416         * @param crs
417         *            CS_CoordinateSystem spatial reference system of the curve
418         * @return the Curve defined by the WKB and the given CRS
419         * @throws GeometryException
420         *             if the wkb is not known or invalid
421         * 
422         */
423        public static Curve createCurve( byte[] wkb, CoordinateSystem crs )
424                                throws GeometryException {
425            int wkbType = -1;
426            int numPoints = -1;
427            Position[] points = null;
428            double x = 0;
429            double y = 0;
430    
431            byte byteorder = wkb[0];
432    
433            if ( byteorder == 0 ) {
434                wkbType = ByteUtils.readBEInt( wkb, 1 );
435            } else {
436                wkbType = ByteUtils.readLEInt( wkb, 1 );
437            }
438    
439            // check if it's realy a linestrin/curve
440            if ( wkbType != 2 ) {
441                throw new GeometryException( "invalid byte stream for Curve" );
442            }
443    
444            // read number of points
445            if ( byteorder == 0 ) {
446                numPoints = ByteUtils.readBEInt( wkb, 5 );
447            } else {
448                numPoints = ByteUtils.readLEInt( wkb, 5 );
449            }
450    
451            int offset = 9;
452    
453            points = new Position[numPoints];
454    
455            // read the i-th point depending on the byteorde
456            if ( byteorder == 0 ) {
457                for ( int i = 0; i < numPoints; i++ ) {
458                    x = ByteUtils.readBEDouble( wkb, offset );
459                    offset += 8;
460                    y = ByteUtils.readBEDouble( wkb, offset );
461                    offset += 8;
462                    points[i] = new PositionImpl( x, y );
463                }
464            } else {
465                for ( int i = 0; i < numPoints; i++ ) {
466                    x = ByteUtils.readLEDouble( wkb, offset );
467                    offset += 8;
468                    y = ByteUtils.readLEDouble( wkb, offset );
469                    offset += 8;
470                    points[i] = new PositionImpl( x, y );
471                }
472            }
473    
474            CurveSegment[] segment = new CurveSegment[1];
475    
476            segment[0] = createCurveSegment( points, crs );
477    
478            return createCurve( segment );
479        }
480    
481        /**
482         * creates a Surface composed of one SurfacePatch from array(s) of Position
483         * 
484         * @param exteriorRing
485         *            exterior ring of the patch
486         * @param interiorRings
487         *            interior rings of the patch
488         * @param si
489         *            SurfaceInterpolation
490         * @param crs
491         *            CS_CoordinateSystem spatial reference system of the surface patch
492         * @return a Surface composed of one SurfacePatch from array(s) of Position
493         * @throws GeometryException
494         *             if the implicite orientation is not '+' or '-', or the rings aren't closed
495         */
496        public static Surface createSurface( Position[] exteriorRing, Position[][] interiorRings, SurfaceInterpolation si,
497                                             CoordinateSystem crs )
498                                throws GeometryException {
499            SurfacePatch sp = new PolygonImpl( si, exteriorRing, interiorRings, crs );
500            return createSurface( sp );
501        }
502    
503        /**
504         * creates a Surface from an array of SurfacePatch.
505         * 
506         * @param patch
507         *            patches that build the surface
508         * @return a Surface from an array of SurfacePatch.
509         * @throws GeometryException
510         *             if implicite the orientation is not '+' or '-'
511         */
512        public static Surface createSurface( SurfacePatch patch )
513                                throws GeometryException {
514            return new SurfaceImpl( patch );
515        }
516    
517        /**
518         * creates a Surface from an array of SurfacePatch.
519         * 
520         * @param patches
521         *            patches that build the surface
522         * 
523         * @return a Surface from an array of SurfacePatch.
524         * @throws GeometryException
525         *             if implicite the orientation is not '+' or '-'
526         */
527        public static Surface createSurface( SurfacePatch[] patches )
528                                throws GeometryException {
529            return new SurfaceImpl( patches );
530        }
531    
532        /**
533         * creates a Surface from an array of SurfacePatch.
534         * 
535         * @param patches
536         *            patches that build the surface
537         * @param crs
538         * 
539         * @return a Surface from an array of SurfacePatch.
540         * @throws GeometryException
541         *             if implicite the orientation is not '+' or '-'
542         */
543        public static Surface createSurface( SurfacePatch[] patches, CoordinateSystem crs )
544                                throws GeometryException {
545            return new SurfaceImpl( patches, crs );
546        }
547    
548        /**
549         * creates a Surface from a wkb.
550         * 
551         * @param wkb
552         *            byte stream that contains the wkb information
553         * @param crs
554         *            CS_CoordinateSystem spatial reference system of the curve
555         * @param si
556         *            SurfaceInterpolation
557         * @return a Surface from a wkb.
558         * @throws GeometryException
559         *             if the implicite orientation is not '+' or '-' or the wkb is not known or invalid
560         */
561        public static Surface createSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
562                                throws GeometryException {
563            int wkbtype = -1;
564            int numRings = 0;
565            int numPoints = 0;
566            int offset = 0;
567            double x = 0;
568            double y = 0;
569    
570            Position[] externalBoundary = null;
571            Position[][] internalBoundaries = null;
572    
573            byte byteorder = wkb[offset++];
574    
575            if ( byteorder == 0 ) {
576                wkbtype = ByteUtils.readBEInt( wkb, offset );
577            } else {
578                wkbtype = ByteUtils.readLEInt( wkb, offset );
579            }
580    
581            offset += 4;
582    
583            if ( wkbtype == 6 ) {
584                return null;
585            }
586    
587            // is the geometry respresented by wkb a polygon?
588            if ( wkbtype != 3 ) {
589                throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
590            }
591    
592            // read number of rings of the polygon
593            if ( byteorder == 0 ) {
594                numRings = ByteUtils.readBEInt( wkb, offset );
595            } else {
596                numRings = ByteUtils.readLEInt( wkb, offset );
597            }
598    
599            offset += 4;
600    
601            // read number of points of the external ring
602            if ( byteorder == 0 ) {
603                numPoints = ByteUtils.readBEInt( wkb, offset );
604            } else {
605                numPoints = ByteUtils.readLEInt( wkb, offset );
606            }
607    
608            offset += 4;
609    
610            // allocate memory for the external boundary
611            externalBoundary = new Position[numPoints];
612    
613            if ( byteorder == 0 ) {
614                // read points of the external boundary from the byte[]
615                for ( int i = 0; i < numPoints; i++ ) {
616                    x = ByteUtils.readBEDouble( wkb, offset );
617                    offset += 8;
618                    y = ByteUtils.readBEDouble( wkb, offset );
619                    offset += 8;
620                    externalBoundary[i] = new PositionImpl( x, y );
621                }
622            } else {
623                // read points of the external boundary from the byte[]
624                for ( int i = 0; i < numPoints; i++ ) {
625                    x = ByteUtils.readLEDouble( wkb, offset );
626                    offset += 8;
627                    y = ByteUtils.readLEDouble( wkb, offset );
628                    offset += 8;
629                    externalBoundary[i] = new PositionImpl( x, y );
630                }
631            }
632    
633            // only if numRings is larger then one there internal rings
634            if ( numRings > 1 ) {
635                internalBoundaries = new Position[numRings - 1][];
636            }
637    
638            if ( byteorder == 0 ) {
639                for ( int j = 1; j < numRings; j++ ) {
640                    // read number of points of the j-th internal ring
641                    numPoints = ByteUtils.readBEInt( wkb, offset );
642                    offset += 4;
643    
644                    // allocate memory for the j-th internal boundary
645                    internalBoundaries[j - 1] = new Position[numPoints];
646    
647                    // read points of the external boundary from the byte[]
648                    for ( int i = 0; i < numPoints; i++ ) {
649                        x = ByteUtils.readBEDouble( wkb, offset );
650                        offset += 8;
651                        y = ByteUtils.readBEDouble( wkb, offset );
652                        offset += 8;
653                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
654                    }
655                }
656            } else {
657                for ( int j = 1; j < numRings; j++ ) {
658                    // read number of points of the j-th internal ring
659                    numPoints = ByteUtils.readLEInt( wkb, offset );
660                    offset += 4;
661    
662                    // allocate memory for the j-th internal boundary
663                    internalBoundaries[j - 1] = new Position[numPoints];
664    
665                    // read points of the external boundary from the byte[]
666                    for ( int i = 0; i < numPoints; i++ ) {
667                        x = ByteUtils.readLEDouble( wkb, offset );
668                        offset += 8;
669                        y = ByteUtils.readLEDouble( wkb, offset );
670                        offset += 8;
671                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
672                    }
673                }
674            }
675    
676            SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
677    
678            return createSurface( patch );
679        }
680    
681        /**
682         * Creates a <tt>Surface</tt> from a <tt>Envelope</tt>.
683         * <p>
684         * 
685         * @param bbox
686         *            envelope to be converted
687         * @param crs
688         *            spatial reference system of the surface
689         * @return corresponding surface
690         * 
691         * @throws GeometryException
692         *             if the implicite orientation is not '+' or '-'
693         */
694        public static Surface createSurface( Envelope bbox, CoordinateSystem crs )
695                                throws GeometryException {
696    
697            Position min = bbox.getMin();
698            Position max = bbox.getMax();
699            Position[] exteriorRing = null;
700            if ( min.getCoordinateDimension() == 2 ) {
701                exteriorRing = new Position[] { min, new PositionImpl( min.getX(), max.getY() ), max,
702                                               new PositionImpl( max.getX(), min.getY() ), min };
703            } else {
704                exteriorRing = new Position[] {
705                                               min,
706                                               new PositionImpl( min.getX(), max.getY(),
707                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ),
708                                               max,
709                                               new PositionImpl( max.getX(), min.getY(),
710                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ), min };
711            }
712    
713            return createSurface( exteriorRing, null, new SurfaceInterpolationImpl(), crs );
714        }
715    
716        /**
717         * Creates a <tt>GM_Surface</tt> from the ordinates of the exterior ring and the the interior
718         * rings
719         * <p>
720         * 
721         * @param exterior
722         *            ring
723         * @param interior
724         *            ring
725         * @param dim
726         *            of the surface
727         * @param crs
728         *            spatial reference system of the surface
729         * @return corresponding surface
730         * @throws GeometryException
731         *             if the implicite orientation is not '+' or '-'
732         * 
733         */
734        public static Surface createSurface( double[] exterior, double[][] interior, int dim, CoordinateSystem crs )
735                                throws GeometryException {
736    
737            // get exterior ring
738            Position[] ext = new Position[exterior.length / dim];
739            int i = 0;
740            int k = 0;
741            while ( i < exterior.length - 1 ) {
742                double[] o = new double[dim];
743                for ( int j = 0; j < dim; j++ ) {
744                    o[j] = exterior[i++];
745                }
746                ext[k++] = GeometryFactory.createPosition( o );
747            }
748    
749            // get interior rings if available
750            Position[][] in = null;
751            if ( interior != null && interior.length > 0 ) {
752                in = new Position[interior.length][];
753                for ( int j = 0; j < in.length; j++ ) {
754                    in[j] = new Position[interior[j].length / dim];
755                    i = 0;
756                    while ( i < interior[j].length ) {
757                        double[] o = new double[dim];
758                        for ( int z = 0; z < dim; z++ ) {
759                            o[z] = interior[j][i++];
760                        }
761                        in[j][i / dim - 1] = GeometryFactory.createPosition( o );
762                    }
763                }
764            }
765    
766            // default - linear - interpolation
767            SurfaceInterpolation si = new SurfaceInterpolationImpl();
768            return GeometryFactory.createSurface( ext, in, si, crs );
769        }
770    
771        /**
772         * creates a MultiPoint from an array of Point.
773         * 
774         * @param points
775         *            array of Points
776         * @return a MultiPoint from an array of Point.
777         * 
778         */
779        public static MultiPoint createMultiPoint( Point[] points ) {
780            return new MultiPointImpl( points );
781        }
782    
783        /**
784         * creates a MultiPoint from an array of Point.
785         * 
786         * @param points
787         *            array of Points
788         * @param crs
789         * @return a MultiPoint from an array of Point.
790         */
791        public static MultiPoint createMultiPoint( Point[] points, CoordinateSystem crs ) {
792            return new MultiPointImpl( points, crs );
793        }
794    
795        /**
796         * creates a MultiPoint from a wkb.
797         * 
798         * @param wkb
799         *            byte stream that contains the wkb information
800         * @param crs
801         *            CS_CoordinateSystem spatial reference system of the curve
802         * @return the MultiPoint defined by the WKB and the given CRS
803         * @throws GeometryException
804         *             if the wkb is not known or invalid
805         * 
806         */
807        public static MultiPoint createMultiPoint( byte[] wkb, CoordinateSystem crs )
808                                throws GeometryException {
809            Point[] points = null;
810            int wkbType = -1;
811            int numPoints = -1;
812            double x = 0;
813            double y = 0;
814            byte byteorder = wkb[0];
815    
816            // read wkbType
817            if ( byteorder == 0 ) {
818                wkbType = ByteUtils.readBEInt( wkb, 1 );
819            } else {
820                wkbType = ByteUtils.readLEInt( wkb, 1 );
821            }
822    
823            // if the geometry isn't a multipoint throw exception
824            if ( wkbType != 4 ) {
825                throw new GeometryException( "Invalid byte stream for MultiPoint" );
826            }
827    
828            // read number of points
829            if ( byteorder == 0 ) {
830                numPoints = ByteUtils.readBEInt( wkb, 5 );
831            } else {
832                numPoints = ByteUtils.readLEInt( wkb, 5 );
833            }
834    
835            points = new Point[numPoints];
836    
837            int offset = 9;
838    
839            Object[] o = new Object[3];
840            o[2] = crs;
841    
842            // read all points
843            for ( int i = 0; i < numPoints; i++ ) {
844                // byteorder of the i-th point
845                byteorder = wkb[offset];
846    
847                // wkbType of the i-th geometry
848                if ( byteorder == 0 ) {
849                    wkbType = ByteUtils.readBEInt( wkb, offset + 1 );
850                } else {
851                    wkbType = ByteUtils.readLEInt( wkb, offset + 1 );
852                }
853    
854                // if the geometry isn't a point throw exception
855                if ( wkbType != 1 ) {
856                    throw new GeometryException( "Invalid byte stream for Point as " + "part of a multi point" );
857                }
858    
859                // read the i-th point depending on the byteorde
860                if ( byteorder == 0 ) {
861                    x = ByteUtils.readBEDouble( wkb, offset + 5 );
862                    y = ByteUtils.readBEDouble( wkb, offset + 13 );
863                } else {
864                    x = ByteUtils.readLEDouble( wkb, offset + 5 );
865                    y = ByteUtils.readLEDouble( wkb, offset + 13 );
866                }
867    
868                offset += 21;
869    
870                points[i] = new PointImpl( x, y, crs );
871            }
872    
873            return createMultiPoint( points );
874        }
875    
876        /**
877         * creates a MultiCurve from an array of Curves.
878         * 
879         * @param curves
880         * @return a MultiCurve from an array of Curves.
881         */
882        public static MultiCurve createMultiCurve( Curve[] curves ) {
883            return new MultiCurveImpl( curves );
884        }
885    
886        /**
887         * creates a MultiCurve from an array of Curves.
888         * 
889         * @param curves
890         * @param crs
891         * @return a MultiCurve from an array of Curves.
892         */
893        public static MultiCurve createMultiCurve( Curve[] curves, CoordinateSystem crs ) {
894            return new MultiCurveImpl( curves, crs );
895        }
896    
897        /**
898         * creates a MultiCurve from a wkb.
899         * 
900         * @param wkb
901         *            byte stream that contains the wkb information
902         * @param crs
903         *            CS_CoordinateSystem spatial reference system of the curve
904         * @return the MultiCurve defined by the WKB and the given CRS
905         * @throws GeometryException
906         *             if the wkb is not known or invalid
907         */
908        public static MultiCurve createMultiCurve( byte[] wkb, CoordinateSystem crs )
909                                throws GeometryException {
910            int wkbType = -1;
911            int numPoints = -1;
912            int numParts = -1;
913            double x = 0;
914            double y = 0;
915            Position[][] points = null;
916            int offset = 0;
917            byte byteorder = wkb[offset++];
918    
919            if ( byteorder == 0 ) {
920                wkbType = ByteUtils.readBEInt( wkb, offset );
921            } else {
922                wkbType = ByteUtils.readLEInt( wkb, offset );
923            }
924    
925            offset += 4;
926    
927            // check if it's realy a linestring
928            if ( wkbType != 5 ) {
929                throw new GeometryException( "Invalid byte stream for MultiCurve" );
930            }
931    
932            // read number of linestrings
933            if ( byteorder == 0 ) {
934                numParts = ByteUtils.readBEInt( wkb, offset );
935            } else {
936                numParts = ByteUtils.readLEInt( wkb, offset );
937            }
938    
939            offset += 4;
940    
941            points = new Position[numParts][];
942    
943            // for every linestring
944            for ( int j = 0; j < numParts; j++ ) {
945                byteorder = wkb[offset++];
946    
947                if ( byteorder == 0 ) {
948                    wkbType = ByteUtils.readBEInt( wkb, offset );
949                } else {
950                    wkbType = ByteUtils.readLEInt( wkb, offset );
951                }
952    
953                offset += 4;
954    
955                // check if it's realy a linestring
956                if ( wkbType != 2 ) {
957                    throw new GeometryException( "Invalid byte stream for Curve as " + " part of a MultiCurve." );
958                }
959    
960                // read number of points
961                if ( byteorder == 0 ) {
962                    numPoints = ByteUtils.readBEInt( wkb, offset );
963                } else {
964                    numPoints = ByteUtils.readLEInt( wkb, offset );
965                }
966    
967                offset += 4;
968    
969                points[j] = new Position[numPoints];
970    
971                // read the i-th point depending on the byteorde
972                if ( byteorder == 0 ) {
973                    for ( int i = 0; i < numPoints; i++ ) {
974                        x = ByteUtils.readBEDouble( wkb, offset );
975                        offset += 8;
976                        y = ByteUtils.readBEDouble( wkb, offset );
977                        offset += 8;
978                        points[j][i] = new PositionImpl( x, y );
979                    }
980                } else {
981                    for ( int i = 0; i < numPoints; i++ ) {
982                        x = ByteUtils.readLEDouble( wkb, offset );
983                        offset += 8;
984                        y = ByteUtils.readLEDouble( wkb, offset );
985                        offset += 8;
986                        points[j][i] = new PositionImpl( x, y );
987                    }
988                }
989            }
990    
991            CurveSegment[] segment = new CurveSegment[1];
992            Curve[] curves = new Curve[numParts];
993    
994            for ( int i = 0; i < numParts; i++ ) {
995                segment[0] = createCurveSegment( points[i], crs );
996                curves[i] = createCurve( segment );
997            }
998    
999            return createMultiCurve( curves );
1000        }
1001    
1002        /**
1003         * creates a MultiSurface from an array of surfaces
1004         * 
1005         * @param surfaces
1006         * @return a MultiSurface from an array of surfaces
1007         */
1008        public static MultiSurface createMultiSurface( Surface[] surfaces ) {
1009            return new MultiSurfaceImpl( surfaces );
1010        }
1011    
1012        /**
1013         * creates a MultiSurface from an array of surfaces
1014         * 
1015         * @param surfaces
1016         * @param crs
1017         * @return a MultiSurface from an array of surfaces
1018         */
1019        public static MultiSurface createMultiSurface( Surface[] surfaces, CoordinateSystem crs ) {
1020            return new MultiSurfaceImpl( surfaces, crs );
1021        }
1022    
1023        /**
1024         * creates a MultiSurface from a wkb
1025         * 
1026         * @param wkb
1027         *            geometry in Well-Known Binary (WKB) format
1028         * @param crs
1029         *            spatial reference system of the geometry
1030         * @param si
1031         *            surface interpolation
1032         * @return the MultiSurface defined by the WKB and the given CRS
1033         * @throws GeometryException
1034         *             if the wkb is not known or invalid
1035         */
1036        public static MultiSurface createMultiSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
1037                                throws GeometryException {
1038            int wkbtype = -1;
1039            int numPoly = 0;
1040            int numRings = 0;
1041            int numPoints = 0;
1042            int offset = 0;
1043            double x = 0;
1044            double y = 0;
1045            Position[] externalBoundary = null;
1046            Position[][] internalBoundaries = null;
1047            byte byteorder = wkb[offset++];
1048    
1049            if ( byteorder == 0 ) {
1050                wkbtype = ByteUtils.readBEInt( wkb, offset );
1051            } else {
1052                wkbtype = ByteUtils.readLEInt( wkb, offset );
1053            }
1054    
1055            offset += 4;
1056    
1057            // is the wkbmetry a multipolygon?
1058            if ( wkbtype != 6 ) {
1059                throw new GeometryException( "Invalid byte stream for MultiSurface" );
1060            }
1061    
1062            // read number of polygons on the byte[]
1063            if ( byteorder == 0 ) {
1064                numPoly = ByteUtils.readBEInt( wkb, offset );
1065            } else {
1066                numPoly = ByteUtils.readLEInt( wkb, offset );
1067            }
1068    
1069            offset += 4;
1070    
1071            ArrayList<Surface> list = new ArrayList<Surface>( numPoly );
1072    
1073            for ( int ip = 0; ip < numPoly; ip++ ) {
1074                byteorder = wkb[offset];
1075                offset++;
1076    
1077                if ( byteorder == 0 ) {
1078                    wkbtype = ByteUtils.readBEInt( wkb, offset );
1079                } else {
1080                    wkbtype = ByteUtils.readLEInt( wkb, offset );
1081                }
1082    
1083                offset += 4;
1084    
1085                // is the geometry respresented by wkb a polygon?
1086                if ( wkbtype != 3 ) {
1087                    throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
1088                }
1089    
1090                // read number of rings of the polygon
1091                if ( byteorder == 0 ) {
1092                    numRings = ByteUtils.readBEInt( wkb, offset );
1093                } else {
1094                    numRings = ByteUtils.readLEInt( wkb, offset );
1095                }
1096    
1097                offset += 4;
1098    
1099                // read number of points of the external ring
1100                if ( byteorder == 0 ) {
1101                    numPoints = ByteUtils.readBEInt( wkb, offset );
1102                } else {
1103                    numPoints = ByteUtils.readLEInt( wkb, offset );
1104                }
1105    
1106                offset += 4;
1107    
1108                // allocate memory for the external boundary
1109                externalBoundary = new Position[numPoints];
1110    
1111                if ( byteorder == 0 ) {
1112                    // read points of the external boundary from the byte[]
1113                    for ( int i = 0; i < numPoints; i++ ) {
1114                        x = ByteUtils.readBEDouble( wkb, offset );
1115                        offset += 8;
1116                        y = ByteUtils.readBEDouble( wkb, offset );
1117                        offset += 8;
1118                        externalBoundary[i] = new PositionImpl( x, y );
1119                    }
1120                } else {
1121                    // read points of the external boundary from the byte[]
1122                    for ( int i = 0; i < numPoints; i++ ) {
1123                        x = ByteUtils.readLEDouble( wkb, offset );
1124                        offset += 8;
1125                        y = ByteUtils.readLEDouble( wkb, offset );
1126                        offset += 8;
1127                        externalBoundary[i] = new PositionImpl( x, y );
1128                    }
1129                }
1130    
1131                // only if numRings is larger then one there internal rings
1132                if ( numRings > 1 ) {
1133                    internalBoundaries = new Position[numRings - 1][];
1134                }
1135    
1136                if ( byteorder == 0 ) {
1137                    for ( int j = 1; j < numRings; j++ ) {
1138                        // read number of points of the j-th internal ring
1139                        numPoints = ByteUtils.readBEInt( wkb, offset );
1140                        offset += 4;
1141    
1142                        // allocate memory for the j-th internal boundary
1143                        internalBoundaries[j - 1] = new Position[numPoints];
1144    
1145                        // read points of the external boundary from the byte[]
1146                        for ( int i = 0; i < numPoints; i++ ) {
1147                            x = ByteUtils.readBEDouble( wkb, offset );
1148                            offset += 8;
1149                            y = ByteUtils.readBEDouble( wkb, offset );
1150                            offset += 8;
1151                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1152                        }
1153                    }
1154                } else {
1155                    for ( int j = 1; j < numRings; j++ ) {
1156                        // read number of points of the j-th internal ring
1157                        numPoints = ByteUtils.readLEInt( wkb, offset );
1158                        offset += 4;
1159    
1160                        // allocate memory for the j-th internal boundary
1161                        internalBoundaries[j - 1] = new Position[numPoints];
1162    
1163                        // read points of the external boundary from the byte[]
1164                        for ( int i = 0; i < numPoints; i++ ) {
1165                            x = ByteUtils.readLEDouble( wkb, offset );
1166                            offset += 8;
1167                            y = ByteUtils.readLEDouble( wkb, offset );
1168                            offset += 8;
1169                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1170                        }
1171                    }
1172                }
1173    
1174                SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
1175    
1176                list.add( createSurface( patch ) );
1177            }
1178    
1179            MultiSurface multisurface = new MultiSurfaceImpl( crs );
1180    
1181            for ( int i = 0; i < list.size(); i++ ) {
1182                multisurface.addSurface( list.get( i ) );
1183            }
1184    
1185            return multisurface;
1186        }
1187    
1188    }