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