001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/model/spatialschema/CurveImpl.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.util.ArrayList;
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
046     *
047     * @see org.deegree.model.spatialschema.Curve
048     *
049     * @author Andreas Poth
050     * @version $Revision: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
051     */
052    public class CurveImpl extends OrientableCurveImpl implements Curve, GenericCurve {
053    
054        private static final ILogger LOG = LoggerFactory.getLogger( CurveImpl.class );
055    
056        /** Use serialVersionUID for interoperability. */
057        private final static long serialVersionUID = 4060425075179654976L;
058    
059        protected ArrayList<CurveSegment> segments = null;
060    
061        /**
062         * initialize the curve by submitting a spatial reference system and an array of curve segments. the orientation of
063         * the curve is '+'
064         *
065         * @param segments
066         *            array of CurveSegment
067         * @throws GeometryException
068         */
069        protected CurveImpl( CurveSegment segments ) throws GeometryException {
070            this( '+', new CurveSegment[] { segments } );
071        }
072    
073        /**
074         * initialize the curve by submitting a spatial reference system and an array of curve segments. the orientation of
075         * the curve is '+'
076         *
077         * @param segments
078         *            array of CurveSegment
079         * @throws GeometryException
080         */
081        protected CurveImpl( CurveSegment[] segments ) throws GeometryException {
082            this( '+', segments );
083        }
084    
085        /**
086         * initialize the curve by submitting a spatial reference system and an array of curve segments. the orientation of
087         * the curve is '+'
088         *
089         * @param segments
090         *            array of CurveSegment
091         * @param crs
092         * @throws GeometryException
093         */
094        protected CurveImpl( CurveSegment[] segments, CoordinateSystem crs ) throws GeometryException {
095            this( '+', segments );
096            this.crs = crs;
097        }
098    
099        /**
100         * initialize the curve by submitting a spatial reference system, an array of curve segments and the orientation of
101         * the curve
102         *
103         * @param segments
104         *            array of CurveSegment
105         * @param orientation
106         *            of the curve
107         * @throws GeometryException
108         */
109        protected CurveImpl( char orientation, CurveSegment[] segments ) throws GeometryException {
110            super( segments[0].getCoordinateSystem(), orientation );
111            this.segments = new ArrayList<CurveSegment>( segments.length );
112            for ( int i = 0; i < segments.length; i++ ) {
113                this.segments.add( segments[i] );
114                // TODO check if segments touch
115                if ( i > 0 ) {
116                    if ( !segments[i - 1].getEndPoint().equals( segments[i].getStartPoint() ) ) {
117                        String msg = "Topological error in Curve: end-point of segment " + ( i - 1 ) + ": "
118                                     + WKTAdapter.export( segments[i - 1].getEndPoint() )
119                                     + "doesn't match start-point of segment " + i + ": "
120                                     + WKTAdapter.export( segments[i].getStartPoint() ) + "!";
121                        throw new GeometryException( msg );
122                    }
123                }
124            }
125            setValid( false );
126        }
127    
128        /**
129         * calculates the envelope of the Curve
130         */
131        private void calculateEnvelope() {
132            try {
133                Position[] positions = getAsLineString().getPositions();
134    
135                double[] min = positions[0].getAsArray().clone();
136                double[] max = min.clone();
137    
138                for ( int i = 1; i < positions.length; i++ ) {
139                    double[] pos = positions[i].getAsArray();
140    
141                    for ( int j = 0; j < pos.length; j++ ) {
142                        if ( pos[j] < min[j] ) {
143                            min[j] = pos[j];
144                        } else if ( pos[j] > max[j] ) {
145                            max[j] = pos[j];
146                        }
147                    }
148                }
149    
150                envelope = new EnvelopeImpl( new PositionImpl( min ), new PositionImpl( max ), this.crs );
151            } catch ( GeometryException e ) {
152                // do nothing
153            }
154        }
155    
156        /**
157         * calculates the boundary of the Curve
158         */
159        private void calculateBoundary() {
160            boundary = new CurveBoundaryImpl( getCoordinateSystem(), getStartPoint().getPosition(),
161                                              getEndPoint().getPosition() );
162        }
163    
164        /**
165         * calculates the centroid of the Curve
166         */
167        private void calculateCentroid() {
168            Position[] positions = null;
169            try {
170                positions = getAsLineString().getPositions();
171            } catch ( GeometryException e ) {
172                LOG.logError( e.getMessage(), e );
173            }
174            if ( positions != null ) {
175    
176                double[] cen = new double[positions[0].getAsArray().length];
177    
178                for ( int i = 0; i < positions.length; i++ ) {
179                    double[] pos = positions[i].getAsArray();
180    
181                    for ( int j = 0; j < pos.length; j++ ) {
182                        cen[j] += ( pos[j] / positions.length );
183                    }
184                }
185    
186                centroid = new PointImpl( new PositionImpl( cen ), null );
187            }
188        }
189    
190        @Override
191        protected void calculateParam() {
192            calculateCentroid();
193            calculateEnvelope();
194            calculateBoundary();
195            setValid( true );
196        }
197    
198        /**
199         * returns the boundary of the curve
200         */
201        public CurveBoundary getCurveBoundary() {
202            return (CurveBoundary) boundary;
203        }
204    
205        /**
206         * The operation "dimension" shall return the inherent dimension of this Geometry, which shall be less than or equal
207         * to the coordinate dimension. The dimension of a collection of geometric objects shall be the largest dimension of
208         * any of its pieces. Points are 0-dimensional, curves are 1-dimensional, surfaces are 2-dimensional, and solids are
209         * 3-dimensional.
210         */
211        public int getDimension() {
212            return 1;
213        }
214    
215        /**
216         * The operation "coordinateDimension" shall return the dimension of the coordinates that define this Geometry,
217         * which must be the same as the coordinate dimension of the coordinate reference system for this Geometry.
218         */
219        public int getCoordinateDimension() {
220            return getStartPoint().getPosition().getCoordinateDimension();
221        }
222    
223        /**
224         * The Boolean valued operation "intersects" shall return TRUE if this Geometry intersects another Geometry. Within
225         * a Complex, the Primitives do not intersect one another. In general, topologically structured data uses shared
226         * geometric objects to capture intersection information.
227         * <p>
228         * </p>
229         * dummy implementation
230         */
231        @Override
232        public boolean intersects( Geometry gmo ) {
233            boolean inter = false;
234    
235            try {
236                for ( int i = 0; i < segments.size(); i++ ) {
237                    CurveSegment cs = getCurveSegmentAt( i );
238    
239                    if ( cs.intersects( gmo ) ) {
240                        inter = true;
241                        break;
242                    }
243                }
244            } catch ( Exception e ) {
245                LOG.logError( e.getMessage(), e );
246            }
247    
248            return inter;
249        }
250    
251        /**
252         * returns the length of the curve in units of the related spatial reference system
253         */
254        public double getLength() {
255            double d = 0;
256            for ( int i = 0; i < segments.size(); i++ ) {
257                d += segments.get( i ).getLength();
258            }
259            return d;
260        }
261    
262        /**
263         * returns the number of segments building the curve
264         */
265        public int getNumberOfCurveSegments() {
266            return segments.size();
267        }
268    
269        /**
270         * returns the first point of the curve. if the curve doesn't contain a segment or the first segment doesn't contain
271         * a point null will be returned
272         */
273        public Point getStartPoint() {
274            if ( getNumberOfCurveSegments() == 0 ) {
275                return null;
276            }
277    
278            Point gmp = null;
279    
280            try {
281                gmp = getCurveSegmentAt( 0 ).getStartPoint();
282            } catch ( GeometryException e ) {
283                LOG.logError( "", e );
284            }
285    
286            return gmp;
287        }
288    
289        /**
290         * returns the last point of the curve.if the curve doesn't contain a segment or the last segment doesn't contain a
291         * point null will be returned
292         */
293        public Point getEndPoint() {
294            if ( getNumberOfCurveSegments() == 0 ) {
295                return null;
296            }
297    
298            Point gmp = null;
299    
300            try {
301                gmp = getCurveSegmentAt( getNumberOfCurveSegments() - 1 ).getEndPoint();
302            } catch ( GeometryException e ) {
303                LOG.logError( "", e );
304            }
305    
306            return gmp;
307        }
308    
309        /**
310         * returns the curve as LineString. if there isn't a curve segment within the curve null will be returned
311         */
312        public LineString getAsLineString()
313                                throws GeometryException {
314            if ( getNumberOfCurveSegments() == 0 ) {
315                return null;
316            }
317    
318            Position[] tmp = null;
319    
320            // normal orientaton
321            if ( getOrientation() == '+' ) {
322                int cnt = 0;
323    
324                for ( int i = 0; i < getNumberOfCurveSegments(); i++ ) {
325                    cnt += getCurveSegmentAt( i ).getNumberOfPoints();
326                }
327    
328                tmp = new Position[cnt];
329    
330                int k = 0;
331    
332                for ( int i = 0; i < getNumberOfCurveSegments(); i++ ) {
333                    Position[] gmps = getCurveSegmentAt( i ).getPositions();
334    
335                    for ( int j = 0; j < gmps.length; j++ ) {
336                        tmp[k++] = gmps[j];
337                    }
338                }
339            } else {
340                // inverse orientation
341                int cnt = 0;
342    
343                for ( int i = getNumberOfCurveSegments() - 1; i >= 0; i-- ) {
344                    cnt += getCurveSegmentAt( i ).getNumberOfPoints();
345                }
346    
347                tmp = new Position[cnt];
348    
349                int k = 0;
350    
351                for ( int i = getNumberOfCurveSegments() - 1; i >= 0; i-- ) {
352                    Position[] gmps = getCurveSegmentAt( i ).getPositions();
353    
354                    for ( int j = gmps.length - 1; j >= 0; j-- ) {
355                        tmp[k++] = gmps[j];
356                    }
357                }
358            }
359    
360            return new LineStringImpl( tmp, this.crs );
361        }
362    
363        /**
364         * returns the curve segment at the submitted index
365         *
366         * @param index
367         *            index of the curve segment that should be returned
368         * @exception GeometryException
369         *                a exception will be thrown if <tt>index</tt> is smaller than '0' or larger than
370         *                <tt>getNumberOfCurveSegments()-1</tt>
371         */
372        public CurveSegment getCurveSegmentAt( int index )
373                                throws GeometryException {
374            if ( ( index < 0 ) || ( index > getNumberOfCurveSegments() - 1 ) ) {
375                throw new GeometryException( "invalid index/position to get a segment!" );
376            }
377    
378            return segments.get( index );
379        }
380    
381        /**
382         *
383         * @return all segments of a Curve
384         * @throws GeometryException
385         */
386        public CurveSegment[] getCurveSegments()
387                                throws GeometryException {
388            return segments.toArray( new CurveSegment[segments.size()] );
389        }
390    
391        /**
392         * @return true if no segment is within the curve
393         */
394        @Override
395        public boolean isEmpty() {
396            return ( getNumberOfCurveSegments() == 0 );
397        }
398    
399        /**
400         * translate each point of the curve with the values of the submitted double array.
401         */
402        @Override
403        public void translate( double[] d ) {
404            try {
405                for ( int i = 0; i < segments.size(); i++ ) {
406                    Position[] pos = getCurveSegmentAt( i ).getPositions();
407    
408                    for ( int j = 0; j < pos.length; j++ ) {
409                        pos[j].translate( d );
410                    }
411                }
412            } catch ( Exception e ) {
413                LOG.logError( e.getLocalizedMessage(), e );
414            }
415            setValid( false );
416        }
417    
418        /**
419         * checks if this curve is completely equal to the submitted geometry
420         *
421         * @param other
422         *            object to compare to
423         */
424        @Override
425        public boolean equals( Object other ) {
426            if ( envelope == null ) {
427                calculateEnvelope();
428            }
429            if ( !super.equals( other ) ) {
430                return false;
431            }
432    
433            if ( !( other instanceof CurveImpl ) ) {
434                return false;
435            }
436    
437            if ( !envelope.equals( ( (Geometry) other ).getEnvelope() ) ) {
438                return false;
439            }
440    
441            if ( segments.size() != ( (Curve) other ).getNumberOfCurveSegments() ) {
442                return false;
443            }
444    
445            try {
446                for ( int i = 0; i < segments.size(); i++ ) {
447                    if ( !getCurveSegmentAt( i ).equals( ( (Curve) other ).getCurveSegmentAt( i ) ) ) {
448                        return false;
449                    }
450                }
451            } catch ( Exception e ) {
452                return false;
453            }
454    
455            return true;
456        }
457    
458        @Override
459        public Object clone() {
460            CurveImpl c = null;
461            try {
462                CurveSegment[] cs = new CurveSegment[getNumberOfCurveSegments()];
463                for ( int i = 0; i < segments.size(); i++ ) {
464                    cs[i] = (CurveSegment) ( (CurveSegmentImpl) segments.get( i ) ).clone();
465                }
466                c = new CurveImpl( getOrientation(), cs );
467                c.crs = crs;
468            } catch ( Exception ex ) {
469                LOG.logError( "CurveImpl.clone: ", ex );
470            }
471            return c;
472        }
473    
474        @Override
475        public String toString() {
476            String ret = null;
477            ret = "segments = " + segments + "\n";
478            ret += ( "envelope = " + envelope + "\n" );
479            return ret;
480        }
481    }