001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/GMLGeometryAdapter.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: klaus.greve@uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.model.spatialschema;
045    
046    import java.io.ByteArrayOutputStream;
047    import java.io.OutputStream;
048    import java.io.PrintWriter;
049    import java.io.StringReader;
050    import java.rmi.RemoteException;
051    import java.util.ArrayList;
052    import java.util.HashMap;
053    import java.util.List;
054    import java.util.Map;
055    import java.util.StringTokenizer;
056    
057    import org.deegree.framework.log.ILogger;
058    import org.deegree.framework.log.LoggerFactory;
059    import org.deegree.framework.util.StringTools;
060    import org.deegree.framework.xml.ElementList;
061    import org.deegree.framework.xml.NamespaceContext;
062    import org.deegree.framework.xml.XMLFragment;
063    import org.deegree.framework.xml.XMLParsingException;
064    import org.deegree.framework.xml.XMLTools;
065    import org.deegree.i18n.Messages;
066    import org.deegree.model.crs.CRSFactory;
067    import org.deegree.model.crs.CoordinateSystem;
068    import org.deegree.model.crs.UnknownCRSException;
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
077     * problems results from the fact that an envelope isn't a geometry according to ISO 19107 (where
078     * the deegree geometry model is based on) but 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
080     * representing the envelops shape. To export an <tt>Envelope</tt> to a GML box/envelope two
081     * specialized export methods are available.<BR>
082     * The export method(s) doesn't return a DOM element as one may expect but a <tt>StringBuffer</tt>.
083     * This is done because the transformation from deegree geometries to GML mainly is required when a
084     * GML representation of a geometry shall be serialized to a file or to a network connection. For
085     * both cases the string representation is required and it is simply faster to create the string
086     * directly instead of first creating a DOM tree that after this must be serialized to a string.<BR>
087     * In future version geometries will be serialized to a stream.
088     * 
089     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
090     * @author last edited by: $Author: rbezema $
091     * 
092     * @version $Revision: 11344 $, $Date: 2008-04-22 13:38:48 +0200 (Di, 22 Apr 2008) $
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        /**
111         * Converts the given string representation of a GML geometry object to a corresponding
112         * <code>Geometry</code>. Notice that GML Boxes will be converted to Surfaces because in ISO
113         * 19107 Envelopes are no geometries.
114         * 
115         * @param gml
116         * @param srsName
117         *            default SRS for the geometry (may be overwritten in geometry elements)
118         * @return corresponding geometry object
119         * @throws GeometryException
120         * @throws XMLParsingException
121         */
122        public static Geometry wrap( String gml, String srsName )
123                                throws GeometryException, XMLParsingException {
124            StringReader sr = new StringReader( gml );
125            Document doc = null;
126            try {
127                doc = XMLTools.parse( sr );
128            } catch ( Exception e ) {
129                LOG.logError( "could not parse: '" + gml + "' as GML/XML", e );
130                throw new XMLParsingException( "could not parse: '" + gml + "' as GML/XML: " + e.getMessage() );
131            }
132            return wrap( doc.getDocumentElement(), srsName );
133        }
134    
135        /**
136         * Converts the given DOM representation of a GML geometry to a corresponding
137         * <code>Geometry</code>. Notice that GML Boxes will be converted to Surfaces because in ISO
138         * 19107 Envelopes are no geometries.
139         * <p>
140         * Currently, the following conversions are supported:
141         * <ul>
142         * <li>GML Point -> Point
143         * <li>GML MultiPoint -> MultiPoint
144         * <li>GML LineString -> Curve
145         * <li>GML MultiLineString -> MultiCurve
146         * <li>GML Polygon -> Surface
147         * <li>GML MultiPolygon -> MultiSurface
148         * <li>GML Box -> Surface
149         * <li>GML Curve -> Curve
150         * <li>GML Surface -> Surface
151         * <li>GML MultiCurve -> MultiCurve
152         * <li>GML MultiSurface -> MultiSurface
153         * </ul>
154         * <p>
155         * 
156         * @param element
157         * @param srsName
158         *            default SRS for the geometry
159         * @return corresponding <code>Geometry</code> instance
160         * @throws GeometryException
161         *             if type unsupported or conversion failed
162         */
163        public static Geometry wrap( Element element, String srsName )
164                                throws GeometryException {
165    
166            Geometry geometry = null;
167            try {
168                String name = element.getLocalName();
169                if ( ( name.equals( "Point" ) ) || ( name.equals( "Center" ) ) ) {
170                    geometry = wrapPoint( element, srsName );
171                } else if ( name.equals( "LineString" ) ) {
172                    geometry = wrapLineString( element, srsName );
173                } else if ( name.equals( "Polygon" ) ) {
174                    geometry = wrapPolygon( element, srsName );
175                } else if ( name.equals( "MultiPoint" ) ) {
176                    geometry = wrapMultiPoint( element, srsName );
177                } else if ( name.equals( "MultiLineString" ) ) {
178                    geometry = wrapMultiLineString( element, srsName );
179                } else if ( name.equals( "MultiPolygon" ) ) {
180                    geometry = wrapMultiPolygon( element, srsName );
181                } else if ( name.equals( "Box" ) || name.equals( "Envelope" ) ) {
182                    geometry = wrapBoxAsSurface( element, srsName );
183                } else if ( name.equals( "Curve" ) ) {
184                    geometry = wrapCurveAsCurve( element, srsName );
185                } else if ( name.equals( "Surface" ) ) {
186                    geometry = wrapSurfaceAsSurface( element, srsName );
187                } else if ( name.equals( "MultiCurve" ) ) {
188                    geometry = wrapMultiCurveAsMultiCurve( element, srsName );
189                } else if ( name.equals( "MultiSurface" ) ) {
190                    geometry = wrapMultiSurfaceAsMultiSurface( element, srsName );
191                } else if ( name.equals( "CompositeSurface" ) ) {
192                    geometry = wrapCompositeSurface( element, srsName );
193                } else {
194                    new GeometryException( "Not a supported geometry type: " + name );
195                }
196            } catch ( Exception e ) {
197                LOG.logError( e.getMessage(), e );
198                throw new GeometryException( StringTools.stackTraceToString( e ) );
199            }
200            return geometry;
201        }
202    
203        /**
204         * Returns an instance of {@link Envelope} created from the passed <code>gml:Box</code> or
205         * <code>gml:Envelope</code> element.
206         * 
207         * @param element
208         *            <code>gml:Box</code> or <code>gml:Envelope</code> element
209         * @param srsName
210         *            default SRS for the geometry
211         * @return instance of <code>Envelope</code>
212         * @throws XMLParsingException
213         * @throws InvalidGMLException
214         * @throws UnknownCRSException
215         */
216        public static Envelope wrapBox( Element element, String srsName )
217                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
218            srsName = findSrsName( element, srsName );
219            CoordinateSystem crs = null;
220            if ( srsName != null ) {
221                crs = getCRS( srsName );
222            }
223            Position[] bb = null;
224            List nl = XMLTools.getNodes( element, COORD, nsContext );
225            if ( nl != null && nl.size() > 0 ) {
226                bb = new Position[2];
227                bb[0] = createPositionFromCoord( (Element) nl.get( 0 ) );
228                bb[1] = createPositionFromCoord( (Element) nl.get( 1 ) );
229            } else {
230                nl = XMLTools.getNodes( element, COORDINATES, nsContext );
231                if ( nl != null && nl.size() > 0 ) {
232                    bb = createPositionFromCoordinates( (Element) nl.get( 0 ) );
233                } else {
234                    nl = XMLTools.getNodes( element, POS, nsContext );
235                    if ( nl != null && nl.size() > 0 ) {
236                        bb = new Position[2];
237                        bb[0] = createPositionFromPos( (Element) nl.get( 0 ) );
238                        bb[1] = createPositionFromPos( (Element) nl.get( 1 ) );
239                    } else {
240                        Element lowerCorner = (Element) XMLTools.getRequiredNode( element, "gml:lowerCorner", nsContext );
241                        Element upperCorner = (Element) XMLTools.getRequiredNode( element, "gml:upperCorner", nsContext );
242                        bb = new Position[2];
243                        bb[0] = createPositionFromCorner( lowerCorner );
244                        bb[1] = createPositionFromCorner( upperCorner );
245                    }
246                }
247            }
248            Envelope box = GeometryFactory.createEnvelope( bb[0], bb[1], crs );
249            return box;
250        }
251    
252        /**
253         * Returns an instance of {@link Curve} created from the passed <code>gml:Curve</code>
254         * element.
255         * 
256         * @param element
257         *            <code>gml:Curve</code> element
258         * @param srsName
259         *            default SRS for the geometry
260         * @return corresponding Curve instance
261         * @throws XMLParsingException
262         * @throws GeometryException
263         * @throws UnknownCRSException
264         */
265        protected static Curve wrapCurveAsCurve( Element element, String srsName )
266                                throws XMLParsingException, GeometryException, UnknownCRSException {
267    
268            srsName = findSrsName( element, srsName );
269            CoordinateSystem crs = null;
270            if ( srsName != null ) {
271                crs = getCRS( srsName );
272            }
273    
274            Element segment = (Element) XMLTools.getRequiredNode( element, "gml:segments", nsContext );
275            CurveSegment[] segments = parseCurveSegments( crs, segment );
276    
277            return GeometryFactory.createCurve( segments, crs );
278        }
279    
280        /**
281         * parses CurveSegments
282         * 
283         * @param crs
284         * @param segment
285         * @return CurveSegments
286         * @throws XMLParsingException
287         * @throws GeometryException
288         */
289        private static CurveSegment[] parseCurveSegments( CoordinateSystem crs, Element segment )
290                                throws XMLParsingException, GeometryException {
291            List<Node> list = XMLTools.getNodes( segment, "child::*", nsContext );
292    
293            CurveSegment[] segments = new CurveSegment[list.size()];
294            for ( int i = 0; i < list.size(); i++ ) {
295                if ( list.get( i ).getLocalName().equals( "LineStringSegment" ) ) {
296                    segments[i] = parseLineStringSegment( (Element) list.get( i ), crs );
297                } else if ( list.get( i ).getLocalName().equals( "Arc" ) ) {
298                    segments[i] = parseArc( (Element) list.get( i ), crs );
299                } else {
300                    throw new GeometryException( "not supported type for a CurveSegment: " + list.get( i ).getLocalName() );
301                }
302            }
303            return segments;
304        }
305    
306        /**
307         * parses an Arc
308         * 
309         * @param element
310         * @param crs
311         * @return
312         * @throws GeometryException
313         */
314        private static CurveSegment parseArc( Element element, CoordinateSystem crs )
315                                throws GeometryException {
316            CurveSegment segment = null;
317            try {
318                Position[] pos = createPositions( element, null );
319                segment = GeometryFactory.createCurveSegment( pos, crs );
320            } catch ( Exception e ) {
321                throw new GeometryException( "Error creating segments for the element Arc." );
322            }
323            return segment;
324        }
325    
326        /**
327         * parses a LineStringSegment (linear interpolated CurveSegment)
328         * 
329         * @param element
330         * @param crs
331         * @return
332         * @throws GeometryException
333         */
334        private static CurveSegment parseLineStringSegment( Element element, CoordinateSystem crs )
335                                throws GeometryException {
336            CurveSegment segment = null;
337            try {
338                Position[] pos = createPositions( element, null );
339                segment = GeometryFactory.createCurveSegment( pos, crs );
340            } catch ( Exception e ) {
341                throw new GeometryException( "Error creating segments for the element LineStringSegment." );
342            }
343            return segment;
344    
345        }
346    
347        /**
348         * Returns an instance of {@link MultiCurve} created from the passed <code>gml:MultiCurve</code>
349         * element.
350         * 
351         * @param element
352         *            <code>gml:MultiCurve</code> element
353         * @param srsName
354         *            default SRS for the geometry
355         * @return <code>MultiCurve</code> instance
356         * @throws XMLParsingException
357         * @throws GeometryException
358         * @throws UnknownCRSException
359         * @throws InvalidGMLException
360         */
361        protected static MultiCurve wrapMultiCurveAsMultiCurve( Element element, String srsName )
362                                throws XMLParsingException, GeometryException, UnknownCRSException, InvalidGMLException {
363    
364            srsName = findSrsName( element, srsName );
365            CoordinateSystem crs = null;
366            if ( srsName != null ) {
367                crs = getCRS( srsName );
368            }
369    
370            MultiCurve multiCurve = null;
371            try {
372                // gml:curveMember
373                List listCurveMember = XMLTools.getNodes( element, "gml:curveMember", nsContext );
374                List<Curve> curveList = new ArrayList<Curve>();
375                if ( listCurveMember.size() > 0 ) {
376    
377                    for ( int i = 0; i < listCurveMember.size(); i++ ) {
378                        Element curveMember = (Element) listCurveMember.get( i );
379                        Element curve = (Element) XMLTools.getNode( curveMember, "gml:Curve", nsContext );
380                        if ( curve != null ) {
381                            curveList.add( wrapCurveAsCurve( curve, srsName ) );
382                        } else {
383                            curve = (Element) XMLTools.getRequiredNode( curveMember, "gml:LineString", nsContext );
384                            curveList.add( wrapLineString( curve, srsName ) );
385                        }
386                    }
387                }
388                Element curveMembers = (Element) XMLTools.getNode( element, "gml:curveMembers", nsContext );
389                if ( curveMembers != null ) {
390                    // gml:curveMembers
391                    List listCurves = XMLTools.getNodes( curveMembers, "gml:Curve", nsContext );
392                    if ( listCurves != null ) {
393                        for ( int i = 0; i < listCurves.size(); i++ ) {
394                            Element curve = (Element) listCurves.get( i );
395                            curveList.add( wrapCurveAsCurve( curve, srsName ) );
396                        }
397                    }
398                    listCurves = XMLTools.getNodes( curveMembers, "gml:LineString", nsContext );
399                    if ( listCurves != null ) {
400                        for ( int i = 0; i < listCurves.size(); i++ ) {
401                            Element curve = (Element) listCurves.get( i );
402                            curveList.add( wrapLineString( curve, srsName ) );
403                        }
404                    }
405                }
406                Curve[] curves = new Curve[curveList.size()];
407                multiCurve = GeometryFactory.createMultiCurve( curveList.toArray( curves ), crs );
408            } catch ( XMLParsingException e ) {
409                LOG.logError( e.getMessage(), e );
410                throw new XMLParsingException( "Error parsing <gml:curveMember> elements. Please check the xml document." );
411            } catch ( GeometryException e ) {
412                LOG.logError( e.getMessage(), e );
413                throw new GeometryException(
414                                             "Error creating a curve from the curve element. Please check the GML specifications "
415                                                                     + "for correct element declaration." );
416            }
417            return multiCurve;
418        }
419    
420        /**
421         * Returns an instance of {@link Surface} created from the passed <code>gml:Surface</code>
422         * element.
423         * 
424         * @param element
425         * @param srsName
426         *            default SRS for the geometry
427         * @return Surface
428         * @throws XMLParsingException
429         * @throws GeometryException
430         * @throws UnknownCRSException
431         */
432        protected static Surface wrapSurfaceAsSurface( Element element, String srsName )
433                                throws XMLParsingException, GeometryException, UnknownCRSException {
434    
435            srsName = findSrsName( element, srsName );
436            CoordinateSystem crs = null;
437            if ( srsName != null ) {
438                crs = getCRS( srsName );
439            }
440    
441            Element patches = extractPatches( element );
442            List<Element> polygonList = XMLTools.getRequiredElements( patches, "gml:Polygon | gml:PolygonPatch", nsContext );
443    
444            SurfacePatch[] surfacePatches = new SurfacePatch[polygonList.size()];
445    
446            for ( int i = 0; i < polygonList.size(); i++ ) {
447                Curve exteriorRing = null;
448                Element polygon = polygonList.get( i );
449                try {
450                    Element exterior = (Element) XMLTools.getNode( polygon, "gml:exterior | gml:outerBounderyIs", nsContext );
451                    if ( exterior != null ) {
452                        exteriorRing = parseRing( crs, exterior );
453                    } else {
454                        String msg = Messages.getMessage( "GEOM_SURFACE_NO_EXTERIOR_RING" );
455                        throw new XMLParsingException( msg );
456                    }
457    
458                    List<Element> interiorList = XMLTools.getElements( polygon, "gml:interior | gml:outerBounderyIs",
459                                                                       nsContext );
460                    Curve[] interiorRings = null;
461                    if ( interiorList != null && interiorList.size() > 0 ) {
462    
463                        interiorRings = new Curve[interiorList.size()];
464    
465                        for ( int j = 0; j < interiorRings.length; j++ ) {
466                            Element interior = interiorList.get( j );
467                            interiorRings[j] = parseRing( crs, interior );
468                        }
469                    }
470                    surfacePatches[i] = GeometryFactory.createSurfacePatch( exteriorRing, interiorRings, crs );
471                } catch ( InvalidGMLException e ) {
472                    LOG.logError( e.getMessage(), e );
473                    throw new XMLParsingException( "Error parsing the polygon element '" + polygon.getNodeName()
474                                                   + "' to create a surface geometry." );
475                }
476    
477            }
478            Surface surface = null;
479            try {
480                surface = GeometryFactory.createSurface( surfacePatches, crs );
481            } catch ( GeometryException e ) {
482                throw new GeometryException( "Error creating a surface from '" + surfacePatches.length + "' polygons." );
483            }
484            return surface;
485        }
486    
487        private static String findSrsName( Element element, String srsName )
488                                throws XMLParsingException {
489            Node elem = element;
490            while ( srsName == null && elem != null ) {
491                srsName = XMLTools.getNodeAsString( elem, "@srsName", nsContext, srsName );
492                elem = elem.getParentNode();
493            }
494            elem = element;
495            if ( srsName == null ) {
496                srsName = XMLTools.getNodeAsString( elem, "//@srsName", nsContext, srsName );
497            }
498            return srsName;
499        }
500    
501        /**
502         * parses a ring; a ring may is a gml:LinearRing or a gml:Ring
503         * 
504         * @param crs
505         * @param parent
506         *            parent of a gml:LinearRing or gml:Ring
507         * @return curves of a ring
508         * @throws XMLParsingException
509         * @throws InvalidGMLException
510         * @throws GeometryException
511         */
512        private static Curve parseRing( CoordinateSystem crs, Element parent )
513                                throws XMLParsingException, InvalidGMLException, GeometryException {
514            String srsName = null;
515            if ( crs != null ) {
516                srsName = crs.getName();
517            }
518            List<CurveSegment> curveMembers = null;
519            Element ring = (Element) XMLTools.getNode( parent, "gml:LinearRing", nsContext );
520            if ( ring != null ) {
521                Position[] exteriorRing = createPositions( ring, srsName );
522                curveMembers = new ArrayList<CurveSegment>();
523                curveMembers.add( GeometryFactory.createCurveSegment( exteriorRing, crs ) );
524            } else {
525                List<Node> members = XMLTools.getRequiredNodes( parent, "gml:Ring/gml:curveMember/child::*", nsContext );
526                curveMembers = new ArrayList<CurveSegment>( members.size() );
527                for ( Node node : members ) {
528                    Curve curve = (Curve) wrap( (Element) node, srsName );
529                    CurveSegment[] tmp = curve.getCurveSegments();
530                    for ( int i = 0; i < tmp.length; i++ ) {
531                        curveMembers.add( tmp[i] );
532                    }
533                }
534            }
535            CurveSegment[] cs = curveMembers.toArray( new CurveSegment[curveMembers.size()] );
536    
537            return GeometryFactory.createCurve( cs );
538        }
539    
540        /**
541         * Returns an instance of {@link MultiSurface} created from the passed
542         * <code>gml:MultiSurface</code> element.
543         * 
544         * @param element
545         *            <code>gml:MultiSurface</code> element
546         * @param srsName
547         *            default SRS for the geometry
548         * @return MultiSurface
549         * @throws XMLParsingException
550         * @throws GeometryException
551         * @throws InvalidGMLException
552         * @throws UnknownCRSException
553         */
554        protected static MultiSurface wrapMultiSurfaceAsMultiSurface( Element element, String srsName )
555                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
556    
557            srsName = findSrsName( element, srsName );
558            CoordinateSystem crs = null;
559            if ( srsName != null ) {
560                crs = getCRS( srsName );
561            }
562            MultiSurface multiSurface = null;
563            try {
564                List<Surface> surfaceList = new ArrayList<Surface>();
565                // gml:surfaceMember
566                List listSurfaceMember = XMLTools.getNodes( element, "gml:surfaceMember", nsContext );
567                if ( listSurfaceMember != null ) {
568                    for ( int i = 0; i < listSurfaceMember.size(); i++ ) {
569                        Element surfaceMember = (Element) listSurfaceMember.get( i );
570                        Element surface = (Element) XMLTools.getNode( surfaceMember, "gml:Surface", nsContext );
571                        if ( surface != null ) {
572                            surfaceList.add( wrapSurfaceAsSurface( surface, srsName ) );
573                        } else {
574                            surface = (Element) XMLTools.getRequiredNode( surfaceMember, ".//gml:Polygon", nsContext );
575                            surfaceList.add( wrapPolygon( surface, srsName ) );
576                        }
577                    }
578                }
579    
580                Element surfaceMembers = (Element) XMLTools.getNode( element, "gml:surfaceMembers", nsContext );
581                if ( surfaceMembers != null ) {
582                    // gml:surfaceMembers
583    
584                    List listSurfaces = XMLTools.getNodes( surfaceMembers, "gml:Surface", nsContext );
585                    if ( listSurfaces != null ) {
586                        for ( int i = 0; i < listSurfaces.size(); i++ ) {
587                            Element surface = (Element) listSurfaces.get( i );
588                            surfaceList.add( wrapSurfaceAsSurface( surface, srsName ) );
589                        }
590                    }
591    
592                    listSurfaces = XMLTools.getNodes( surfaceMembers, ".//gml:Polygon", nsContext );
593                    if ( listSurfaces != null ) {
594                        for ( int i = 0; i < listSurfaces.size(); i++ ) {
595                            Element surface = (Element) listSurfaces.get( i );
596                            surfaceList.add( wrapPolygon( surface, srsName ) );
597                        }
598                    }
599                }
600                Surface[] surfaces = new Surface[surfaceList.size()];
601                surfaces = surfaceList.toArray( surfaces );
602                multiSurface = GeometryFactory.createMultiSurface( surfaces, crs );
603            } catch ( XMLParsingException e ) {
604                LOG.logError( e.getMessage(), e );
605                String msg = Messages.getMessage( "GEOM_MULTISURFACE_PARSING_ERROR" );
606                throw new XMLParsingException( msg );
607            } catch ( GeometryException e ) {
608                LOG.logError( e.getMessage(), e );
609                String msg = Messages.getMessage( "GEOM_MULTISURFACE_FORMAT_ERROR" );
610                throw new GeometryException( msg );
611            }
612            return multiSurface;
613        }
614    
615        /**
616         * Returns a {@link Point} instance created from the passed <code>gml:Point</code> element.
617         * 
618         * @param element
619         *            <code>gml:Point</code> element
620         * @param srsName
621         *            default SRS for the geometry
622         * @return instance of Point
623         * @throws XMLParsingException
624         * @throws UnknownCRSException
625         */
626        private static Point wrapPoint( Element element, String srsName )
627                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
628    
629            srsName = findSrsName( element, srsName );
630            CoordinateSystem crs = null;
631            if ( srsName != null ) {
632                crs = getCRS( srsName );
633            }
634    
635            Position[] bb = null;
636            List nl = XMLTools.getNodes( element, COORD, nsContext );
637            if ( nl != null && nl.size() > 0 ) {
638                bb = new Position[1];
639                bb[0] = createPositionFromCoord( (Element) nl.get( 0 ) );
640            } else {
641                nl = XMLTools.getNodes( element, COORDINATES, nsContext );
642                if ( nl != null && nl.size() > 0 ) {
643                    bb = createPositionFromCoordinates( (Element) nl.get( 0 ) );
644                } else {
645                    nl = XMLTools.getNodes( element, POS, nsContext );
646                    bb = new Position[1];
647                    bb[0] = createPositionFromPos( (Element) nl.get( 0 ) );
648                }
649            }
650            Point point = GeometryFactory.createPoint( bb[0], crs );
651            return point;
652        }
653    
654        /**
655         * Returns a {@link Curve} instance created from the passed <code>gml:LineString</code>
656         * element.
657         * 
658         * @param element
659         *            <code>gml:LineString</code> element
660         * @param srsName
661         *            default SRS for the geometry
662         * @return instance of Curve
663         * @throws XMLParsingException
664         * @throws UnknownCRSException
665         */
666        private static Curve wrapLineString( Element element, String srsName )
667                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
668    
669            srsName = findSrsName( element, srsName );
670            CoordinateSystem crs = null;
671            if ( srsName != null ) {
672                crs = getCRS( srsName );
673            }
674            Position[] pos = createPositions( element, srsName );
675            Curve curve = GeometryFactory.createCurve( pos, crs );
676            return curve;
677        }
678    
679        /**
680         * Returns a {@link Surface} instance created from the passed <code>gml:Polygon</code>
681         * element.
682         * 
683         * @param element
684         *            <code>gml:Polygon</code> element
685         * @param srsName
686         *            default SRS for the geometry
687         * @return instance of Surface
688         * @throws XMLParsingException
689         * @throws UnknownCRSException
690         */
691        private static Surface wrapPolygon( Element element, String srsName )
692                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
693    
694            srsName = findSrsName( element, srsName );
695            CoordinateSystem crs = null;
696            if ( srsName != null ) {
697                crs = getCRS( srsName );
698            }
699    
700            List nl = XMLTools.getNodes( element, CommonNamespaces.GML_PREFIX + ":outerBoundaryIs", nsContext );
701            if ( nl == null || nl.size() == 0 ) {
702                nl = XMLTools.getRequiredNodes( element, CommonNamespaces.GML_PREFIX + ":exterior", nsContext );
703            }
704            Element outs = (Element) nl.get( 0 );
705            nl = XMLTools.getRequiredNodes( outs, CommonNamespaces.GML_PREFIX + ":LinearRing", nsContext );
706            Element ring = (Element) nl.get( 0 );
707            nl = XMLTools.getNodes( ring, COORDINATES, nsContext );
708            Position[] outerRing = createPositions( ring, srsName );
709    
710            Position[][] innerRings = null;
711            List inns = XMLTools.getNodes( element, CommonNamespaces.GML_PREFIX + ":innerBoundaryIs", nsContext );
712            if ( inns == null || inns.size() == 0 ) {
713                inns = XMLTools.getNodes( element, CommonNamespaces.GML_PREFIX + ":interior", nsContext );
714            }
715            if ( inns != null && inns.size() > 0 ) {
716                innerRings = new Position[inns.size()][];
717                for ( int i = 0; i < innerRings.length; i++ ) {
718    
719                    nl = XMLTools.getRequiredNodes( (Node) inns.get( i ), CommonNamespaces.GML_PREFIX + ":LinearRing",
720                                                    nsContext );
721    
722                    ring = (Element) nl.get( 0 );
723                    innerRings[i] = createPositions( ring, srsName );
724                }
725            }
726    
727            SurfaceInterpolation si = new SurfaceInterpolationImpl();
728            Surface surface = GeometryFactory.createSurface( outerRing, innerRings, si, crs );
729            return surface;
730        }
731    
732        /**
733         * Returns a {@link MultiPoint} instance created from the passed <code>gml:MultiPoint</code>
734         * element.
735         * 
736         * @param element
737         *            <code>gml:MultiPoint</code> element
738         * @param srsName
739         *            default SRS for the geometry
740         * @return instance of MultiPoint
741         * @throws XMLParsingException
742         * @throws UnknownCRSException
743         */
744        private static MultiPoint wrapMultiPoint( Element element, String srsName )
745                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
746    
747            srsName = findSrsName( element, srsName );
748            CoordinateSystem crs = null;
749            if ( srsName != null ) {
750                crs = getCRS( srsName );
751            }
752    
753            List<Point> pointList = new ArrayList<Point>();
754            List listPointMember = XMLTools.getNodes( element, "gml:pointMember", nsContext );
755            if ( listPointMember != null ) {
756                for ( int i = 0; i < listPointMember.size(); i++ ) {
757                    Element pointMember = (Element) listPointMember.get( i );
758                    Element point = (Element) XMLTools.getNode( pointMember, "gml:Point", nsContext );
759                    pointList.add( wrapPoint( point, srsName ) );
760                }
761            }
762    
763            Element pointMembers = (Element) XMLTools.getNode( element, "gml:pointMembers", nsContext );
764            if ( pointMembers != null ) {
765                List pointElems = XMLTools.getNodes( pointMembers, "gml:Point", nsContext );
766                for ( int j = 0; j < pointElems.size(); j++ ) {
767                    pointList.add( wrapPoint( (Element) pointElems.get( j ), srsName ) );
768                }
769            }
770    
771            Point[] points = new Point[pointList.size()];
772            return GeometryFactory.createMultiPoint( pointList.toArray( points ), crs );
773    
774        }
775    
776        /**
777         * Returns a {@link MultiCurve} instance created from the passed
778         * <code>gml:MultiLineString</code> element.
779         * 
780         * @param element
781         *            <code>gml:MultiLineString</code> element
782         * @param srsName
783         *            default SRS for the geometry
784         * @return instance of MultiCurve
785         * @throws XMLParsingException
786         * @throws UnknownCRSException
787         */
788        private static MultiCurve wrapMultiLineString( Element element, String srsName )
789                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
790    
791            srsName = findSrsName( element, srsName );
792            CoordinateSystem crs = null;
793            if ( srsName != null ) {
794                crs = getCRS( srsName );
795            }
796    
797            ElementList el = XMLTools.getChildElements( "lineStringMember", CommonNamespaces.GMLNS, element );
798            Curve[] curves = new Curve[el.getLength()];
799            for ( int i = 0; i < curves.length; i++ ) {
800                curves[i] = wrapLineString( XMLTools.getFirstChildElement( el.item( i ) ), srsName );
801            }
802            MultiCurve mp = GeometryFactory.createMultiCurve( curves, crs );
803            return mp;
804        }
805    
806        /**
807         * Returns a {@link MultiSurface} instance created from the passed <code>gml:MultiPolygon</code>
808         * element.
809         * 
810         * @param element
811         *            <code>gml:MultiPolygon</code> element
812         * @param srsName
813         *            default SRS for the geometry
814         * @return instance of MultiCurve
815         * 
816         * @throws XMLParsingException
817         * @throws UnknownCRSException
818         */
819        private static MultiSurface wrapMultiPolygon( Element element, String srsName )
820                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
821    
822            srsName = findSrsName( element, srsName );
823            CoordinateSystem crs = null;
824            if ( srsName != null ) {
825                crs = getCRS( srsName );
826            }
827    
828            ElementList el = XMLTools.getChildElements( "polygonMember", CommonNamespaces.GMLNS, element );
829            Surface[] surfaces = new Surface[el.getLength()];
830            for ( int i = 0; i < surfaces.length; i++ ) {
831                surfaces[i] = wrapPolygon( XMLTools.getFirstChildElement( el.item( i ) ), srsName );
832            }
833            return GeometryFactory.createMultiSurface( surfaces, crs );
834        }
835    
836        /**
837         * Returns a <code>Surface</code> created from the given <code>gml:Box</code> element. This
838         * method is useful because an Envelope that would normally be created from a Box isn't a
839         * geometry in context of ISO 19107.
840         * 
841         * @param element
842         *            <code>gml:Box</code> element
843         * @return instance of <code>Surface</code>
844         * @throws XMLParsingException
845         * @throws UnknownCRSException
846         */
847        private static Surface wrapBoxAsSurface( Element element, String srsName )
848                                throws XMLParsingException, GeometryException, InvalidGMLException, UnknownCRSException {
849            Envelope env = wrapBox( element, srsName );
850            return GeometryFactory.createSurface( env, env.getCoordinateSystem() );
851        }
852    
853        /**
854         * Returns an instance of {@link CompositeSurface} created from the passed
855         * <code>gml:CompositeSurface</code> element.
856         * 
857         * TODO
858         * 
859         * @param element
860         * @param srsName
861         *            default SRS for the geometry
862         * @return CompositeSurface
863         * @throws GeometryException
864         */
865        private static CompositeSurface wrapCompositeSurface( Element element, String srsName ) {
866            throw new UnsupportedOperationException(
867                                                     "#wrapCompositeSurface(Element) is not implemented as yet. Work in Progress." );
868        }
869    
870        /**
871         * Extract the <gml:patches> node from a <gml:Surface> element.
872         * 
873         * @param surface
874         * @return Element
875         * @throws XMLParsingException
876         */
877        private static Element extractPatches( Element surface )
878                                throws XMLParsingException {
879            Element patches = null;
880            try {
881                patches = (Element) XMLTools.getRequiredNode( surface, "gml:patches", nsContext );
882            } catch ( XMLParsingException e ) {
883                throw new XMLParsingException( "Error retrieving the patches element from the surface element." );
884            }
885            return patches;
886        }
887    
888        /**
889         * returns an instance of CS_CoordinateSystem corrsponding to the passed crs name
890         * 
891         * @param name
892         *            name of the crs
893         * 
894         * @return CS_CoordinateSystem
895         * @throws UnknownCRSException
896         */
897        private static CoordinateSystem getCRS( String name )
898                                throws UnknownCRSException {
899    
900            if ( ( name != null ) && ( name.length() > 2 ) ) {
901                if ( name.startsWith( "http://www.opengis.net/gml/srs/" ) ) {
902                    // as declared in the GML 2.1.1 specification
903                    // http://www.opengis.net/gml/srs/epsg.xml#4326
904                    int p = name.lastIndexOf( "/" );
905    
906                    if ( p >= 0 ) {
907                        name = name.substring( p, name.length() );
908                        p = name.indexOf( "." );
909    
910                        String s1 = name.substring( 1, p ).toUpperCase();
911                        p = name.indexOf( "#" );
912    
913                        String s2 = name.substring( p + 1, name.length() );
914                        name = s1 + ":" + s2;
915                    }
916                }
917            }
918    
919            CoordinateSystem crs = crsMap.get( name );
920            if ( crs == null ) {
921                crs = CRSFactory.create( name );
922                crsMap.put( name, crs );
923            }
924            return crs;
925        }
926    
927        private static Position createPositionFromCorner( Element corner )
928                                throws InvalidGMLException {
929    
930            String tmp = XMLTools.getAttrValue( corner, null, "dimension", null );
931            int dim = 0;
932            if ( tmp != null ) {
933                dim = Integer.parseInt( tmp );
934            }
935            tmp = XMLTools.getStringValue( corner );
936            double[] vals = StringTools.toArrayDouble( tmp, ", " );
937            if ( dim != 0 ) {
938                if ( vals.length != dim ) {
939                    throw new InvalidGMLException( "dimension must be equal to the number of coordinate values defined "
940                                                   + "in pos element." );
941                }
942            } else {
943                dim = vals.length;
944            }
945    
946            Position pos = null;
947            if ( dim == 3 ) {
948                pos = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
949            } else {
950                pos = GeometryFactory.createPosition( vals[0], vals[1] );
951            }
952    
953            return pos;
954    
955        }
956    
957        /**
958         * returns an instance of Position created from the passed coord
959         * 
960         * @param element
961         *            <coord>
962         * 
963         * @return instance of <tt>Position</tt>
964         * 
965         * @throws XMLParsingException
966         */
967        private static Position createPositionFromCoord( Element element )
968                                throws XMLParsingException {
969    
970            Position pos = null;
971            Element elem = XMLTools.getRequiredChildElement( "X", CommonNamespaces.GMLNS, element );
972            double x = Double.parseDouble( XMLTools.getStringValue( elem ) );
973            elem = XMLTools.getRequiredChildElement( "Y", CommonNamespaces.GMLNS, element );
974            double y = Double.parseDouble( XMLTools.getStringValue( elem ) );
975            elem = XMLTools.getChildElement( "Z", CommonNamespaces.GMLNS, element );
976    
977            if ( elem != null ) {
978                double z = Double.parseDouble( XMLTools.getStringValue( elem ) );
979                pos = GeometryFactory.createPosition( new double[] { x, y, z } );
980            } else {
981                pos = GeometryFactory.createPosition( new double[] { x, y } );
982            }
983    
984            return pos;
985        }
986    
987        /**
988         * returns an array of Positions created from the passed coordinates
989         * 
990         * @param element
991         *            <coordinates>
992         * 
993         * @return instance of <tt>Position[]</tt>
994         * 
995         * @throws XMLParsingException
996         */
997        private static Position[] createPositionFromCoordinates( Element element ) {
998    
999            Position[] points = null;
1000            // fixing the failure coming from the usage of the xmltools.getAttrib method
1001            String ts = XMLTools.getAttrValue( element, null, "ts", " " );
1002    
1003            // not used because javas current decimal seperator will be used
1004            // String ds = XMLTools.getAttrValue( element, null, "decimal", "." );
1005            String cs = XMLTools.getAttrValue( element, null, "cs", "," );
1006    
1007            String value = XMLTools.getStringValue( element ).trim();
1008    
1009            // first tokenizer, tokens the tuples
1010            StringTokenizer tuple = new StringTokenizer( value, ts );
1011            points = new Position[tuple.countTokens()];
1012            int i = 0;
1013            while ( tuple.hasMoreTokens() ) {
1014                String s = tuple.nextToken();
1015                // second tokenizer, tokens the coordinates
1016                StringTokenizer coort = new StringTokenizer( s, cs );
1017                double[] p = new double[coort.countTokens()];
1018    
1019                for ( int k = 0; k < p.length; k++ ) {
1020                    s = coort.nextToken();
1021                    p[k] = Double.parseDouble( s );
1022                }
1023    
1024                points[i++] = GeometryFactory.createPosition( p );
1025            }
1026    
1027            return points;
1028        }
1029    
1030        /**
1031         * creates a <tt>Point</tt> from the passed <pos> element containing a GML pos.
1032         * 
1033         * @param element
1034         * @return created <tt>Point</tt>
1035         * @throws XMLParsingException
1036         * @throws InvalidGMLException
1037         */
1038        private static Position createPositionFromPos( Element element )
1039                                throws InvalidGMLException {
1040    
1041            String tmp = XMLTools.getAttrValue( element, null, "dimension", null );
1042            int dim = 0;
1043            if ( tmp != null ) {
1044                dim = Integer.parseInt( tmp );
1045            }
1046            tmp = XMLTools.getStringValue( element );
1047            double[] vals = StringTools.toArrayDouble( tmp, "\t\n\r\f ," );
1048            if ( vals != null ) {
1049                if ( dim != 0 ) {
1050                    if ( vals.length != dim ) {
1051                        throw new InvalidGMLException( "dimension must be equal to the number of "
1052                                                       + "coordinate values defined in pos element." );
1053                    }
1054                } else {
1055                    dim = vals.length;
1056                }
1057            } else {
1058                throw new InvalidGMLException( "The given element {" + element.getNamespaceURI() + "}"
1059                                               + element.getLocalName()
1060                                               + " does not contain any coordinates, this may not be!" );
1061            }
1062    
1063            Position pos = null;
1064            if ( dim == 3 ) {
1065                pos = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
1066            } else {
1067                pos = GeometryFactory.createPosition( vals[0], vals[1] );
1068            }
1069    
1070            return pos;
1071        }
1072    
1073        /**
1074         * 
1075         * @param element
1076         * @return Position
1077         * @throws InvalidGMLException
1078         * @throws XMLParsingException
1079         */
1080        private static Position[] createPositionFromPosList( Element element, String srsName )
1081                                throws InvalidGMLException, XMLParsingException {
1082    
1083            Node elem = element;
1084            while ( srsName == null && elem != null ) {
1085                srsName = XMLTools.getNodeAsString( elem, "@srsName", nsContext, srsName );
1086                elem = elem.getParentNode();
1087            }
1088    
1089            String srsDimension = XMLTools.getAttrValue( element, null, "srsDimension", null );
1090            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
1091                XMLFragment doc = new XMLFragment( element );
1092                System.out.println( doc.getAsPrettyString() );
1093            }
1094            int dim = 0;
1095            if ( srsDimension != null ) {
1096                dim = Integer.parseInt( srsDimension );
1097            }
1098            if ( dim == 0 ) {
1099                // TODO
1100                // determine dimension from CRS
1101                // default dimension set.
1102                dim = 2;
1103    
1104            }
1105    
1106            String axisLabels = XMLTools.getAttrValue( element, null, "gml:axisAbbrev", null );
1107    
1108            String uomLabels = XMLTools.getAttrValue( element, null, "uomLabels", null );
1109    
1110            if ( srsName == null ) {
1111                if ( srsDimension != null ) {
1112                    throw new InvalidGMLException(
1113                                                   "Attribute srsDimension cannot be defined unless attribute srsName has been defined." );
1114                }
1115                if ( axisLabels != null ) {
1116                    throw new InvalidGMLException(
1117                                                   "Attribute axisLabels cannot be defined unless attribute srsName has been defined." );
1118                }
1119    
1120            }
1121            if ( axisLabels == null ) {
1122                if ( uomLabels != null ) {
1123                    throw new InvalidGMLException(
1124                                                   "Attribute uomLabels cannot be defined unless attribute axisLabels has been defined." );
1125                }
1126            }
1127            String tmp = XMLTools.getStringValue( element );
1128            double[] values = StringTools.toArrayDouble( tmp, "\t\n\r\f ," );
1129            int size = values.length / dim;
1130            LOG.logDebug( "Number of points = ", size );
1131            LOG.logDebug( "Size of the original array: ", values.length );
1132            LOG.logDebug( "Dimension: ", dim );
1133    
1134            if ( values.length < 4 ) {
1135                throw new InvalidGMLException( "A point list must have minimum 2 coordinate tuples. Here only '" + size
1136                                               + "' are defined." );
1137            }
1138            double positions[][] = new double[size][dim];
1139            int a = 0, b = 0;
1140            for ( int i = 0; i < values.length; i++ ) {
1141                if ( b == dim ) {
1142                    a++;
1143                    b = 0;
1144                }
1145                positions[a][b] = values[i];
1146                b++;
1147            }
1148    
1149            Position[] position = new Position[positions.length];
1150            for ( int i = 0; i < positions.length; i++ ) {
1151                double[] vals = positions[i];
1152                if ( dim == 3 ) {
1153                    position[i] = GeometryFactory.createPosition( vals[0], vals[1], vals[2] );
1154                } else {
1155                    position[i] = GeometryFactory.createPosition( vals[0], vals[1] );
1156                }
1157            }
1158    
1159            return position;
1160    
1161        }
1162    
1163        /**
1164         * creates an array of <tt>Position</tt>s from the <coordinates> or <pos> Elements located as
1165         * children under the passed parent element.
1166         * <p>
1167         * example:<br>
1168         * 
1169         * <pre>
1170         *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          &lt;gml:Box&gt;
1171         *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                &lt;gml:coordinates cs=&quot;,&quot; decimal=&quot;.&quot; ts=&quot; &quot;&gt;0,0 4000,4000&lt;/gml:coordinates&gt;
1172         *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          &lt;/gml:Box&gt;
1173         * </pre>
1174         * 
1175         * </p>
1176         * 
1177         * @param parent
1178         * @param srsName
1179         * @return
1180         * @throws XMLParsingException
1181         * @throws InvalidGMLException
1182         */
1183        private static Position[] createPositions( Element parent, String srsName )
1184                                throws XMLParsingException, InvalidGMLException {
1185    
1186            List nl = XMLTools.getNodes( parent, COORDINATES, nsContext );
1187            Position[] pos = null;
1188            if ( nl != null && nl.size() > 0 ) {
1189                pos = createPositionFromCoordinates( (Element) nl.get( 0 ) );
1190            } else {
1191                nl = XMLTools.getNodes( parent, POS, nsContext );
1192                if ( nl != null && nl.size() > 0 ) {
1193                    pos = new Position[nl.size()];
1194                    for ( int i = 0; i < pos.length; i++ ) {
1195                        pos[i] = createPositionFromPos( (Element) nl.get( i ) );
1196                    }
1197                } else {
1198                    Element posList = (Element) XMLTools.getRequiredNode( parent, POSLIST, nsContext );
1199                    if ( posList != null ) {
1200                        pos = createPositionFromPosList( posList, srsName );
1201                    }
1202                }
1203            }
1204            return pos;
1205        }
1206    
1207        /**
1208         * Creates a GML representation from the passed <code>Geometry<code>
1209         *  
1210         * @param geometry
1211         * @param target 
1212         * @throws GeometryException
1213         */
1214        public static PrintWriter export( Geometry geometry, OutputStream target )
1215                                throws GeometryException {
1216    
1217            PrintWriter printwriter = new PrintWriter( target );
1218    
1219            if ( geometry instanceof SurfacePatch ) {
1220                geometry = new SurfaceImpl( (SurfacePatch) geometry );
1221            } else if ( geometry instanceof LineString ) {
1222                geometry = new CurveImpl( (LineString) geometry );
1223            }
1224            // create geometries from the wkb considering the geomerty typ
1225            if ( geometry instanceof Point ) {
1226                exportPoint( (Point) geometry, printwriter );
1227            } else if ( geometry instanceof Curve ) {
1228                exportCurve( (Curve) geometry, printwriter );
1229            } else if ( geometry instanceof Surface ) {
1230                exportSurface( (Surface) geometry, printwriter );
1231            } else if ( geometry instanceof MultiPoint ) {
1232                exportMultiPoint( (MultiPoint) geometry, printwriter );
1233            } else if ( geometry instanceof MultiCurve ) {
1234                exportMultiCurve( (MultiCurve) geometry, printwriter );
1235            } else if ( geometry instanceof MultiSurface ) {
1236                exportMultiSurface( (MultiSurface) geometry, printwriter );
1237            }
1238            printwriter.flush();
1239            return printwriter;
1240        }
1241    
1242        /**
1243         * Creates a GML representation from the passed <code>Geometry</code>.
1244         * 
1245         * @param geometry
1246         * @return a string buffer containing the XML
1247         * @throws GeometryException
1248         */
1249        public static StringBuffer export( Geometry geometry )
1250                                throws GeometryException {
1251    
1252            ByteArrayOutputStream bos = new ByteArrayOutputStream( 5000 );
1253    
1254            export( geometry, bos );
1255    
1256            return new StringBuffer( new String( bos.toByteArray() ) );
1257        }
1258    
1259        /**
1260         * creates a GML representation from the passed <tt>Envelope</tt>. This method is required
1261         * because in ISO 19107 Envelops are no geometries.
1262         * 
1263         * @param envelope
1264         * @return
1265         * @throws GeometryException
1266         */
1267        public static StringBuffer exportAsBox( Envelope envelope ) {
1268    
1269            StringBuffer sb = new StringBuffer( "<gml:Box xmlns:gml='http://www.opengis.net/gml'>" );
1270            sb.append( "<gml:coordinates cs=\",\" decimal=\".\" ts=\" \">" );
1271            sb.append( envelope.getMin().getX() ).append( ',' );
1272            sb.append( envelope.getMin().getY() );
1273            int dim = envelope.getMax().getCoordinateDimension();
1274            if ( dim == 3 ) {
1275                sb.append( ',' ).append( envelope.getMin().getZ() );
1276            }
1277            sb.append( ' ' ).append( envelope.getMax().getX() );
1278            sb.append( ',' ).append( envelope.getMax().getY() );
1279            if ( dim == 3 ) {
1280                sb.append( ',' ).append( envelope.getMax().getZ() );
1281            }
1282            sb.append( "</gml:coordinates></gml:Box>" );
1283    
1284            return sb;
1285        }
1286    
1287        /**
1288         * creates a GML representation from the passed <tt>Envelope</tt>. This method is required
1289         * because in ISO 19107 Envelops are no geometries.
1290         * 
1291         * @param envelope
1292         * @return
1293         * @throws GeometryException
1294         */
1295        public static StringBuffer exportAsEnvelope( Envelope envelope ) {
1296    
1297            StringBuffer sb = new StringBuffer( "<gml:Envelope " );
1298            sb.append( "xmlns:gml='http://www.opengis.net/gml'>" );
1299            sb.append( "<gml:coordinates cs=\",\" decimal=\".\" ts=\" \">" );
1300            sb.append( envelope.getMin().getX() ).append( ',' );
1301            sb.append( envelope.getMin().getY() );
1302            int dim = envelope.getMax().getCoordinateDimension();
1303            if ( dim == 3 ) {
1304                sb.append( ',' ).append( envelope.getMin().getZ() );
1305            }
1306            sb.append( ' ' ).append( envelope.getMax().getX() );
1307            sb.append( ',' ).append( envelope.getMax().getY() );
1308            if ( dim == 3 ) {
1309                sb.append( ',' ).append( envelope.getMax().getZ() );
1310            }
1311            sb.append( "</gml:coordinates></gml:Envelope>" );
1312    
1313            return sb;
1314        }
1315    
1316        /**
1317         * creates a GML expression of a point geometry
1318         * 
1319         * @param point
1320         *            point geometry
1321         * 
1322         * @return
1323         */
1324        private static void exportPoint( Point point, PrintWriter pw ) {
1325    
1326            String crs = null;
1327            int dim = point.getCoordinateDimension();
1328            if ( point.getCoordinateSystem() != null ) {
1329                crs = point.getCoordinateSystem().getIdentifier().replace( ' ', ':' );
1330                dim = point.getCoordinateSystem().getDimension();
1331            }
1332            String srs = null;
1333            if ( crs != null ) {
1334                srs = "<gml:Point srsName=\"" + crs + "\">";
1335            } else {
1336                srs = "<gml:Point>";
1337            }
1338            pw.println( srs );
1339    
1340            if ( dim != 0 ) {
1341                String dimension = "<gml:pos srsDimension=\"" + dim + "\">";
1342                pw.print( dimension );
1343            } else {
1344                pw.print( "<gml:pos>" );
1345            }
1346    
1347            String coordinates = point.getX() + " " + point.getY();
1348            if ( dim == 3 ) {
1349                coordinates = coordinates + " " + point.getZ();
1350            }
1351            pw.print( coordinates );
1352            pw.println( "</gml:pos>" );
1353            pw.print( "</gml:Point>" );
1354            
1355        }
1356    
1357        /**
1358         * creates a GML expression of a curve geometry
1359         * 
1360         * @param o
1361         *            curve geometry
1362         * 
1363         * @return
1364         * 
1365         * @throws GeometryException
1366         */
1367        private static void exportCurve( Curve o, PrintWriter pw )
1368                                throws GeometryException {
1369    
1370            String crs = null;
1371            if ( o.getCoordinateSystem() != null ) {
1372                crs = o.getCoordinateSystem().getName().replace( ' ', ':' );
1373            }
1374            String srs = null;
1375            if ( crs != null ) {
1376                srs = "<gml:Curve srsName=\"" + crs + "\">";
1377            } else {
1378                srs = "<gml:Curve>";
1379            }
1380            pw.println( srs );
1381            pw.println( "<gml:segments>" );
1382    
1383            int curveSegments = o.getNumberOfCurveSegments();
1384            for ( int i = 0; i < curveSegments; i++ ) {
1385                pw.print( "<gml:LineStringSegment>" );
1386                CurveSegment segment = o.getCurveSegmentAt( i );
1387                Position[] p = segment.getAsLineString().getPositions();
1388                pw.print( "<gml:posList>" );
1389                for ( int j = 0; j < ( p.length - 1 ); j++ ) {
1390                    pw.print( p[j].getX() + " " + p[j].getY() );
1391                    if ( o.getCoordinateDimension() == 3 ) {
1392                        pw.print( ' ' );
1393                        pw.print( p[j].getZ() );
1394                        pw.print( ' ' );
1395                    } else {
1396                        pw.print( ' ' );
1397                    }
1398                }
1399                pw.print( p[p.length - 1].getX() + " " + p[p.length - 1].getY() );
1400                if ( o.getCoordinateDimension() == 3 ) {
1401                    pw.print( " " + p[p.length - 1].getZ() );
1402                }
1403                pw.println( "</gml:posList>" );
1404                pw.println( "</gml:LineStringSegment>" );
1405            }
1406            pw.println( "</gml:segments>" );
1407            pw.print( "</gml:Curve>" );
1408    
1409        }
1410    
1411        /**
1412         * @param sur
1413         * @throws RemoteException
1414         * @throws GeometryException
1415         */
1416        private static void exportSurface( Surface surface, PrintWriter pw )
1417                                throws GeometryException {
1418    
1419            String crs = null;
1420            if ( surface.getCoordinateSystem() != null ) {
1421                crs = surface.getCoordinateSystem().getName().replace( ' ', ':' );
1422            }
1423            String srs = null;
1424            if ( crs != null ) {
1425                srs = "<gml:Surface srsName='" + crs + "'>";
1426            } else {
1427                srs = "<gml:Surface>";
1428            }
1429            pw.println( srs );
1430            int patches = surface.getNumberOfSurfacePatches();
1431            pw.println( "<gml:patches>" );
1432            for ( int i = 0; i < patches; i++ ) {
1433                pw.println( "<gml:PolygonPatch>" );
1434                SurfacePatch patch = surface.getSurfacePatchAt( i );
1435                printExteriorRing( surface, pw, patch );
1436                printInteriorRing( surface, pw, patch );
1437                pw.println( "</gml:PolygonPatch>" );
1438            }
1439            pw.println( "</gml:patches>" );
1440            pw.print( "</gml:Surface>" );
1441    
1442        }
1443    
1444        /**
1445         * @param surface
1446         * @param pw
1447         * @param patch
1448         */
1449        private static void printInteriorRing( Surface surface, PrintWriter pw, SurfacePatch patch ) {
1450            // interior rings
1451            Position[][] ip = patch.getInteriorRings();
1452            if ( ip != null ) {
1453                for ( int j = 0; j < ip.length; j++ ) {
1454                    pw.println( "<gml:interior>" );
1455                    pw.println( "<gml:LinearRing>" );
1456                    if ( surface.getCoordinateSystem() != null ) {
1457                        printPositions( pw, ip[j], surface.getCoordinateDimension() );
1458                    } else {
1459                        printPositions( pw, ip[j], 0 );
1460                    }
1461                    pw.println( "</gml:LinearRing>" );
1462                    pw.println( "</gml:interior>" );
1463                }
1464            }
1465        }
1466    
1467        /**
1468         * @param surface
1469         * @param pw
1470         * @param patch
1471         */
1472        private static void printExteriorRing( Surface surface, PrintWriter pw, SurfacePatch patch ) {
1473            // exterior ring
1474            pw.print( "<gml:exterior>" );
1475            pw.print( "<gml:LinearRing>" );
1476            if ( surface.getCoordinateSystem() != null ) {
1477                printPositions( pw, patch.getExteriorRing(), surface.getCoordinateDimension() );
1478            } else {
1479                printPositions( pw, patch.getExteriorRing(), 0 );
1480            }
1481            pw.print( "</gml:LinearRing>" );
1482            pw.print( "</gml:exterior>" );
1483        }
1484    
1485        /**
1486         * TODO using this method for exporting Surfaces will change to output, so it must be tested
1487         * carefully
1488         * 
1489         * @param pw
1490         * @param ring
1491         * @param coordinateDimension
1492         */
1493        private static void printRing( PrintWriter pw, Ring ring, int coordinateDimension ) {
1494            pw.print( "<gml:Ring><gml:curveMember><gml:Curve><gml:segments>" );
1495            CurveSegment[] cs = ring.getCurveSegments();
1496            for ( int i = 0; i < cs.length; i++ ) {
1497                printCurveSegment( pw, cs[i], coordinateDimension );
1498            }
1499            pw.print( "</gml:segments></gml:Curve></gml:curveMember></gml:Ring>" );
1500    
1501        }
1502    
1503        /**
1504         * 
1505         * @param pw
1506         * @param segment
1507         * @param coordinateDimension
1508         */
1509        private static void printCurveSegment( PrintWriter pw, CurveSegment segment, int coordinateDimension ) {
1510            pw.print( "<gml:LineStringSegment>" );
1511            printPositions( pw, segment.getPositions(), coordinateDimension );
1512            pw.print( "</gml:LineStringSegment>" );
1513        }
1514    
1515        /**
1516         * 
1517         * @param pw
1518         * @param p
1519         * @param coordinateDimension
1520         */
1521        private static void printPositions( PrintWriter pw, Position[] p, int coordinateDimension ) {
1522            StringBuilder posList = new StringBuilder( "<gml:posList" );
1523    
1524            if ( coordinateDimension > 0 ) {
1525                posList.append( " srsDimension='" ).append( coordinateDimension ).append( "' " );
1526            }
1527            posList.append( ">" );
1528            // pw.print( "<gml:posList>" );
1529            pw.print( posList );
1530    
1531            for ( int j = 0; j < ( p.length - 1 ); j++ ) {
1532                pw.print( p[j].getX() + " " + p[j].getY() );
1533                if ( coordinateDimension == 3 ) {
1534                    pw.print( " " + p[j].getZ() + " " );
1535                } else {
1536                    pw.print( ' ' );
1537                }
1538            }
1539            pw.print( p[p.length - 1].getX() + " " + p[p.length - 1].getY() );
1540            if ( coordinateDimension == 3 ) {
1541                pw.print( " " + p[p.length - 1].getZ() );
1542            }
1543            pw.print( "</gml:posList>" );
1544        }
1545    
1546        /**
1547         * @param mp
1548         * @return
1549         * @throws RemoteException
1550         */
1551        private static void exportMultiPoint( MultiPoint mp, PrintWriter pw ) {
1552    
1553            String crs = null;
1554            if ( mp.getCoordinateSystem() != null ) {
1555                crs = mp.getCoordinateSystem().getName().replace( ' ', ':' );
1556            }
1557            String srs = null;
1558            if ( crs != null ) {
1559                srs = "<gml:MultiPoint srsName=\"" + crs + "\">";
1560            } else {
1561                srs = "<gml:MultiPoint>";
1562            }
1563            pw.println( srs );
1564            pw.println( "<gml:pointMembers>" );
1565            for ( int i = 0; i < mp.getSize(); i++ ) {
1566    
1567                pw.println( "<gml:Point>" );
1568                pw.print( "<gml:pos>" );
1569                pw.print( mp.getPointAt( i ).getX() + " " + mp.getPointAt( i ).getY() );
1570                if ( mp.getPointAt( i ).getCoordinateDimension() == 3 ) {
1571                    pw.print( " " + mp.getPointAt( i ).getZ() );
1572                }
1573                pw.println( "</gml:pos>" );
1574                pw.println( "</gml:Point>" );
1575            }
1576            pw.println( "</gml:pointMembers>" );
1577            pw.print( "</gml:MultiPoint>" );
1578    
1579        }
1580    
1581        /**
1582         * @param multiCurve
1583         * @return
1584         * @throws RemoteException
1585         * @throws GeometryException
1586         */
1587        private static void exportMultiCurve( MultiCurve multiCurve, PrintWriter pw )
1588                                throws GeometryException {
1589    
1590            String crs = null;
1591            if ( multiCurve.getCoordinateSystem() != null ) {
1592                crs = multiCurve.getCoordinateSystem().getName().replace( ' ', ':' );
1593            }
1594            String srs = null;
1595            if ( crs != null ) {
1596                srs = "<gml:MultiCurve srsName=\"" + crs + "\">";
1597            } else {
1598                srs = "<gml:MultiCurve>";
1599            }
1600            pw.println( srs );
1601    
1602            Curve[] curves = multiCurve.getAllCurves();
1603            pw.println( "<gml:curveMembers>" );
1604            for ( int i = 0; i < curves.length; i++ ) {
1605                Curve curve = curves[i];
1606                pw.println( "<gml:Curve>" );
1607                pw.println( "<gml:segments>" );
1608                pw.println( "<gml:LineStringSegment>" );
1609                int numberCurveSegments = curve.getNumberOfCurveSegments();
1610                for ( int j = 0; j < numberCurveSegments; j++ ) {
1611                    CurveSegment curveSegment = curve.getCurveSegmentAt( j );
1612                    Position[] p = curveSegment.getAsLineString().getPositions();
1613                    pw.print( "<gml:posList>" );
1614                    for ( int k = 0; k < ( p.length - 1 ); k++ ) {
1615                        pw.print( p[k].getX() + " " + p[k].getY() );
1616                        if ( curve.getCoordinateDimension() == 3 ) {
1617                            pw.print( " " + p[k].getZ() + " " );
1618                        } else {
1619                            pw.print( " " );
1620                        }
1621                    }
1622                    pw.print( p[p.length - 1].getX() + " " + p[p.length - 1].getY() );
1623                    if ( curve.getCoordinateDimension() == 3 ) {
1624                        pw.print( " " + p[p.length - 1].getZ() );
1625                    }
1626                    pw.println( "</gml:posList>" );
1627                }
1628                pw.println( "</gml:LineStringSegment>" );
1629                pw.println( "</gml:segments>" );
1630                pw.println( "</gml:Curve>" );
1631            }
1632            pw.println( "</gml:curveMembers>" );
1633            pw.print( "</gml:MultiCurve>" );
1634    
1635        }
1636    
1637        /**
1638         * @param multiSurface
1639         * @return
1640         * @throws RemoteException
1641         * @throws GeometryException
1642         */
1643        private static void exportMultiSurface( MultiSurface multiSurface, PrintWriter pw )
1644                                throws GeometryException {
1645    
1646            String crs = null;
1647            if ( multiSurface.getCoordinateSystem() != null ) {
1648                crs = multiSurface.getCoordinateSystem().getName().replace( ' ', ':' );
1649            }
1650            String srs = null;
1651            if ( crs != null ) {
1652                srs = "<gml:MultiSurface srsName=\"" + crs + "\">";
1653            } else {
1654                srs = "<gml:MultiSurface>";
1655            }
1656            pw.println( srs );
1657    
1658            Surface[] surfaces = multiSurface.getAllSurfaces();
1659    
1660            pw.println( "<gml:surfaceMembers>" );
1661            for ( int i = 0; i < surfaces.length; i++ ) {
1662                Surface surface = surfaces[i];
1663                exportSurface( surface, pw );
1664            }
1665            pw.println( "</gml:surfaceMembers>" );
1666            // substitution as requested in issue
1667            // http://wald.intevation.org/tracker/index.php?func=detail&aid=477&group_id=27&atid=212
1668            // can be removed if it was inserted correctly
1669            // pw.println( "<gml:surfaceMembers>" );
1670            // for ( int i = 0; i < surfaces.length; i++ ) {
1671            // Surface surface = surfaces[i];
1672            // pw.println( "<gml:Surface>" );
1673            // pw.println( "<gml:patches>" );
1674            // pw.println( "<gml:Polygon>" );
1675            // int numberSurfaces = surface.getNumberOfSurfacePatches();
1676            // for ( int j = 0; j < numberSurfaces; j++ ) {
1677            // SurfacePatch surfacePatch = surface.getSurfacePatchAt( j );
1678            // printExteriorRing( surface, pw, surfacePatch );
1679            // printInteriorRing( surface, pw, surfacePatch );
1680            // }
1681            // pw.println( "</gml:Polygon>" );
1682            // pw.println( "</gml:patches>" );
1683            // pw.println( "</gml:Surface>" );
1684            // }
1685            // pw.println( "</gml:surfaceMembers>" );
1686            pw.print( "</gml:MultiSurface>" );
1687    
1688        }
1689    
1690        /**
1691         * Converts the string representation of a GML geometry object to a corresponding
1692         * <code>Geometry</code>. Notice that GML Boxes will be converted to Surfaces because in ISO
1693         * 19107 Envelopes are no geometries.
1694         * 
1695         * @param gml
1696         * @return corresponding geometry object
1697         * @throws GeometryException
1698         * @throws XMLParsingException
1699         * @deprecated this method cannot provide default SRS information, please use
1700         *             {@link #wrap(String,String)} instead
1701         */
1702        @Deprecated
1703        public static Geometry wrap( String gml )
1704                                throws GeometryException, XMLParsingException {
1705            return wrap( gml, null );
1706        }
1707    
1708        /**
1709         * Converts a GML geometry object to a corresponding <tt>Geometry</tt>. Notice that GML Boxes
1710         * will be converted to Surfaces because in ISO 19107 Envelops are no geometries.
1711         * <p>
1712         * Currently, the following conversions are supported:
1713         * <ul>
1714         * <li>GML Point -> Point
1715         * <li>GML MultiPoint -> MultiPoint
1716         * <li>GML LineString -> Curve
1717         * <li>GML MultiLineString -> MultiCurve
1718         * <li>GML Polygon -> Surface
1719         * <li>GML MultiPolygon -> MultiSurface
1720         * <li>GML Box -> Surface
1721         * <li>GML Curve -> Curve
1722         * <li>GML Surface -> Surface
1723         * <li>GML MultiCurve -> MultiCurve
1724         * <li>GML MultiSurface -> MultiSurface
1725         * </ul>
1726         * <p>
1727         * 
1728         * @param gml
1729         * @return the corresponding <tt>Geometry</tt>
1730         * @throws GeometryException
1731         *             if type unsupported or conversion failed
1732         * @deprecated this method cannot provide default SRS information, please use
1733         *             {@link #wrap(Element,String)} instead
1734         */
1735        @Deprecated
1736        public static Geometry wrap( Element gml )
1737                                throws GeometryException {
1738            return wrap( gml, null );
1739        }
1740    
1741        /**
1742         * returns a Envelope created from Box element
1743         * 
1744         * @param element
1745         *            <boundedBy>
1746         * 
1747         * @return instance of <tt>Envelope</tt>
1748         * 
1749         * @throws XMLParsingException
1750         * @throws InvalidGMLException
1751         * @throws UnknownCRSException
1752         * @deprecated this method cannot provide default SRS information, please use
1753         *             {@link #wrapBox(Element,String)} instead
1754         */
1755        @Deprecated
1756        public static Envelope wrapBox( Element element )
1757                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
1758            return wrapBox( element, null );
1759        }
1760    
1761    }