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