001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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 }