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 }