    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }