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