001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/GeometryFactory.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     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: 9343 $, $Date: 2007-12-27 14:30:32 +0100 (Do, 27 Dez 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, CoordinateSystem crs )
398                                throws GeometryException {
399            CurveSegment[] e = exteriorRing.getCurveSegments();
400            CurveSegment[][] i = null;
401            if ( interiorRings != null ) {
402                i = new CurveSegment[interiorRings.length][];
403                for ( int j = 0; j < i.length; j++ ) {
404                    i[j] = interiorRings[j].getCurveSegments();
405                }
406            }
407            return createSurfacePatch( e, i, crs );
408        }
409    
410        /**
411         * creates a Curve from a wkb.
412         * 
413         * @param wkb
414         *            byte stream that contains the wkb information
415         * @param crs
416         *            CS_CoordinateSystem spatial reference system of the curve
417         * @return the Curve defined by the WKB and the given CRS
418         * @throws GeometryException
419         *             if the wkb is not known or invalid
420         * 
421         */
422        public static Curve createCurve( byte[] wkb, CoordinateSystem crs )
423                                throws GeometryException {
424            int wkbType = -1;
425            int numPoints = -1;
426            Position[] points = null;
427            double x = 0;
428            double y = 0;
429    
430            byte byteorder = wkb[0];
431    
432            if ( byteorder == 0 ) {
433                wkbType = ByteUtils.readBEInt( wkb, 1 );
434            } else {
435                wkbType = ByteUtils.readLEInt( wkb, 1 );
436            }
437    
438            // check if it's realy a linestrin/curve
439            if ( wkbType != 2 ) {
440                throw new GeometryException( "invalid byte stream for Curve" );
441            }
442    
443            // read number of points
444            if ( byteorder == 0 ) {
445                numPoints = ByteUtils.readBEInt( wkb, 5 );
446            } else {
447                numPoints = ByteUtils.readLEInt( wkb, 5 );
448            }
449    
450            int offset = 9;
451    
452            points = new Position[numPoints];
453    
454            // read the i-th point depending on the byteorde
455            if ( byteorder == 0 ) {
456                for ( int i = 0; i < numPoints; i++ ) {
457                    x = ByteUtils.readBEDouble( wkb, offset );
458                    offset += 8;
459                    y = ByteUtils.readBEDouble( wkb, offset );
460                    offset += 8;
461                    points[i] = new PositionImpl( x, y );
462                }
463            } else {
464                for ( int i = 0; i < numPoints; i++ ) {
465                    x = ByteUtils.readLEDouble( wkb, offset );
466                    offset += 8;
467                    y = ByteUtils.readLEDouble( wkb, offset );
468                    offset += 8;
469                    points[i] = new PositionImpl( x, y );
470                }
471            }
472    
473            CurveSegment[] segment = new CurveSegment[1];
474    
475            segment[0] = createCurveSegment( points, crs );
476    
477            return createCurve( segment );
478        }
479    
480        /**
481         * creates a Surface composed of one SurfacePatch from array(s) of Position
482         * 
483         * @param exteriorRing
484         *            exterior ring of the patch
485         * @param interiorRings
486         *            interior rings of the patch
487         * @param si
488         *            SurfaceInterpolation
489         * @param crs
490         *            CS_CoordinateSystem spatial reference system of the surface patch
491         * @return a Surface composed of one SurfacePatch from array(s) of Position
492         * @throws GeometryException
493         *             if the implicite orientation is not '+' or '-', or the rings aren't closed
494         */
495        public static Surface createSurface( Position[] exteriorRing, Position[][] interiorRings, SurfaceInterpolation si,
496                                             CoordinateSystem crs )
497                                throws GeometryException {
498            SurfacePatch sp = new PolygonImpl( si, exteriorRing, interiorRings, crs );
499            return createSurface( sp );
500        }
501    
502        /**
503         * creates a Surface from an array of SurfacePatch.
504         * 
505         * @param patch
506         *            patches that build the surface
507         * @return a Surface from an array of SurfacePatch.
508         * @throws GeometryException
509         *             if implicite the orientation is not '+' or '-'
510         */
511        public static Surface createSurface( SurfacePatch patch )
512                                throws GeometryException {
513            return new SurfaceImpl( patch );
514        }
515    
516        /**
517         * creates a Surface from an array of SurfacePatch.
518         * 
519         * @param patches
520         *            patches that build the surface
521         * 
522         * @return a Surface from an array of SurfacePatch.
523         * @throws GeometryException
524         *             if implicite the orientation is not '+' or '-'
525         */
526        public static Surface createSurface( SurfacePatch[] patches )
527                                throws GeometryException {
528            return new SurfaceImpl( patches );
529        }
530    
531        /**
532         * creates a Surface from an array of SurfacePatch.
533         * 
534         * @param patches
535         *            patches that build the surface
536         * @param crs
537         * 
538         * @return a Surface from an array of SurfacePatch.
539         * @throws GeometryException
540         *             if implicite the orientation is not '+' or '-'
541         */
542        public static Surface createSurface( SurfacePatch[] patches, CoordinateSystem crs )
543                                throws GeometryException {
544            return new SurfaceImpl( patches, crs );
545        }
546    
547        /**
548         * creates a Surface from a wkb.
549         * 
550         * @param wkb
551         *            byte stream that contains the wkb information
552         * @param crs
553         *            CS_CoordinateSystem spatial reference system of the curve
554         * @param si
555         *            SurfaceInterpolation
556         * @return a Surface from a wkb.
557         * @throws GeometryException
558         *             if the implicite orientation is not '+' or '-' or the wkb is not known or invalid
559         */
560        public static Surface createSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
561                                throws GeometryException {
562            int wkbtype = -1;
563            int numRings = 0;
564            int numPoints = 0;
565            int offset = 0;
566            double x = 0;
567            double y = 0;
568    
569            Position[] externalBoundary = null;
570            Position[][] internalBoundaries = null;
571    
572            byte byteorder = wkb[offset++];
573    
574            if ( byteorder == 0 ) {
575                wkbtype = ByteUtils.readBEInt( wkb, offset );
576            } else {
577                wkbtype = ByteUtils.readLEInt( wkb, offset );
578            }
579    
580            offset += 4;
581    
582            if ( wkbtype == 6 ) {
583                return null;
584            }
585    
586            // is the geometry respresented by wkb a polygon?
587            if ( wkbtype != 3 ) {
588                throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
589            }
590    
591            // read number of rings of the polygon
592            if ( byteorder == 0 ) {
593                numRings = ByteUtils.readBEInt( wkb, offset );
594            } else {
595                numRings = ByteUtils.readLEInt( wkb, offset );
596            }
597    
598            offset += 4;
599    
600            // read number of points of the external ring
601            if ( byteorder == 0 ) {
602                numPoints = ByteUtils.readBEInt( wkb, offset );
603            } else {
604                numPoints = ByteUtils.readLEInt( wkb, offset );
605            }
606    
607            offset += 4;
608    
609            // allocate memory for the external boundary
610            externalBoundary = new Position[numPoints];
611    
612            if ( byteorder == 0 ) {
613                // read points of the external boundary from the byte[]
614                for ( int i = 0; i < numPoints; i++ ) {
615                    x = ByteUtils.readBEDouble( wkb, offset );
616                    offset += 8;
617                    y = ByteUtils.readBEDouble( wkb, offset );
618                    offset += 8;
619                    externalBoundary[i] = new PositionImpl( x, y );
620                }
621            } else {
622                // read points of the external boundary from the byte[]
623                for ( int i = 0; i < numPoints; i++ ) {
624                    x = ByteUtils.readLEDouble( wkb, offset );
625                    offset += 8;
626                    y = ByteUtils.readLEDouble( wkb, offset );
627                    offset += 8;
628                    externalBoundary[i] = new PositionImpl( x, y );
629                }
630            }
631    
632            // only if numRings is larger then one there internal rings
633            if ( numRings > 1 ) {
634                internalBoundaries = new Position[numRings - 1][];
635            }
636    
637            if ( byteorder == 0 ) {
638                for ( int j = 1; j < numRings; j++ ) {
639                    // read number of points of the j-th internal ring
640                    numPoints = ByteUtils.readBEInt( wkb, offset );
641                    offset += 4;
642    
643                    // allocate memory for the j-th internal boundary
644                    internalBoundaries[j - 1] = new Position[numPoints];
645    
646                    // read points of the external boundary from the byte[]
647                    for ( int i = 0; i < numPoints; i++ ) {
648                        x = ByteUtils.readBEDouble( wkb, offset );
649                        offset += 8;
650                        y = ByteUtils.readBEDouble( wkb, offset );
651                        offset += 8;
652                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
653                    }
654                }
655            } else {
656                for ( int j = 1; j < numRings; j++ ) {
657                    // read number of points of the j-th internal ring
658                    numPoints = ByteUtils.readLEInt( wkb, offset );
659                    offset += 4;
660    
661                    // allocate memory for the j-th internal boundary
662                    internalBoundaries[j - 1] = new Position[numPoints];
663    
664                    // read points of the external boundary from the byte[]
665                    for ( int i = 0; i < numPoints; i++ ) {
666                        x = ByteUtils.readLEDouble( wkb, offset );
667                        offset += 8;
668                        y = ByteUtils.readLEDouble( wkb, offset );
669                        offset += 8;
670                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
671                    }
672                }
673            }
674    
675            SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
676    
677            return createSurface( patch );
678        }
679    
680        /**
681         * Creates a <tt>Surface</tt> from a <tt>Envelope</tt>.
682         * <p>
683         * 
684         * @param bbox
685         *            envelope to be converted
686         * @param crs
687         *            spatial reference system of the surface
688         * @return corresponding surface
689         * 
690         * @throws GeometryException
691         *             if the implicite orientation is not '+' or '-'
692         */
693        public static Surface createSurface( Envelope bbox, CoordinateSystem crs )
694                                throws GeometryException {
695    
696            Position min = bbox.getMin();
697            Position max = bbox.getMax();
698            Position[] exteriorRing = null;
699            if ( min.getCoordinateDimension() == 2 ) {
700                exteriorRing = new Position[] { min, new PositionImpl( min.getX(), max.getY() ), max,
701                                               new PositionImpl( max.getX(), min.getY() ), min };
702            } else {
703                exteriorRing = new Position[] {
704                                               min,
705                                               new PositionImpl( min.getX(), max.getY(),
706                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ),
707                                               max,
708                                               new PositionImpl( max.getX(), min.getY(),
709                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ), min };
710            }
711    
712            return createSurface( exteriorRing, null, new SurfaceInterpolationImpl(), crs );
713        }
714    
715        /**
716         * Creates a <tt>GM_Surface</tt> from the ordinates of the exterior ring and the the interior
717         * rings
718         * <p>
719         * 
720         * @param exterior
721         *            ring
722         * @param interior
723         *            ring
724         * @param dim
725         *            of the surface
726         * @param crs
727         *            spatial reference system of the surface
728         * @return corresponding surface
729         * @throws GeometryException
730         *             if the implicite orientation is not '+' or '-'
731         * 
732         */
733        public static Surface createSurface( double[] exterior, double[][] interior, int dim, CoordinateSystem crs )
734                                throws GeometryException {
735    
736            // get exterior ring
737            Position[] ext = new Position[exterior.length / dim];
738            int i = 0;
739            int k = 0;
740            while ( i < exterior.length - 1 ) {
741                double[] o = new double[dim];
742                for ( int j = 0; j < dim; j++ ) {
743                    o[j] = exterior[i++];
744                }
745                ext[k++] = GeometryFactory.createPosition( o );
746            }
747    
748            // get interior rings if available
749            Position[][] in = null;
750            if ( interior != null && interior.length > 0 ) {
751                in = new Position[interior.length][];
752                for ( int j = 0; j < in.length; j++ ) {
753                    in[j] = new Position[interior[j].length / dim];
754                    i = 0;
755                    while ( i < interior[j].length ) {
756                        double[] o = new double[dim];
757                        for ( int z = 0; z < dim; z++ ) {
758                            o[z] = interior[j][i++];
759                        }
760                        in[j][i / dim - 1] = GeometryFactory.createPosition( o );
761                    }
762                }
763            }
764    
765            // default - linear - interpolation
766            SurfaceInterpolation si = new SurfaceInterpolationImpl();
767            return GeometryFactory.createSurface( ext, in, si, crs );
768        }
769    
770        /**
771         * creates a MultiPoint from an array of Point.
772         * 
773         * @param points
774         *            array of Points
775         * @return a MultiPoint from an array of Point.
776         * 
777         */
778        public static MultiPoint createMultiPoint( Point[] points ) {
779            return new MultiPointImpl( points );
780        }
781    
782        /**
783         * creates a MultiPoint from an array of Point.
784         * 
785         * @param points
786         *            array of Points
787         * @param crs
788         * @return a MultiPoint from an array of Point.
789         */
790        public static MultiPoint createMultiPoint( Point[] points, CoordinateSystem crs ) {
791            return new MultiPointImpl( points, crs );
792        }
793    
794        /**
795         * creates a MultiPoint from a wkb.
796         * 
797         * @param wkb
798         *            byte stream that contains the wkb information
799         * @param crs
800         *            CS_CoordinateSystem spatial reference system of the curve
801         * @return the MultiPoint defined by the WKB and the given CRS
802         * @throws GeometryException
803         *             if the wkb is not known or invalid
804         * 
805         */
806        public static MultiPoint createMultiPoint( byte[] wkb, CoordinateSystem crs )
807                                throws GeometryException {
808            Point[] points = null;
809            int wkbType = -1;
810            int numPoints = -1;
811            double x = 0;
812            double y = 0;
813            byte byteorder = wkb[0];
814    
815            // read wkbType
816            if ( byteorder == 0 ) {
817                wkbType = ByteUtils.readBEInt( wkb, 1 );
818            } else {
819                wkbType = ByteUtils.readLEInt( wkb, 1 );
820            }
821    
822            // if the geometry isn't a multipoint throw exception
823            if ( wkbType != 4 ) {
824                throw new GeometryException( "Invalid byte stream for MultiPoint" );
825            }
826    
827            // read number of points
828            if ( byteorder == 0 ) {
829                numPoints = ByteUtils.readBEInt( wkb, 5 );
830            } else {
831                numPoints = ByteUtils.readLEInt( wkb, 5 );
832            }
833    
834            points = new Point[numPoints];
835    
836            int offset = 9;
837    
838            Object[] o = new Object[3];
839            o[2] = crs;
840    
841            // read all points
842            for ( int i = 0; i < numPoints; i++ ) {
843                // byteorder of the i-th point
844                byteorder = wkb[offset];
845    
846                // wkbType of the i-th geometry
847                if ( byteorder == 0 ) {
848                    wkbType = ByteUtils.readBEInt( wkb, offset + 1 );
849                } else {
850                    wkbType = ByteUtils.readLEInt( wkb, offset + 1 );
851                }
852    
853                // if the geometry isn't a point throw exception
854                if ( wkbType != 1 ) {
855                    throw new GeometryException( "Invalid byte stream for Point as " + "part of a multi point" );
856                }
857    
858                // read the i-th point depending on the byteorde
859                if ( byteorder == 0 ) {
860                    x = ByteUtils.readBEDouble( wkb, offset + 5 );
861                    y = ByteUtils.readBEDouble( wkb, offset + 13 );
862                } else {
863                    x = ByteUtils.readLEDouble( wkb, offset + 5 );
864                    y = ByteUtils.readLEDouble( wkb, offset + 13 );
865                }
866    
867                offset += 21;
868    
869                points[i] = new PointImpl( x, y, crs );
870            }
871    
872            return createMultiPoint( points );
873        }
874    
875        /**
876         * creates a MultiCurve from an array of Curves.
877         * 
878         * @param curves
879         * @return a MultiCurve from an array of Curves.
880         */
881        public static MultiCurve createMultiCurve( Curve[] curves ) {
882            return new MultiCurveImpl( curves );
883        }
884    
885        /**
886         * creates a MultiCurve from an array of Curves.
887         * 
888         * @param curves
889         * @param crs
890         * @return a MultiCurve from an array of Curves.
891         */
892        public static MultiCurve createMultiCurve( Curve[] curves, CoordinateSystem crs ) {
893            return new MultiCurveImpl( curves, crs );
894        }
895    
896        /**
897         * creates a MultiCurve from a wkb.
898         * 
899         * @param wkb
900         *            byte stream that contains the wkb information
901         * @param crs
902         *            CS_CoordinateSystem spatial reference system of the curve
903         * @return the MultiCurve defined by the WKB and the given CRS
904         * @throws GeometryException
905         *             if the wkb is not known or invalid
906         */
907        public static MultiCurve createMultiCurve( byte[] wkb, CoordinateSystem crs )
908                                throws GeometryException {
909            int wkbType = -1;
910            int numPoints = -1;
911            int numParts = -1;
912            double x = 0;
913            double y = 0;
914            Position[][] points = null;
915            int offset = 0;
916            byte byteorder = wkb[offset++];
917    
918            if ( byteorder == 0 ) {
919                wkbType = ByteUtils.readBEInt( wkb, offset );
920            } else {
921                wkbType = ByteUtils.readLEInt( wkb, offset );
922            }
923    
924            offset += 4;
925    
926            // check if it's realy a linestring
927            if ( wkbType != 5 ) {
928                throw new GeometryException( "Invalid byte stream for MultiCurve" );
929            }
930    
931            // read number of linestrings
932            if ( byteorder == 0 ) {
933                numParts = ByteUtils.readBEInt( wkb, offset );
934            } else {
935                numParts = ByteUtils.readLEInt( wkb, offset );
936            }
937    
938            offset += 4;
939    
940            points = new Position[numParts][];
941    
942            // for every linestring
943            for ( int j = 0; j < numParts; j++ ) {
944                byteorder = wkb[offset++];
945    
946                if ( byteorder == 0 ) {
947                    wkbType = ByteUtils.readBEInt( wkb, offset );
948                } else {
949                    wkbType = ByteUtils.readLEInt( wkb, offset );
950                }
951    
952                offset += 4;
953    
954                // check if it's realy a linestring
955                if ( wkbType != 2 ) {
956                    throw new GeometryException( "Invalid byte stream for Curve as " + " part of a MultiCurve." );
957                }
958    
959                // read number of points
960                if ( byteorder == 0 ) {
961                    numPoints = ByteUtils.readBEInt( wkb, offset );
962                } else {
963                    numPoints = ByteUtils.readLEInt( wkb, offset );
964                }
965    
966                offset += 4;
967    
968                points[j] = new Position[numPoints];
969    
970                // read the i-th point depending on the byteorde
971                if ( byteorder == 0 ) {
972                    for ( int i = 0; i < numPoints; i++ ) {
973                        x = ByteUtils.readBEDouble( wkb, offset );
974                        offset += 8;
975                        y = ByteUtils.readBEDouble( wkb, offset );
976                        offset += 8;
977                        points[j][i] = new PositionImpl( x, y );
978                    }
979                } else {
980                    for ( int i = 0; i < numPoints; i++ ) {
981                        x = ByteUtils.readLEDouble( wkb, offset );
982                        offset += 8;
983                        y = ByteUtils.readLEDouble( wkb, offset );
984                        offset += 8;
985                        points[j][i] = new PositionImpl( x, y );
986                    }
987                }
988            }
989    
990            CurveSegment[] segment = new CurveSegment[1];
991            Curve[] curves = new Curve[numParts];
992    
993            for ( int i = 0; i < numParts; i++ ) {
994                segment[0] = createCurveSegment( points[i], crs );
995                curves[i] = createCurve( segment );
996            }
997    
998            return createMultiCurve( curves );
999        }
1000    
1001        /**
1002         * creates a MultiSurface from an array of surfaces
1003         * 
1004         * @param surfaces
1005         * @return a MultiSurface from an array of surfaces
1006         */
1007        public static MultiSurface createMultiSurface( Surface[] surfaces ) {
1008            return new MultiSurfaceImpl( surfaces );
1009        }
1010    
1011        /**
1012         * creates a MultiSurface from an array of surfaces
1013         * 
1014         * @param surfaces
1015         * @param crs
1016         * @return a MultiSurface from an array of surfaces
1017         */
1018        public static MultiSurface createMultiSurface( Surface[] surfaces, CoordinateSystem crs ) {
1019            return new MultiSurfaceImpl( surfaces, crs );
1020        }
1021    
1022        /**
1023         * creates a MultiSurface from a wkb
1024         * 
1025         * @param wkb
1026         *            geometry in Well-Known Binary (WKB) format
1027         * @param crs
1028         *            spatial reference system of the geometry
1029         * @param si
1030         *            surface interpolation
1031         * @return the MultiSurface defined by the WKB and the given CRS
1032         * @throws GeometryException
1033         *             if the wkb is not known or invalid
1034         */
1035        public static MultiSurface createMultiSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
1036                                throws GeometryException {
1037            int wkbtype = -1;
1038            int numPoly = 0;
1039            int numRings = 0;
1040            int numPoints = 0;
1041            int offset = 0;
1042            double x = 0;
1043            double y = 0;
1044            Position[] externalBoundary = null;
1045            Position[][] internalBoundaries = null;
1046            byte byteorder = wkb[offset++];
1047    
1048            if ( byteorder == 0 ) {
1049                wkbtype = ByteUtils.readBEInt( wkb, offset );
1050            } else {
1051                wkbtype = ByteUtils.readLEInt( wkb, offset );
1052            }
1053    
1054            offset += 4;
1055    
1056            // is the wkbmetry a multipolygon?
1057            if ( wkbtype != 6 ) {
1058                throw new GeometryException( "Invalid byte stream for MultiSurface" );
1059            }
1060    
1061            // read number of polygons on the byte[]
1062            if ( byteorder == 0 ) {
1063                numPoly = ByteUtils.readBEInt( wkb, offset );
1064            } else {
1065                numPoly = ByteUtils.readLEInt( wkb, offset );
1066            }
1067    
1068            offset += 4;
1069    
1070            ArrayList<Surface> list = new ArrayList<Surface>( numPoly );
1071    
1072            for ( int ip = 0; ip < numPoly; ip++ ) {
1073                byteorder = wkb[offset];
1074                offset++;
1075    
1076                if ( byteorder == 0 ) {
1077                    wkbtype = ByteUtils.readBEInt( wkb, offset );
1078                } else {
1079                    wkbtype = ByteUtils.readLEInt( wkb, offset );
1080                }
1081    
1082                offset += 4;
1083    
1084                // is the geometry respresented by wkb a polygon?
1085                if ( wkbtype != 3 ) {
1086                    throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
1087                }
1088    
1089                // read number of rings of the polygon
1090                if ( byteorder == 0 ) {
1091                    numRings = ByteUtils.readBEInt( wkb, offset );
1092                } else {
1093                    numRings = ByteUtils.readLEInt( wkb, offset );
1094                }
1095    
1096                offset += 4;
1097    
1098                // read number of points of the external ring
1099                if ( byteorder == 0 ) {
1100                    numPoints = ByteUtils.readBEInt( wkb, offset );
1101                } else {
1102                    numPoints = ByteUtils.readLEInt( wkb, offset );
1103                }
1104    
1105                offset += 4;
1106    
1107                // allocate memory for the external boundary
1108                externalBoundary = new Position[numPoints];
1109    
1110                if ( byteorder == 0 ) {
1111                    // read points of the external boundary from the byte[]
1112                    for ( int i = 0; i < numPoints; i++ ) {
1113                        x = ByteUtils.readBEDouble( wkb, offset );
1114                        offset += 8;
1115                        y = ByteUtils.readBEDouble( wkb, offset );
1116                        offset += 8;
1117                        externalBoundary[i] = new PositionImpl( x, y );
1118                    }
1119                } else {
1120                    // read points of the external boundary from the byte[]
1121                    for ( int i = 0; i < numPoints; i++ ) {
1122                        x = ByteUtils.readLEDouble( wkb, offset );
1123                        offset += 8;
1124                        y = ByteUtils.readLEDouble( wkb, offset );
1125                        offset += 8;
1126                        externalBoundary[i] = new PositionImpl( x, y );
1127                    }
1128                }
1129    
1130                // only if numRings is larger then one there internal rings
1131                if ( numRings > 1 ) {
1132                    internalBoundaries = new Position[numRings - 1][];
1133                }
1134    
1135                if ( byteorder == 0 ) {
1136                    for ( int j = 1; j < numRings; j++ ) {
1137                        // read number of points of the j-th internal ring
1138                        numPoints = ByteUtils.readBEInt( wkb, offset );
1139                        offset += 4;
1140    
1141                        // allocate memory for the j-th internal boundary
1142                        internalBoundaries[j - 1] = new Position[numPoints];
1143    
1144                        // read points of the external boundary from the byte[]
1145                        for ( int i = 0; i < numPoints; i++ ) {
1146                            x = ByteUtils.readBEDouble( wkb, offset );
1147                            offset += 8;
1148                            y = ByteUtils.readBEDouble( wkb, offset );
1149                            offset += 8;
1150                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1151                        }
1152                    }
1153                } else {
1154                    for ( int j = 1; j < numRings; j++ ) {
1155                        // read number of points of the j-th internal ring
1156                        numPoints = ByteUtils.readLEInt( wkb, offset );
1157                        offset += 4;
1158    
1159                        // allocate memory for the j-th internal boundary
1160                        internalBoundaries[j - 1] = new Position[numPoints];
1161    
1162                        // read points of the external boundary from the byte[]
1163                        for ( int i = 0; i < numPoints; i++ ) {
1164                            x = ByteUtils.readLEDouble( wkb, offset );
1165                            offset += 8;
1166                            y = ByteUtils.readLEDouble( wkb, offset );
1167                            offset += 8;
1168                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1169                        }
1170                    }
1171                }
1172    
1173                SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
1174    
1175                list.add( createSurface( patch ) );
1176            }
1177    
1178            MultiSurface multisurface = new MultiSurfaceImpl( crs );
1179    
1180            for ( int i = 0; i < list.size(); i++ ) {
1181                multisurface.addSurface( list.get( i ) );
1182            }
1183    
1184            return multisurface;
1185        }
1186    
1187    }