001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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: 22430 $, $Date: 2010-02-08 11:43:00 +0100 (Mo, 08 Feb 2010) $
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            arc -= dS;
466            while ( arc <= end+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                arc += dS;
478            }
479            return createCurve( list.toArray( new Position[list.size()] ), crs );
480        }
481    
482        /**
483         * creates a Curve from a wkb.
484         * 
485         * @param wkb
486         *            byte stream that contains the wkb information
487         * @param crs
488         *            CS_CoordinateSystem spatial reference system of the curve
489         * @return the Curve defined by the WKB and the given CRS
490         * @throws GeometryException
491         *             if the wkb is not known or invalid
492         * 
493         */
494        public static Curve createCurve( byte[] wkb, CoordinateSystem crs )
495                                throws GeometryException {
496            int wkbType = -1;
497            int numPoints = -1;
498            Position[] points = null;
499            double x = 0;
500            double y = 0;
501    
502            byte byteorder = wkb[0];
503    
504            if ( byteorder == 0 ) {
505                wkbType = ByteUtils.readBEInt( wkb, 1 );
506            } else {
507                wkbType = ByteUtils.readLEInt( wkb, 1 );
508            }
509    
510            // check if it's realy a linestrin/curve
511            if ( wkbType != 2 ) {
512                throw new GeometryException( "invalid byte stream for Curve" );
513            }
514    
515            // read number of points
516            if ( byteorder == 0 ) {
517                numPoints = ByteUtils.readBEInt( wkb, 5 );
518            } else {
519                numPoints = ByteUtils.readLEInt( wkb, 5 );
520            }
521    
522            int offset = 9;
523    
524            points = new Position[numPoints];
525    
526            // read the i-th point depending on the byteorde
527            if ( byteorder == 0 ) {
528                for ( int i = 0; i < numPoints; i++ ) {
529                    x = ByteUtils.readBEDouble( wkb, offset );
530                    offset += 8;
531                    y = ByteUtils.readBEDouble( wkb, offset );
532                    offset += 8;
533                    points[i] = new PositionImpl( x, y );
534                }
535            } else {
536                for ( int i = 0; i < numPoints; i++ ) {
537                    x = ByteUtils.readLEDouble( wkb, offset );
538                    offset += 8;
539                    y = ByteUtils.readLEDouble( wkb, offset );
540                    offset += 8;
541                    points[i] = new PositionImpl( x, y );
542                }
543            }
544    
545            CurveSegment[] segment = new CurveSegment[1];
546    
547            segment[0] = createCurveSegment( points, crs );
548    
549            return createCurve( segment );
550        }
551    
552        /**
553         * creates a surface in form of an ellipse. If <code>radiusX</code> == <code>radiusY</code> a circle will be created
554         * 
555         * @param centerX
556         * @param centerY
557         * @param radiusX
558         * @param radiusY
559         * @param nSeg
560         *            number of segments the ellipse will have
561         * @param crs
562         * @throws GeometryException
563         */
564        public static Surface createSurfaceAsEllipse( double centerX, double centerY, double radiusX, double radiusY,
565                                                      int nSeg, CoordinateSystem crs )
566                                throws GeometryException {
567            double x;
568            double _x = 0;
569            double _y = 0;
570            double y = 0;
571            double __x = 0;
572            double __y = 0;
573    
574            List<Position> list = new ArrayList<Position>();
575            for ( int i = 0; i < nSeg; i++ ) {
576                x = radiusX * Math.sin( ( (double) i / (double) nSeg ) * ( Math.PI * 2.0 ) );
577                y = radiusY * Math.cos( ( (double) i / (double) nSeg ) * ( Math.PI * 2.0 ) );
578                if ( i > 0 ) {
579                    list.add( GeometryFactory.createPosition( centerX + _x, centerY + -_y ) );
580                } else {
581                    // Save the first point coordinate to link it with the last one
582                    __x = x;
583                    __y = y;
584                }
585                // Save the actual point coordinate to link it with the next one
586                _x = x;
587                _y = y;
588            }
589            list.add( GeometryFactory.createPosition( centerX + _x, centerY + -y ) );
590            list.add( GeometryFactory.createPosition( centerX + __x, centerY + -__y ) );
591            return createSurface( list.toArray( new Position[list.size()] ), null, new SurfaceInterpolationImpl(), crs );
592        }
593    
594        /**
595         * creates a Surface composed of one SurfacePatch from array(s) of Position
596         * 
597         * @param exteriorRing
598         *            exterior ring of the patch
599         * @param interiorRings
600         *            interior rings of the patch
601         * @param si
602         *            SurfaceInterpolation
603         * @param crs
604         *            CS_CoordinateSystem spatial reference system of the surface patch
605         * @return a Surface composed of one SurfacePatch from array(s) of Position
606         * @throws GeometryException
607         *             if the implicite orientation is not '+' or '-', or the rings aren't closed
608         */
609        public static Surface createSurface( Position[] exteriorRing, Position[][] interiorRings, SurfaceInterpolation si,
610                                             CoordinateSystem crs )
611                                throws GeometryException {
612            SurfacePatch sp = new PolygonImpl( si, exteriorRing, interiorRings, crs );
613            return createSurface( sp );
614        }
615    
616        /**
617         * creates a Surface from an array of SurfacePatch.
618         * 
619         * @param patch
620         *            patches that build the surface
621         * @return a Surface from an array of SurfacePatch.
622         * @throws GeometryException
623         *             if implicite the orientation is not '+' or '-'
624         */
625        public static Surface createSurface( SurfacePatch patch )
626                                throws GeometryException {
627            return new SurfaceImpl( patch );
628        }
629    
630        /**
631         * creates a Surface from an array of SurfacePatch.
632         * 
633         * @param patches
634         *            patches that build the surface
635         * 
636         * @return a Surface from an array of SurfacePatch.
637         * @throws GeometryException
638         *             if implicite the orientation is not '+' or '-'
639         */
640        public static Surface createSurface( SurfacePatch[] patches )
641                                throws GeometryException {
642            return new SurfaceImpl( patches );
643        }
644    
645        /**
646         * creates a Surface from an array of SurfacePatch.
647         * 
648         * @param patches
649         *            patches that build the surface
650         * @param crs
651         * 
652         * @return a Surface from an array of SurfacePatch.
653         * @throws GeometryException
654         *             if implicite the orientation is not '+' or '-'
655         */
656        public static Surface createSurface( SurfacePatch[] patches, CoordinateSystem crs )
657                                throws GeometryException {
658            return new SurfaceImpl( patches, crs );
659        }
660    
661        /**
662         * creates a Surface from a wkb.
663         * 
664         * @param wkb
665         *            byte stream that contains the wkb information
666         * @param crs
667         *            CS_CoordinateSystem spatial reference system of the curve
668         * @param si
669         *            SurfaceInterpolation
670         * @return a Surface from a wkb.
671         * @throws GeometryException
672         *             if the implicite orientation is not '+' or '-' or the wkb is not known or invalid
673         */
674        public static Surface createSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
675                                throws GeometryException {
676            int wkbtype = -1;
677            int numRings = 0;
678            int numPoints = 0;
679            int offset = 0;
680            double x = 0;
681            double y = 0;
682    
683            Position[] externalBoundary = null;
684            Position[][] internalBoundaries = null;
685    
686            byte byteorder = wkb[offset++];
687    
688            if ( byteorder == 0 ) {
689                wkbtype = ByteUtils.readBEInt( wkb, offset );
690            } else {
691                wkbtype = ByteUtils.readLEInt( wkb, offset );
692            }
693    
694            offset += 4;
695    
696            if ( wkbtype == 6 ) {
697                return null;
698            }
699    
700            // is the geometry respresented by wkb a polygon?
701            if ( wkbtype != 3 ) {
702                throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
703            }
704    
705            // read number of rings of the polygon
706            if ( byteorder == 0 ) {
707                numRings = ByteUtils.readBEInt( wkb, offset );
708            } else {
709                numRings = ByteUtils.readLEInt( wkb, offset );
710            }
711    
712            offset += 4;
713    
714            // read number of points of the external ring
715            if ( byteorder == 0 ) {
716                numPoints = ByteUtils.readBEInt( wkb, offset );
717            } else {
718                numPoints = ByteUtils.readLEInt( wkb, offset );
719            }
720    
721            offset += 4;
722    
723            // allocate memory for the external boundary
724            externalBoundary = new Position[numPoints];
725    
726            if ( byteorder == 0 ) {
727                // read points of the external boundary from the byte[]
728                for ( int i = 0; i < numPoints; i++ ) {
729                    x = ByteUtils.readBEDouble( wkb, offset );
730                    offset += 8;
731                    y = ByteUtils.readBEDouble( wkb, offset );
732                    offset += 8;
733                    externalBoundary[i] = new PositionImpl( x, y );
734                }
735            } else {
736                // read points of the external boundary from the byte[]
737                for ( int i = 0; i < numPoints; i++ ) {
738                    x = ByteUtils.readLEDouble( wkb, offset );
739                    offset += 8;
740                    y = ByteUtils.readLEDouble( wkb, offset );
741                    offset += 8;
742                    externalBoundary[i] = new PositionImpl( x, y );
743                }
744            }
745    
746            // only if numRings is larger then one there internal rings
747            if ( numRings > 1 ) {
748                internalBoundaries = new Position[numRings - 1][];
749            }
750    
751            if ( byteorder == 0 ) {
752                for ( int j = 1; j < numRings; j++ ) {
753                    // read number of points of the j-th internal ring
754                    numPoints = ByteUtils.readBEInt( wkb, offset );
755                    offset += 4;
756    
757                    // allocate memory for the j-th internal boundary
758                    internalBoundaries[j - 1] = new Position[numPoints];
759    
760                    // read points of the external boundary from the byte[]
761                    for ( int i = 0; i < numPoints; i++ ) {
762                        x = ByteUtils.readBEDouble( wkb, offset );
763                        offset += 8;
764                        y = ByteUtils.readBEDouble( wkb, offset );
765                        offset += 8;
766                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
767                    }
768                }
769            } else {
770                for ( int j = 1; j < numRings; j++ ) {
771                    // read number of points of the j-th internal ring
772                    numPoints = ByteUtils.readLEInt( wkb, offset );
773                    offset += 4;
774    
775                    // allocate memory for the j-th internal boundary
776                    internalBoundaries[j - 1] = new Position[numPoints];
777    
778                    // read points of the external boundary from the byte[]
779                    for ( int i = 0; i < numPoints; i++ ) {
780                        x = ByteUtils.readLEDouble( wkb, offset );
781                        offset += 8;
782                        y = ByteUtils.readLEDouble( wkb, offset );
783                        offset += 8;
784                        internalBoundaries[j - 1][i] = new PositionImpl( x, y );
785                    }
786                }
787            }
788    
789            SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
790    
791            return createSurface( patch );
792        }
793    
794        /**
795         * Creates a <tt>Surface</tt> from a <tt>Envelope</tt>.
796         * <p>
797         * 
798         * @param bbox
799         *            envelope to be converted
800         * @param crs
801         *            spatial reference system of the surface
802         * @return corresponding surface
803         * 
804         * @throws GeometryException
805         *             if the implicite orientation is not '+' or '-'
806         */
807        public static Surface createSurface( Envelope bbox, CoordinateSystem crs )
808                                throws GeometryException {
809    
810            Position min = bbox.getMin();
811            Position max = bbox.getMax();
812            Position[] exteriorRing = null;
813            if ( min.getCoordinateDimension() == 2 ) {
814                exteriorRing = new Position[] { min, new PositionImpl( min.getX(), max.getY() ), max,
815                                               new PositionImpl( max.getX(), min.getY() ), min };
816            } else {
817                exteriorRing = new Position[] {
818                                               min,
819                                               new PositionImpl( min.getX(), max.getY(),
820                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ),
821                                               max,
822                                               new PositionImpl( max.getX(), min.getY(),
823                                                                 min.getZ() + ( ( max.getZ() - min.getZ() ) * 0.5 ) ), min };
824            }
825    
826            return createSurface( exteriorRing, null, new SurfaceInterpolationImpl(), crs );
827        }
828    
829        /**
830         * Creates a <tt>GM_Surface</tt> from the ordinates of the exterior ring and the the interior rings
831         * <p>
832         * 
833         * @param exterior
834         *            ring
835         * @param interior
836         *            ring
837         * @param dim
838         *            of the surface
839         * @param crs
840         *            spatial reference system of the surface
841         * @return corresponding surface
842         * @throws GeometryException
843         *             if the implicite orientation is not '+' or '-'
844         * 
845         */
846        public static Surface createSurface( double[] exterior, double[][] interior, int dim, CoordinateSystem crs )
847                                throws GeometryException {
848    
849            // get exterior ring
850            Position[] ext = new Position[exterior.length / dim];
851            int i = 0;
852            int k = 0;
853            while ( i < exterior.length - 1 ) {
854                double[] o = new double[dim];
855                for ( int j = 0; j < dim; j++ ) {
856                    o[j] = exterior[i++];
857                }
858                ext[k++] = GeometryFactory.createPosition( o );
859            }
860    
861            // get interior rings if available
862            Position[][] in = null;
863            if ( interior != null && interior.length > 0 ) {
864                in = new Position[interior.length][];
865                for ( int j = 0; j < in.length; j++ ) {
866                    in[j] = new Position[interior[j].length / dim];
867                    i = 0;
868                    while ( i < interior[j].length ) {
869                        double[] o = new double[dim];
870                        for ( int z = 0; z < dim; z++ ) {
871                            o[z] = interior[j][i++];
872                        }
873                        in[j][i / dim - 1] = GeometryFactory.createPosition( o );
874                    }
875                }
876            }
877    
878            // default - linear - interpolation
879            SurfaceInterpolation si = new SurfaceInterpolationImpl();
880            return GeometryFactory.createSurface( ext, in, si, crs );
881        }
882    
883        /**
884         * Creates a {@link MultiGeometry} from an array of {@link Geometry} objects.
885         * 
886         * @param members
887         *            member geometries
888         * @param crs
889         *            coordinate system
890         * @return {@link MultiGeometry} that contains all given members
891         */
892        public static MultiGeometry createMultiGeometry( Geometry[] members, CoordinateSystem crs ) {
893            return new MultiGeometryImpl( members, crs );
894        }
895    
896        /**
897         * Creates a {@link MultiGeometry} from an array of {@link Geometry} objects.
898         * 
899         * @param wkb
900         *            wkb information
901         * @param crs
902         *            coordinate system
903         * @return {@link MultiGeometry} that contains all given members
904         * @throws GeometryException
905         *             if the wkb is not known or invalid
906         */
907        public static MultiGeometry createMultiGeometry( byte[] wkb, CoordinateSystem crs )
908                                throws GeometryException {
909            throw new GeometryException( "Generation of MultiGeometry instances from WKB is not implemented yet." );
910        }
911    
912        /**
913         * creates a MultiPoint from an array of Point.
914         * 
915         * @param points
916         *            array of Points
917         * @return a MultiPoint from an array of Point.
918         * 
919         */
920        public static MultiPoint createMultiPoint( Point[] points ) {
921            return new MultiPointImpl( points );
922        }
923    
924        /**
925         * creates a MultiPoint from an array of Point.
926         * 
927         * @param points
928         *            array of Points
929         * @param crs
930         * @return a MultiPoint from an array of Point.
931         */
932        public static MultiPoint createMultiPoint( Point[] points, CoordinateSystem crs ) {
933            return new MultiPointImpl( points, crs );
934        }
935    
936        /**
937         * creates a MultiPoint from a wkb.
938         * 
939         * @param wkb
940         *            byte stream that contains the wkb information
941         * @param crs
942         *            CS_CoordinateSystem spatial reference system of the curve
943         * @return the MultiPoint defined by the WKB and the given CRS
944         * @throws GeometryException
945         *             if the wkb is not known or invalid
946         * 
947         */
948        public static MultiPoint createMultiPoint( byte[] wkb, CoordinateSystem crs )
949                                throws GeometryException {
950            Point[] points = null;
951            int wkbType = -1;
952            int numPoints = -1;
953            double x = 0;
954            double y = 0;
955            byte byteorder = wkb[0];
956    
957            // read wkbType
958            if ( byteorder == 0 ) {
959                wkbType = ByteUtils.readBEInt( wkb, 1 );
960            } else {
961                wkbType = ByteUtils.readLEInt( wkb, 1 );
962            }
963    
964            // if the geometry isn't a multipoint throw exception
965            if ( wkbType != 4 ) {
966                throw new GeometryException( "Invalid byte stream for MultiPoint" );
967            }
968    
969            // read number of points
970            if ( byteorder == 0 ) {
971                numPoints = ByteUtils.readBEInt( wkb, 5 );
972            } else {
973                numPoints = ByteUtils.readLEInt( wkb, 5 );
974            }
975    
976            points = new Point[numPoints];
977    
978            int offset = 9;
979    
980            Object[] o = new Object[3];
981            o[2] = crs;
982    
983            // read all points
984            for ( int i = 0; i < numPoints; i++ ) {
985                // byteorder of the i-th point
986                byteorder = wkb[offset];
987    
988                // wkbType of the i-th geometry
989                if ( byteorder == 0 ) {
990                    wkbType = ByteUtils.readBEInt( wkb, offset + 1 );
991                } else {
992                    wkbType = ByteUtils.readLEInt( wkb, offset + 1 );
993                }
994    
995                // if the geometry isn't a point throw exception
996                if ( wkbType != 1 ) {
997                    throw new GeometryException( "Invalid byte stream for Point as " + "part of a multi point" );
998                }
999    
1000                // read the i-th point depending on the byteorde
1001                if ( byteorder == 0 ) {
1002                    x = ByteUtils.readBEDouble( wkb, offset + 5 );
1003                    y = ByteUtils.readBEDouble( wkb, offset + 13 );
1004                } else {
1005                    x = ByteUtils.readLEDouble( wkb, offset + 5 );
1006                    y = ByteUtils.readLEDouble( wkb, offset + 13 );
1007                }
1008    
1009                offset += 21;
1010    
1011                points[i] = new PointImpl( x, y, crs );
1012            }
1013    
1014            return createMultiPoint( points );
1015        }
1016    
1017        /**
1018         * creates a MultiCurve from an array of Curves.
1019         * 
1020         * @param curves
1021         * @return a MultiCurve from an array of Curves.
1022         */
1023        public static MultiCurve createMultiCurve( Curve[] curves ) {
1024            return new MultiCurveImpl( curves );
1025        }
1026    
1027        /**
1028         * creates a MultiCurve from an array of Curves.
1029         * 
1030         * @param curves
1031         * @param crs
1032         * @return a MultiCurve from an array of Curves.
1033         */
1034        public static MultiCurve createMultiCurve( Curve[] curves, CoordinateSystem crs ) {
1035            return new MultiCurveImpl( curves, crs );
1036        }
1037    
1038        /**
1039         * creates a MultiCurve from a wkb.
1040         * 
1041         * @param wkb
1042         *            byte stream that contains the wkb information
1043         * @param crs
1044         *            CS_CoordinateSystem spatial reference system of the curve
1045         * @return the MultiCurve defined by the WKB and the given CRS
1046         * @throws GeometryException
1047         *             if the wkb is not known or invalid
1048         */
1049        public static MultiCurve createMultiCurve( byte[] wkb, CoordinateSystem crs )
1050                                throws GeometryException {
1051            int wkbType = -1;
1052            int numPoints = -1;
1053            int numParts = -1;
1054            double x = 0;
1055            double y = 0;
1056            Position[][] points = null;
1057            int offset = 0;
1058            byte byteorder = wkb[offset++];
1059    
1060            if ( byteorder == 0 ) {
1061                wkbType = ByteUtils.readBEInt( wkb, offset );
1062            } else {
1063                wkbType = ByteUtils.readLEInt( wkb, offset );
1064            }
1065    
1066            offset += 4;
1067    
1068            // check if it's realy a linestring
1069            if ( wkbType != 5 ) {
1070                throw new GeometryException( "Invalid byte stream for MultiCurve" );
1071            }
1072    
1073            // read number of linestrings
1074            if ( byteorder == 0 ) {
1075                numParts = ByteUtils.readBEInt( wkb, offset );
1076            } else {
1077                numParts = ByteUtils.readLEInt( wkb, offset );
1078            }
1079    
1080            offset += 4;
1081    
1082            points = new Position[numParts][];
1083    
1084            // for every linestring
1085            for ( int j = 0; j < numParts; j++ ) {
1086                byteorder = wkb[offset++];
1087    
1088                if ( byteorder == 0 ) {
1089                    wkbType = ByteUtils.readBEInt( wkb, offset );
1090                } else {
1091                    wkbType = ByteUtils.readLEInt( wkb, offset );
1092                }
1093    
1094                offset += 4;
1095    
1096                // check if it's realy a linestring
1097                if ( wkbType != 2 ) {
1098                    throw new GeometryException( "Invalid byte stream for Curve as " + " part of a MultiCurve." );
1099                }
1100    
1101                // read number of points
1102                if ( byteorder == 0 ) {
1103                    numPoints = ByteUtils.readBEInt( wkb, offset );
1104                } else {
1105                    numPoints = ByteUtils.readLEInt( wkb, offset );
1106                }
1107    
1108                offset += 4;
1109    
1110                points[j] = new Position[numPoints];
1111    
1112                // read the i-th point depending on the byteorde
1113                if ( byteorder == 0 ) {
1114                    for ( int i = 0; i < numPoints; i++ ) {
1115                        x = ByteUtils.readBEDouble( wkb, offset );
1116                        offset += 8;
1117                        y = ByteUtils.readBEDouble( wkb, offset );
1118                        offset += 8;
1119                        points[j][i] = new PositionImpl( x, y );
1120                    }
1121                } else {
1122                    for ( int i = 0; i < numPoints; i++ ) {
1123                        x = ByteUtils.readLEDouble( wkb, offset );
1124                        offset += 8;
1125                        y = ByteUtils.readLEDouble( wkb, offset );
1126                        offset += 8;
1127                        points[j][i] = new PositionImpl( x, y );
1128                    }
1129                }
1130            }
1131    
1132            CurveSegment[] segment = new CurveSegment[1];
1133            Curve[] curves = new Curve[numParts];
1134    
1135            for ( int i = 0; i < numParts; i++ ) {
1136                segment[0] = createCurveSegment( points[i], crs );
1137                curves[i] = createCurve( segment );
1138            }
1139    
1140            return createMultiCurve( curves );
1141        }
1142    
1143        /**
1144         * creates a MultiSurface from an array of surfaces
1145         * 
1146         * @param surfaces
1147         * @return a MultiSurface from an array of surfaces
1148         */
1149        public static MultiSurface createMultiSurface( Surface[] surfaces ) {
1150            return new MultiSurfaceImpl( surfaces );
1151        }
1152    
1153        /**
1154         * creates a MultiSurface from an array of surfaces
1155         * 
1156         * @param surfaces
1157         * @param crs
1158         * @return a MultiSurface from an array of surfaces
1159         */
1160        public static MultiSurface createMultiSurface( Surface[] surfaces, CoordinateSystem crs ) {
1161            return new MultiSurfaceImpl( surfaces, crs );
1162        }
1163    
1164        /**
1165         * creates a MultiSurface from a wkb
1166         * 
1167         * @param wkb
1168         *            geometry in Well-Known Binary (WKB) format
1169         * @param crs
1170         *            spatial reference system of the geometry
1171         * @param si
1172         *            surface interpolation
1173         * @return the MultiSurface defined by the WKB and the given CRS
1174         * @throws GeometryException
1175         *             if the wkb is not known or invalid
1176         */
1177        public static MultiSurface createMultiSurface( byte[] wkb, CoordinateSystem crs, SurfaceInterpolation si )
1178                                throws GeometryException {
1179            int wkbtype = -1;
1180            int numPoly = 0;
1181            int numRings = 0;
1182            int numPoints = 0;
1183            int offset = 0;
1184            double x = 0;
1185            double y = 0;
1186            Position[] externalBoundary = null;
1187            Position[][] internalBoundaries = null;
1188            byte byteorder = wkb[offset++];
1189    
1190            if ( byteorder == 0 ) {
1191                wkbtype = ByteUtils.readBEInt( wkb, offset );
1192            } else {
1193                wkbtype = ByteUtils.readLEInt( wkb, offset );
1194            }
1195    
1196            offset += 4;
1197    
1198            // is the wkbmetry a multipolygon?
1199            if ( wkbtype != 6 ) {
1200                throw new GeometryException( "Invalid byte stream for MultiSurface" );
1201            }
1202    
1203            // read number of polygons on the byte[]
1204            if ( byteorder == 0 ) {
1205                numPoly = ByteUtils.readBEInt( wkb, offset );
1206            } else {
1207                numPoly = ByteUtils.readLEInt( wkb, offset );
1208            }
1209    
1210            offset += 4;
1211    
1212            ArrayList<Surface> list = new ArrayList<Surface>( numPoly );
1213    
1214            for ( int ip = 0; ip < numPoly; ip++ ) {
1215                byteorder = wkb[offset];
1216                offset++;
1217    
1218                if ( byteorder == 0 ) {
1219                    wkbtype = ByteUtils.readBEInt( wkb, offset );
1220                } else {
1221                    wkbtype = ByteUtils.readLEInt( wkb, offset );
1222                }
1223    
1224                offset += 4;
1225    
1226                // is the geometry respresented by wkb a polygon?
1227                if ( wkbtype != 3 ) {
1228                    throw new GeometryException( "invalid byte stream for Surface " + wkbtype );
1229                }
1230    
1231                // read number of rings of the polygon
1232                if ( byteorder == 0 ) {
1233                    numRings = ByteUtils.readBEInt( wkb, offset );
1234                } else {
1235                    numRings = ByteUtils.readLEInt( wkb, offset );
1236                }
1237    
1238                offset += 4;
1239    
1240                // read number of points of the external ring
1241                if ( byteorder == 0 ) {
1242                    numPoints = ByteUtils.readBEInt( wkb, offset );
1243                } else {
1244                    numPoints = ByteUtils.readLEInt( wkb, offset );
1245                }
1246    
1247                offset += 4;
1248    
1249                // allocate memory for the external boundary
1250                externalBoundary = new Position[numPoints];
1251    
1252                if ( byteorder == 0 ) {
1253                    // read points of the external boundary from the byte[]
1254                    for ( int i = 0; i < numPoints; i++ ) {
1255                        x = ByteUtils.readBEDouble( wkb, offset );
1256                        offset += 8;
1257                        y = ByteUtils.readBEDouble( wkb, offset );
1258                        offset += 8;
1259                        externalBoundary[i] = new PositionImpl( x, y );
1260                    }
1261                } else {
1262                    // read points of the external boundary from the byte[]
1263                    for ( int i = 0; i < numPoints; i++ ) {
1264                        x = ByteUtils.readLEDouble( wkb, offset );
1265                        offset += 8;
1266                        y = ByteUtils.readLEDouble( wkb, offset );
1267                        offset += 8;
1268                        externalBoundary[i] = new PositionImpl( x, y );
1269                    }
1270                }
1271    
1272                // only if numRings is larger then one there internal rings
1273                if ( numRings > 1 ) {
1274                    internalBoundaries = new Position[numRings - 1][];
1275                }
1276    
1277                if ( byteorder == 0 ) {
1278                    for ( int j = 1; j < numRings; j++ ) {
1279                        // read number of points of the j-th internal ring
1280                        numPoints = ByteUtils.readBEInt( wkb, offset );
1281                        offset += 4;
1282    
1283                        // allocate memory for the j-th internal boundary
1284                        internalBoundaries[j - 1] = new Position[numPoints];
1285    
1286                        // read points of the external boundary from the byte[]
1287                        for ( int i = 0; i < numPoints; i++ ) {
1288                            x = ByteUtils.readBEDouble( wkb, offset );
1289                            offset += 8;
1290                            y = ByteUtils.readBEDouble( wkb, offset );
1291                            offset += 8;
1292                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1293                        }
1294                    }
1295                } else {
1296                    for ( int j = 1; j < numRings; j++ ) {
1297                        // read number of points of the j-th internal ring
1298                        numPoints = ByteUtils.readLEInt( wkb, offset );
1299                        offset += 4;
1300    
1301                        // allocate memory for the j-th internal boundary
1302                        internalBoundaries[j - 1] = new Position[numPoints];
1303    
1304                        // read points of the external boundary from the byte[]
1305                        for ( int i = 0; i < numPoints; i++ ) {
1306                            x = ByteUtils.readLEDouble( wkb, offset );
1307                            offset += 8;
1308                            y = ByteUtils.readLEDouble( wkb, offset );
1309                            offset += 8;
1310                            internalBoundaries[j - 1][i] = new PositionImpl( x, y );
1311                        }
1312                    }
1313                }
1314    
1315                SurfacePatch patch = createSurfacePatch( externalBoundary, internalBoundaries, si, crs );
1316    
1317                list.add( createSurface( patch ) );
1318            }
1319    
1320            MultiSurface multisurface = new MultiSurfaceImpl( crs );
1321    
1322            for ( int i = 0; i < list.size(); i++ ) {
1323                multisurface.addSurface( list.get( i ) );
1324            }
1325    
1326            return multisurface;
1327        }
1328    
1329    }