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