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