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