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