001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/model/spatialschema/GeometryImpl.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 Geometry interface from package deegree.model. The implementation is abstract because
046 * only the management of the spatial reference system is unique for all geometries.
047 *
048 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
049 *
050 * @author last edited by: $Author: mschneider $
051 *
052 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
053 *
054 */
055 public abstract class GeometryImpl implements Geometry, Serializable {
056
057 private static final ILogger LOG = LoggerFactory.getLogger( GeometryImpl.class );
058
059 private final static long serialVersionUID = 130728662284673112L;
060
061 protected double mute = 0.000000001;
062
063 protected CoordinateSystem crs = null;
064
065 protected Boundary boundary = null;
066
067 protected Envelope envelope = null;
068
069 protected Geometry convexHull = null;
070
071 protected Point centroid = null;
072
073 protected boolean empty = true;
074
075 protected boolean valid = false;
076
077 /**
078 * constructor that sets the spatial reference system
079 *
080 * @param crs
081 * new spatial reference system
082 */
083 protected GeometryImpl( CoordinateSystem crs ) {
084 setCoordinateSystem( crs );
085 }
086
087 /**
088 * @return the spatial reference system of a geometry
089 */
090 public CoordinateSystem getCoordinateSystem() {
091 return crs;
092 }
093
094 /**
095 * sets the spatial reference system
096 *
097 * @param crs
098 * new spatial reference system
099 */
100 public void setCoordinateSystem( CoordinateSystem crs ) {
101 this.crs = crs;
102 valid = false;
103 }
104
105 @Override
106 public Object clone()
107 throws CloneNotSupportedException {
108 throw new CloneNotSupportedException();
109 }
110
111 /**
112 * @return true if no geometry values resp. points stored within the geometry.
113 */
114 public boolean isEmpty() {
115 return empty;
116 }
117
118 /**
119 *
120 * @param empty
121 * indicates the geometry as empty
122 */
123 public void setEmpty( boolean empty ) {
124 this.empty = empty;
125 }
126
127 /**
128 * returns the boundary of the surface as general boundary
129 *
130 */
131 public Boundary getBoundary() {
132 if ( !isValid() ) {
133 calculateParam();
134 }
135 return boundary;
136 }
137
138 /**
139 * dummy implementation of this method
140 */
141 public void translate( double[] d ) {
142 setValid( false );
143 }
144
145 /**
146 * <p>
147 * The operation "distance" shall return the distance between this Geometry and another Geometry. This distance is
148 * defined to be the greatest lower bound of the set of distances between all pairs of points that include one each
149 * from each of the two Geometries. A "distance" value shall be a positive number associated to distance units such
150 * as meters or standard foot. If necessary, the second geometric object shall be transformed into the same
151 * coordinate reference system as the first before the distance is calculated.
152 * </p>
153 * <p>
154 * If the geometric objects overlap, or touch, then their distance apart shall be zero. Some current implementations
155 * use a "negative" distance for such cases, but the approach is neither consistent between implementations, nor
156 * theoretically viable.
157 * </p>
158 */
159 public double distance( Geometry gmo ) {
160 try {
161 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
162 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( gmo );
163 return jtsThis.distance( jtsThat );
164 } catch ( GeometryException e ) {
165 LOG.logError( e.getMessage(), e );
166 return -1;
167 }
168 }
169
170 /**
171 * <p>
172 * The operation "centroid" shall return the mathematical centroid for this Geometry. The result is not guaranteed
173 * to be on the object. For heterogeneous collections of primitives, the centroid only takes into account those of
174 * the largest dimension. For example, when calculating the centroid of surfaces, an average is taken weighted by
175 * area. Since curves have no area they do not contribute to the average.
176 * </p>
177 */
178 public Point getCentroid() {
179 if ( !isValid() ) {
180 calculateParam();
181 }
182 return centroid;
183 }
184
185 /**
186 * returns the bounding box / envelope of a geometry
187 */
188 public Envelope getEnvelope() {
189 if ( !isValid() ) {
190 calculateParam();
191 }
192 return envelope;
193 }
194
195 /**
196 * The operation "convexHull" shall return a Geometry that represents the convex hull of this Geometry.
197 *
198 * @see java.lang.UnsupportedOperationException an may has an useful implementation in extending classes
199 */
200 public Geometry getConvexHull() {
201 throw new UnsupportedOperationException();
202 }
203
204 /**
205 * The operation "buffer" shall return a Geometry containing all points whose distance from this Geometry is less
206 * than or equal to the "distance" passed as a parameter. The Geometry returned is in the same reference system as
207 * this original Geometry. The dimension of the returned Geometry is normally the same as the coordinate dimension -
208 * a collection of Surfaces in 2D space and a collection of Solids in 3D space, but this may be application defined.
209 */
210 public Geometry getBuffer( double distance ) {
211 return getBuffer( distance, 36, BUFFER_CAP_ROUND );
212 }
213
214 /*
215 * (non-Javadoc)
216 *
217 * @see org.deegree.model.spatialschema.Geometry#getBuffer(double, int, int)
218 */
219 public Geometry getBuffer( double distance, int segments, int capStyle ) {
220 try {
221 // let JTS do the hard work
222 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
223 jtsThis = jtsThis.buffer( distance, segments, capStyle );
224 Geometry geom = JTSAdapter.wrap( jtsThis );
225 ( (GeometryImpl) geom ).setCoordinateSystem( getCoordinateSystem() );
226 return geom;
227 } catch ( GeometryException e ) {
228 LOG.logError( e.getMessage(), e );
229 return this;
230 }
231 }
232
233 /**
234 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another Geometry.
235 *
236 * @param that
237 * the Geometry to test (whether is is contained)
238 * @return true if the given object is contained, else false
239 */
240 public boolean contains( Geometry that ) {
241 try {
242 // let JTS do the hard work
243 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
244 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
245 return jtsThis.contains( jtsThat );
246
247 } catch ( GeometryException e ) {
248 LOG.logError( e.getMessage(), e );
249 return false;
250 }
251 }
252
253 /**
254 * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single point given by a
255 * coordinate.
256 *
257 * @param position
258 * Position to test (whether is is contained)
259 * @return true if the given object is contained, else false
260 */
261 public boolean contains( Position position ) {
262 return contains( new PointImpl( position, null ) );
263 }
264
265 /**
266 * The Boolean valued operation "intersects" shall return TRUE if this Geometry intersects another Geometry. Within
267 * a Complex, the Primitives do not intersect one another. In general, topologically structured data uses shared
268 * geometric objects to capture intersection information.
269 *
270 * @param that
271 * the Geometry to intersect with
272 * @return true if the objects intersects, else false
273 */
274 public boolean intersects( Geometry that ) {
275 try {
276 // let JTS do the hard work
277 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
278 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
279 return jtsThis.intersects( jtsThat );
280
281 } catch ( GeometryException e ) {
282 LOG.logError( "", e );
283 return false;
284 }
285 }
286
287 /**
288 * The "union" operation shall return the set theoretic union of this Geometry and the passed Geometry.
289 *
290 * @param that
291 * the Geometry to unify
292 * @return intersection or null, if computation failed
293 */
294 public Geometry union( Geometry that ) {
295 Geometry union = null;
296
297 try {
298 // let JTS do the hard work
299 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
300 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
301 com.vividsolutions.jts.geom.Geometry jtsUnion = jtsThis.union( jtsThat );
302
303 if ( !jtsUnion.isEmpty() ) {
304 union = JTSAdapter.wrap( jtsUnion );
305 ( (GeometryImpl) union ).setCoordinateSystem( getCoordinateSystem() );
306 }
307 } catch ( GeometryException e ) {
308 LOG.logError( e.getLocalizedMessage(), e );
309 }
310 return union;
311 }
312
313 /**
314 * The "intersection" operation shall return the set theoretic intersection of this <tt>Geometry</tt> and the passed
315 * <tt>Geometry</tt>.
316 *
317 * @param that
318 * the Geometry to intersect with
319 * @return intersection or null, if it is empty (or computation failed)
320 */
321 public Geometry intersection( Geometry that )
322 throws GeometryException {
323
324 Geometry intersection = null;
325
326 // let JTS do the hard work
327 com.vividsolutions.jts.geom.Geometry jtsIntersection = null;
328 try {
329 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
330 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
331 jtsIntersection = jtsThis.intersection( jtsThat );
332 } catch ( Exception e ) {
333 // LOG.logError( e.getMessage(), e );
334 throw new GeometryException( e.getLocalizedMessage() );
335 }
336
337 if ( jtsIntersection != null && !jtsIntersection.isEmpty() ) {
338 intersection = JTSAdapter.wrap( jtsIntersection );
339 ( (GeometryImpl) intersection ).setCoordinateSystem( getCoordinateSystem() );
340 }
341
342 return intersection;
343 }
344
345 /**
346 * The "difference" operation shall return the set theoretic difference of this Geometry and the passed Geometry.
347 *
348 * @param that
349 * the Geometry to calculate the difference with
350 * @return difference or null, if it is empty (or computation failed)
351 */
352 public Geometry difference( Geometry that ) {
353 Geometry difference = null;
354
355 try {
356 // let JTS do the hard work
357 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
358 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
359 com.vividsolutions.jts.geom.Geometry jtsDifference = jtsThis.difference( jtsThat );
360
361 if ( !jtsDifference.isEmpty() ) {
362 difference = JTSAdapter.wrap( jtsDifference );
363 ( (GeometryImpl) difference ).setCoordinateSystem( getCoordinateSystem() );
364 }
365 } catch ( GeometryException e ) {
366 LOG.logError( "", e );
367 }
368 return difference;
369 }
370
371 /**
372 * Compares the Geometry to be equal to another Geometry.
373 *
374 * @param that
375 * the Geometry to test for equality
376 * @return true if the objects are equal, else false
377 */
378 @Override
379 public boolean equals( Object that ) {
380 if ( ( that == null ) || !( that instanceof GeometryImpl ) ) {
381 return false;
382 }
383 if ( crs != null ) {
384 if ( !crs.equals( ( (Geometry) that ).getCoordinateSystem() ) ) {
385 return false;
386 }
387 } else {
388 if ( ( (Geometry) that ).getCoordinateSystem() != null ) {
389 return false;
390 }
391 }
392
393 // do not add JTS calls here!!!!
394
395 return true;
396
397 }
398
399 /**
400 * provide optimized proximity queries within for a distance . calvin added on 10/21/2003
401 */
402 public boolean isWithinDistance( Geometry that, double distance ) {
403 if ( that == null )
404 return false;
405 try {
406 // let JTS do the hard work
407 com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
408 com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
409 return jtsThis.isWithinDistance( jtsThat, distance );
410 } catch ( GeometryException e ) {
411 LOG.logError( e.getMessage(), e );
412 return false;
413 }
414
415 }
416
417 /**
418 * sets tolerance value use for topological operations
419 *
420 * @param tolerance
421 */
422 public void setTolerance( double tolerance ) {
423 mute = tolerance;
424 }
425
426 /**
427 * returns the tolerance value use for topological operations
428 *
429 * @return tolerance value use for topological operations
430 */
431 public double getTolerance() {
432 return mute;
433 }
434
435 /**
436 *
437 * @param valid
438 * invalidates the calculated parameters of the Geometry
439 */
440 protected void setValid( boolean valid ) {
441 this.valid = valid;
442 }
443
444 /**
445 * @return true if the calculated parameters of the Geometry are valid and false if they must be recalculated
446 */
447 protected boolean isValid() {
448 return valid;
449 }
450
451 /**
452 * recalculates internal parameters
453 */
454 protected abstract void calculateParam();
455
456 @Override
457 public String toString() {
458 String ret = null;
459 ret = "CoordinateSystem = " + crs + "\n";
460 ret += ( "empty = " + empty + "\n" );
461 ret += ( "mute = " + mute + "\n" );
462 return ret;
463 }
464 }