    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/model/spatialschema/GMLGeometryAdapter.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 static org.deegree.model.spatialschema.GeometryFactory.createCurveSegment;
    039    import static org.deegree.ogcwebservices.wfs.configuration.WFSDeegreeParams.getSwitchAxes;
    040
    041    import java.io.ByteArrayOutputStream;
    042    import java.io.IOException;
    043    import java.io.InputStream;
    044    import java.io.OutputStream;
    045    import java.io.PrintWriter;
    046    import java.io.StringReader;
    047    import java.util.ArrayList;
    048    import java.util.HashMap;
    049    import java.util.Iterator;
    050    import java.util.List;
    051    import java.util.Map;
    052    import java.util.Properties;
    053    import java.util.StringTokenizer;
    054
    055    import org.deegree.crs.coordinatesystems.GeographicCRS;
    056    import org.deegree.framework.log.ILogger;
    057    import org.deegree.framework.log.LoggerFactory;
    058    import org.deegree.framework.util.StringTools;
    059    import org.deegree.framework.xml.ElementList;
    060    import org.deegree.framework.xml.NamespaceContext;
    061    import org.deegree.framework.xml.XMLFragment;
    062    import org.deegree.framework.xml.XMLParsingException;
    063    import org.deegree.framework.xml.XMLTools;
    064    import org.deegree.i18n.Messages;
    065    import org.deegree.model.crs.CRSFactory;
    066    import org.deegree.model.crs.CoordinateSystem;
    067    import org.deegree.model.crs.UnknownCRSException;
    068    import org.deegree.model.filterencoding.Function;
    069    import org.deegree.ogcbase.CommonNamespaces;
    070    import org.deegree.ogcbase.InvalidGMLException;
    071    import org.w3c.dom.Document;
    072    import org.w3c.dom.Element;
    073    import org.w3c.dom.Node;
    074
    075    /**
    076     * Adapter class for converting GML geometries to deegree geometries and vice versa. Some logical problems result from
    077     * the fact that an envelope isn't a geometry according to ISO 19107 (where the deegree geometry model is based on) but
    078     * according to GML2/3 specification it is.<br>
    079     * So if the wrap(..) method is called with an envelope a <tt>Surface</tt> will be returned representing the envelops
    080     * shape. To export an <tt>Envelope</tt> to a GML box/envelope two specialized export methods are available.<BR>
    081     * The export method(s) doesn't return a DOM element as one may expect but a <tt>StringBuffer</tt>. This is done because
    082     * the transformation from deegree geometries to GML mainly is required when a GML representation of a geometry shall be
    083     * serialized to a file or to a network connection. For both cases the string representation is required and it is
    084     * simply faster to create the string directly instead of first creating a DOM tree that after this must be serialized
    085     * to a string.<BR>
    086     * In future version geometries will be serialized to a stream.
    087     *
    088     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    089     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
    090     * @author last edited by: $Author: rbezema $
    091     *
    092     * @version $Revision: 23371 $, $Date: 2010-04-01 10:32:28 +0200 (Do, 01. Apr 2010) $
    093     */
    094    public class GMLGeometryAdapter {
    095
    096        private static final ILogger LOG = LoggerFactory.getLogger( GMLGeometryAdapter.class );
    097
    098        private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
    099
    100        private static Map<String, CoordinateSystem> crsMap = new HashMap<String, CoordinateSystem>();
    101
    102        private static final String COORD = CommonNamespaces.GML_PREFIX + ":coord";
    103
    104        private static final String COORDINATES = CommonNamespaces.GML_PREFIX + ":coordinates";
    105
    106        private static final String POS = CommonNamespaces.GML_PREFIX + ":pos";
    107
    108        private static final String POSLIST = CommonNamespaces.GML_PREFIX + ":posList";
    109
    110        private static Properties arcProperties;
    111
    112        /**
    113         * This differs from GeographiCRS.WGS84 in the identifiers (equals fails for WGS84.equals(EPSG4326)).
    114         */
    115        public static CoordinateSystem EPSG4326;
    116
    117        static {
    118            try {
    119                EPSG4326 = CRSFactory.create( "EPSG:4326" );
    120            } catch ( UnknownCRSException e ) {
    121                LOG.logDebug( "Unknown error", e );
    122                EPSG4326 = new org.deegree.model.crs.CoordinateSystem( GeographicCRS.WGS84 );
    123            }
    124        }
    125
    126        static {
    127            if ( GMLGeometryAdapter.arcProperties == null ) {
    128                GMLGeometryAdapter.initialize();
    129            }
    130        }
    131
    132        private static void initialize() {
    133
    134            arcProperties = new Properties();
    135
    136            try {
    137                // initialize mappings with mappings from "arc.properties" file in this package
    138                InputStream is = GMLGeometryAdapter.class.getResourceAsStream( "arc.properties" );
    139                try {
    140                    arcProperties.load( is );
    141                } catch ( IOException e ) {
    142                    LOG.logError( e.getMessage(), e );
    143                }
    144
    145                // override mappings with mappings from "arc.properties" file in root package
    146                is = Function.class.getResourceAsStream( "/arc.properties" );
    147                if ( is != null ) {
    148                    Properties props = new Properties();
    149                    props.load( is );
    150                    Iterator<?> iter = props.keySet().iterator();
    151                    while ( iter.hasNext() ) {
    152                        String key = (String) iter.next();
    153                        String value = props.getProperty( key );
    154                        arcProperties.put( key, value );
    155                    }
    156                }
    157            } catch ( Exception e ) {
    158                LOG.logError( e.getMessage(), e );
    159            }
    160        }
    161
    162        /**
    163         * Returns true if the given localName != <code>null</code> or empty and matches one of the following:
    164         * <ul>
    165         * <li>Box (alternative name: Envelope)</li>
    166         * <li>CompositeSurface</li>
    167         * <li>Curve</li>
    168         * <li>LinearRing</li>
    169         * <li>LineString</li>
    170         * <li>MultiCurve</li>
    171         * <li>MultiGeometry</li>
    172         * <li>MultiLineString</li>
    173         * <li>MultiPoint</li>
    174         * <li>MultiPolygon</li>
    175         * <li>MultiSurface</li>
    176         * <li>Point (alternative name: Center)</li>
    177         * <li>Polygon</li>
    178         * <li>Ring</li>
    179         * <li>Surface</li>
    180         * </ul>
    181         *
    182         * @param localName
    183         *            name to check
    184         * @return true if localName equals (ignore-case) one of the above strings
    185         */
    186        public static boolean isGeometrieSupported( String localName ) {
    187            if ( localName == null || "".equals( localName.trim() ) ) {
    188                return false;
    189            }
    190            return "Point".equalsIgnoreCase( localName ) || "Center".equalsIgnoreCase( localName )
    191                   || "LineString".equalsIgnoreCase( localName ) || "Polygon".equalsIgnoreCase( localName )
    192                   || "MultiPoint".equalsIgnoreCase( localName ) || "MultiLineString".equalsIgnoreCase( localName )
    193                   || "MultiPolygon".equalsIgnoreCase( localName ) || "Box".equalsIgnoreCase( localName )
    194                   || "Envelope".equalsIgnoreCase( localName ) || "Curve".equalsIgnoreCase( localName )
    195                   || "Surface".equalsIgnoreCase( localName ) || "MultiCurve".equalsIgnoreCase( localName )
    196                   || "MultiSurface".equalsIgnoreCase( localName ) || "CompositeSurface".equalsIgnoreCase( localName )
    197                   || "MultiGeometry".equalsIgnoreCase( localName ) || "LinearRing".equalsIgnoreCase( localName )
    198                   || "Ring".equalsIgnoreCase( localName );
    199        }
    200
    201        /**
    202         * Parses the given DOM element of a GML geometry and returns a corresponding deegree {@link Geometry} object.
    203         * <p>
    204         * Notice that GML boxes will be converted to surfaces because in ISO 19107 envelopes are no geometries. Rings are
    205         * returned as {@link Curve} objects.
    206         * <p>
    207         * Currently, the following geometry elements are supported:
    208         * <table border="1">
    209         * <tr>
    210         * <th>Geometry element name<br>
    211         * (in gml namespace)</th>
    212         * <th>Return type<br>
    213         * (deegree {@link Geometry})</th>
    214         * </tr>
    215         * <tr>
    216         * <td align="center">Box/Envelope</td>
    217         * <td align="center">{@link Envelope}</td>
    218         * </tr>
    219         * <tr>
    220         * <td align="center">CompositeSurface</td>
    221         * <td align="center">{@link CompositeSurface}</td>
    222         * </tr>
    223         * <tr>
    224         * <td align="center">Curve</td>
    225         * <td align="center">{@link Curve}</td>
    226         * </tr>
    227         * <tr>
    228         * <td align="center">LinearRing</td>
    229         * <td align="center">{@link Curve}</td>
    230         * </tr>
    231         * <tr>
    232         * <td align="center">LineString</td>
    233         * <td align="center">{@link Curve}</td>
    234         * </tr>
    235         * <tr>
    236         * <td align="center">MultiCurve</td>
    237         * <td align="center">{@link MultiCurve}</td>
    238         * </tr>
    239         * <tr>
    240         * <td align="center">MultiGeometry</td>
    241         * <td align="center">{@link MultiGeometry}</td>
    242         * </tr>
    243         * <tr>
    244         * <td align="center">MultiLineString</td>
    245         * <td align="center">{@link MultiCurve}</td>
    246         * </tr>
    247         * <tr>
    248         * <td align="center">MultiPoint</td>
    249         * <td align="center">{@link MultiPoint}</td>
    250         * </tr>
    251         * <tr>
    252         * <td align="center">MultiPolygon</td>
    253         * <td align="center">{@link MultiSurface}</td>
    254         * </tr>
    255         * <tr>
    256         * <td align="center">MultiSurface</td>
    257         * <td align="center">{@link MultiSurface}</td>
    258         * </tr>
    259         * <tr>
    260         * <td align="center">Point/Center</td>
    261         * <td align="center">{@link Point}</td>
    262         * </tr>
    263         * <tr>
    264         * <td align="center">Polygon</td>
    265         * <td align="center">{@link Surface}</td>
    266         * </tr>
    267         * <tr>
    268         * <td align="center">Ring</td>
    269         * <td align="center">{@link Curve}</td>
    270         * </tr>
    271         * <tr>
    272         * <td align="center">Surface</td>
    273         * <td align="center">{@link Surface}</td>
    274         * </tr>
    275         * </table>
    276         *
    277         * @param element
    278         *            gml geometry element
    279         * @param srsName
    280         *            default SRS for the geometry
    281         * @return corresponding {@link Geometry} instance
    282         * @throws GeometryException
    283         *             if type unsupported or parsing / conversion failed
    284         */
    285        public static Geometry wrap( Element element, String srsName )
    286                                throws GeometryException {
    287
    288            Geometry geometry = null;
    289            try {
    290                String name = element.getLocalName();
    291                if ( ( name.equals( "Point" ) ) || ( name.equals( "Center" ) ) ) {
    292                    geometry = wrapPoint( element, srsName );
    293                } else if ( name.equals( "LineString" ) ) {
    294                    geometry = wrapLineString( element, srsName );
    295                } else if ( name.equals( "Polygon" ) ) {
    296                    geometry = wrapPolygon( element, srsName );
    297                } else if ( name.equals( "MultiPoint" ) ) {
    298                    geometry = wrapMultiPoint( element, srsName );
    299                } else if ( name.equals( "MultiLineString" ) ) {
    300                    geometry = wrapMultiLineString( element, srsName );
    301                } else if ( name.equals( "MultiPolygon" ) ) {
    302                    geometry = wrapMultiPolygon( element, srsName );
    303                } else if ( name.equals( "Box" ) || name.equals( "Envelope" ) ) {
    304                    geometry = wrapBoxAsSurface( element, srsName );
    305                } else if ( name.equals( "Curve" ) ) {
    306                    geometry = wrapCurveAsCurve( element, srsName );
    307                } else if ( name.equals( "Surface" ) ) {
    308                    geometry = wrapSurfaceAsSurface( element, srsName );
    309                } else if ( name.equals( "MultiCurve" ) ) {
    310                    geometry = wrapMultiCurveAsMultiCurve( element, srsName );
    311                } else if ( name.equals( "MultiSurface" ) ) {
    312                    geometry = wrapMultiSurfaceAsMultiSurface( element, srsName );
    313                } else if ( name.equals( "CompositeSurface" ) ) {
    314                    geometry = wrapCompositeSurface( element, srsName );
    315                } else if ( name.equals( "MultiGeometry" ) ) {
    316                    geometry = wrapMultiGeometry( element, srsName );
    317                } else if ( name.equals( "LinearRing" ) ) {
    318                    geometry = wrapLinearRing( element, srsName );
    319                } else if ( name.equals( "Ring" ) ) {
    320                    geometry = wrapRing( element, srsName );
    321                } else {
    322                    throw new GeometryException( "Not a supported geometry type: " + name );
    323                }
    324            } catch ( Exception e ) {
    325                LOG.logError( e.getMessage(), e );
    326                throw new GeometryException( StringTools.stackTraceToString( e ) );
    327            }
    328
    329            return geometry;
    330        }
    331
    332        /**
    333         * Converts the given string representation of a GML geometry object to a corresponding deegree {@link Geometry}.
    334         * Notice that GML Boxes will be converted to Surfaces because in ISO 19107 Envelopes are no geometries.
    335         *
    336         * @see #wrap(Element, String)
    337         *
    338         * @param gml
    339         * @param srsName
    340         *            default SRS for the geometry (may be overwritten in geometry elements)
    341         * @return corresponding geometry object
    342         * @throws GeometryException
    343         * @throws XMLParsingException
    344         */
    345        public static Geometry wrap( String gml, String srsName )
    346                                throws GeometryException, XMLParsingException {
    347            StringReader sr = new StringReader( gml );
    348            Document doc = null;
    349            try {
    350                doc = XMLTools.parse( sr );
    351            } catch ( Exception e ) {
    352                LOG.logError( "could not parse: '" + gml + "' as GML/XML", e );
    353                throw new XMLParsingException( "could not parse: '" + gml + "' as GML/XML: " + e.getMessage() );
    354            }
    355            return wrap( doc.getDocumentElement(), srsName );
    356        }
    357
    358        /**
    359         * Returns an instance of {@link Envelope} created from the passed <code>gml:Box</code> or <code>gml:Envelope</code>
    360         * element.
    361         *
    362         * @param element
    363         *            <code>gml:Box</code> or <code>gml:Envelope</code> element
    364         * @param srsName
    365         *            default SRS for the geometry
    366         * @return instance of <code>Envelope</code>
    367         * @throws XMLParsingException
    368         * @throws InvalidGMLException
    369         * @throws UnknownCRSException
    370         */
    371        public static Envelope wrapBox( Element element, String srsName )
    372                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
    373
    374            srsName = findSrsName( element, srsName );
    375
    376            boolean swap = swap( srsName );
    377
    378            Position[] bb = null;
    379            List<Element> nl = XMLTools.getElements( element, COORD, nsContext );
    380            if ( nl != null && nl.size() > 0 ) {
    381                bb = new Position[2];
    382                bb[0] = createPositionFromCoord( nl.get( 0 ), swap );
    383                bb[1] = createPositionFromCoord( nl.get( 1 ), swap );
    384            } else {
    385                nl = XMLTools.getElements( element, COORDINATES, nsContext );
    386                if ( nl != null && nl.size() > 0 ) {
    387                    bb = createPositionFromCoordinates( nl.get( 0 ), swap );
    388                } else {
    389                    nl = XMLTools.getElements( element, POS, nsContext );
    390                    if ( nl != null && nl.size() > 0 ) {
    391                        bb = new Position[2];
    392                        bb[0] = createPositionFromPos( nl.get( 0 ), swap );
    393                        bb[1] = createPositionFromPos( nl.get( 1 ), swap );
    394                    } else {
    395                        Element lowerCorner = (Element) XMLTools.getRequiredNode( element, "gml:lowerCorner", nsContext );
    396                        Element upperCorner = (Element) XMLTools.getRequiredNode( element, "gml:upperCorner", nsContext );
    397                        bb = new Position[2];
    398                        bb[0] = createPositionFromCorner( lowerCorner, swap );
    399                        bb[1] = createPositionFromCorner( upperCorner, swap );
    400                    }
    401                }
    402            }
    403            Envelope box = GeometryFactory.createEnvelope( bb[0], bb[1], getCRS( srsName ) );
    404            return box;
    405        }
    406
    407        /**
    408         * Returns an instance of {@link Curve} created from the passed element.
    409         * <p>
    410         * The element must be substitutable for <code>gml:_Curve</code>. Currently, the following concrete elements are
    411         * supported:
    412         * <ul>
    413         * <li><code>gml:Curve</code></li>
    414         * <li><code>gml:LineString</code>
    415         * <li>
    416         * </ul>
    417         *
    418         * @param element
    419         *            must be substitutable for the abstract element <code>gml:_Curve</code>
    420         * @param srsName
    421         * @return curve instance
    422         * @throws UnknownCRSException
    423         * @throws GeometryException
    424         * @throws XMLParsingException
    425         * @throws InvalidGMLException
    426         */
    427        private static Curve wrapAbstractCurve( Element element, String srsName )
    428                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    429            Curve curve = null;
    430            String localName = element.getLocalName();
    431            if ( "Curve".equals( localName ) ) {
    432                curve = wrapCurveAsCurve( element, srsName );
    433            } else if ( "LineString".equals( localName ) ) {
    434                curve = wrapLineString( element, srsName );
    435            } else {
    436                String msg = "'" + localName + "' is not a valid or supported substitution for 'gml:_Curve'.";
    437                throw new XMLParsingException( msg );
    438            }
    439            return curve;
    440        }
    441
    442        /**
    443         * Returns an instance of {@link Curve} created from the passed <code>gml:Curve</code> element.
    444         *
    445         * @param element
    446         *            <code>gml:Curve</code> element
    447         * @param srsName
    448         *            default SRS for the geometry
    449         * @return corresponding Curve instance
    450         * @throws XMLParsingException
    451         * @throws GeometryException
    452         * @throws UnknownCRSException
    453         */
    454        public static Curve wrapCurveAsCurve( Element element, String srsName )
    455                                throws XMLParsingException, GeometryException, UnknownCRSException {
    456
    457            srsName = findSrsName( element, srsName );
    458            Element segmentsElement = (Element) XMLTools.getRequiredNode( element, "gml:segments", nsContext );
    459            ElementList childElements = XMLTools.getChildElements( segmentsElement );
    460            CurveSegment[] segments = new CurveSegment[childElements.getLength()];
    461            for ( int i = 0; i < childElements.getLength(); i++ ) {
    462                segments[i] = parseAbstractCurveSegment( childElements.item( i ), srsName );
    463            }
    464            return GeometryFactory.createCurve( segments, getCRS( srsName ) );
    465        }
    466
    467        /**
    468         * Parses the given element as a {@link CurveSegment}.
    469         * <p>
    470         * The element must be substitutable for <code>gml:_CurveSegment</code>. Currently, the following concrete elements
    471         * are supported:
    472         * <ul>
    473         * <li><code>gml:Arc</code></li>
    474         * <li><code>gml:Circle</code>
    475         * <li><code>gml:LineStringSegment</code>
    476         * <li>
    477         * </ul>
    478         *
    479         * @param element
    480         *            must be substitutable for the abstract element <code>gml:_CurveSegment</code>
    481         * @param srsName
    482         * @return curve instance
    483         * @throws GeometryException
    484         * @throws UnknownCRSException
    485         * @throws XMLParsingException
    486         */
    487        private static CurveSegment parseAbstractCurveSegment( Element element, String srsName )
    488                                throws GeometryException, XMLParsingException, UnknownCRSException {
    489            CurveSegment segment = null;
    490            String localName = element.getLocalName();
    491            if ( "Arc".equals( localName ) ) {
    492                segment = parseArc( element, srsName );
    493            } else if ( "Circle".equals( localName ) ) {
    494                segment = parseCircle( element, srsName );
    495            } else if ( "LineStringSegment".equals( localName ) ) {
    496                segment = parseLineStringSegment( element, srsName );
    497            } else {
    498                String msg = "'" + localName + "' is not a valid or supported substitution for 'gml:_CurveSegment'.";
    499                throw new GeometryException( msg );
    500            }
    501            return segment;
    502        }
    503
    504        /**
    505         * Parses the given <code>gml:Arc</code> element as a {@link CurveSegment}.
    506         *
    507         * @param element
    508         * @param srsName
    509         * @return the CurveSegment created from the given element
    510         * @throws GeometryException
    511         * @throws XMLParsingException
    512         * @throws UnknownCRSException
    513         */
    514        private static CurveSegment parseArc( Element element, String srsName )
    515                                throws GeometryException, XMLParsingException, UnknownCRSException {
    516
    517            srsName = findSrsName( element, srsName );
    518            Position[] pos = null;
    519            try {
    520                pos = createPositions( element, srsName );
    521            } catch ( Exception e ) {
    522                throw new GeometryException( "Error parsing gml:Arc: " + e.getMessage() );
    523            }
    524            if ( pos.length != 3 ) {
    525                throw new GeometryException( "A gml:Arc must be described by exactly three points" );
    526            }
    527            int count = Integer.parseInt( arcProperties.getProperty( "defaultNumPoints" ) );
    528            Position[] linearizedPos = LinearizationUtil.linearizeArc( pos[0], pos[1], pos[2], count );
    529            return GeometryFactory.createCurveSegment( linearizedPos, getCRS( srsName ) );
    530        }
    531
    532        /**
    533         * Parses the given <code>gml:Circle</code> element as a {@link CurveSegment}.
    534         *
    535         * @param element
    536         * @param srsName
    537         * @return the CurveSegment created from the given element
    538         * @throws GeometryException
    539         * @throws XMLParsingException
    540         * @throws UnknownCRSException
    541         */
    542        private static CurveSegment parseCircle( Element element, String srsName )
    543                                throws GeometryException, XMLParsingException, UnknownCRSException {
    544
    545            srsName = findSrsName( element, srsName );
    546            Position[] pos;
    547            try {
    548                pos = createPositions( element, srsName );
    549            } catch ( Exception e ) {
    550                throw new GeometryException( "Error parsing gml:Circle: " + e.getMessage() );
    551            }
    552            if ( pos.length != 3 ) {
    553                throw new GeometryException( "A gml:Circle element must be described by exactly three points." );
    554            }
    555            int count = Integer.parseInt( arcProperties.getProperty( "defaultNumPoints" ) );
    556            Position[] linearizedPos = LinearizationUtil.linearizeCircle( pos[0], pos[1], pos[2], count );
    557            return GeometryFactory.createCurveSegment( linearizedPos, getCRS( srsName ) );
    558        }
    559
    560        /**
    561         * Parses the given <code>gml:LineStringSegment</code> element as a {@link CurveSegment}.
    562         *
    563         * @param element
    564         * @param srsName
    565         * @return the curve segment created from the given element
    566         * @throws GeometryException
    567         * @throws XMLParsingException
    568         */
    569        private static CurveSegment parseLineStringSegment( Element element, String srsName )
    570                                throws GeometryException, XMLParsingException {
    571
    572            srsName = findSrsName( element, srsName );
    573            CurveSegment segment = null;
    574            try {
    575                Position[] pos = createPositions( element, srsName );
    576                segment = createCurveSegment( pos, getCRS( srsName ) );
    577            } catch ( Exception e ) {
    578                LOG.logError( "Real error:", e );
    579                throw new GeometryException( "Error parsing LineStringSegment: " + e.getMessage() );
    580            }
    581            return segment;
    582        }
    583
    584        /**
    585         * Parses the given element as a {@link Surface}.
    586         * <p>
    587         * The element must be substitutable for <code>gml:_Surface</code>. Currently, the following concrete elements are
    588         * supported:
    589         * <ul>
    590         * <li><code>gml:Surface</code></li>
    591         * <li><code>gml:Polygon</code>
    592         * <li>
    593         * </ul>
    594         *
    595         * @param element
    596         *            must be substitutable for the abstract element <code>gml:_Surface</code>
    597         * @param srsName
    598         * @return corresponding surface instance
    599         * @throws UnknownCRSException
    600         * @throws GeometryException
    601         * @throws XMLParsingException
    602         * @throws InvalidGMLException
    603         */
    604        private static Surface wrapAbstractSurface( Element element, String srsName )
    605                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    606            Surface surface = null;
    607            String localName = element.getLocalName();
    608            if ( "Polygon".equals( localName ) ) {
    609                surface = wrapPolygon( element, srsName );
    610            } else if ( "Surface".equals( localName ) ) {
    611                surface = wrapSurfaceAsSurface( element, srsName );
    612            } else {
    613                String msg = "'" + localName + "' is not a valid or supported substitution for 'gml:_Surface'.";
    614                throw new XMLParsingException( msg );
    615            }
    616            return surface;
    617        }
    618
    619        /**
    620         * Returns an instance of {@link Surface} created from the passed <code>gml:Surface</code> element.
    621         *
    622         * @param element
    623         * @param srsName
    624         *            default SRS for the geometry
    625         * @return Surface
    626         * @throws XMLParsingException
    627         * @throws GeometryException
    628         * @throws UnknownCRSException
    629         */
    630        public static Surface wrapSurfaceAsSurface( Element element, String srsName )
    631                                throws XMLParsingException, GeometryException, UnknownCRSException {
    632
    633            srsName = findSrsName( element, srsName );
    634
    635            Element patches = extractPatches( element );
    636            List<Element> polygonList = XMLTools.getRequiredElements( patches, "gml:Polygon | gml:PolygonPatch", nsContext );
    637
    638            SurfacePatch[] surfacePatches = new SurfacePatch[polygonList.size()];
    639
    640            for ( int i = 0; i < polygonList.size(); i++ ) {
    641                Curve exteriorRing = null;
    642                Element polygon = polygonList.get( i );
    643                try {
    644                    Element exterior = (Element) XMLTools.getNode( polygon, "gml:exterior | gml:outerBounderyIs", nsContext );
    645                    if ( exterior != null ) {
    646                        exteriorRing = parseRing( srsName, exterior );
    647                    } else {
    648                        String msg = Messages.getMessage( "GEOM_SURFACE_NO_EXTERIOR_RING" );
    649                        throw new XMLParsingException( msg );
    650                    }
    651
    652                    List<Element> interiorList = XMLTools.getElements( polygon, "gml:interior | gml:outerBounderyIs",
    653                                                                       nsContext );
    654                    Curve[] interiorRings = null;
    655                    if ( interiorList != null && interiorList.size() > 0 ) {
    656
    657                        interiorRings = new Curve[interiorList.size()];
    658
    659                        for ( int j = 0; j < interiorRings.length; j++ ) {
    660                            Element interior = interiorList.get( j );
    661                            interiorRings[j] = parseRing( srsName, interior );
    662                        }
    663                    }
    664                    surfacePatches[i] = GeometryFactory.createSurfacePatch( exteriorRing, interiorRings, getCRS( srsName ) );
    665                } catch ( InvalidGMLException e ) {
    666                    LOG.logError( e.getMessage(), e );
    667                    throw new XMLParsingException( "Error parsing the polygon element '" + polygon.getNodeName()
    668                                                   + "' to create a surface geometry." );
    669                }
    670
    671            }
    672            Surface surface = null;
    673            try {
    674                surface = GeometryFactory.createSurface( surfacePatches, getCRS( srsName ) );
    675            } catch ( GeometryException e ) {
    676                throw new GeometryException( "Error creating a surface from '" + surfacePatches.length + "' polygons." );
    677            }
    678            return surface;
    679        }
    680
    681        /**
    682         * Determines the relevant srsName attribute for a geometry element.
    683         * <p>
    684         * Strategy:
    685         * <nl>
    686         * <li>Check if the geometry element has a srsName attribute, or any ancestor. If an srsName is found this way, it
    687         * is used.</li>
    688         * <li>If no srsName was found, but an srsName was given as parameter to the method, it is returned.</li>
    689         * <li>If no srsName was found *and* no srsName was given as parameter to the method, the whole DOM tree is searched
    690         * for for an srsName attribute.</li>
    691         * </nl>
    692         *
    693         * @param element
    694         *            geometry element
    695         * @param srsName
    696         *            default value that is used when the element (or an ancestor element) does not have an own srsName
    697         *            attribute
    698         * @return the srs name
    699         * @throws XMLParsingException
    700         */
    701        private static String findSrsName( Element element, String srsName )
    702                                throws XMLParsingException {
    703
    704            Node elem = element;
    705            String tmp = null;
    706            while ( tmp == null && elem != null ) {
    707                tmp = XMLTools.getNodeAsString( elem, "@srsName", nsContext, null );
    708                elem = elem.getParentNode();
    709            }
    710
    711            if ( tmp != null ) {
    712                srsName = tmp;
    713            } else if ( srsName == null ) {
    714                // TODO do this is a better way!!!
    715                srsName = XMLTools.getNodeAsString( element, "//@srsName", nsContext, null );
    716            }
    717            return srsName;
    718        }
    719
    720        /**
    721         * Returns the {@link CoordinateSystem} corresponding to the passed crs name.
    722         *
    723         * @param name
    724         *            name of the crs or null (not specified)
    725         * @return CoordinateSystem corresponding to the given crs name or null if name is null
    726         * @throws UnknownCRSException
    727         */
    728        private static CoordinateSystem getCRS( String name )
    729                                throws UnknownCRSException {
    730
    731            if ( name == null ) {
    732                return null;
    733            }
    734
    735            if ( name.length() > 2 ) {
    736                if ( name.startsWith( "http://www.opengis.net/gml/srs/" ) ) {
    737                    // as declared in the GML 2.1.1 specification
    738                    // http://www.opengis.net/gml/srs/epsg.xml#4326
    739                    int p = name.lastIndexOf( "/" );
    740
    741                    if ( p >= 0 ) {
    742                        name = name.substring( p, name.length() );
    743                        p = name.indexOf( "." );
    744
    745                        String s1 = name.substring( 1, p ).toUpperCase();
    746                        p = name.indexOf( "#" );
    747
    748                        String s2 = name.substring( p + 1, name.length() );
    749                        name = s1 + ":" + s2;
    750                    }
    751                }
    752            }
    753
    754            CoordinateSystem crs = crsMap.get( name );
    755            if ( crs == null ) {
    756                crs = CRSFactory.create( name );
    757                crsMap.put( name, crs );
    758            }
    759            return crs;
    760        }
    761
    762        /**
    763         * Parses a ring element; this may be a gml:LinearRing or a gml:Ring.
    764         *
    765         * @param srsName
    766         * @param parent
    767         *            parent of a gml:LinearRing or gml:Ring
    768         * @return curves of a ring
    769         * @throws XMLParsingException
    770         * @throws InvalidGMLException
    771         * @throws GeometryException
    772         * @throws UnknownCRSException
    773         */
    774        private static Curve parseRing( String srsName, Element parent )
    775                                throws XMLParsingException, InvalidGMLException, GeometryException, UnknownCRSException {
    776
    777            List<CurveSegment> curveMembers = null;
    778            Element ring = (Element) XMLTools.getNode( parent, "gml:LinearRing", nsContext );
    779            if ( ring != null ) {
    780                Position[] exteriorRing = createPositions( ring, srsName );
    781                curveMembers = new ArrayList<CurveSegment>();
    782                curveMembers.add( GeometryFactory.createCurveSegment( exteriorRing, getCRS( srsName ) ) );
    783            } else {
    784                List<Node> members = XMLTools.getRequiredNodes( parent, "gml:Ring/gml:curveMember/child::*", nsContext );
    785                curveMembers = new ArrayList<CurveSegment>( members.size() );
    786                for ( Node node : members ) {
    787                    Curve curve = (Curve) wrap( (Element) node, srsName );
    788                    CurveSegment[] tmp = curve.getCurveSegments();
    789                    for ( int i = 0; i < tmp.length; i++ ) {
    790                        curveMembers.add( tmp[i] );
    791                    }
    792                }
    793            }
    794            CurveSegment[] cs = curveMembers.toArray( new CurveSegment[curveMembers.size()] );
    795            return GeometryFactory.createCurve( cs );
    796        }
    797
    798        /**
    799         * Returns a {@link Point} instance created from the passed <code>gml:Point</code> element.
    800         *
    801         * @param element
    802         *            <code>gml:Point</code> element
    803         * @param srsName
    804         *            default SRS for the geometry
    805         * @return instance of Point
    806         * @throws XMLParsingException
    807         * @throws InvalidGMLException
    808         * @throws UnknownCRSException
    809         */
    810        public static Point wrapPoint( Element element, String srsName )
    811                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
    812
    813            srsName = findSrsName( element, srsName );
    814
    815            boolean swap = swap( srsName );
    816
    817            Position[] bb = null;
    818            List<Element> nl = XMLTools.getElements( element, COORD, nsContext );
    819            if ( nl != null && nl.size() > 0 ) {
    820                bb = new Position[1];
    821                bb[0] = createPositionFromCoord( nl.get( 0 ), swap );
    822            } else {
    823                nl = XMLTools.getElements( element, COORDINATES, nsContext );
    824                if ( nl != null && nl.size() > 0 ) {
    825                    bb = createPositionFromCoordinates( nl.get( 0 ), swap );
    826                } else {
    827                    nl = XMLTools.getElements( element, POS, nsContext );
    828                    bb = new Position[1];
    829                    bb[0] = createPositionFromPos( nl.get( 0 ), swap );
    830                }
    831            }
    832            return GeometryFactory.createPoint( bb[0], getCRS( srsName ) );
    833        }
    834
    835        /**
    836         * Returns a {@link Curve} instance created from the passed <code>gml:LineString</code> element.
    837         *
    838         * @param element
    839         *            <code>gml:LineString</code> element
    840         * @param srsName
    841         *            default SRS for the geometry
    842         * @return instance of Curve
    843         * @throws XMLParsingException
    844         * @throws GeometryException
    845         * @throws InvalidGMLException
    846         * @throws UnknownCRSException
    847         */
    848        public static Curve wrapLineString( Element element, String srsName )
    849                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    850
    851            srsName = findSrsName( element, srsName );
    852            Position[] pos = createPositions( element, srsName );
    853            return GeometryFactory.createCurve( pos, getCRS( srsName ) );
    854        }
    855
    856        /**
    857         * Parses the given <code>gml:Polygon</code> element as a {@link Surface}.
    858         *
    859         * @param element
    860         *            <code>gml:Polygon</code> element
    861         * @param srsName
    862         *            default SRS for the geometry
    863         * @return corresponding Surface instance
    864         * @throws XMLParsingException
    865         * @throws GeometryException
    866         * @throws InvalidGMLException
    867         * @throws UnknownCRSException
    868         */
    869        public static Surface wrapPolygon( Element element, String srsName )
    870                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    871
    872            srsName = findSrsName( element, srsName );
    873
    874            // get positions for outer boundary
    875            Element exteriorRingElement = XMLTools.getRequiredElement( element, "gml:exterior/*|gml:outerBoundaryIs/*",
    876                                                                       nsContext );
    877            Curve exteriorRing = wrapAbstractRing( exteriorRingElement, srsName );
    878            Position[] exteriorPositions = getCurvePositions( exteriorRing );
    879
    880            // get positions for inner boundaries
    881            List<Element> interiorRingElements = XMLTools.getElements( element, "gml:interior/*|gml:innerBoundaryIs/*",
    882                                                                       nsContext );
    883            Position[][] interiorPositions = new Position[interiorRingElements.size()][];
    884            for ( int i = 0; i < interiorPositions.length; i++ ) {
    885                Curve interiorRing = wrapAbstractRing( interiorRingElements.get( i ), srsName );
    886                interiorPositions[i] = getCurvePositions( interiorRing );
    887            }
    888
    889            SurfaceInterpolation si = new SurfaceInterpolationImpl();
    890            return GeometryFactory.createSurface( exteriorPositions, interiorPositions, si, getCRS( srsName ) );
    891        }
    892
    893        private static Position[] getCurvePositions( Curve curve )
    894                                throws GeometryException {
    895            Position[] positions = null;
    896            CurveSegment[] segments = curve.getCurveSegments();
    897            if ( segments.length == 1 ) {
    898                positions = segments[0].getPositions();
    899            } else {
    900                int numPositions = 0;
    901                for ( int i = 0; i < segments.length; i++ ) {
    902                    numPositions += segments[i].getNumberOfPoints();
    903                }
    904                positions = new Position[numPositions];
    905                int positionId = 0;
    906                for ( int i = 0; i < segments.length; i++ ) {
    907                    Position[] segmentPositions = segments[i].getPositions();
    908                    for ( int j = 0; j < segmentPositions.length; j++ ) {
    909                        positions[positionId++] = segmentPositions[j];
    910                    }
    911                }
    912            }
    913            return positions;
    914        }
    915
    916        /**
    917         * Returns an instance of {@link Curve} created from the passed element.
    918         * <p>
    919         * The element must be substitutable for <code>gml:_Ring</code>. Currently, the following concrete elements are
    920         * supported:
    921         * <ul>
    922         * <li><code>gml:LinearRing</code></li>
    923         * <li><code>gml:Ring</code>
    924         * <li>
    925         * </ul>
    926         *
    927         * @param element
    928         *            must be substitutable for the abstract element <code>gml:_Ring</code>
    929         * @param srsName
    930         * @return curve instance
    931         * @throws UnknownCRSException
    932         * @throws GeometryException
    933         * @throws XMLParsingException
    934         * @throws InvalidGMLException
    935         */
    936        private static Curve wrapAbstractRing( Element element, String srsName )
    937                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    938            Curve curve = null;
    939            String localName = element.getLocalName();
    940            if ( "LinearRing".equals( localName ) ) {
    941                curve = wrapLinearRing( element, srsName );
    942            } else if ( "Ring".equals( localName ) ) {
    943                curve = wrapRing( element, srsName );
    944            } else {
    945                String msg = "'" + localName + "' is not a valid or supported substitution for 'gml:_Ring'.";
    946                throw new XMLParsingException( msg );
    947            }
    948            return curve;
    949        }
    950
    951        /**
    952         * Parses the given <code>gml:LinearRing</code> element as a {@link Curve}.
    953         *
    954         * @param element
    955         * @param srsName
    956         * @return a polygon containing the ring
    957         * @throws XMLParsingException
    958         * @throws GeometryException
    959         * @throws InvalidGMLException
    960         * @throws UnknownCRSException
    961         */
    962        public static Curve wrapLinearRing( Element element, String srsName )
    963                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    964
    965            srsName = findSrsName( element, srsName );
    966            Position[] pos = createPositions( element, srsName );
    967            return GeometryFactory.createCurve( pos, getCRS( srsName ) );
    968        }
    969
    970        /**
    971         * Parses the given <code>gml:Ring</code> element as a {@link Curve}.
    972         *
    973         * @param element
    974         *            <code>gml:Ring</code> element
    975         * @param srsName
    976         *            default SRS for the geometry
    977         * @return corresponding Curve instance
    978         * @throws XMLParsingException
    979         * @throws GeometryException
    980         * @throws UnknownCRSException
    981         * @throws InvalidGMLException
    982         */
    983        public static Curve wrapRing( Element element, String srsName )
    984                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    985
    986            srsName = findSrsName( element, srsName );
    987            Element curveElement = (Element) XMLTools.getRequiredNode( element, "gml:curveMember/*", nsContext );
    988            return wrapAbstractCurve( curveElement, srsName );
    989        }
    990
    991        /**
    992         * Returns a {@link MultiPoint} instance created from the passed <code>gml:MultiPoint</code> element.
    993         *
    994         * @param element
    995         *            <code>gml:MultiPoint</code> element
    996         * @param srsName
    997         *            default SRS for the geometry
    998         * @return instance of MultiPoint
    999         * @throws XMLParsingException
    1000         * @throws InvalidGMLException
    1001         * @throws UnknownCRSException
    1002         */
    1003        public static MultiPoint wrapMultiPoint( Element element, String srsName )
    1004                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
    1005
    1006            srsName = findSrsName( element, srsName );
    1007
    1008            // gml:pointMember
    1009            List<Point> pointList = new ArrayList<Point>();
    1010            List<Element> listPointMember = XMLTools.getElements( element, "gml:pointMember", nsContext );
    1011            if ( listPointMember != null ) {
    1012                for ( int i = 0; i < listPointMember.size(); i++ ) {
    1013                    Element pointMember = listPointMember.get( i );
    1014                    Element point = XMLTools.getElement( pointMember, "gml:Point", nsContext );
    1015                    pointList.add( wrapPoint( point, srsName ) );
    1016                }
    1017            }
    1018
    1019            // gml:pointMembers
    1020            Element pointMembers = XMLTools.getElement( element, "gml:pointMembers", nsContext );
    1021            if ( pointMembers != null ) {
    1022                List<Element> pointElems = XMLTools.getElements( pointMembers, "gml:Point", nsContext );
    1023                for ( int j = 0; j < pointElems.size(); j++ ) {
    1024                    pointList.add( wrapPoint( pointElems.get( j ), srsName ) );
    1025                }
    1026            }
    1027
    1028            Point[] points = new Point[pointList.size()];
    1029            return GeometryFactory.createMultiPoint( pointList.toArray( points ), getCRS( srsName ) );
    1030        }
    1031
    1032        /**
    1033         * Returns a {@link MultiCurve} instance created from the passed <code>gml:MultiLineString</code> element.
    1034         *
    1035         * @param element
    1036         *            <code>gml:MultiLineString</code> element
    1037         * @param srsName
    1038         *            default SRS for the geometry
    1039         * @return instance of MultiCurve
    1040         * @throws XMLParsingException
    1041         * @throws GeometryException
    1042         * @throws InvalidGMLException
    1043         * @throws UnknownCRSException
    1044         */
    1045        public static MultiCurve wrapMultiLineString( Element element, String srsName )
    1046                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    1047
    1048            srsName = findSrsName( element, srsName );
    1049
    1050            // ElementList el = XMLTools.getChildElements( "lineStringMember", CommonNamespaces.GMLNS,
    1051            // element );
    1052            List<Element> el = XMLTools.getElements( element, CommonNamespaces.GML_PREFIX + ":lineStringMember", nsContext );
    1053            Curve[] curves = new Curve[el.size()];
    1054            for ( int i = 0; i < curves.length; i++ ) {
    1055                curves[i] = wrapLineString( XMLTools.getFirstChildElement( el.get( i ) ), srsName );
    1056            }
    1057            return GeometryFactory.createMultiCurve( curves, getCRS( srsName ) );
    1058        }
    1059
    1060        /**
    1061         * Parses the given <code>gml:MultiCurve</code> element as a {@link MultiCurve}.
    1062         *
    1063         * @param element
    1064         *            <code>gml:MultiCurve</code> element
    1065         * @param srsName
    1066         *            default SRS for the geometry
    1067         * @return corresponding <code>MultiCurve</code> instance
    1068         * @throws XMLParsingException
    1069         * @throws GeometryException
    1070         * @throws UnknownCRSException
    1071         * @throws InvalidGMLException
    1072         */
    1073        public static MultiCurve wrapMultiCurveAsMultiCurve( Element element, String srsName )
    1074                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    1075
    1076            srsName = findSrsName( element, srsName );
    1077
    1078            // gml:curveMember
    1079            List<Element> curveMemberElements = XMLTools.getElements( element, "gml:curveMember", nsContext );
    1080            List<Curve> curves = new ArrayList<Curve>();
    1081            if ( curveMemberElements.size() > 0 ) {
    1082                for ( int i = 0; i < curveMemberElements.size(); i++ ) {
    1083                    Element curveMemberElement = curveMemberElements.get( i );
    1084                    Element curveElement = XMLTools.getFirstChildElement( curveMemberElement );
    1085                    Curve curve = wrapAbstractCurve( curveElement, srsName );
    1086                    curves.add( curve );
    1087                }
    1088            }
    1089            // gml:curveMembers
    1090            Element curveMembersElement = (Element) XMLTools.getNode( element, "gml:curveMembers", nsContext );
    1091            if ( curveMembersElement != null ) {
    1092                ElementList curveElements = XMLTools.getChildElements( curveMembersElement );
    1093                for ( int i = 0; i < curveElements.getLength(); i++ ) {
    1094                    Curve curve = wrapAbstractCurve( curveElements.item( i ), srsName );
    1095                    curves.add( curve );
    1096                }
    1097            }
    1098            return GeometryFactory.createMultiCurve( curves.toArray( new Curve[curves.size()] ), getCRS( srsName ) );
    1099        }
    1100
    1101        /**
    1102         * Parses the given <code>gml:MultiSurface</code> element as a {@link MultiSurface}.
    1103         *
    1104         * @param element
    1105         *            <code>gml:MultiSurface</code> element
    1106         * @param srsName
    1107         *            default SRS for the geometry
    1108         * @return corresponding <code>MultiSurface</code> instance
    1109         * @throws XMLParsingException
    1110         * @throws GeometryException
    1111         * @throws UnknownCRSException
    1112         * @throws InvalidGMLException
    1113         */
    1114        public static MultiSurface wrapMultiSurfaceAsMultiSurface( Element element, String srsName )
    1115                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
    1116
    1117            srsName = findSrsName( element, srsName );
    1118
    1119            // gml:surfaceMember
    1120            List<Element> surfaceMemberElements = XMLTools.getElements( element, "gml:surfaceMember", nsContext );
    1121            List<Surface> surfaces = new ArrayList<Surface>();
    1122            if ( surfaceMemberElements.size() > 0 ) {
    1123                for ( int i = 0; i < surfaceMemberElements.size(); i++ ) {
    1124                    Element surfaceMemberElement = surfaceMemberElements.get( i );
    1125                    Element surfaceElement = XMLTools.getFirstChildElement( surfaceMemberElement );
    1126                    Surface surface = wrapAbstractSurface( surfaceElement, srsName );
    1127                    surfaces.add( surface );
    1128                }
    1129            }
    1130            // gml:surfaceMembers
    1131            Element surfaceMembersElement = (Element) XMLTools.getNode( element, "gml:surfaceMembers", nsContext );
    1132            if ( surfaceMembersElement != null ) {
    1133                ElementList surfaceElements = XMLTools.getChildElements( surfaceMembersElement );
    1134                for ( int i = 0; i < surfaceElements.getLength(); i++ ) {
    1135                    Surface surface = wrapAbstractSurface( surfaceElements.item( i ), srsName );
    1136                    surfaces.add( surface );
    1137                }
    1138            }
    1139            return GeometryFactory.createMultiSurface( surfaces.toArray( new Surface[surfaces.size()] ), getCRS( srsName ) );
    1140        }
    1141
    1142        /**
    1143         * Returns a {@link MultiSurface} instance created from the passed <code>gml:MultiPolygon</code> element.
    1144         *
    1145         * @param element
    1146         *            <code>gml:MultiPolygon</code> element
    1147         * @param srsName
    1148         *            default SRS for the geometry
    1149         * @return instance of MultiCurve
    1150         *
    1151         * @throws XMLParsingException
    1152         * @throws GeometryException
    1153         * @throws InvalidGMLException
    1154         * @throws UnknownCRSException
    1155         */
    1156        public static MultiSurface wrapMultiPolygon( Element element, String srsName )
    1157                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    1158
    1159            srsName = findSrsName( element, srsName );
    1160
    1161            // ElementList el = XMLTools.getChildElements( "polygonMember", CommonNamespaces.GMLNS,
    1162            // element );
    1163            List<Element> el = XMLTools.getElements( element, CommonNamespaces.GML_PREFIX + ":polygonMember", nsContext );
    1164            Surface[] surfaces = new Surface[el.size()];
    1165            for ( int i = 0; i < surfaces.length; i++ ) {
    1166                surfaces[i] = wrapPolygon( XMLTools.getFirstChildElement( el.get( i ) ), srsName );
    1167            }
    1168            return GeometryFactory.createMultiSurface( surfaces, getCRS( srsName ) );
    1169        }
    1170
    1171        /**
    1172         * Creates an instance of {@link MultiGeometry} from the passed <code>gml:MultiGeometry</code> element.
    1173         *
    1174         * @param element
    1175         *            <code>gml:MultiGeometry</code> element
    1176         * @param srsName
    1177         *            default SRS for the geometry
    1178         * @return MultiSurface
    1179         * @throws XMLParsingException
    1180         * @throws GeometryException
    1181         * @throws UnknownCRSException
    1182         * @throws GeometryException
    1183         */
    1184        public static MultiGeometry wrapMultiGeometry( Element element, String srsName )
    1185                                throws XMLParsingException, UnknownCRSException, GeometryException {
    1186
    1187            srsName = findSrsName( element, srsName );
    1188            List<Geometry> memberGeometries = new ArrayList<Geometry>();
    1189
    1190            // gml:geometryMember
    1191            List<Element> geometryMemberElements = XMLTools.getElements( element, "gml:geometryMember", nsContext );
    1192            if ( geometryMemberElements != null ) {
    1193                for ( Element geometryMemberElement : geometryMemberElements ) {
    1194                    Element geometryElement = XMLTools.getFirstChildElement( geometryMemberElement );
    1195                    memberGeometries.add( wrap( geometryElement, srsName ) );
    1196                }
    1197            }
    1198
    1199            // gml:geometryMembers
    1200            Element surfaceMembers = (Element) XMLTools.getNode( element, "gml:geometryMembers", nsContext );
    1201            if ( surfaceMembers != null ) {
    1202                ElementList geometryElements = XMLTools.getChildElements( surfaceMembers );
    1203                for ( int i = 0; i < geometryElements.getLength(); i++ ) {
    1204                    Element geometryElement = geometryElements.item( i );
    1205                    memberGeometries.add( wrap( geometryElement, srsName ) );
    1206                }
    1207            }
    1208            return GeometryFactory.createMultiGeometry( memberGeometries.toArray( new Geometry[memberGeometries.size()] ),
    1209                                                        getCRS( srsName ) );
    1210        }
    1211
    1212        /**
    1213         * Returns a <code>Surface</code> created from the given <code>gml:Box</code> element. This method is useful because
    1214         * an Envelope that would normally be created from a Box isn't a geometry in context of ISO 19107.
    1215         *
    1216         * @param element
    1217         *            <code>gml:Box</code> element
    1218         * @param srsName
    1219         * @return instance of <code>Surface</code>
    1220         * @throws XMLParsingException
    1221         * @throws GeometryException
    1222         * @throws InvalidGMLException
    1223         * @throws UnknownCRSException
    1224         */
    1225        public static Surface wrapBoxAsSurface( Element element, String srsName )
    1226                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
    1227            Envelope env = wrapBox( element, srsName );
    1228            return GeometryFactory.createSurface( env, env.getCoordinateSystem() );
    1229        }
    1230
    1231        /**
    1232         * Returns an instance of {@link CompositeSurface} created from the passed <code>gml:CompositeSurface</code>
    1233         * element.
    1234         *
    1235         * TODO
    1236         *
    1237         * @param element
    1238         * @param srsName
    1239         *            default SRS for the geometry
    1240         * @return CompositeSurface
    1241         */
    1242        public static CompositeSurface wrapCompositeSurface( Element element, String srsName ) {
    1243            throw new UnsupportedOperationException(
    1244                                                     "#wrapCompositeSurface(Element) is not implemented as yet. Work in Progress." );
    1245        }
    1246
    1247        /**
    1248         * Extract the <gml:patches> node from a <gml:Surface> element.
    1249         *
    1250         * @param surface
    1251         * @return Element
    1252         * @throws XMLParsingException
    1253         */
    1254        private static Element extractPatches( Element surface )
    1255                                throws XMLParsingException {
    1256            Element patches = null;
    1257            try {
    1258                patches = (Element) XMLTools.getRequiredNode( surface, "gml:patches", nsContext );
    1259            } catch ( XMLParsingException e ) {
    1260                throw new XMLParsingException( "Error retrieving the patches element from the surface element." );
    1261            }
    1262            return patches;
    1263        }
    1264
    1265        private static Position createPositionFromCorner( Element corner, boolean swap )
    1266                                throws InvalidGMLException {
    1267
    1268            String tmp = XMLTools.getAttrValue( corner, null, "dimension", null );
    1269            int dim = 0;
    1270            if ( tmp != null ) {
    1271                dim = Integer.parseInt( tmp );
    1272            }
    1273            tmp = XMLTools.getStringValue( corner );
    1274            double[] vals = StringTools.toArrayDouble( tmp, ", " );
    1275            if ( dim != 0 ) {
    1276                if ( vals.length != dim ) {
    1277                    throw new InvalidGMLException( "dimension must be equal to the number of coordinate values defined "
    1278                                                   + "in pos element." );
    1279                }
    1280            } else {
    1281                dim = vals.length;
    1282            }
    1283
    1284            Position pos = null;
    1285            if ( dim == 3 ) {
    1286                pos = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
    1287            } else {
    1288                pos = GeometryFactory.createPosition( vals[swap ? 1 : 0], vals[swap ? 0 : 1] );
    1289            }
    1290
    1291            return pos;
    1292
    1293        }
    1294
    1295        /**
    1296         * returns an instance of Position created from the passed coord
    1297         *
    1298         * @param element
    1299         *            &lt;coord&gt;
    1300         *
    1301         * @return instance of <tt>Position</tt>
    1302         *
    1303         * @throws XMLParsingException
    1304         */
    1305        private static Position createPositionFromCoord( Element element, boolean swap )
    1306                                throws XMLParsingException {
    1307
    1308            Position pos = null;
    1309            // Element elem = XMLTools.getRequiredChildElement( "X", CommonNamespaces.GMLNS, element );
    1310            Element elem = XMLTools.getRequiredElement( element, CommonNamespaces.GML_PREFIX + ":X", nsContext );
    1311            double x = Double.parseDouble( XMLTools.getStringValue( elem ) );
    1312            // elem = XMLTools.getRequiredChildElement( "Y", CommonNamespaces.GMLNS, element );
    1313            elem = XMLTools.getRequiredElement( element, CommonNamespaces.GML_PREFIX + ":Y", nsContext );
    1314            double y = Double.parseDouble( XMLTools.getStringValue( elem ) );
    1315            // elem = XMLTools.getChildElement( "Z", CommonNamespaces.GMLNS, element );
    1316            elem = XMLTools.getElement( element, CommonNamespaces.GML_PREFIX + ":Z", nsContext );
    1317
    1318            if ( elem != null ) {
    1319                double z = Double.parseDouble( XMLTools.getStringValue( elem ) );
    1320                // no swapping for 3D
    1321                pos = GeometryFactory.createPosition( new double[] { x, y, z } );
    1322            } else {
    1323                pos = GeometryFactory.createPosition( new double[] { swap ? y : x, swap ? x : y } );
    1324            }
    1325            return pos;
    1326        }
    1327
    1328        /**
    1329         * returns an array of Positions created from the passed coordinates
    1330         *
    1331         * @param element
    1332         *            <coordinates>
    1333         *
    1334         * @return instance of <tt>Position[]</tt>
    1335         */
    1336        private static Position[] createPositionFromCoordinates( Element element, boolean swap ) {
    1337
    1338            Position[] points = null;
    1339            // fixing the failure coming from the usage of the xmltools.getAttrib method
    1340            String ts = XMLTools.getAttrValue( element, null, "ts", " " );
    1341
    1342            // not used because javas current decimal seperator will be used
    1343            // String ds = XMLTools.getAttrValue( element, null, "decimal", "." );
    1344            String cs = XMLTools.getAttrValue( element, null, "cs", "," );
    1345
    1346            String value = XMLTools.getStringValue( element ).trim();
    1347
    1348            // first tokenizer, tokens the tuples
    1349            StringTokenizer tuple = new StringTokenizer( value, ts );
    1350            points = new Position[tuple.countTokens()];
    1351            int i = 0;
    1352            while ( tuple.hasMoreTokens() ) {
    1353                String s = tuple.nextToken();
    1354                // second tokenizer, tokens the coordinates
    1355                StringTokenizer coort = new StringTokenizer( s, cs );
    1356                double[] p = new double[coort.countTokens()];
    1357
    1358                int idx;
    1359                for ( int k = 0; k < p.length; k++ ) {
    1360                    idx = swap ? ( k % 2 == 0 ? k + 1 : k - 1 ) : k;
    1361                    s = coort.nextToken();
    1362                    p[idx] = Double.parseDouble( s );
    1363                }
    1364
    1365                points[i++] = GeometryFactory.createPosition( p );
    1366            }
    1367
    1368            return points;
    1369        }
    1370
    1371        /**
    1372         * creates a <tt>Point</tt> from the passed <pos> element containing a GML pos.
    1373         *
    1374         * @param element
    1375         * @return created <tt>Point</tt>
    1376         * @throws InvalidGMLException
    1377         */
    1378        private static Position createPositionFromPos( Element element, boolean swap )
    1379                                throws InvalidGMLException {
    1380
    1381            String tmp = XMLTools.getAttrValue( element, null, "dimension", null );
    1382            int dim = 0;
    1383            if ( tmp != null ) {
    1384                dim = Integer.parseInt( tmp );
    1385            } else {
    1386                tmp = element.getAttribute( "srsDimension" );
    1387                if ( tmp != null && !"".equals( tmp.trim() ) ) {
    1388                    dim = Integer.parseInt( tmp );
    1389                }
    1390            }
    1391            tmp = XMLTools.getStringValue( element );
    1392            double[] vals = StringTools.toArrayDouble( tmp, "\t\n\r\f ," );
    1393            if ( vals != null ) {
    1394                if ( dim != 0 ) {
    1395                    if ( vals.length != dim ) {
    1396                        throw new InvalidGMLException(
    1397                                                       "The dimension of a position must be equal to the number of coordinate values defined in the pos element." );
    1398                    }
    1399                } else {
    1400                    dim = vals.length;
    1401                }
    1402            } else {
    1403                throw new InvalidGMLException( "The given element {" + element.getNamespaceURI() + "}"
    1404                                               + element.getLocalName()
    1405                                               + " does not contain any coordinates, this may not be!" );
    1406            }
    1407
    1408            Position pos = null;
    1409            if ( dim == 3 ) {
    1410                // TODO again I guess no swapping for 3D
    1411                pos = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
    1412            } else {
    1413                pos = GeometryFactory.createPosition( vals[swap ? 1 : 0], vals[swap ? 0 : 1] );
    1414            }
    1415
    1416            return pos;
    1417        }
    1418
    1419        /**
    1420         *
    1421         * @param element
    1422         * @return Position
    1423         * @throws InvalidGMLException
    1424         * @throws XMLParsingException
    1425         */
    1426        private static Position[] createPositionFromPosList( Element element, String srsName )
    1427                                throws InvalidGMLException, XMLParsingException {
    1428
    1429            srsName = findSrsName( element, srsName );
    1430            String srsDimension = XMLTools.getAttrValue( element, null, "srsDimension", null );
    1431            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    1432                XMLFragment doc = new XMLFragment( element );
    1433                LOG.logDebug( doc.getAsPrettyString() );
    1434            }
    1435
    1436            boolean swap = swap( srsName );
    1437
    1438            int dim = 0;
    1439            if ( srsDimension != null ) {
    1440                dim = Integer.parseInt( srsDimension );
    1441            }
    1442
    1443            if ( dim == 0 ) {
    1444                if ( srsName == null ) {
    1445                    dim = 2;
    1446                } else {
    1447                    try {
    1448                        dim = CRSFactory.create( srsName ).getCRS().getDimension();
    1449                    } catch ( UnknownCRSException e ) {
    1450                        LOG.logError( e.getMessage(), e );
    1451                        dim = 2;
    1452                    }
    1453                }
    1454            }
    1455
    1456            String axisLabels = XMLTools.getAttrValue( element, null, "gml:axisAbbrev", null );
    1457            String uomLabels = XMLTools.getAttrValue( element, null, "uomLabels", null );
    1458
    1459            if ( srsName == null ) {
    1460                if ( srsDimension != null ) {
    1461                    String msg = "Attribute srsDimension cannot be defined unless attribute srsName has been defined.";
    1462                    throw new InvalidGMLException( msg );
    1463                }
    1464                if ( axisLabels != null ) {
    1465                    String msg = "Attribute axisLabels cannot be defined unless attribute srsName has been defined.";
    1466                    throw new InvalidGMLException( msg );
    1467                }
    1468            }
    1469            if ( axisLabels == null ) {
    1470                if ( uomLabels != null ) {
    1471                    String msg = "Attribute uomLabels cannot be defined unless attribute axisLabels has been defined.";
    1472                    throw new InvalidGMLException( msg );
    1473                }
    1474            }
    1475            String tmp = XMLTools.getStringValue( element );
    1476            double[] values = StringTools.toArrayDouble( tmp, "\t\n\r\f ," );
    1477            int size = values.length / dim;
    1478            LOG.logDebug( "Number of points = ", size );
    1479            LOG.logDebug( "Size of the original array: ", values.length );
    1480            LOG.logDebug( "Dimension: ", dim );
    1481
    1482            if ( values.length < 4 ) {
    1483                if ( values.length == 2 ) {
    1484                    values = new double[] { values[swap ? 1 : 0], values[swap ? 0 : 1], values[swap ? 1 : 0],
    1485                                           values[swap ? 0 : 1] };
    1486                    size = 2;
    1487                } else {
    1488                    for ( double d : values ) {
    1489                        System.out.println( "value: " + d );
    1490                    }
    1491                    throw new InvalidGMLException( "A point list must have minimum 2 coordinate tuples. Here only '" + size
    1492                                                   + "' are defined." );
    1493                }
    1494            }
    1495            double positions[][] = new double[size][dim];
    1496            int a = 0, b = 0;
    1497            for ( int i = 0; i < values.length; i++ ) {
    1498                if ( b == dim ) {
    1499                    a++;
    1500                    b = 0;
    1501                }
    1502                positions[a][b] = values[i];
    1503                b++;
    1504            }
    1505
    1506            Position[] position = new Position[positions.length];
    1507            for ( int i = 0; i < positions.length; i++ ) {
    1508                double[] vals = positions[i];
    1509                if ( dim == 3 ) {
    1510                    // TODO no swapping for 3D I guess?
    1511                    position[i] = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
    1512                } else {
    1513                    position[i] = GeometryFactory.createPosition( vals[swap ? 1 : 0], vals[swap ? 0 : 1] );
    1514                }
    1515            }
    1516            return position;
    1517        }
    1518
    1519        /**
    1520         * creates an array of <tt>Position</tt>s from the <coordinates> or <pos> Elements located as children under the
    1521         * passed parent element.
    1522         * <p>
    1523         * example:<br>
    1524         *
    1525         * <pre>
    1526         *
    1527         * &lt;gml:Box&gt; &lt;gml:coordinates cs=&quot;,&quot; decimal=&quot;.&quot; ts=&quot; &quot;&gt;0,0
    1528         * 4000,4000&lt;/gml:coordinates&gt; &lt;/gml:Box&gt;
    1529         *
    1530         * </pre>
    1531         *
    1532         * </p>
    1533         *
    1534         * @param parent
    1535         * @param srsName
    1536         * @return the positions of the given element.
    1537         * @throws XMLParsingException
    1538         * @throws InvalidGMLException
    1539         */
    1540        private static Position[] createPositions( Element parent, String srsName )
    1541                                throws XMLParsingException, InvalidGMLException {
    1542
    1543            srsName = findSrsName( parent, srsName );
    1544
    1545            boolean swap = swap( srsName );
    1546
    1547            List<Element> nl = XMLTools.getElements( parent, COORDINATES, nsContext );
    1548            Position[] pos = null;
    1549            if ( nl != null && nl.size() > 0 ) {
    1550                pos = createPositionFromCoordinates( nl.get( 0 ), swap );
    1551            } else {
    1552                nl = XMLTools.getElements( parent, POS, nsContext );
    1553                if ( nl != null && nl.size() > 0 ) {
    1554                    pos = new Position[nl.size()];
    1555                    for ( int i = 0; i < pos.length; i++ ) {
    1556                        pos[i] = createPositionFromPos( nl.get( i ), swap );
    1557                    }
    1558                } else {
    1559                    Element posList = (Element) XMLTools.getRequiredNode( parent, POSLIST, nsContext );
    1560                    if ( posList != null ) {
    1561                        pos = createPositionFromPosList( posList, srsName );
    1562                    }
    1563                }
    1564            }
    1565            return pos;
    1566        }
    1567
    1568        /**
    1569         * Writes the GML representation of the given {@link Geometry} to the given {@link OutputStream}.
    1570         * <p>
    1571         * Currently, the {@link Geometry} realizations are handled:
    1572         * <ul>
    1573         * <li>SurfacePatch
    1574         * <li>LineString
    1575         * <li>Point
    1576         * <li>Curve
    1577         * <li>Surface
    1578         * <li>MultiPoint
    1579         * <li>MultiCurve
    1580         * <li>MultiSurface
    1581         * <li>MultiGeometry
    1582         * </ul>
    1583         *
    1584         * @param geometry
    1585         *            geometry to be exported
    1586         * @param os
    1587         *            target {@link OutputStream}
    1588         * @return a {@link PrintWriter} created from the {@link OutputStream}
    1589         * @throws GeometryException
    1590         */
    1591        public static PrintWriter export( Geometry geometry, OutputStream os )
    1592                                throws GeometryException {
    1593            return export( geometry, new PrintWriter( os ) );
    1594        }
    1595
    1596        /**
    1597         * @param geometry
    1598         * @param os
    1599         * @param id
    1600         * @return a {@link PrintWriter} created from the {@link OutputStream}
    1601         * @throws GeometryException
    1602         */
    1603        public static PrintWriter export( Geometry geometry, OutputStream os, String id )
    1604                                throws GeometryException {
    1605            return export( geometry, new PrintWriter( os ), id );
    1606        }
    1607
    1608        /**
    1609         * Writes the GML representation of the given {@link Geometry} to the given {@link PrintWriter}.
    1610         * <p>
    1611         * Currently, the {@link Geometry} realizations are handled:
    1612         * <ul>
    1613         * <li>SurfacePatch
    1614         * <li>LineString
    1615         * <li>Point
    1616         * <li>Curve
    1617         * <li>Surface
    1618         * <li>MultiPoint
    1619         * <li>MultiCurve
    1620         * <li>MultiSurface
    1621         * <li>MultiGeometry
    1622         * </ul>
    1623         *
    1624         * @param geometry
    1625         *            geometry to be exported
    1626         * @param pw
    1627         *            target {@link PrintWriter}
    1628         * @return same as input {@link PrintWriter}
    1629         * @throws GeometryException
    1630         */
    1631        public static PrintWriter export( Geometry geometry, PrintWriter pw )
    1632                                throws GeometryException {
    1633            return export( geometry, pw, null );
    1634        }
    1635
    1636        /**
    1637         * @param geometry
    1638         * @param pw
    1639         * @param id
    1640         * @return same as input {@link PrintWriter}
    1641         * @throws GeometryException
    1642         */
    1643        public static PrintWriter export( Geometry geometry, PrintWriter pw, String id )
    1644                                throws GeometryException {
    1645            if ( geometry instanceof SurfacePatch ) {
    1646                geometry = new SurfaceImpl( (SurfacePatch) geometry );
    1647            } else if ( geometry instanceof LineString ) {
    1648                geometry = new CurveImpl( (LineString) geometry );
    1649            }
    1650            if ( id == null ) {
    1651                id = "";
    1652            } else {
    1653                id = "gml:id='" + id + "'";
    1654            }
    1655
    1656            if ( geometry instanceof Point ) {
    1657                exportPoint( (Point) geometry, pw, id );
    1658            } else if ( geometry instanceof Curve ) {
    1659                exportCurve( (Curve) geometry, pw, id );
    1660            } else if ( geometry instanceof Surface ) {
    1661                exportSurface( (Surface) geometry, pw, id );
    1662            } else if ( geometry instanceof MultiPoint ) {
    1663                exportMultiPoint( (MultiPoint) geometry, pw, id );
    1664            } else if ( geometry instanceof MultiCurve ) {
    1665                exportMultiCurve( (MultiCurve) geometry, pw, id );
    1666            } else if ( geometry instanceof MultiSurface ) {
    1667                exportMultiSurface( (MultiSurface) geometry, pw, id );
    1668            } else if ( geometry instanceof MultiGeometry ) {
    1669                exportMultiGeometry( (MultiGeometry) geometry, pw, id );
    1670            }
    1671            pw.flush();
    1672            return pw;
    1673        }
    1674
    1675        /**
    1676         * Creates a GML representation from the passed <code>Geometry</code>.
    1677         *
    1678         * @param geometry
    1679         * @return a string buffer containing the XML
    1680         * @throws GeometryException
    1681         */
    1682        public static StringBuffer export( Geometry geometry )
    1683                                throws GeometryException {
    1684            return export( geometry, (String) null );
    1685        }
    1686
    1687        /**
    1688         * @param geometry
    1689         * @param id
    1690         * @return a string buffer containing the XML
    1691         * @throws GeometryException
    1692         */
    1693        public static StringBuffer export( Geometry geometry, String id )
    1694                                throws GeometryException {
    1695            ByteArrayOutputStream bos = new ByteArrayOutputStream( 5000 );
    1696            export( geometry, bos, id );
    1697            return new StringBuffer( new String( bos.toByteArray() ) );
    1698        }
    1699
    1700        /**
    1701         * creates a GML representation from the passed <tt>Envelope</tt>. This method is required because in ISO 19107
    1702         * Envelops are no geometries.
    1703         *
    1704         * @param envelope
    1705         * @return the stringbuffer filled with the envelope.
    1706         */
    1707        public static StringBuffer exportAsBox( Envelope envelope ) {
    1708
    1709            StringBuffer sb = new StringBuffer( "<gml:Box xmlns:gml='http://www.opengis.net/gml'" );
    1710
    1711            if ( envelope.getCoordinateSystem() != null ) {
    1712                sb.append( " srsName='" ).append( envelope.getCoordinateSystem().getIdentifier() ).append( "'" );
    1713            }
    1714            sb.append( ">" );
    1715
    1716            boolean swap = swap( envelope );
    1717
    1718            sb.append( "<gml:coordinates cs=\",\" decimal=\".\" ts=\" \">" );
    1719            sb.append( swap ? envelope.getMin().getY() : envelope.getMin().getX() ).append( ',' );
    1720            sb.append( swap ? envelope.getMin().getX() : envelope.getMin().getY() );
    1721            int dim = envelope.getMax().getCoordinateDimension();
    1722            if ( dim == 3 ) {
    1723                sb.append( ',' ).append( envelope.getMin().getZ() );
    1724            }
    1725            sb.append( ' ' ).append( swap ? envelope.getMax().getY() : envelope.getMax().getX() );
    1726            sb.append( ',' ).append( swap ? envelope.getMax().getX() : envelope.getMax().getY() );
    1727            if ( dim == 3 ) {
    1728                sb.append( ',' ).append( envelope.getMax().getZ() );
    1729            }
    1730            sb.append( "</gml:coordinates></gml:Box>" );
    1731
    1732            return sb;
    1733        }
    1734
    1735        /**
    1736         * creates a GML representation from the passed <tt>Envelope</tt>. This method is required because in ISO 19107
    1737         * Envelops are no geometries.
    1738         *
    1739         * @param envelope
    1740         * @return the String representation of the given envelope
    1741         */
    1742        public static StringBuffer exportAsEnvelope( Envelope envelope ) {
    1743
    1744            StringBuffer sb = new StringBuffer( "<gml:Envelope " );
    1745            if ( envelope.getCoordinateSystem() != null ) {
    1746                sb.append( "srsName='" ).append( envelope.getCoordinateSystem().getIdentifier() ).append( "' " );
    1747            }
    1748
    1749            boolean swap = swap( envelope );
    1750
    1751            sb.append( "xmlns:gml='http://www.opengis.net/gml'>" );
    1752            sb.append( "<gml:coordinates cs=\",\" decimal=\".\" ts=\" \">" );
    1753            sb.append( swap ? envelope.getMin().getY() : envelope.getMin().getX() ).append( ',' );
    1754            sb.append( swap ? envelope.getMin().getX() : envelope.getMin().getY() );
    1755            int dim = envelope.getMax().getCoordinateDimension();
    1756            if ( dim == 3 ) {
    1757                sb.append( ',' ).append( envelope.getMin().getZ() );
    1758            }
    1759            sb.append( ' ' ).append( swap ? envelope.getMax().getY() : envelope.getMax().getX() );
    1760            sb.append( ',' ).append( swap ? envelope.getMax().getX() : envelope.getMax().getY() );
    1761            if ( dim == 3 ) {
    1762                sb.append( ',' ).append( envelope.getMax().getZ() );
    1763            }
    1764            sb.append( "</gml:coordinates></gml:Envelope>" );
    1765
    1766            return sb;
    1767        }
    1768
    1769        /**
    1770         * creates a GML expression of a point geometry
    1771         *
    1772         * @param point
    1773         *            point geometry
    1774         *
    1775         */
    1776        private static void exportPoint( Point point, PrintWriter pw, String id ) {
    1777
    1778            String crs = null;
    1779            if ( point.getCoordinateSystem() != null ) {
    1780                crs = point.getCoordinateSystem().getIdentifier();
    1781            }
    1782
    1783            boolean swap = swap( point );
    1784
    1785            String srs = null;
    1786            if ( crs != null ) {
    1787                srs = "<gml:Point srsName=\"" + crs + "\" " + id + ">";
    1788            } else {
    1789                srs = "<gml:Point " + id + ">";
    1790            }
    1791            pw.println( srs );
    1792            pw.print( appendPos( point.getPosition(), point.getCoordinateSystem(), swap ) );
    1793            pw.print( "</gml:Point>" );
    1794
    1795        }
    1796
    1797        /**
    1798         * creates a GML expression of a curve geometry
    1799         *
    1800         * @param o
    1801         *            curve geometry
    1802         *
    1803         *
    1804         * @throws GeometryException
    1805         */
    1806        private static void exportCurve( Curve o, PrintWriter pw, String id )
    1807                                throws GeometryException {
    1808
    1809            String crs = null;
    1810            if ( o.getCoordinateSystem() != null ) {
    1811                crs = o.getCoordinateSystem().getIdentifier();
    1812            }
    1813
    1814            boolean swap = swap( o );
    1815
    1816            String srs = null;
    1817            if ( crs != null ) {
    1818                srs = "<gml:Curve srsName=\"" + crs + "\" " + id + ">";
    1819            } else {
    1820                srs = "<gml:Curve " + id + ">";
    1821            }
    1822            pw.println( srs );
    1823            pw.println( "<gml:segments>" );
    1824
    1825            int curveSegments = o.getNumberOfCurveSegments();
    1826            for ( int i = 0; i < curveSegments; i++ ) {
    1827                pw.print( "<gml:LineStringSegment>" );
    1828                CurveSegment segment = o.getCurveSegmentAt( i );
    1829                Position[] p = segment.getAsLineString().getPositions();
    1830                pw.print( appendPosList( p, o.getCoordinateDimension(), swap ) );
    1831                pw.println( "</gml:LineStringSegment>" );
    1832            }
    1833            pw.println( "</gml:segments>" );
    1834            pw.print( "</gml:Curve>" );
    1835
    1836        }
    1837
    1838        private static StringBuilder appendPosList( Position[] p, int coordinateDimension, boolean swap ) {
    1839            StringBuilder sb = new StringBuilder();
    1840            if ( p != null && p.length > 0 ) {
    1841                sb.append( "<gml:posList" );
    1842                if ( coordinateDimension > 0 ) {
    1843                    sb.append( " srsDimension='" ).append( coordinateDimension );
    1844                    sb.append( "' count='" ).append( p.length ).append( "'" );
    1845                }
    1846                sb.append( ">" );
    1847                for ( int i = 0; i < p.length; ++i ) {
    1848                    sb.append( swap ? ( p[i].getY() + " " + p[i].getX() ) : ( p[i].getX() + " " + p[i].getY() ) );
    1849                    if ( coordinateDimension == 3 ) {
    1850                        sb.append( " " ).append( p[i].getZ() );
    1851                    }
    1852                    if ( i < p.length - 1 ) {
    1853                        sb.append( " " );
    1854                    }
    1855                }
    1856                sb.append( "</gml:posList>" );
    1857            }
    1858            return sb;
    1859        }
    1860
    1861        private static StringBuilder appendPos( Position pos, CoordinateSystem crs, boolean swap ) {
    1862            StringBuilder sb = new StringBuilder();
    1863            if ( pos != null ) {
    1864                sb.append( "<gml:pos" );
    1865                int dimension = pos.getCoordinateDimension();
    1866                if ( dimension > 0 ) {
    1867                    if ( crs != null ) {
    1868                        int tmp = crs.getDimension();
    1869                        if ( dimension != tmp ) {
    1870                            sb.append( " srsDimension='" ).append( dimension ).append( "'" );
    1871                            sb.append( " srsName='" ).append( crs.getIdentifier() ).append( "'" );
    1872                        }
    1873                    }
    1874                }
    1875                sb.append( ">" );
    1876                sb.append( swap ? ( pos.getY() + " " + pos.getX() ) : ( pos.getX() + " " + pos.getY() ) );
    1877                if ( dimension == 3 ) {
    1878                    sb.append( " " ).append( pos.getZ() );
    1879                }
    1880                sb.append( "</gml:pos>" );
    1881            }
    1882            return sb;
    1883        }
    1884
    1885        /**
    1886         * @throws GeometryException
    1887         */
    1888        private static void exportSurface( Surface surface, PrintWriter pw, String id )
    1889                                throws GeometryException {
    1890
    1891            String crs = null;
    1892            if ( surface.getCoordinateSystem() != null ) {
    1893                crs = surface.getCoordinateSystem().getIdentifier().replace( ' ', ':' );
    1894            }
    1895
    1896            boolean swap = swap( surface );
    1897
    1898            String srs = null;
    1899            if ( crs != null ) {
    1900                srs = "<gml:Surface srsName='" + crs + "\' " + id + ">";
    1901            } else {
    1902                srs = "<gml:Surface " + id + ">";
    1903            }
    1904            pw.println( srs );
    1905            int patches = surface.getNumberOfSurfacePatches();
    1906            pw.println( "<gml:patches>" );
    1907            for ( int i = 0; i < patches; i++ ) {
    1908                pw.println( "<gml:PolygonPatch>" );
    1909                SurfacePatch patch = surface.getSurfacePatchAt( i );
    1910                printExteriorRing( surface, pw, patch, swap );
    1911                printInteriorRing( surface, pw, patch, swap );
    1912                pw.println( "</gml:PolygonPatch>" );
    1913            }
    1914            pw.println( "</gml:patches>" );
    1915            pw.print( "</gml:Surface>" );
    1916
    1917        }
    1918
    1919        /**
    1920         * @param surface
    1921         * @param pw
    1922         * @param patch
    1923         */
    1924        private static void printInteriorRing( Surface surface, PrintWriter pw, SurfacePatch patch, boolean swap ) {
    1925            // interior rings
    1926            Position[][] ip = patch.getInteriorRings();
    1927            if ( ip != null ) {
    1928                for ( int j = 0; j < ip.length; j++ ) {
    1929                    pw.println( "<gml:interior>" );
    1930                    pw.println( "<gml:LinearRing>" );
    1931                    if ( surface.getCoordinateSystem() != null ) {
    1932                        pw.print( appendPosList( ip[j], surface.getCoordinateDimension(), swap ) );
    1933                    } else {
    1934                        pw.print( appendPosList( ip[j], 0, swap ) );
    1935                    }
    1936                    pw.println( "</gml:LinearRing>" );
    1937                    pw.println( "</gml:interior>" );
    1938                }
    1939            }
    1940        }
    1941
    1942        /**
    1943         * @param surface
    1944         * @param pw
    1945         * @param patch
    1946         */
    1947        private static void printExteriorRing( Surface surface, PrintWriter pw, SurfacePatch patch, boolean swap ) {
    1948            // exterior ring
    1949            pw.print( "<gml:exterior>" );
    1950            pw.print( "<gml:LinearRing>" );
    1951            if ( surface.getCoordinateSystem() != null ) {
    1952                pw.print( appendPosList( patch.getExteriorRing(), surface.getCoordinateDimension(), swap ) );
    1953            } else {
    1954                pw.print( appendPosList( patch.getExteriorRing(), 0, swap ) );
    1955            }
    1956            pw.print( "</gml:LinearRing>" );
    1957            pw.print( "</gml:exterior>" );
    1958        }
    1959
    1960        /**
    1961         * @param mp
    1962         */
    1963        private static void exportMultiPoint( MultiPoint mp, PrintWriter pw, String id ) {
    1964
    1965            String crs = null;
    1966            if ( mp.getCoordinateSystem() != null ) {
    1967                crs = mp.getCoordinateSystem().getIdentifier();
    1968            }
    1969
    1970            boolean swap = swap( mp );
    1971
    1972            String srs = null;
    1973            if ( crs != null ) {
    1974                srs = "<gml:MultiPoint srsName=\"" + crs + "\" " + id + ">";
    1975            } else {
    1976                srs = "<gml:MultiPoint " + id + ">";
    1977            }
    1978            pw.println( srs );
    1979            pw.println( "<gml:pointMembers>" );
    1980            for ( int i = 0; i < mp.getSize(); i++ ) {
    1981                pw.println( "<gml:Point>" );
    1982                pw.print( appendPos( mp.getPointAt( i ).getPosition(), mp.getCoordinateSystem(), swap ) );
    1983                pw.println( "</gml:Point>" );
    1984            }
    1985            pw.println( "</gml:pointMembers>" );
    1986            pw.print( "</gml:MultiPoint>" );
    1987
    1988        }
    1989
    1990        /**
    1991         * @param multiCurve
    1992         * @throws GeometryException
    1993         */
    1994        private static void exportMultiCurve( MultiCurve multiCurve, PrintWriter pw, String id )
    1995                                throws GeometryException {
    1996
    1997            String crs = null;
    1998            if ( multiCurve.getCoordinateSystem() != null ) {
    1999                crs = multiCurve.getCoordinateSystem().getIdentifier().replace( ' ', ':' );
    2000            }
    2001
    2002            boolean swap = swap( multiCurve );
    2003
    2004            String srs = null;
    2005            if ( crs != null ) {
    2006                srs = "<gml:MultiCurve srsName=\"" + crs + "\" " + id + ">";
    2007            } else {
    2008                srs = "<gml:MultiCurve " + id + ">";
    2009            }
    2010            pw.println( srs );
    2011
    2012            Curve[] curves = multiCurve.getAllCurves();
    2013            pw.println( "<gml:curveMembers>" );
    2014            for ( int i = 0; i < curves.length; i++ ) {
    2015                Curve curve = curves[i];
    2016                pw.println( "<gml:Curve>" );
    2017                pw.println( "<gml:segments>" );
    2018                int numberCurveSegments = curve.getNumberOfCurveSegments();
    2019                for ( int j = 0; j < numberCurveSegments; j++ ) {
    2020                    pw.println( "<gml:LineStringSegment>" );
    2021                    CurveSegment curveSegment = curve.getCurveSegmentAt( j );
    2022                    Position[] p = curveSegment.getAsLineString().getPositions();
    2023                    pw.print( appendPosList( p, curve.getCoordinateDimension(), swap ) );
    2024                    pw.println( "</gml:LineStringSegment>" );
    2025                }
    2026                pw.println( "</gml:segments>" );
    2027                pw.println( "</gml:Curve>" );
    2028            }
    2029            pw.println( "</gml:curveMembers>" );
    2030            pw.print( "</gml:MultiCurve>" );
    2031        }
    2032
    2033        /**
    2034         * @param multiSurface
    2035         * @throws GeometryException
    2036         */
    2037        private static void exportMultiSurface( MultiSurface multiSurface, PrintWriter pw, String id )
    2038                                throws GeometryException {
    2039
    2040            String crs = null;
    2041            if ( multiSurface.getCoordinateSystem() != null ) {
    2042                crs = multiSurface.getCoordinateSystem().getIdentifier().replace( ' ', ':' );
    2043            }
    2044            String srs = null;
    2045            if ( crs != null ) {
    2046                srs = "<gml:MultiSurface srsName=\"" + crs + "\" " + id + ">";
    2047            } else {
    2048                srs = "<gml:MultiSurface " + id + ">";
    2049            }
    2050            pw.println( srs );
    2051
    2052            Surface[] surfaces = multiSurface.getAllSurfaces();
    2053
    2054            pw.println( "<gml:surfaceMembers>" );
    2055            for ( int i = 0; i < surfaces.length; i++ ) {
    2056                Surface surface = surfaces[i];
    2057                exportSurface( surface, pw, "" );
    2058            }
    2059            pw.println( "</gml:surfaceMembers>" );
    2060            // substitution as requested in issue
    2061            // http://wald.intevation.org/tracker/index.php?func=detail&aid=477&group_id=27&atid=212
    2062            // can be removed if it was inserted correctly
    2063            // pw.println( "<gml:surfaceMembers>" );
    2064            // for ( int i = 0; i < surfaces.length; i++ ) {
    2065            // Surface surface = surfaces[i];
    2066            // pw.println( "<gml:Surface>" );
    2067            // pw.println( "<gml:patches>" );
    2068            // pw.println( "<gml:Polygon>" );
    2069            // int numberSurfaces = surface.getNumberOfSurfacePatches();
    2070            // for ( int j = 0; j < numberSurfaces; j++ ) {
    2071            // SurfacePatch surfacePatch = surface.getSurfacePatchAt( j );
    2072            // printExteriorRing( surface, pw, surfacePatch );
    2073            // printInteriorRing( surface, pw, surfacePatch );
    2074            // }
    2075            // pw.println( "</gml:Polygon>" );
    2076            // pw.println( "</gml:patches>" );
    2077            // pw.println( "</gml:Surface>" );
    2078            // }
    2079            // pw.println( "</gml:surfaceMembers>" );
    2080            pw.print( "</gml:MultiSurface>" );
    2081
    2082        }
    2083
    2084        /**
    2085         * Exports the given {@link MultiGeometry} as a <code>gml:MultiGeometry</code> element.
    2086         *
    2087         * @param multiGeometry
    2088         * @param pw
    2089         * @throws GeometryException
    2090         */
    2091        public static void exportMultiGeometry( MultiGeometry multiGeometry, PrintWriter pw )
    2092                                throws GeometryException {
    2093            exportMultiGeometry( multiGeometry, pw, null );
    2094        }
    2095
    2096        /**
    2097         * Exports the given {@link MultiGeometry} as a <code>gml:MultiGeometry</code> element.
    2098         *
    2099         * @param multiGeometry
    2100         * @param pw
    2101         * @param id
    2102         * @throws GeometryException
    2103         */
    2104        public static void exportMultiGeometry( MultiGeometry multiGeometry, PrintWriter pw, String id )
    2105                                throws GeometryException {
    2106
    2107            String crs = null;
    2108            if ( multiGeometry.getCoordinateSystem() != null ) {
    2109                crs = multiGeometry.getCoordinateSystem().getIdentifier().replace( ' ', ':' );
    2110            }
    2111
    2112            // opening tag
    2113            if ( crs != null ) {
    2114                pw.print( "<gml:MultiGeometry srsName=\"" + crs + "\" " + id + ">" );
    2115            } else {
    2116                pw.print( "<gml:MultiGeometry " + id + ">" );
    2117            }
    2118
    2119            Geometry[] memberGeometries = multiGeometry.getAll();
    2120            if ( memberGeometries.length > 0 ) {
    2121                pw.print( "<gml:geometryMembers>" );
    2122                for ( Geometry geometry : memberGeometries ) {
    2123                    export( geometry, pw );
    2124                }
    2125                pw.print( "</gml:geometryMembers>" );
    2126            }
    2127            pw.print( "</gml:MultiGeometry>" );
    2128
    2129        }
    2130
    2131        /**
    2132         * Converts the string representation of a GML geometry object to a corresponding <code>Geometry</code>. Notice that
    2133         * GML Boxes will be converted to Surfaces because in ISO 19107 Envelopes are no geometries.
    2134         *
    2135         * @param gml
    2136         * @return corresponding geometry object
    2137         * @throws GeometryException
    2138         * @throws XMLParsingException
    2139         * @deprecated this method cannot provide default SRS information, please use {@link #wrap(String,String)} instead
    2140         */
    2141        @Deprecated
    2142        public static Geometry wrap( String gml )
    2143                                throws GeometryException, XMLParsingException {
    2144            return wrap( gml, null );
    2145        }
    2146
    2147        /**
    2148         * Converts a GML geometry object to a corresponding <tt>Geometry</tt>. Notice that GML Boxes will be converted to
    2149         * Surfaces because in ISO 19107 Envelops are no geometries.
    2150         * <p>
    2151         * Currently, the following conversions are supported:
    2152         * <ul>
    2153         * <li>GML Point -> Point
    2154         * <li>GML MultiPoint -> MultiPoint
    2155         * <li>GML LineString -> Curve
    2156         * <li>GML MultiLineString -> MultiCurve
    2157         * <li>GML Polygon -> Surface
    2158         * <li>GML MultiPolygon -> MultiSurface
    2159         * <li>GML Box -> Surface
    2160         * <li>GML Curve -> Curve
    2161         * <li>GML Surface -> Surface
    2162         * <li>GML MultiCurve -> MultiCurve
    2163         * <li>GML MultiSurface -> MultiSurface
    2164         * </ul>
    2165         * <p>
    2166         *
    2167         * @param gml
    2168         * @return the corresponding <tt>Geometry</tt>
    2169         * @throws GeometryException
    2170         *             if type unsupported or conversion failed
    2171         * @deprecated this method cannot provide default SRS information, please use {@link #wrap(Element,String)} instead
    2172         */
    2173        @Deprecated
    2174        public static Geometry wrap( Element gml )
    2175                                throws GeometryException {
    2176            return wrap( gml, null );
    2177        }
    2178
    2179        /**
    2180         * returns a Envelope created from Box element
    2181         *
    2182         * @param element
    2183         *            <boundedBy>
    2184         *
    2185         * @return instance of <tt>Envelope</tt>
    2186         *
    2187         * @throws XMLParsingException
    2188         * @throws InvalidGMLException
    2189         * @throws UnknownCRSException
    2190         * @deprecated this method cannot provide default SRS information, please use {@link #wrapBox(Element,String)}
    2191         *             instead
    2192         */
    2193        @Deprecated
    2194        public static Envelope wrapBox( Element element )
    2195                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
    2196            return wrapBox( element, null );
    2197        }
    2198
    2199        // helpers that determine whether to swap x/y coordinates
    2200        private static boolean swap( Geometry geom ) {
    2201            return ( geom.getCoordinateSystem() == null || geom.getCoordinateSystem().equals( EPSG4326 ) )
    2202                   && getSwitchAxes();
    2203        }
    2204
    2205        /**
    2206         * @param env
    2207         * @return true, if configuration and environment say yes to swapping
    2208         */
    2209        public static boolean swap( Envelope env ) {
    2210            return ( env.getCoordinateSystem() == null || env.getCoordinateSystem().equals( EPSG4326 ) ) && getSwitchAxes();
    2211        }
    2212
    2213        private static boolean swap( String srsName ) {
    2214            // TODO use real crs instance just to verify the name? Too expensive?
    2215            return ( srsName == null || srsName.contains( "4326" ) ) && getSwitchAxes();
    2216        }
    2217
    2218    }