001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/model/spatialschema/SurfaceImpl.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 org.deegree.framework.log.ILogger;
041 import org.deegree.framework.log.LoggerFactory;
042 import org.deegree.model.crs.CoordinateSystem;
043
044 /**
045 * default implementation of the Surface interface from package deegree.model.spatialschema.
046 * <p>
047 * </p>
048 * for simplicity of the implementation it is assumed that a surface is build from just one surface
049 * patch. this isn't completely conform to the ISO 19107 and the OGC GAIA specification but
050 * sufficient for most applications.
051 * <p>
052 * </p>
053 * It will be extended to fulfill the complete specification as soon as possible.
054 *
055 * @version 05.04.2002
056 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
057 */
058
059 public class SurfaceImpl extends OrientableSurfaceImpl implements Surface, GenericSurface, Serializable {
060 /** Use serialVersionUID for interoperability. */
061 private final static long serialVersionUID = -2148069106391096842L;
062
063 private static final ILogger LOG = LoggerFactory.getLogger( SurfaceImpl.class );
064
065 protected SurfacePatch[] patch = null;
066
067 private double area = 0;
068
069 /**
070 * initializes the surface with default orientation submitting one surface patch.
071 *
072 * @param surfacePatch
073 * patches of the surface.
074 * @throws GeometryException
075 * will be thrown if orientation is invalid
076 */
077 protected SurfaceImpl( SurfacePatch surfacePatch ) throws GeometryException {
078 this( '+', surfacePatch );
079 }
080
081 /**
082 * initializes the surface with default orientation submitting one surface patch.
083 *
084 * @param surfacePatches
085 * patches of the surface.
086 * @throws GeometryException
087 * will be thrown if orientation is invalid
088 */
089 protected SurfaceImpl( SurfacePatch[] surfacePatches ) throws GeometryException {
090 this( '+', surfacePatches );
091 }
092
093 /**
094 * initializes the surface with default orientation submitting one surface patch.
095 *
096 * @param surfacePatches
097 * patches of the surface.
098 * @param crs
099 * @throws GeometryException
100 * will be thrown if orientation is invalid
101 */
102 protected SurfaceImpl( SurfacePatch[] surfacePatches, CoordinateSystem crs ) throws GeometryException {
103 this( '+', surfacePatches );
104 this.crs = crs;
105 }
106
107 /**
108 * initializes the surface submitting the orientation and one surface patch.
109 *
110 * @param orientation
111 * of the surface
112 *
113 * @param surfacePatch
114 * patches of the surface.
115 * @throws GeometryException
116 * will be thrown if orientation is invalid
117 */
118 protected SurfaceImpl( char orientation, SurfacePatch surfacePatch ) throws GeometryException {
119 super( surfacePatch.getCoordinateSystem(), orientation );
120
121 patch = new SurfacePatch[] { surfacePatch };
122
123 setValid( false );
124 }
125
126 /**
127 * initializes the surface submitting the orientation and one surface patch.
128 *
129 * @param orientation
130 * of the surface
131 *
132 * @param surfacePatches
133 * patches of the surface.
134 * @throws GeometryException
135 * will be thrown if orientation is invalid
136 */
137 protected SurfaceImpl( char orientation, SurfacePatch[] surfacePatches ) throws GeometryException {
138 super( surfacePatches[0].getCoordinateSystem(), orientation );
139 patch = surfacePatches;
140 setValid( false );
141 }
142
143 /**
144 * initializes the surface with default orientation submitting the surfaces boundary
145 *
146 * @param boundary
147 * boundary of the surface
148 * @throws GeometryException
149 * will be thrown if orientation is invalid
150 */
151 protected SurfaceImpl( SurfaceBoundary boundary ) throws GeometryException {
152 this( '+', boundary );
153 }
154
155 /**
156 * initializes the surface submitting the orientation and the surfaces boundary.
157 *
158 * @param orientation
159 * of the surface
160 *
161 * @param boundary
162 * boundary of the surface
163 *
164 * @throws GeometryException
165 * will be thrown if orientation is invalid
166 */
167 protected SurfaceImpl( char orientation, SurfaceBoundary boundary ) throws GeometryException {
168 // todo
169 // extracting surface patches from the boundary
170 super( boundary.getCoordinateSystem(), orientation );
171
172 this.boundary = boundary;
173 }
174
175 /**
176 * calculates the centroid and area of the surface
177 */
178 private void calculateCentroidArea() {
179 double x = 0;
180 double y = 0;
181 area = 0;
182 for ( int i = 0; i < patch.length; i++ ) {
183 if ( patch[i].getCentroid() != null && patch[i].getArea() > 0 ) {
184 x += ( patch[i].getCentroid().getX() * patch[i].getArea() );
185 y += ( patch[i].getCentroid().getY() * patch[i].getArea() );
186 }
187 if ( patch[i].getArea() > 0 ) {
188 area += patch[i].getArea();
189 }
190 }
191 if ( area > 0 ) {
192 centroid = GeometryFactory.createPoint( x / area, y / area, this.crs );
193 } else {
194 // fall back
195 centroid = GeometryFactory.createPoint( 0, 0, this.crs );
196 }
197 }
198
199 /**
200 * calculates the boundary and area of the surface
201 */
202 private void calculateBoundary() {
203 // TODO
204 // consider more than one patch
205 try {
206 Ring ext = new RingImpl( patch[0].getExteriorRing(), crs );
207 Position[][] inn_ = patch[0].getInteriorRings();
208 Ring[] inn = null;
209
210 if ( inn_ != null ) {
211 inn = new RingImpl[inn_.length];
212
213 for ( int i = 0; i < inn_.length; i++ ) {
214 inn[i] = new RingImpl( inn_[i], crs );
215 }
216 }
217 boundary = new SurfaceBoundaryImpl( ext, inn );
218 } catch ( Exception e ) {
219 LOG.logError( e.getMessage(), e );
220 throw new RuntimeException( e.getMessage(), e );
221 }
222 }
223
224 /**
225 * calculates area, centroid and the envelope of the surface
226 */
227 @Override
228 protected void calculateParam() {
229 calculateCentroidArea();
230 try {
231 calculateEnvelope();
232 } catch ( GeometryException e ) {
233 LOG.logError( e.getMessage(), e );
234 }
235 calculateBoundary();
236 setValid( true );
237 }
238
239 /**
240 * calculates the envelope of the surface
241 */
242 private void calculateEnvelope()
243 throws GeometryException {
244
245 envelope = patch[0].getEnvelope();
246 for ( int i = 1; i < patch.length; i++ ) {
247 envelope = envelope.merge( patch[i].getEnvelope() );
248 }
249 envelope = GeometryFactory.createEnvelope( envelope.getMin(), envelope.getMax(), getCoordinateSystem() );
250
251 }
252
253 /**
254 * returns the length of all boundaries of the surface in a reference system appropriate for
255 * measuring distances.
256 */
257 public double getPerimeter() {
258 return -1;
259 }
260
261 /**
262 * The operation "area" shall return the area of this GenericSurface. The area of a 2
263 * dimensional geometric object shall be a numeric measure of its surface area Since area is an
264 * accumulation (integral) of the product of two distances, its return value shall be in a unit
265 * of measure appropriate for measuring distances squared.
266 */
267 public double getArea() {
268 if ( !isValid() ) {
269 calculateParam();
270 }
271 return area;
272 }
273
274 public SurfaceBoundary getSurfaceBoundary() {
275 if ( !isValid() ) {
276 calculateParam();
277 }
278 return (SurfaceBoundary) boundary;
279 }
280
281 public int getNumberOfSurfacePatches() {
282 return patch.length;
283 }
284
285 public SurfacePatch getSurfacePatchAt( int index )
286 throws GeometryException {
287 if ( index >= patch.length ) {
288 throw new GeometryException( "invalid index/position to get a patch!" );
289 }
290 return patch[index];
291 }
292
293 /**
294 * checks if this surface is completly equal to the submitted geometry
295 *
296 * @param other
297 * object to compare to
298 */
299 @Override
300 public boolean equals( Object other ) {
301 if ( !super.equals( other ) ) {
302 return false;
303 }
304 if ( !( other instanceof SurfaceImpl ) ) {
305 return false;
306 }
307 if ( envelope == null ) {
308 try {
309 calculateEnvelope();
310 } catch ( GeometryException e1 ) {
311 return false;
312 }
313 }
314 if ( !envelope.equals( ( (Geometry) other ).getEnvelope() ) ) {
315 return false;
316 }
317 try {
318 for ( int i = 0; i < patch.length; i++ ) {
319 if ( !patch[i].equals( ( (Surface) other ).getSurfacePatchAt( i ) ) ) {
320 return false;
321 }
322 }
323 } catch ( Exception e ) {
324 return false;
325 }
326 return true;
327 }
328
329 /**
330 * The operation "dimension" shall return the inherent dimension of this Geometry, which shall
331 * be less than or equal to the coordinate dimension. The dimension of a collection of geometric
332 * objects shall be the largest dimension of any of its pieces. Points are 0-dimensional, curves
333 * are 1-dimensional, surfaces are 2-dimensional, and solids are 3-dimensional.
334 */
335 public int getDimension() {
336 return 2;
337 }
338
339 /**
340 * The operation "coordinateDimension" shall return the dimension of the coordinates that define
341 * this Geometry, which must be the same as the coordinate dimension of the coordinate reference
342 * system for this Geometry.
343 */
344 public int getCoordinateDimension() {
345 return patch[0].getExteriorRing()[0].getCoordinateDimension();
346 }
347
348 /**
349 * @return a shallow copy of the geometry
350 */
351 @Override
352 public Object clone() {
353 Surface s = null;
354 try {
355 Position[] ext = patch[0].getExteriorRing();
356 Position[] c_ext = new Position[ext.length];
357 Position[][] in = patch[0].getInteriorRings();
358 Position[][] c_in = new Position[in.length][];
359 for ( int i = 0; i < c_ext.length; i++ ) {
360 c_ext[i] = new PositionImpl( ext[i].getX(), ext[i].getY(), ext[i].getZ() );
361 }
362 for ( int i = 0; i < in.length; i++ ) {
363 c_in[i] = new Position[in[i].length];
364 for ( int j = 0; j < in[i].length; j++ ) {
365 c_in[i][j] = new PositionImpl( in[i][j].getX(), in[i][j].getY(), in[i][j].getX() );
366 }
367 }
368
369 s = GeometryFactory.createSurface( c_ext, c_in, patch[0].getInterpolation(), getCoordinateSystem() );
370 } catch ( Exception e ) {
371 LOG.logError( e.getMessage(), e );
372 }
373
374 return s;
375 }
376
377 /**
378 * translate each point of the surface with the values of the submitted double array.
379 */
380 @Override
381 public void translate( double[] d ) {
382 for ( int i = 0; i < patch.length; i++ ) {
383 Position[] ext = patch[i].getExteriorRing();
384 Position[][] inn = patch[i].getInteriorRings();
385 for ( int j = 0; j < ext.length; j++ ) {
386 ext[j].translate( d );
387 }
388 if ( inn != null ) {
389 for ( int j = 0; j < inn.length; j++ ) {
390 for ( int k = 0; k < inn[j].length; k++ ) {
391 inn[j][k].translate( d );
392 }
393 }
394 }
395 }
396 setValid( false );
397 }
398
399 /**
400 * The boolean valued operation "intersects" shall return TRUE if this <tt>SurfaceImpl</tt>
401 * intersects with the given <tt>Geometry</t>.
402 * Within a <tt>Complex</tt>, the <tt>Primitives</tt> do not
403 * intersect one another. In general, topologically structured data uses
404 * shared geometric objects to capture intersection information.
405 * @param gmo the <tt>Geometry</tt> to test for intersection
406 * @return true if the <tt>Geometry</tt> intersects with this
407 */
408 @Override
409 public boolean intersects( Geometry gmo ) {
410 if ( !isValid() ) {
411 calculateParam();
412 }
413
414 for ( int i = 0; i < patch.length; i++ ) {
415 if ( patch[i].intersects( gmo ) ) {
416 return true;
417 }
418 }
419 return false;
420 }
421
422 /**
423 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single
424 * point given by a coordinate.
425 * <p>
426 * </p>
427 */
428 @Override
429 public boolean contains( Position position ) {
430 return contains( new PointImpl( position, null ) );
431 }
432
433 /**
434 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another
435 * Geometry.
436 * <p>
437 * </p>
438 */
439 @Override
440 public boolean contains( Geometry gmo ) {
441 if ( !isValid() ) {
442 calculateParam();
443 }
444 return boundary.contains( gmo );
445 }
446
447 /**
448 *
449 *
450 * @return the Stringrepresenation of this surface.
451 */
452 @Override
453 public String toString() {
454 StringBuffer ret = new StringBuffer( 2000 );
455 ret.append( "\n------------------------------------------\n" );
456 ret.append( getClass().getName() ).append( ":\n" );
457 ret.append( "envelope = " ).append( envelope ).append( "\n" );
458 ret.append( "patch = " ).append( patch.length ).append( "\n" );
459 for ( int i = 0; i < patch.length; i++ ) {
460 Position[] pos = patch[i].getExteriorRing();
461 ret.append( "Exterior Ring: \n" );
462 ret.append( "length: " ).append( pos.length ).append( "\n" );
463 for ( int j = 0; j < pos.length; j++ ) {
464 ret.append( pos[j] + "\n" );
465 }
466 }
467 ret.append( "\n------------------------------------------\n" );
468 return ret.toString();
469 }
470 }