001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/SurfacePatchImpl.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 javax.vecmath.Vector3d; 049 050 import org.deegree.model.crs.CoordinateSystem; 051 052 /** 053 * default implementation of the SurfacePatch interface from package jago.model. the class is 054 * abstract because it should be specialized by derived classes <code>Polygon</code> for example 055 * 056 * 057 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 058 * @author last edited by: $Author: apoth $ 059 * 060 * @version. $Revision: 9343 $, $Date: 2007-12-27 14:30:32 +0100 (Do, 27 Dez 2007) $ 061 */ 062 abstract class SurfacePatchImpl implements GenericSurface, SurfacePatch, Serializable { 063 /** Use serialVersionUID for interoperability. */ 064 private final static long serialVersionUID = 7641735268892225180L; 065 066 protected CoordinateSystem crs = null; 067 068 protected Point centroid = null; 069 070 protected SurfaceInterpolation interpolation = null; 071 072 protected Ring exteriorRing = null; 073 074 protected Ring[] interiorRings = null; 075 076 protected double area = 0; 077 078 protected boolean valid = false; 079 080 /** 081 * 082 * @param exteriorRing 083 * @param interiorRings 084 * @param crs 085 */ 086 protected SurfacePatchImpl( Ring exteriorRing, Ring[] interiorRings, CoordinateSystem crs ) { 087 this.exteriorRing = exteriorRing; 088 this.interiorRings = interiorRings; 089 this.crs = crs; 090 } 091 092 /** 093 * Creates a new SurfacePatchImpl object. 094 * 095 * @param interpolation 096 * @param exteriorRing 097 * @param interiorRings 098 * @param crs 099 * 100 * @throws GeometryException 101 */ 102 protected SurfacePatchImpl( SurfaceInterpolation interpolation, Position[] exteriorRing, 103 Position[][] interiorRings, CoordinateSystem crs ) throws GeometryException { 104 this.crs = crs; 105 106 if ( ( exteriorRing == null ) || ( exteriorRing.length < 3 ) ) { 107 throw new GeometryException( "The exterior ring doesn't contains enough point!" ); 108 } 109 110 // check, if the exteriorRing of the polygon is closed 111 // and if the interiorRings (if !=null) are closed 112 if ( !exteriorRing[0].equals( exteriorRing[exteriorRing.length - 1] ) ) { 113 throw new GeometryException( "The exterior ring isn't closed!" ); 114 } 115 116 if ( interiorRings != null ) { 117 for ( int i = 0; i < interiorRings.length; i++ ) { 118 if ( !interiorRings[i][0].equals( interiorRings[i][interiorRings[i].length - 1] ) ) { 119 throw new GeometryException( "The interior ring " + i + " isn't closed!" ); 120 } 121 } 122 } 123 124 this.interpolation = interpolation; 125 this.exteriorRing = new RingImpl( exteriorRing, crs ); 126 if ( interiorRings != null ) { 127 this.interiorRings = new Ring[interiorRings.length]; 128 for ( int i = 0; i < interiorRings.length; i++ ) { 129 this.interiorRings[i] = new RingImpl( interiorRings[i], crs ); 130 } 131 } 132 133 setValid( false ); 134 } 135 136 /** 137 * invalidates the calculated parameters of the Geometry 138 */ 139 protected void setValid( boolean valid ) { 140 this.valid = valid; 141 } 142 143 /** 144 * returns true if the calculated parameters of the Geometry are valid and false if they must be 145 * recalculated 146 */ 147 protected boolean isValid() { 148 return valid; 149 } 150 151 /** 152 * The interpolation determines the surface interpolation mechanism used for this SurfacePatch. 153 * This mechanism uses the control points and control parameters defined in the various 154 * subclasses to determine the position of this SurfacePatch. 155 */ 156 public SurfaceInterpolation getInterpolation() { 157 return interpolation; 158 } 159 160 /** 161 * returns the bounding box of the surface patch 162 */ 163 public Envelope getEnvelope() { 164 return exteriorRing.getEnvelope(); 165 } 166 167 /** 168 * returns a reference to the exterior ring of the surface 169 * 170 * @return exterior ring as Positions 171 */ 172 public Position[] getExteriorRing() { 173 return exteriorRing.getPositions(); 174 } 175 176 /** 177 * returns a reference to the interior rings of the surface 178 * 179 * @return interior rings as Positions 180 */ 181 public Position[][] getInteriorRings() { 182 if ( interiorRings != null ) { 183 Position[][] pos = new Position[interiorRings.length][]; 184 for ( int i = 0; i < pos.length; i++ ) { 185 pos[i] = interiorRings[i].getPositions(); 186 } 187 return pos; 188 } 189 return null; 190 } 191 192 /** 193 * 194 * @return the exterior ring of a surfacePatch 195 */ 196 public Ring getExterior() { 197 return exteriorRing; 198 } 199 200 /** 201 * 202 * @return the interior rings of a surfacePatch 203 */ 204 public Ring[] getInterior() { 205 return interiorRings; 206 } 207 208 /** 209 * returns the length of all boundaries of the surface in a reference system appropriate for 210 * measuring distances. 211 */ 212 public double getPerimeter() { 213 return -1; 214 } 215 216 /** 217 * returns the coordinate system of the surface patch 218 */ 219 public CoordinateSystem getCoordinateSystem() { 220 return crs; 221 } 222 223 @Override 224 public boolean equals( Object other ) { 225 if ( ( other == null ) || !( other instanceof SurfacePatch ) ) { 226 return false; 227 } 228 229 // Assuming envelope cannot be null (always calculated) 230 if ( !exteriorRing.getEnvelope().equals( ( (SurfacePatch) other ).getEnvelope() ) ) { 231 return false; 232 } 233 234 // check positions of exterior ring 235 Position[] pos1 = exteriorRing.getPositions(); 236 Position[] pos2 = ( (SurfacePatch) other ).getExteriorRing(); 237 if ( pos1.length != pos2.length ) { 238 return false; 239 } 240 for ( int i = 0; i < pos2.length; i++ ) { 241 if ( !pos1[i].equals( pos2[i] ) ) { 242 return false; 243 } 244 } 245 246 // Assuming either can have interiorRings set to null (not checked 247 // by Constructor) 248 if ( interiorRings != null ) { 249 if ( ( (SurfacePatch) other ).getInteriorRings() == null ) { 250 return false; 251 } 252 if ( interiorRings.length != ( (SurfacePatch) other ).getInteriorRings().length ) { 253 return false; 254 } 255 for ( int i = 0; i < interiorRings.length; i++ ) { 256 // TODO 257 // correct comparing of each point considering current tolerance level 258 } 259 } else { 260 if ( ( (SurfacePatch) other ).getInteriorRings() != null ) { 261 return false; 262 } 263 } 264 265 return true; 266 } 267 268 /** 269 * The operation "centroid" shall return the mathematical centroid for this Geometry. The result 270 * is not guaranteed to be on the object. 271 */ 272 public Point getCentroid() { 273 if ( !isValid() ) { 274 calculateParam(); 275 } 276 return centroid; 277 } 278 279 /** 280 * The operation "area" shall return the area of this GenericSurface. The area of a 2 281 * dimensional geometric object shall be a numeric measure of its surface area Since area is an 282 * accumulation (integral) of the product of two distances, its return value shall be in a unit 283 * of measure appropriate for measuring distances squared. 284 */ 285 public double getArea() { 286 if ( !isValid() ) { 287 calculateParam(); 288 } 289 return area; 290 } 291 292 /** 293 * calculates the centroid (2D) and area (2D + 3D) of the surface patch. 294 */ 295 private void calculateCentroidArea() { 296 297 double varea = calculateArea( exteriorRing.getPositions() ); 298 299 Position centroid_ = calculateCentroid( exteriorRing.getPositions() ); 300 301 double x = centroid_.getX(); 302 double y = centroid_.getY(); 303 304 x *= varea; 305 y *= varea; 306 307 double tmp = 0; 308 if ( interiorRings != null ) { 309 for ( int i = 0; i < interiorRings.length; i++ ) { 310 double dum = calculateArea( interiorRings[i].getPositions() ); 311 tmp += dum; 312 // TODO add correct centroid calculation 313 // Position temp = calculateCentroid( interiorRings[i].getPositions() ); 314 // x += ( temp.getX() * -dum ); 315 // y += ( temp.getY() * -dum ); 316 } 317 } 318 319 area = varea; 320 centroid = new PointImpl( x / varea, y / varea, crs ); 321 322 area = varea - tmp; 323 324 } 325 326 /** 327 * calculates the centroid and the area of the surface patch 328 */ 329 protected void calculateParam() { 330 calculateCentroidArea(); 331 setValid( true ); 332 } 333 334 /** 335 * calculates the area of the surface patch 2D: taken from gems iv (modified) 3D: see 336 * http://geometryalgorithms.com/Archive/algorithm_0101/#3D%20Polygons 337 */ 338 private double calculateArea( Position[] point ) { 339 340 double calcArea = 0; 341 342 // two-dimensional 343 if ( point[0].getCoordinateDimension() == 2 ) { 344 int i; 345 int j; 346 double ai; 347 double atmp = 0; 348 349 for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) { 350 double xi = point[i].getX() - point[0].getX(); 351 double yi = point[i].getY() - point[0].getY(); 352 double xj = point[j].getX() - point[0].getX(); 353 double yj = point[j].getY() - point[0].getY(); 354 ai = ( xi * yj ) - ( xj * yi ); 355 atmp += ai; 356 } 357 calcArea = Math.abs( atmp / 2 ); 358 359 } 360 // three-dimensional 361 else if ( point[0].getCoordinateDimension() == 3 ) { 362 363 Vector3d planeNormal = new Vector3d(); 364 planeNormal.cross( sub( point[1], point[0] ), sub( point[2], point[1] ) ); 365 planeNormal.normalize(); 366 367 Vector3d resultVector = new Vector3d(); 368 for ( int i = 0; i < point.length - 1; ++i ) { 369 Vector3d tmp = cross( point[i], point[i + 1] ); 370 resultVector.add( tmp ); 371 } 372 calcArea = ( planeNormal.dot( resultVector ) ) * 0.5; 373 } 374 return calcArea; 375 } 376 377 /** 378 * calculates the centroid of the surface patch 379 * <p> 380 * taken from gems iv (modified) 381 * <p> 382 * </p> 383 * this method is only valid for the two-dimensional case. 384 */ 385 protected Position calculateCentroid( Position[] point ) { 386 387 int i; 388 int j; 389 double ai; 390 double x; 391 double y; 392 double atmp = 0; 393 double xtmp = 0; 394 double ytmp = 0; 395 396 // move points to the origin of the coordinate space 397 // (to solve precision issues) 398 double transX = point[0].getX(); 399 double transY = point[0].getY(); 400 401 for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) { 402 double x1 = point[i].getX() - transX; 403 double y1 = point[i].getY() - transY; 404 double x2 = point[j].getX() - transX; 405 double y2 = point[j].getY() - transY; 406 ai = ( x1 * y2 ) - ( x2 * y1 ); 407 atmp += ai; 408 xtmp += ( ( x2 + x1 ) * ai ); 409 ytmp += ( ( y2 + y1 ) * ai ); 410 } 411 412 if ( atmp != 0 ) { 413 x = xtmp / ( 3 * atmp ) + transX; 414 y = ytmp / ( 3 * atmp ) + transY; 415 } else { 416 x = point[0].getX(); 417 y = point[0].getY(); 418 } 419 420 return new PositionImpl( x, y ); 421 } 422 423 @Override 424 public String toString() { 425 String ret = "SurfacePatch: "; 426 ret = "interpolation = " + interpolation + "\n"; 427 ret += "exteriorRing = \n"; 428 ret += ( exteriorRing + "\n" ); 429 ret += ( "interiorRings = " + interiorRings + "\n" ); 430 ret += ( "envelope = " + getEnvelope() + "\n" ); 431 return ret; 432 } 433 434 /** 435 * this(x,y,z) = a(x,y,z) - b(x,y,z) 436 */ 437 private Vector3d sub( Position a, Position b ) { 438 Vector3d result = new Vector3d( a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ() ); 439 return result; 440 } 441 442 /** 443 * this(x,y,z) = a(x,y,z) x b(x,y,z) 444 */ 445 private Vector3d cross( Position a, Position b ) { 446 Vector3d result = new Vector3d( ( a.getY() * b.getZ() ) - ( a.getZ() * b.getY() ), ( a.getZ() * b.getX() ) 447 - ( a.getX() * b.getZ() ), 448 ( a.getX() * b.getY() ) - ( a.getY() * b.getX() ) ); 449 return result; 450 } 451 }