001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/RingImpl.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     53177 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    import java.util.Arrays;
048    
049    import org.deegree.framework.log.ILogger;
050    import org.deegree.framework.log.LoggerFactory;
051    import org.deegree.model.crs.CoordinateSystem;
052    
053    /**
054     * default implementation of the Ring interface of the
055     * 
056     * 
057     * @version $Revision: 9343 $
058     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
059     * @author last edited by: $Author: apoth $
060     * 
061     * @version 1.0. $Revision: 9343 $, $Date: 2007-12-27 14:30:32 +0100 (Do, 27 Dez 2007) $
062     * 
063     * @since 2.0
064     */
065    public class RingImpl extends OrientableCurveImpl implements Ring, Serializable {
066        /** Use serialVersionUID for interoperability. */
067        private final static long serialVersionUID = 9157144642050604928L;
068    
069        private static final ILogger LOG = LoggerFactory.getLogger( RingImpl.class );
070    
071        private CurveSegment[] segments;
072    
073        private SurfacePatch sp = null;
074    
075        private int nop = 0;
076    
077        private Position[] allPos;
078    
079        /**
080         * Constructor, with an array and CoordinateSystem
081         * 
082         * @param points
083         * @param crs
084         * @throws GeometryException
085         */
086        public RingImpl( Position[] points, CoordinateSystem crs ) throws GeometryException {
087            super( crs );
088            Position[][] tmp = new Position[1][];
089            tmp[0] = points;
090            setPositions( tmp );
091        }
092    
093        /**
094         * Constructor, with an array, CoordinateSystem and Orientation
095         * 
096         * @param points
097         * @param crs
098         * @param orientation
099         * @throws GeometryException
100         */
101        public RingImpl( Position[] points, CoordinateSystem crs, char orientation ) throws GeometryException {
102            super( crs, orientation );
103            Position[][] tmp = new Position[1][];
104            tmp[0] = points;
105            setPositions( tmp );
106        }
107    
108        /**
109         * Constructor, with curve segments, CoordinateSystem and Orientation
110         * 
111         * @param segments
112         * @param crs
113         * @param orientation
114         * @throws GeometryException
115         */
116        public RingImpl( CurveSegment[] segments, CoordinateSystem crs, char orientation ) throws GeometryException {
117            super( crs, orientation );
118            Position[][] tmp = new Position[segments.length][];
119            for ( int i = 0; i < segments.length; i++ ) {
120                tmp[i] = segments[i].getPositions();
121            }
122            setPositions( tmp );
123        }
124    
125        /**
126         * calculates the envelope
127         */
128        private void calculateEnvelope() {
129            double[] min = allPos[0].getAsArray().clone();
130            double[] max = min.clone();
131    
132            for ( int k = 1; k < allPos.length; k++ ) {
133                double[] pos = allPos[k].getAsArray();
134    
135                for ( int j = 0; j < pos.length; j++ ) {
136                    if ( pos[j] < min[j] ) {
137                        min[j] = pos[j];
138                    } else if ( pos[j] > max[j] ) {
139                        max[j] = pos[j];
140                    }
141                }
142            }
143    
144            envelope = new EnvelopeImpl( new PositionImpl( min ), new PositionImpl( max ), this.crs );
145        }
146    
147        /**
148         * Ring must be closed, so isCycle returns TRUE.
149         */
150        public boolean isCycle() {
151            return true;
152        }
153    
154        /**
155         * Ring is a PrimitiveBoundary, so isSimple returns TRUE.
156         */
157        public boolean isSimple() {
158            return true;
159        }
160    
161        /**
162         * The operation "dimension" shall return the inherent dimension of this Geometry, which shall
163         * be less than or equal to the coordinate dimension. The dimension of a collection of geometric
164         * objects shall be the largest dimension of any of its pieces. Points are 0-dimensional, curves
165         * are 1-dimensional, surfaces are 2-dimensional, and solids are 3-dimensional.
166         */
167        public int getDimension() {
168            return 1;
169        }
170    
171        /**
172         * The operation "coordinateDimension" shall return the dimension of the coordinates that define
173         * this Geometry, which must be the same as the coordinate dimension of the coordinate reference
174         * system for this Geometry.
175         */
176        public int getCoordinateDimension() {
177            return getPositions()[0].getCoordinateDimension();
178        }
179    
180        /**
181         * gets the Ring as a Array of positions.
182         */
183        public Position[] getPositions() {
184            if ( getOrientation() == '-' ) {
185                Position[] temp = new Position[allPos.length];
186    
187                for ( int i = 0; i < allPos.length; i++ ) {
188                    temp[i] = allPos[( allPos.length - 1 ) - i];
189                }
190    
191                return temp;
192            }
193            return allPos;
194        }
195    
196        /**
197         * sets the Ring as a ArrayList of points
198         */
199        protected void setPositions( Position[][] positions )
200                                throws GeometryException {
201    
202            segments = new CurveSegment[positions.length];
203            for ( int i = 0; i < positions.length; i++ ) {
204                segments[i] = new LineStringImpl( positions[i], getCoordinateSystem() );
205            }
206    
207            nop = 0;
208            for ( int i = 0; i < positions.length; i++ ) {
209                nop += positions[i].length;
210            }
211            allPos = new Position[nop];
212            int k = 0;
213            for ( int i = 0; i < positions.length; i++ ) {
214                for ( int j = 0; j < positions[i].length; j++ ) {
215                    allPos[k++] = positions[i][j];
216                }
217            }
218    
219            // checks if the ring has more than 3 elements [!(points.length > 3)]
220            if ( nop < 3 ) {
221                throw new GeometryException( "invalid length of a Ring!" );
222            }
223    
224            // checks if the startpoint = endpoint of the ring
225            if ( !allPos[0].equals( allPos[allPos.length - 1] ) ) {
226                throw new GeometryException( "StartPoint of ring isn't equal to EndPoint!" );
227            }
228    
229            setValid( false );
230        }
231    
232        /**
233         * returns the Ring as one CurveSegment
234         */
235        public CurveSegment getAsCurveSegment()
236                                throws GeometryException {
237            return new LineStringImpl( allPos, getCoordinateSystem() );
238        }
239    
240        /**
241         * returns the Ring as a CurveSegments
242         * 
243         * @return curve segments
244         */
245        public CurveSegment[] getCurveSegments() {
246            return segments;
247        }
248    
249        /**
250         * returns the CurveBoundary of the Ring. For a CurveBoundary is defines as the first and the
251         * last point of a Curve the CurveBoundary of a Ring contains two indentical point (because a
252         * Ring is closed)
253         */
254        public CurveBoundary getCurveBoundary() {
255            return (CurveBoundary) boundary;
256        }
257    
258        /**
259         * checks if this curve segment is completly equal to the submitted geometry
260         * 
261         * @param other
262         *            object to compare to
263         */
264        public boolean equals( Object other ) {
265            if ( !super.equals( other ) || !( other instanceof RingImpl ) ) {
266                return false;
267            }
268    
269            if ( !envelope.equals( ( (Geometry) other ).getEnvelope() ) ) {
270                return false;
271            }
272    
273            Position[] p2 = ( (Ring) other ).getPositions();
274    
275            if ( !Arrays.equals( allPos, p2 ) ) {
276                return false;
277            }
278    
279            return true;
280        }
281    
282        /**
283         * returns a shallow copy of the geometry
284         */
285        public Object clone() {
286            Ring r = null;
287            try {
288                CurveSegment[] segments = getCurveSegments();
289                for ( int i = 0; i < segments.length; i++ ) {
290                    segments[i] = new LineStringImpl( segments[i].getPositions(), getCoordinateSystem() );
291                }
292                r = new RingImpl( segments, getCoordinateSystem(), getOrientation() );
293            } catch ( Exception ex ) {
294                LOG.logError( ex.getMessage(), ex );
295            }
296    
297            return r;
298        }
299    
300        /**
301         * The Boolean valued operation "intersects" shall return TRUE if this Geometry intersects
302         * another Geometry. Within a Complex, the Primitives do not intersect one another. In general,
303         * topologically structured data uses shared geometric objects to capture intersection
304         * information.
305         * 
306         * @param gmo
307         * @return true if intersects
308         */
309        public boolean intersects( Geometry gmo ) {
310            boolean inter = false;
311    
312            try {
313                // TODO
314                // use segments
315                CurveSegment sp = new LineStringImpl( allPos, crs );
316    
317                if ( gmo instanceof Point ) {
318                    double tolerance = ( (Point) gmo ).getTolerance();
319                    inter = LinearIntersects.intersects( ( (Point) gmo ).getPosition(), sp, tolerance );
320                } else if ( gmo instanceof Curve ) {
321                    Curve curve = new CurveImpl( new CurveSegment[] { sp } );
322                    inter = LinearIntersects.intersects( (Curve) gmo, curve );
323                } else if ( gmo instanceof Surface ) {
324                    Curve curve = new CurveImpl( new CurveSegment[] { sp } );
325                    inter = LinearIntersects.intersects( curve, (Surface) gmo );
326                } else if ( gmo instanceof MultiPrimitive ) {
327                    inter = intersectsAggregate( (MultiPrimitive) gmo );
328                }
329            } catch ( Exception e ) {
330                LOG.logError( e.getMessage(), e );
331            }
332    
333            return inter;
334        }
335    
336        /**
337         * the operations returns true if the submitted multi primitive intersects with the curve
338         * segment
339         */
340        private boolean intersectsAggregate( Aggregate mprim )
341                                throws Exception {
342            boolean inter = false;
343    
344            int cnt = mprim.getSize();
345    
346            for ( int i = 0; i < cnt; i++ ) {
347                if ( intersects( mprim.getObjectAt( i ) ) ) {
348                    inter = true;
349                    break;
350                }
351            }
352    
353            return inter;
354        }
355    
356        /**
357         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another
358         * Geometry.
359         * <p>
360         * </p>
361         * At the moment the operation just works with point geometries
362         */
363        public boolean contains( Geometry gmo ) {
364    
365            try {
366                if ( sp == null ) {
367                    sp = new PolygonImpl( new SurfaceInterpolationImpl(), allPos, null, crs );
368                }
369                return sp.contains( gmo );
370            } catch ( Exception e ) {
371                LOG.logError( e.getMessage(), e );
372            }
373    
374            return false;
375        }
376    
377        /**
378         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single
379         * point given by a coordinate.
380         * 
381         * @param position
382         * @return true if Ring contains passed position
383         */
384        public boolean contains( Position position ) {
385            return contains( new PointImpl( position, null ) );
386        }
387    
388        /**
389         * calculates the centroid of the ring
390         */
391        protected void calculateCentroid() {
392            double[] cen = new double[getCoordinateDimension()];
393    
394            for ( int k = 0; k < allPos.length; k++ ) {
395                for ( int j = 0; j < getCoordinateDimension(); j++ ) {
396                    cen[j] += ( allPos[k].getAsArray()[j] / allPos.length );
397                }
398            }
399    
400            centroid = new PointImpl( new PositionImpl( cen ), crs );
401        }
402    
403        /**
404         * calculates the centroid and the envelope of the ring
405         */
406        protected void calculateParam() {
407            calculateCentroid();
408            calculateEnvelope();
409            setValid( true );
410        }
411    
412        /**
413         * 
414         * @return string representation
415         */
416        public String toString() {
417            String ret = null;
418            ret = "segements = " + segments.length + "\n";
419            ret += ( "envelope = " + envelope + "\n" );
420            return ret;
421        }
422    }