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 }