001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wpvs/utils/VisADWrapper.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     Aennchenstraße 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    package org.deegree.ogcwebservices.wpvs.utils;
044    
045    import java.rmi.RemoteException;
046    import java.util.ArrayList;
047    import java.util.List;
048    
049    import javax.vecmath.Point3d;
050    
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    
054    import visad.Delaunay;
055    import visad.FlatField;
056    import visad.FunctionType;
057    import visad.Irregular2DSet;
058    import visad.RealTupleType;
059    import visad.RealType;
060    import visad.VisADException;
061    
062    /**
063     * A wrapper for VisAD objects. This class takes care of collecting points to build a TIN, of TIN creation itself and
064     * its output as a geometry collection.
065     * 
066     * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
067     * @author last edited by: $Author: apoth $
068     * 
069     * $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
070     * 
071     * 
072     */
073    public class VisADWrapper {
074    
075        private static final ILogger LOG = LoggerFactory.getLogger( VisADWrapper.class );
076    
077        /**
078         * A list for hold points representing te DEM/TIN.
079         */
080        private List<Point3d> pointsList;
081    
082        private double scale;
083    
084        /**
085         * Initializes the object by creating a common domain field from the geometrical information (the envelope, the
086         * width and the height) supplied. The envelope cannot the null, nor can the dimensions by < 1.
087         * 
088         * @param ptsList
089         *            a list of Points
090         * @param scale
091         *            to multiply to the z-value
092         */
093        public VisADWrapper( List<Point3d> ptsList, double scale ) {
094            this.pointsList = ptsList;
095            this.scale = scale;
096        }
097    
098        /**
099         * Add <code>Point</code>s to the internal list. Lists without any elements (or null lists) are ignored.
100         * 
101         * @param points
102         *            to be added to the list
103         */
104        public final void addPoints( List<Point3d> points ) {
105    
106            if ( points == null || points.size() == 0 ) {
107                return;
108            }
109    
110            this.pointsList.addAll( points );
111        }
112    
113        /**
114         * Generates a list of tringles containing the triangles representing the TIN. Triangles are represented float[3][3]
115         * 
116         * @return a collection of <code>float[3][3]</code>, each of which representing a TIN triangle
117         * 
118         */
119        public final List<float[][]> getTriangleCollectionAsList() {
120    
121            List<float[][]> list = null;
122            try {
123                FlatField tinField = triangulatePoints();
124                if ( tinField == null )
125                    return null;
126                list = toGeoCollectionList( tinField );
127            } catch ( NoClassDefFoundError ncdfe ) {
128                LOG.logError( "WPVS: It seems that the visad library could not be found: " + ncdfe.getLocalizedMessage(),
129                              ncdfe );
130            } catch ( VisADException ve ) {
131                LOG.logError( ve.getLocalizedMessage(), ve );
132            }
133            return list;
134        }
135    
136        /**
137         * Triangulate <code>GM_Point</code>s contained in <code>gmPointsList</code> using the Clarkson algorithm. This
138         * method returns a <code>FlatField</code> containing all points triangulated and with their elevation values.<br/>
139         * 
140         * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
141         * 
142         * @param gmPointsList
143         *            the list of <code>GM_Point</code>s. Cannot be null and must contain at least 3
144         *            <code>GM_Point</code>s.
145         * @return a <code>FlatField</code> containg a TIN (with an <code>Irregular2DSet</code> as its domain set and
146         *         the elevation values)
147         * 
148         */
149        private FlatField triangulatePoints()
150                                             throws NoClassDefFoundError {
151    
152            if ( this.pointsList == null || this.pointsList.size() < 3 ) {
153                throw new IllegalArgumentException( "Points list cannot be null and must contain at least 3 GM_Points." );
154            }
155    
156            // removing double points
157            ArrayList<Point3d> remove = new ArrayList<Point3d>();
158            for ( int i = 0; i < pointsList.size(); ++i ) {
159                Point3d p = pointsList.get( i );
160    
161                if ( !remove.contains( p ) ) {
162                    for ( int j = i + 1; j < pointsList.size(); ++j ) {
163                        Point3d tmpP = pointsList.get( j );
164                        if ( ( Math.abs( tmpP.x - p.x ) < 0.001 ) && ( Math.abs( tmpP.y - p.y ) < 0.001 ) ) {
165                            remove.add( tmpP );
166                        }
167                    }
168                }
169            }
170            for ( Point3d p : remove ) {
171                pointsList.remove( p );
172            }
173            float[][] triPoints = new float[3][this.pointsList.size()];
174            int cnt = 0;
175    
176            for ( Point3d p : this.pointsList ) {
177                triPoints[0][cnt] = (float) p.x;
178                triPoints[1][cnt] = (float) p.y;
179                triPoints[2][cnt++] = (float) ( p.z * scale );
180            }
181    
182            try {
183                FunctionType functionType = new FunctionType( new RealTupleType( RealType.XAxis, RealType.YAxis ),
184                                                              RealType.ZAxis );
185                float[][] ptsXY = new float[][] { triPoints[0], triPoints[1] };
186    
187                // ptsXY = Delaunay.perturb(ptsXY,0.1f, false);
188                // ptsXY = Delaunay.perturb( ptsXY, 5.5f, false );
189                Delaunay delan = Delaunay.factory( ptsXY, false );
190                // Delaunay delan = new DelaunayClarkson( ptsXY );
191                // Delaunay delan = new DelaunayWatson( ptsXY );
192                // DelaunayFast delan = new DelaunayFast( ptsXY );
193                // delan.setNonConvex();
194                try {
195                    // delan.improve( ptsXY, 5 );
196                } catch ( Exception e ) {
197                    // just do noting
198                }
199    
200                // Delaunay delan = new DelaunayClarkson( ptsXY );
201                // Delaunay delan = new DelaunayFast( ptsXY );
202                Irregular2DSet pointsSet = new Irregular2DSet( functionType.getDomain(), ptsXY, null, null, null, delan );
203    
204                FlatField ff = new FlatField( functionType, pointsSet );
205    
206                ff.setSamples( new float[][] { triPoints[2] }, true );
207    
208                return ff;
209    
210            } catch ( VisADException e ) {
211                LOG.logError(e.getMessage(), e );
212                return null;
213            } catch ( RemoteException re ) {
214                LOG.logError(re.getMessage(), re );
215                return null;
216            } catch ( IndexOutOfBoundsException ioobe ) {
217                LOG.logError(ioobe.getMessage(), ioobe );
218                return null;
219            }
220        }
221    
222        /**
223         * Generated a list of triangles from the FlatField passed in as tinField
224         * 
225         * @param tinField
226         *            the FlatField containing triangles
227         * @return a collection of <code>float[3][3]</code>, each of which representing a TIN triangle
228         * @throws VisADException
229         * @throws Exception
230         *             in the unlikely event that a VisAD expcetion is thrown
231         */
232        private final List<float[][]> toGeoCollectionList( FlatField tinField )
233                                                                               throws NoClassDefFoundError, VisADException {
234            if ( tinField == null ) {
235                throw new RuntimeException( "FlatField cannot be null." );
236            }
237    
238            List<float[][]> geoCollec = new ArrayList<float[][]>( 5000 );
239    
240            Irregular2DSet domainSet = (Irregular2DSet) tinField.getDomainSet();
241            float[][] xyPositions = domainSet.getSamples();
242            float[][] zValues = tinField.getFloats();
243            int[][] indices = domainSet.Delan.Tri;
244    
245            // loop over triangles...
246            for ( int i = 0; i < indices.length; i++ ) {
247    
248                // indices[i].length == coords.length == 3
249                // this is float[3][3] -> 3 points per triabngle, each point with 3 coords
250                float[][] myCoords = new float[3][3];
251    
252                // ...then over points
253                for ( int j = 0; j < indices[i].length; j++ ) {
254    
255                    int index = indices[i][j];
256                    myCoords[j] = new float[3];
257                    myCoords[j][0] = xyPositions[0][index];
258                    myCoords[j][1] = xyPositions[1][index];
259                    myCoords[j][2] = zValues[0][index];
260                }
261    
262                geoCollec.add( myCoords );
263            }
264    
265            tinField = null;
266    
267            return geoCollec;
268        }
269    
270        /**
271         * Clear all points and invalidate list.
272         * 
273         */
274        public void clear() {
275            this.pointsList.clear();
276            this.pointsList = null;
277    
278        }
279    
280    }