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