001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/spatialschema/SurfacePatchImpl.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2007 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: aschmitz $ 059 * 060 * @version. $Revision: 8136 $, $Date: 2007-09-11 16:18:32 +0200 (Di, 11 Sep 2007) $ 061 */ 062 abstract class SurfacePatchImpl implements GenericSurface, 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 * returns the length of all boundaries of the surface in a reference system appropriate for 194 * measuring distances. 195 */ 196 public double getPerimeter() { 197 return -1; 198 } 199 200 /** 201 * returns the coordinate system of the surface patch 202 */ 203 public CoordinateSystem getCoordinateSystem() { 204 return crs; 205 } 206 207 /** 208 * 209 * 210 * @param other 211 * 212 * @return 213 */ 214 public boolean equals( Object other ) { 215 if ( ( other == null ) || !( other instanceof SurfacePatch ) ) { 216 return false; 217 } 218 219 // Assuming envelope cannot be null (always calculated) 220 if ( !exteriorRing.getEnvelope().equals( ( (SurfacePatch) other ).getEnvelope() ) ) { 221 return false; 222 } 223 224 // check positions of exterior ring 225 Position[] pos1 = exteriorRing.getPositions(); 226 Position[] pos2 = ((SurfacePatch)other).getExteriorRing(); 227 if ( pos1.length != pos2.length ) { 228 return false; 229 } 230 for ( int i = 0; i < pos2.length; i++ ) { 231 if ( !pos1[i].equals( pos2[i] ) ) { 232 return false; 233 } 234 } 235 236 // Assuming either can have interiorRings set to null (not checked 237 // by Constructor) 238 if ( interiorRings != null ) { 239 if ( ( (SurfacePatch) other ).getInteriorRings() == null ) { 240 return false; 241 } 242 if ( interiorRings.length != ( (SurfacePatch) other ).getInteriorRings().length ) { 243 return false; 244 } 245 for ( int i = 0; i < interiorRings.length; i++ ) { 246 // TODO 247 // correct comparing of each point considering current tolerance level 248 } 249 } else { 250 if ( ( (SurfacePatch) other ).getInteriorRings() != null ) { 251 return false; 252 } 253 } 254 255 return true; 256 } 257 258 /** 259 * The operation "centroid" shall return the mathematical centroid for this Geometry. The result 260 * is not guaranteed to be on the object. 261 */ 262 public Point getCentroid() { 263 if ( !isValid() ) { 264 calculateParam(); 265 } 266 return centroid; 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 /** 283 * calculates the centroid (2D) and area (2D + 3D) of the surface patch. 284 */ 285 private void calculateCentroidArea() { 286 287 double varea = calculateArea( exteriorRing.getPositions() ); 288 289 Position centroid_ = calculateCentroid( exteriorRing.getPositions() ); 290 291 double x = centroid_.getX(); 292 double y = centroid_.getY(); 293 294 x *= varea; 295 y *= varea; 296 297 double tmp = 0; 298 if ( interiorRings != null ) { 299 for ( int i = 0; i < interiorRings.length; i++ ) { 300 double dum = calculateArea( interiorRings[i].getPositions() ); 301 tmp += dum; 302 Position temp = 303 calculateCentroid( interiorRings[i].getPositions() ); 304 x += ( temp.getX() * -dum ); 305 y += ( temp.getY() * -dum ); 306 } 307 } 308 309 area = varea; 310 centroid = new PointImpl( x / varea, y / varea, crs ); 311 312 area = varea - tmp; 313 314 } 315 316 /** 317 * calculates the centroid and the area of the surface patch 318 */ 319 protected void calculateParam() { 320 calculateCentroidArea(); 321 setValid( true ); 322 } 323 324 /** 325 * calculates the area of the surface patch 2D: taken from gems iv (modified) 3D: see 326 * http://geometryalgorithms.com/Archive/algorithm_0101/#3D%20Polygons 327 */ 328 private double calculateArea( Position[] point ) { 329 330 double calcArea = 0; 331 332 // two-dimensional 333 if ( point[0].getCoordinateDimension() == 2 ) { 334 int i; 335 int j; 336 double ai; 337 double atmp = 0; 338 339 for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) { 340 double xi = point[i].getX() - point[0].getX(); 341 double yi = point[i].getY() - point[0].getY(); 342 double xj = point[j].getX() - point[0].getX(); 343 double yj = point[j].getY() - point[0].getY(); 344 ai = ( xi * yj ) - ( xj * yi ); 345 atmp += ai; 346 } 347 calcArea = Math.abs( atmp / 2 ); 348 349 } 350 // three-dimensional 351 else if ( point[0].getCoordinateDimension() == 3 ) { 352 353 Vector3d planeNormal = new Vector3d(); 354 planeNormal.cross( sub( point[1], point[0] ), sub( point[2], point[1] ) ); 355 planeNormal.normalize(); 356 357 Vector3d resultVector = new Vector3d(); 358 for ( int i = 0; i < point.length - 1; ++i ) { 359 Vector3d tmp = cross( point[i], point[i + 1] ); 360 resultVector.add( tmp ); 361 } 362 calcArea = ( planeNormal.dot( resultVector ) ) * 0.5; 363 } 364 return calcArea; 365 } 366 367 /** 368 * calculates the centroid of the surface patch 369 * <p> 370 * taken from gems iv (modified) 371 * <p> 372 * </p> 373 * this method is only valid for the two-dimensional case. 374 */ 375 protected Position calculateCentroid( Position[] point ) { 376 377 int i; 378 int j; 379 double ai; 380 double x; 381 double y; 382 double atmp = 0; 383 double xtmp = 0; 384 double ytmp = 0; 385 386 // move points to the origin of the coordinate space 387 // (to solve precision issues) 388 double transX = point[0].getX(); 389 double transY = point[0].getY(); 390 391 for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) { 392 double x1 = point[i].getX() - transX; 393 double y1 = point[i].getY() - transY; 394 double x2 = point[j].getX() - transX; 395 double y2 = point[j].getY() - transY; 396 ai = ( x1 * y2 ) - ( x2 * y1 ); 397 atmp += ai; 398 xtmp += ( ( x2 + x1 ) * ai ); 399 ytmp += ( ( y2 + y1 ) * ai ); 400 } 401 402 if ( atmp != 0 ) { 403 x = xtmp / ( 3 * atmp ) + transX; 404 y = ytmp / ( 3 * atmp ) + transY; 405 } else { 406 x = point[0].getX(); 407 y = point[0].getY(); 408 } 409 410 return new PositionImpl( x, y ); 411 } 412 413 @Override 414 public String toString() { 415 String ret = "SurfacePatch: "; 416 ret = "interpolation = " + interpolation + "\n"; 417 ret += "exteriorRing = \n"; 418 ret += ( exteriorRing + "\n" ); 419 ret += ( "interiorRings = " + interiorRings + "\n" ); 420 ret += ( "envelope = " + getEnvelope() + "\n" ); 421 return ret; 422 } 423 424 /** 425 * this(x,y,z) = a(x,y,z) - b(x,y,z) 426 */ 427 private Vector3d sub( Position a, Position b ) { 428 Vector3d result = new Vector3d( a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ() ); 429 return result; 430 } 431 432 /** 433 * this(x,y,z) = a(x,y,z) x b(x,y,z) 434 */ 435 private Vector3d cross( Position a, Position b ) { 436 Vector3d result = new Vector3d( ( a.getY() * b.getZ() ) - ( a.getZ() * b.getY() ), ( a.getZ() * b.getX() ) 437 - ( a.getX() * b.getZ() ), 438 ( a.getX() * b.getY() ) - ( a.getY() * b.getX() ) ); 439 return result; 440 } 441 }