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