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