001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/SurfaceImpl.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 043 ---------------------------------------------------------------------------*/ 044 package org.deegree.model.spatialschema; 045 046 import java.io.Serializable; 047 048 import org.deegree.framework.log.ILogger; 049 import org.deegree.framework.log.LoggerFactory; 050 import org.deegree.model.crs.CoordinateSystem; 051 052 /** 053 * default implementation of the Surface interface from package jago.model. 054 * <p> 055 * </p> 056 * for simplicity of the implementation it is assumed that a surface is build from just one surface 057 * patch. this isn't completly confrom to the ISO 19107 and the OGC GAIA specification but 058 * sufficient for most applications. 059 * <p> 060 * </p> 061 * It will be extended to fullfill the complete specs as soon as possible. 062 * 063 * @version 05.04.2002 064 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 065 */ 066 067 public class SurfaceImpl extends OrientableSurfaceImpl implements Surface, GenericSurface, Serializable { 068 /** Use serialVersionUID for interoperability. */ 069 private final static long serialVersionUID = -2148069106391096842L; 070 071 private static final ILogger LOG = LoggerFactory.getLogger( SurfaceImpl.class ); 072 073 protected SurfacePatch[] patch = null; 074 075 private double area = 0; 076 077 /** 078 * initializes the surface with default orientation submitting one surface patch. 079 * 080 * @param surfacePatch 081 * patches of the surface. 082 * @throws GeometryException 083 * will be thrown if orientation is invalid 084 */ 085 public SurfaceImpl( SurfacePatch surfacePatch ) throws GeometryException { 086 this( '+', surfacePatch ); 087 } 088 089 /** 090 * initializes the surface with default orientation submitting one surface patch. 091 * 092 * @param surfacePatches 093 * patches of the surface. 094 * @throws GeometryException 095 * will be thrown if orientation is invalid 096 */ 097 public SurfaceImpl( SurfacePatch[] surfacePatches ) throws GeometryException { 098 this( '+', surfacePatches ); 099 } 100 101 /** 102 * initializes the surface with default orientation submitting one surface patch. 103 * 104 * @param surfacePatches 105 * patches of the surface. 106 * @param crs 107 * @throws GeometryException 108 * will be thrown if orientation is invalid 109 */ 110 public SurfaceImpl( SurfacePatch[] surfacePatches, CoordinateSystem crs ) throws GeometryException { 111 this( '+', surfacePatches ); 112 this.crs = crs; 113 } 114 115 /** 116 * initializes the surface submitting the orientation and one surface patch. 117 * 118 * @param orientation 119 * of the surface 120 * 121 * @param surfacePatch 122 * patches of the surface. 123 * @throws GeometryException 124 * will be thrown if orientation is invalid 125 */ 126 public SurfaceImpl( char orientation, SurfacePatch surfacePatch ) throws GeometryException { 127 super( surfacePatch.getCoordinateSystem(), orientation ); 128 129 patch = new SurfacePatch[] { surfacePatch }; 130 131 setValid( false ); 132 } 133 134 /** 135 * initializes the surface submitting the orientation and one surface patch. 136 * 137 * @param orientation 138 * of the surface 139 * 140 * @param surfacePatches 141 * patches of the surface. 142 * @throws GeometryException 143 * will be thrown if orientation is invalid 144 */ 145 public SurfaceImpl( char orientation, SurfacePatch[] surfacePatches ) throws GeometryException { 146 super( surfacePatches[0].getCoordinateSystem(), orientation ); 147 patch = surfacePatches; 148 setValid( false ); 149 } 150 151 /** 152 * initializes the surface with default orientation submitting the surfaces boundary 153 * 154 * @param boundary 155 * boundary of the surface 156 * @throws GeometryException 157 * will be thrown if orientation is invalid 158 */ 159 public SurfaceImpl( SurfaceBoundary boundary ) throws GeometryException { 160 this( '+', boundary ); 161 } 162 163 /** 164 * initializes the surface submitting the orientation and the surfaces boundary. 165 * 166 * @param orientation 167 * of the surface 168 * 169 * @param boundary 170 * boundary of the surface 171 * 172 * @throws GeometryException 173 * will be thrown if orientation is invalid 174 */ 175 public SurfaceImpl( char orientation, SurfaceBoundary boundary ) throws GeometryException { 176 // todo 177 // extracting surface patches from the boundary 178 super( boundary.getCoordinateSystem(), orientation ); 179 180 this.boundary = boundary; 181 } 182 183 /** 184 * calculates the centroid and area of the surface 185 */ 186 private void calculateCentroidArea() { 187 double x = 0; 188 double y = 0; 189 area = 0; 190 for ( int i = 0; i < patch.length; i++ ) { 191 if ( patch[i].getCentroid() != null && patch[i].getArea() > 0 ) { 192 x += ( patch[i].getCentroid().getX() * patch[i].getArea() ); 193 y += ( patch[i].getCentroid().getY() * patch[i].getArea() ); 194 } 195 if ( patch[i].getArea() > 0 ) { 196 area += patch[i].getArea(); 197 } 198 } 199 if ( area > 0 ) { 200 centroid = GeometryFactory.createPoint( x / area, y / area, this.crs ); 201 } else { 202 // fall back 203 centroid = GeometryFactory.createPoint( 0, 0, this.crs ); 204 } 205 } 206 207 /** 208 * calculates the boundary and area of the surface 209 */ 210 private void calculateBoundary() { 211 // TODO 212 // consider more than one patch 213 try { 214 Ring ext = new RingImpl( patch[0].getExteriorRing(), crs ); 215 Position[][] inn_ = patch[0].getInteriorRings(); 216 Ring[] inn = null; 217 218 if ( inn_ != null ) { 219 inn = new RingImpl[inn_.length]; 220 221 for ( int i = 0; i < inn_.length; i++ ) { 222 inn[i] = new RingImpl( inn_[i], crs ); 223 } 224 } 225 boundary = new SurfaceBoundaryImpl( ext, inn ); 226 } catch ( Exception e ) { 227 LOG.logError( e.getMessage(), e ); 228 throw new RuntimeException( e.getMessage(), e ); 229 } 230 } 231 232 /** 233 * calculates area, centroid and the envelope of the surface 234 */ 235 @Override 236 protected void calculateParam() { 237 calculateCentroidArea(); 238 try { 239 calculateEnvelope(); 240 } catch ( GeometryException e ) { 241 LOG.logError( e.getMessage(), e ); 242 } 243 calculateBoundary(); 244 setValid( true ); 245 } 246 247 /** 248 * calculates the envelope of the surface 249 */ 250 private void calculateEnvelope() 251 throws GeometryException { 252 253 envelope = patch[0].getEnvelope(); 254 for ( int i = 1; i < patch.length; i++ ) { 255 envelope = envelope.merge( patch[i].getEnvelope() ); 256 } 257 envelope = GeometryFactory.createEnvelope( envelope.getMin(), envelope.getMax(), getCoordinateSystem() ); 258 259 } 260 261 /** 262 * returns the length of all boundaries of the surface in a reference system appropriate for 263 * measuring distances. 264 */ 265 public double getPerimeter() { 266 return -1; 267 } 268 269 /** 270 * The operation "area" shall return the area of this GenericSurface. The area of a 2 271 * dimensional geometric object shall be a numeric measure of its surface area Since area is an 272 * accumulation (integral) of the product of two distances, its return value shall be in a unit 273 * of measure appropriate for measuring distances squared. 274 */ 275 public double getArea() { 276 if ( !isValid() ) { 277 calculateParam(); 278 } 279 return area; 280 } 281 282 public SurfaceBoundary getSurfaceBoundary() { 283 if ( !isValid() ) { 284 calculateParam(); 285 } 286 return (SurfaceBoundary) boundary; 287 } 288 289 public int getNumberOfSurfacePatches() { 290 return patch.length; 291 } 292 293 public SurfacePatch getSurfacePatchAt( int index ) 294 throws GeometryException { 295 if ( index >= patch.length ) { 296 throw new GeometryException( "invalid index/position to get a patch!" ); 297 } 298 return patch[index]; 299 } 300 301 /** 302 * checks if this surface is completly equal to the submitted geometry 303 * 304 * @param other 305 * object to compare to 306 */ 307 @Override 308 public boolean equals( Object other ) { 309 if ( !super.equals( other ) ) { 310 return false; 311 } 312 if ( !( other instanceof SurfaceImpl ) ) { 313 return false; 314 } 315 if ( envelope == null ) { 316 try { 317 calculateEnvelope(); 318 } catch ( GeometryException e1 ) { 319 return false; 320 } 321 } 322 if ( !envelope.equals( ( (Geometry) other ).getEnvelope() ) ) { 323 return false; 324 } 325 try { 326 for ( int i = 0; i < patch.length; i++ ) { 327 if ( !patch[i].equals( ( (Surface) other ).getSurfacePatchAt( i ) ) ) { 328 return false; 329 } 330 } 331 } catch ( Exception e ) { 332 return false; 333 } 334 return true; 335 } 336 337 /** 338 * The operation "dimension" shall return the inherent dimension of this Geometry, which shall 339 * be less than or equal to the coordinate dimension. The dimension of a collection of geometric 340 * objects shall be the largest dimension of any of its pieces. Points are 0-dimensional, curves 341 * are 1-dimensional, surfaces are 2-dimensional, and solids are 3-dimensional. 342 */ 343 public int getDimension() { 344 return 2; 345 } 346 347 /** 348 * The operation "coordinateDimension" shall return the dimension of the coordinates that define 349 * this Geometry, which must be the same as the coordinate dimension of the coordinate reference 350 * system for this Geometry. 351 */ 352 public int getCoordinateDimension() { 353 return patch[0].getExteriorRing()[0].getCoordinateDimension(); 354 } 355 356 /** 357 * @return a shallow copy of the geometry 358 */ 359 @Override 360 public Object clone() { 361 Surface s = null; 362 try { 363 s = new SurfaceImpl( getOrientation(), patch ); 364 } catch ( Exception e ) { 365 LOG.logError( e.getMessage(), e ); 366 } 367 368 return s; 369 } 370 371 /** 372 * translate each point of the surface with the values of the submitted double array. 373 */ 374 @Override 375 public void translate( double[] d ) { 376 for ( int i = 0; i < patch.length; i++ ) { 377 Position[] ext = patch[i].getExteriorRing(); 378 Position[][] inn = patch[i].getInteriorRings(); 379 for ( int j = 0; j < ext.length; j++ ) { 380 ext[j].translate( d ); 381 } 382 if ( inn != null ) { 383 for ( int j = 0; j < inn.length; j++ ) { 384 for ( int k = 0; k < inn[j].length; k++ ) { 385 inn[j][k].translate( d ); 386 } 387 } 388 } 389 } 390 setValid( false ); 391 } 392 393 /** 394 * The boolean valued operation "intersects" shall return TRUE if this <tt>SurfaceImpl</tt> 395 * intersects with the given <tt>Geometry</t>. 396 * Within a <tt>Complex</tt>, the <tt>Primitives</tt> do not 397 * intersect one another. In general, topologically structured data uses 398 * shared geometric objects to capture intersection information. 399 * @param gmo the <tt>Geometry</tt> to test for intersection 400 * @return true if the <tt>Geometry</tt> intersects with this 401 */ 402 @Override 403 public boolean intersects( Geometry gmo ) { 404 if ( !isValid() ) { 405 calculateParam(); 406 } 407 408 for ( int i = 0; i < patch.length; i++ ) { 409 if ( patch[i].intersects( gmo ) ) { 410 return true; 411 } 412 } 413 return false; 414 } 415 416 /** 417 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single 418 * point given by a coordinate. 419 * <p> 420 * </p> 421 */ 422 @Override 423 public boolean contains( Position position ) { 424 return contains( new PointImpl( position, null ) ); 425 } 426 427 /** 428 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another 429 * Geometry. 430 * <p> 431 * </p> 432 */ 433 @Override 434 public boolean contains( Geometry gmo ) { 435 if ( !isValid() ) { 436 calculateParam(); 437 } 438 return boundary.contains( gmo ); 439 } 440 441 /** 442 * 443 * 444 * @return the Stringrepresenation of this surface. 445 */ 446 @Override 447 public String toString() { 448 StringBuffer ret = new StringBuffer( 2000 ); 449 ret.append( "\n------------------------------------------\n" ); 450 ret.append( getClass().getName() ).append( ":\n" ); 451 ret.append( "envelope = " ).append( envelope ).append( "\n" ); 452 ret.append( "patch = " ).append( patch.length ).append( "\n" ); 453 for ( int i = 0; i < patch.length; i++ ) { 454 Position[] pos = patch[i].getExteriorRing(); 455 ret.append( "Exterior Ring: \n" ); 456 ret.append( "length: " ).append( pos.length ).append( "\n" ); 457 for ( int j = 0; j < pos.length; j++ ) { 458 ret.append( pos[j] + "\n" ); 459 } 460 } 461 ret.append( "\n------------------------------------------\n" ); 462 return ret.toString(); 463 } 464 }