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