001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wpvs/j3d/TriangleTerrain.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2006 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/exse/
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     Aennchenstrasse 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Jens Fitzke
035     lat/lon GmbH
036     Aennchenstrasse 19
037     53177 Bonn
038     Germany
039     E-Mail: jens.fitzke@uni-bonn.de
040    
041     ---------------------------------------------------------------------------*/
042    
043    package org.deegree.ogcwebservices.wpvs.j3d;
044    
045    import java.awt.image.BufferedImage;
046    import java.util.ArrayList;
047    import java.util.List;
048    import java.util.Random;
049    import java.util.Set;
050    import java.util.SortedMap;
051    import java.util.TreeMap;
052    
053    import javax.vecmath.Point3f;
054    import javax.vecmath.TexCoord2f;
055    
056    import org.deegree.framework.log.ILogger;
057    import org.deegree.framework.log.LoggerFactory;
058    import org.deegree.model.spatialschema.Envelope;
059    import org.deegree.model.spatialschema.GeometryException;
060    import org.deegree.model.spatialschema.GeometryFactory;
061    import org.deegree.model.spatialschema.Point;
062    import org.deegree.model.spatialschema.Position;
063    import org.deegree.model.spatialschema.WKTAdapter;
064    import org.deegree.ogcwebservices.wpvs.utils.VisADWrapper;
065    
066    import com.sun.j3d.utils.geometry.GeometryInfo;
067    import com.sun.j3d.utils.geometry.NormalGenerator;
068    
069    /**
070     * The <code>TriangleTerrain</code> class respresents the Java3D shape of a set of measurepoints. Before this Terrrain
071     * can be drawn the createTerrain method must be invoked, it will create triangles of the given measurepoints and will
072     * add the optional texture to the apearance of the Shap3D.
073     * 
074     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
075     * 
076     * @author last edited by: $Author: rbezema $
077     * 
078     * @version $Revision: 7959 $, $Date: 2007-08-09 12:01:20 +0200 (Do, 09 Aug 2007) $
079     * 
080     */
081    
082    public class TriangleTerrain extends TerrainModel {
083    
084        private static ILogger LOG = LoggerFactory.getLogger( TriangleTerrain.class );
085    
086        private Envelope boundingBox;
087    
088        private Point lowerLeft = null;
089    
090        private Point lowerRight = null;
091    
092        private Point upperRight = null;
093    
094        private Point upperLeft = null;
095    
096        private double terrainWidth;
097    
098        private double terrainHeight;
099    
100        private List<Point> measurePoints = null;
101    
102        private double minimalHeightlevel;
103    
104        private Random rand;
105    
106        private double scale;
107    
108        // /**
109        // * @param triangles
110        // * to be connected to a Triangle(Geometry)Array
111        // * @param bbox
112        // * a geometry representation of the BoundingBox of the TriangleArray (needed to scale
113        // * the texturecoordinates).
114        // */
115        // public TriangleTerrain( List<float[][]> triangles, Envelope bbox ) {
116        // super();
117        // this.triangles = triangles;
118        // boundingBox = bbox;
119        // }
120    
121        /**
122         * @param measurePoints
123         *            indicating height values inside this terrain. They will be triangulated in the createTerrain method.
124         * @param lowerLeft
125         *            the lowerLeft point defining the area of this terrain.
126         * @param lowerRight
127         *            the lowerRight point defining the area of this terrain.
128         * @param upperRight
129         *            the upperRight point defining the area of this terrain.
130         * @param upperLeft
131         *            the upperLeft point defining the area of this terrain.
132         * @param minimalHeightlevel
133         *            which will be used if the measurepoints have no height set.
134         * @param scale
135         *            to multiply onto the z-value of the measurepoints
136         */
137        public TriangleTerrain( List<Point> measurePoints, Point lowerLeft, Point lowerRight, Point upperRight,
138                                Point upperLeft, double minimalHeightlevel, double scale ) {
139            super();
140            this.measurePoints = measurePoints;
141            this.lowerLeft = lowerLeft;
142            this.lowerRight = lowerRight;
143            this.upperRight = upperRight;
144            this.upperLeft = upperLeft;
145            this.minimalHeightlevel = minimalHeightlevel;
146            this.scale = scale;
147    
148            if ( !measurePoints.contains( lowerLeft ) ) {
149                measurePoints.add( lowerLeft );
150            }
151            if ( !measurePoints.contains( lowerRight ) ) {
152                measurePoints.add( lowerRight );
153            }
154            if ( !measurePoints.contains( upperRight ) ) {
155                measurePoints.add( upperRight );
156            }
157            if ( !measurePoints.contains( upperLeft ) ) {
158                measurePoints.add( upperLeft );
159            }
160    
161            boundingBox = GeometryFactory.createEnvelope( lowerLeft.getPosition(),
162                                                          upperRight.getPosition(),
163                                                          lowerLeft.getCoordinateSystem() );
164            this.terrainWidth = boundingBox.getWidth();
165            this.terrainHeight = boundingBox.getHeight();
166            rand = new Random( System.currentTimeMillis() );
167        }
168    
169        // @Override
170        // public void createTerrain() {
171        //
172        //
173        // GeometryInfo geometryInfo = new GeometryInfo( GeometryInfo.TRIANGLE_ARRAY );
174        //
175        // BufferedImage texture = getTexture();
176        // double textureWidthInv = 0;
177        // double textureHeightInv = 0;
178        // if ( texture != null ){
179        // geometryInfo.setTextureCoordinateParams( 1, 2 );
180        // textureWidthInv = 1d/texture.getWidth();
181        // textureHeightInv = 1d/ texture.getHeight();
182        // }
183        //        
184        // Point3f[] coordinates = new Point3f[triangles.size() * 3];
185        // TexCoord2f[] texCoords = new TexCoord2f[triangles.size() * 3];
186        //
187        //
188        // int coordNr = 0;
189        // for ( float[][] triangleCoords : triangles ) {
190        // for ( int k = 0; k < 3; k++ ) {
191        // // System.out.println( Thread.currentThread() + "-> coordNR: " + coordNr );
192        // Point3f modelCoordinate = new Point3f( triangleCoords[k][0], triangleCoords[k][1],
193        // triangleCoords[k][2] );
194        // coordinates[coordNr] = modelCoordinate;
195        //
196        // if ( texture != null ) {
197        // Position pos = GeometryFactory.createPosition( triangleCoords[k][0], triangleCoords[k][1],
198        // triangleCoords[k][2] );
199        //
200        // double widthInv = 0;
201        // double heightInv = 0;
202        // double lowerLeftX = 0;
203        // double lowerLeftY = 0;
204        //
205        // double textureHeightPos = 0;
206        // double textureHeightOffset = 0;
207        // double widthInTexture = 0;
208        // double heightInTexture = 0;
209        // boolean output = false;
210        // for( ResolutionStripe stripe: sortedResolutionStripes ){
211        // if( stripe.getSurface().contains( pos ) ){
212        // Envelope env = stripe.getSurface().getEnvelope();
213        // widthInv = 1d / env.getWidth();
214        // heightInv = 1d / env.getHeight();
215        // lowerLeftX = env.getMin().getX();
216        // lowerLeftY = env.getMin().getY();
217        // if( !output && (Math.abs( lowerLeftX - 2579155.675254344 ) <= 0.0001) &&
218        // (Math.abs( lowerLeftY - 5620065.141020698 ) <= 0.0001) ){
219        // output = true;
220        // }
221        // widthInTexture = stripe.getRequestWidthForBBox() * textureWidthInv;
222        // //length of forth going textures
223        // textureHeightOffset = textureHeightPos * textureHeightInv;
224        // heightInTexture = stripe.getRequestHeightForBBox() * textureHeightInv;
225        // break;
226        // }
227        // textureHeightPos += stripe.getRequestHeightForBBox();
228        // }
229        //
230        // double texCoordX = widthInTexture*(( triangleCoords[k][0] - lowerLeftX ) * widthInv);
231        // double texCoordY = textureHeightOffset + (heightInTexture * (( triangleCoords[k][1] -
232        // lowerLeftY ) * heightInv) );
233        // if( output ){
234        // System.out.println("\n*******\nPoint: " + pos );
235        // System.out.println("lowerLeftX: " + lowerLeftX );
236        // System.out.println("lowerLeftY: " + lowerLeftY+"\n*******" );
237        // System.out.println("widthInv (width):" + widthInv +" ( " + 1d/widthInv +")");
238        // System.out.println("heihgtInv (height):" + heightInv +" ( " + 1d/heightInv +")");
239        // System.out.println("textureWidthInv (width): "+ textureWidthInv + " ("+ 1d/textureWidthInv +
240        // ")");
241        // System.out.println("textureHeightInv (height): "+ textureHeightInv + " ("+
242        // 1d/textureHeightInv + ")");
243        //
244        // System.out.println("relativePosX : " + ( triangleCoords[k][0] - lowerLeftX ) * widthInv );
245        // System.out.println("relativePosY : " + (( triangleCoords[k][1] - lowerLeftY ) * heightInv) );
246        // System.out.println("widthInTexture: " + widthInTexture );
247        // System.out.println("heightInTexture: " + heightInTexture );
248        // System.out.println("textureHeightOffset: " + textureHeightOffset );
249        // System.out.println( "texCoordX: " + texCoordX );
250        // System.out.println( "texCoordY: " + texCoordY );
251        // }
252        //
253        // texCoords[coordNr] = new TexCoord2f( (float) texCoordX, (float) texCoordY );
254        // }
255        // coordNr++;
256        // }
257        //
258        // }
259        // geometryInfo.setCoordinates( coordinates );
260        // if ( texture != null ) {
261        // geometryInfo.setTextureCoordinates( 0, texCoords );
262        // }
263        // geometryInfo.recomputeIndices();
264        // NormalGenerator ng = new NormalGenerator();
265        // ng.generateNormals( geometryInfo );
266        //
267        // setGeometry( geometryInfo.getGeometryArray() );
268        // }
269    
270        /**
271         * @return the boundingBox of this TriangleTerrain
272         */
273        public Envelope getBoundingBox() {
274            return boundingBox;
275        }
276    
277        @Override
278        public void createTerrain() {
279            // try {
280            // Surface bbox = GeometryFactory.createSurface( boundingBox,
281            // boundingBox.getCoordinateSystem() );
282            // System.out.println( "creating terrain:\n" + WKTAdapter.export( bbox ) + "\n"
283            // + WKTAdapter.export( lowerLeft ) + "\n"
284            // + WKTAdapter.export( lowerRight ) + "\n"
285            // + WKTAdapter.export( upperRight ) + "\n"
286            // + WKTAdapter.export( upperLeft ) );
287            // for ( Point p : measurePoints ) {
288            // if ( p.getZ() > minimalHeightlevel ) {
289            // System.out.println( WKTAdapter.export( p ) );
290            // }
291            // }
292            // } catch ( GeometryException ge ) {
293            // ge.printStackTrace();
294            // }
295            List<float[][]> triangles = new ArrayList<float[][]>();
296            if ( measurePoints.size() == 4 ) { // just a square, asssuming a simple plane,
297                float[][] triangle = new float[3][3];
298                triangle[0][0] = (float) lowerLeft.getX();
299                triangle[0][1] = (float) lowerLeft.getY();
300                triangle[0][2] = (float) minimalHeightlevel;
301    
302                triangle[1][0] = (float) upperRight.getX();
303                triangle[1][1] = (float) upperRight.getY();
304                triangle[1][2] = (float) minimalHeightlevel;
305    
306                triangle[2][0] = (float) upperLeft.getX();
307                triangle[2][1] = (float) upperLeft.getY();
308                triangle[2][2] = (float) minimalHeightlevel;
309    
310                triangles.add( triangle );
311    
312                triangle = new float[3][3];
313                triangle[0][0] = (float) lowerLeft.getX();
314                triangle[0][1] = (float) lowerLeft.getY();
315                triangle[0][2] = (float) minimalHeightlevel;
316    
317                triangle[1][0] = (float) lowerRight.getX();
318                triangle[1][1] = (float) lowerRight.getY();
319                triangle[1][2] = (float) minimalHeightlevel;
320    
321                triangle[2][0] = (float) upperRight.getX();
322                triangle[2][1] = (float) upperRight.getY();
323                triangle[2][2] = (float) minimalHeightlevel;
324                triangles.add( triangle );
325            } else {
326                setLowerLeft( GeometryFactory.createPoint( lowerLeft.getX(),
327                                                           lowerLeft.getY(),
328                                                           minimalHeightlevel,
329                                                           lowerLeft.getCoordinateSystem() ) );
330                setLowerRight( GeometryFactory.createPoint( lowerRight.getX(),
331                                                            lowerRight.getY(),
332                                                            minimalHeightlevel,
333                                                            lowerRight.getCoordinateSystem() ) );
334                setUpperRight( GeometryFactory.createPoint( upperRight.getX(),
335                                                            upperRight.getY(),
336                                                            minimalHeightlevel,
337                                                            upperRight.getCoordinateSystem() ) );
338                setUpperLeft( GeometryFactory.createPoint( upperLeft.getX(),
339                                                           upperLeft.getY(),
340                                                           minimalHeightlevel,
341                                                           upperLeft.getCoordinateSystem() ) );
342                LOG.logDebug( "Trying to create triangles with the visad library" );
343                VisADWrapper vw = new VisADWrapper( measurePoints, scale );
344                triangles = vw.getTriangleCollectionAsList();
345                LOG.logDebug( "Creation of triangles with the visad library was successfull." );
346            }
347    
348            double widthInv = 1d / terrainWidth;
349            double heightInv = 1d / terrainHeight;
350            Position originalLowerLeft = boundingBox.getMin();
351    
352            GeometryInfo geometryInfo = new GeometryInfo( GeometryInfo.TRIANGLE_ARRAY );
353    
354            BufferedImage texture = getTexture();
355            if ( texture != null )
356                geometryInfo.setTextureCoordinateParams( 1, 2 );
357    
358            Point3f[] coordinates = new Point3f[triangles.size() * 3];
359            TexCoord2f[] texCoords = new TexCoord2f[triangles.size() * 3];
360    
361            int coordNr = 0;
362            for ( float[][] triangleCoords : triangles ) {
363                for ( int k = 0; k < 3; k++ ) {
364                    // System.out.println( Thread.currentThread() + "-> coordNR: " + coordNr );
365                    Point3f modelCoordinate = new Point3f( triangleCoords[k][0], triangleCoords[k][1], triangleCoords[k][2] );
366                    coordinates[coordNr] = modelCoordinate;
367    
368                    if ( texture != null ) {
369                        double texCoordX = ( modelCoordinate.x - originalLowerLeft.getX() ) * widthInv;
370                        double texCoordY = ( modelCoordinate.y - originalLowerLeft.getY() ) * heightInv;
371                        texCoords[coordNr] = new TexCoord2f( (float) Math.min( 1d, texCoordX ),
372                                                             (float) Math.min( 1d, texCoordY ) );
373                    }
374                    coordNr++;
375                }
376    
377            }
378            geometryInfo.setCoordinates( coordinates );
379            if ( texture != null ) {
380                geometryInfo.setTextureCoordinates( 0, texCoords );
381            }
382            geometryInfo.recomputeIndices();
383            NormalGenerator ng = new NormalGenerator();
384            ng.generateNormals( geometryInfo );
385    
386            setGeometry( geometryInfo.getGeometryArray() );
387        }
388    
389        /**
390         * Finds ther heights in the proximity of the border and creates the heights on the seam which will be added to the
391         * (wfs) measure-points of both the caller and the argument.
392         * 
393         * @param neighbour
394         *            the right neighbour of the envelope of this resolutionstripe. <code>
395         * ______seam__________
396         *         |
397         *  this   |  neighbour
398         * ________|___________
399         *       seam
400         *</code>
401         */
402        public void createVerticalSeam( TriangleTerrain neighbour ) {
403            if ( measurePoints == null || neighbour.measurePoints == null ) {
404                LOG.logError( "No measurepoints available in this ResolutionStripe, therefor the method createVerticalSeam immediately returns" );
405                return;
406            }
407    
408            // variables for this ResolutionStripe
409            double rightXLine = boundingBox.getMax().getX() - ( terrainWidth * 0.1 );
410            // sorted in y diretion
411            SortedMap<Double, Point> myApproximatePoints = new TreeMap<Double, Point>();
412    
413            // variables for the neighbour
414            List<Point> neighboursMeasurePoints = neighbour.measurePoints;
415            Envelope neighbourBBox = neighbour.boundingBox;
416            double neighbourWidth = neighbour.terrainWidth;
417            double leftXLine = boundingBox.getMin().getX() + ( neighbourWidth * 0.1 );
418            // sorted in y diretion
419            SortedMap<Double, Point> neighbourApproximatePoints = new TreeMap<Double, Point>();
420    
421            double lowerBorder = boundingBox.getMin().getY();
422            if ( lowerBorder < neighbourBBox.getMin().getY() ) {
423                lowerBorder = neighbourBBox.getMin().getY();
424            }
425            double upperBorder = boundingBox.getMax().getY();
426            if ( upperBorder > neighbourBBox.getMax().getY() ) {
427                upperBorder = neighbourBBox.getMax().getY();
428            }
429    
430            //
431            // find the nearest z-level values to insert into the dgm on the boundingbox edges, these
432            // might be slidely wrong but approximate the terrain fairly well.
433            for ( Point p : measurePoints ) {
434                double xValue = p.getX();
435                double yValue = p.getY();
436                // only check those points which lie on the border of the two adjacent resolutionstripes
437                if ( yValue >= lowerBorder && yValue <= upperBorder ) {
438                    if ( xValue > rightXLine ) {
439                        Double fromMinYSmall = new Double( yValue - 0.001 );
440                        Double fromMinYLarge = new Double( yValue + 0.001 );
441    
442                        // finding keys which are epsilon near to the min and max values
443                        SortedMap<Double, Point> tileMinYMap = myApproximatePoints.subMap( fromMinYSmall, fromMinYLarge );
444    
445                        if ( tileMinYMap.isEmpty() ) {
446                            myApproximatePoints.put( Double.valueOf( yValue ), p );
447                        } else {// there is a point on this horizontal line take the most right
448                            Point tmpPoint = myApproximatePoints.get( tileMinYMap.firstKey() );
449                            if ( tmpPoint != null ) {
450                                if ( tmpPoint.getX() < xValue ) {
451                                    myApproximatePoints.put( tileMinYMap.firstKey(), p );
452                                }
453                            } else {// was mapped to null
454                                myApproximatePoints.put( Double.valueOf( yValue ), p );
455                            }
456                        }
457                    }
458                }
459            }
460    
461            for ( Point p : neighboursMeasurePoints ) {
462                double xValue = p.getX();
463                double yValue = p.getY();
464                // only check those points which lie on the border of the two adjacent resolutionstripes
465                if ( yValue >= lowerBorder && yValue <= upperBorder ) {
466                    if ( xValue <= leftXLine ) {
467                        Double fromMinYSmall = new Double( yValue - 0.001 );
468                        Double fromMinYLarge = new Double( yValue + 0.001 );
469    
470                        // finding keys which are epsilon near to the min and max values
471                        SortedMap<Double, Point> tileMinYMap = neighbourApproximatePoints.subMap( fromMinYSmall,
472                                                                                                  fromMinYLarge );
473    
474                        if ( tileMinYMap.isEmpty() ) {
475                            neighbourApproximatePoints.put( Double.valueOf( yValue ), p );
476                        } else {// there is a point on this horizontal line take the most right
477                            Point tmpPoint = neighbourApproximatePoints.get( tileMinYMap.firstKey() );
478                            if ( tmpPoint != null ) {
479                                if ( tmpPoint.getX() > xValue ) {
480                                    neighbourApproximatePoints.put( tileMinYMap.firstKey(), p );
481                                }
482                            } else {// was mapped to null
483                                neighbourApproximatePoints.put( Double.valueOf( yValue ), p );
484                            }
485                        }
486                    }
487                }
488    
489            }
490    
491            SortedMap<Double, Point> seam = new TreeMap<Double, Point>();
492    
493            // add points on the left envelope bound
494            Set<Double> keys = myApproximatePoints.keySet();
495            for ( Double key : keys ) {
496                Point p = myApproximatePoints.get( key );
497                if ( p != null ) {
498                    double zValue = ( p.getZ() < minimalHeightlevel ) ? minimalHeightlevel : p.getZ();
499                    seam.put( key, GeometryFactory.createPoint( boundingBox.getMax().getX(),
500                                                                key.doubleValue(),
501                                                                zValue,
502                                                                boundingBox.getCoordinateSystem() ) );
503                    if ( Math.abs( zValue - minimalHeightlevel ) > 0.0001 ) {
504                        int sign = ( rand.nextBoolean() ) ? 1 : -1;
505                        double extra = sign * ( rand.nextDouble() );
506                        double newKey = key.doubleValue() + extra;
507                        seam.put( new Double( newKey ),
508                                  GeometryFactory.createPoint( boundingBox.getMax().getX() + ( extra * 10 ),
509                                                               newKey,
510                                                               minimalHeightlevel,
511                                                               boundingBox.getCoordinateSystem() ) );
512                    }
513                }
514            }
515            // add points on the right envelope bound
516            keys = neighbourApproximatePoints.keySet();
517            for ( Double key : keys ) {
518                Point p = neighbourApproximatePoints.get( key );
519                if ( p != null ) {
520                    double zValue = ( p.getZ() < minimalHeightlevel ) ? minimalHeightlevel : p.getZ();
521                    seam.put( key, GeometryFactory.createPoint( neighbourBBox.getMin().getX(),
522                                                                key.doubleValue(),
523                                                                zValue,
524                                                                neighbourBBox.getCoordinateSystem() ) );
525                    if ( Math.abs( zValue - minimalHeightlevel ) > 0.0001 ) {
526                        int sign = ( rand.nextBoolean() ) ? 1 : -1;
527                        double extra = sign * ( rand.nextDouble() );
528                        double newKey = key.doubleValue() + extra;
529                        seam.put( new Double( newKey ),
530                                  GeometryFactory.createPoint( neighbourBBox.getMin().getX() + ( extra * 10 ),
531                                                               newKey,
532                                                               minimalHeightlevel,
533                                                               boundingBox.getCoordinateSystem() ) );
534                    }
535                }
536            }
537    
538            Point neighbourLowerLeft = neighbour.lowerLeft;
539            Point neighbourUpperLeft = neighbour.upperLeft;
540    
541            if ( Math.abs( lowerRight.getX() - neighbourLowerLeft.getX() ) <= 0.0001 && Math.abs( lowerRight.getY() - neighbourLowerLeft.getY() ) <= 0.0001 ) {
542                if ( lowerRight.getZ() < neighbourLowerLeft.getZ() ) {
543                    neighbour.setLowerLeft( lowerRight );
544                } else {
545                    setLowerRight( neighbourLowerLeft );
546                }
547            } else {
548                if ( Math.abs( lowerRight.getY() - lowerBorder ) <= 0.0001 ) {
549                    seam.put( new Double( lowerRight.getY() ), lowerRight );
550                } else {
551                    seam.put( new Double( neighbourLowerLeft.getY() ), neighbourLowerLeft );
552                }
553            }
554            if ( Math.abs( upperRight.getX() - neighbourUpperLeft.getX() ) <= 0.0001 && Math.abs( upperRight.getY() - neighbourUpperLeft.getY() ) <= 0.0001 ) {
555                if ( upperRight.getZ() < neighbourUpperLeft.getZ() ) {
556                    neighbour.setUpperLeft( upperRight );
557                } else {
558                    setUpperRight( neighbourUpperLeft );
559                }
560            } else {
561                if ( Math.abs( upperRight.getY() - upperBorder ) <= 0.0001 ) {
562                    seam.put( new Double( upperRight.getY() ), upperRight );
563                } else {
564                    seam.put( new Double( neighbourUpperLeft.getY() ), neighbourUpperLeft );
565                }
566            }
567    
568            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
569                LOG.logDebug( "Vertically sowing: \n" + WKTAdapter.export( boundingBox )
570                              + "\n"
571                              + WKTAdapter.export( neighbour.boundingBox ) );
572                for ( Point p : seam.values() ) {
573                    try {
574                        LOG.logDebug( WKTAdapter.export( p ).toString() );
575                    } catch ( GeometryException e ) {
576                        e.printStackTrace();
577                    }
578                }
579            }
580    
581            measurePoints.addAll( seam.values() );
582            neighboursMeasurePoints.addAll( seam.values() );
583        }
584    
585        /**
586         * Finds ther heights in the proximity of the border and creates the heights on the seam which will be added to the
587         * (wfs) measure-points of both the caller and the argument.
588         * 
589         * @param neighbour
590         *            the upper neighbour of the envelope of this resolutionstripe. <code>
591         *      |           |
592         *      | neighbour |
593         * seam -------------seam
594         *      |    this   |
595         *      |           |
596         *  leftBorder  rightBorder
597         *</code>
598         */
599        public void createHorizontalSeam( TriangleTerrain neighbour ) {
600            if ( measurePoints == null || neighbour.measurePoints == null ) {
601                LOG.logError( "No measurepoints available in this ResolutionStripe, therefor the method createVerticalSeam immediately returns" );
602                return;
603            }
604            // variables for this ResolutionStripe
605            double lowerYLine = boundingBox.getMax().getY() - ( terrainHeight * 0.1 );
606            // sorted in x direction
607            SortedMap<Double, Point> myApproximatePoints = new TreeMap<Double, Point>();
608    
609            // variables for the neighbour
610            List<Point> neighboursMeasurePoints = neighbour.measurePoints;
611            Envelope neighbourBBox = neighbour.boundingBox;
612            double neighbourHeight = neighbourBBox.getHeight();
613            double upperYLine = boundingBox.getMin().getY() + ( neighbourHeight * 0.1 );
614            // sorted in x direction
615            SortedMap<Double, Point> neighbourApproximatePoints = new TreeMap<Double, Point>();
616    
617            double leftBorder = boundingBox.getMin().getX();
618            if ( leftBorder < neighbourBBox.getMin().getX() ) {
619                leftBorder = neighbourBBox.getMin().getX();
620            }
621            double rightBorder = boundingBox.getMax().getX();
622            if ( rightBorder > neighbourBBox.getMax().getX() ) {
623                rightBorder = neighbourBBox.getMax().getX();
624            }
625    
626            //
627            // find the nearest z-level values to insert into the dgm on the boundingbox edges, these
628            // might be slidely wrong but approximate the terrain fairly well.
629            for ( Point p : measurePoints ) {
630                double xValue = p.getX();
631                double yValue = p.getY();
632                // only check those points which lie on the border of the two adjacent resolutionstripes
633                if ( xValue >= leftBorder && xValue <= rightBorder ) {
634                    if ( yValue >= lowerYLine ) {
635                        Double fromXSmall = new Double( xValue - 0.001 );
636                        Double fromXLarge = new Double( xValue + 0.001 );
637    
638                        // finding keys which are epsilon near to the min and max values
639                        SortedMap<Double, Point> tileMinYMap = myApproximatePoints.subMap( fromXSmall, fromXLarge );
640    
641                        if ( tileMinYMap.isEmpty() ) {
642                            myApproximatePoints.put( Double.valueOf( xValue ), p );
643                        } else {// there is a point on this horizontal line take the most right
644                            Point tmpPoint = myApproximatePoints.get( tileMinYMap.firstKey() );
645                            if ( tmpPoint != null ) {
646                                if ( tmpPoint.getY() < yValue ) {
647                                    myApproximatePoints.put( tileMinYMap.firstKey(), p );
648                                }
649                            } else {// was mapped to null
650                                myApproximatePoints.put( Double.valueOf( xValue ), p );
651                            }
652                        }
653                    }
654                }
655            }
656    
657            for ( Point p : neighboursMeasurePoints ) {
658                double xValue = p.getX();
659                double yValue = p.getY();
660                // only check those points which lie on the border of the two adjacent resolutionstripes
661                if ( xValue >= leftBorder && xValue <= rightBorder ) {
662                    if ( yValue <= upperYLine ) {
663                        Double fromXSmall = new Double( xValue - 0.001 );
664                        Double fromXLarge = new Double( xValue + 0.001 );
665    
666                        // finding keys which are epsilon near to the min and max values
667                        SortedMap<Double, Point> tileMinYMap = neighbourApproximatePoints.subMap( fromXSmall, fromXLarge );
668    
669                        if ( tileMinYMap.isEmpty() ) {
670                            neighbourApproximatePoints.put( Double.valueOf( xValue ), p );
671                        } else {// there is a point on this horizontal line take the most right
672                            Point tmpPoint = neighbourApproximatePoints.get( tileMinYMap.firstKey() );
673                            if ( tmpPoint != null ) {
674                                if ( tmpPoint.getY() > yValue ) {
675                                    neighbourApproximatePoints.put( tileMinYMap.firstKey(), p );
676                                }
677                            } else {// was mapped to null
678                                neighbourApproximatePoints.put( Double.valueOf( xValue ), p );
679                            }
680                        }
681                    }
682                }
683    
684            }
685    
686            SortedMap<Double, Point> seam = new TreeMap<Double, Point>();
687    
688            // add points on the left envelope bound
689            Set<Double> keys = myApproximatePoints.keySet();
690            for ( Double key : keys ) {
691                Point p = myApproximatePoints.get( key );
692                if ( p != null ) {
693                    double zValue = ( p.getZ() < minimalHeightlevel ) ? minimalHeightlevel : p.getZ();
694                    seam.put( key, GeometryFactory.createPoint( key.doubleValue(),
695                                                                boundingBox.getMax().getY(),
696                                                                zValue,
697                                                                boundingBox.getCoordinateSystem() ) );
698                    if ( Math.abs( zValue - minimalHeightlevel ) > 0.0001 ) {
699                        int sign = ( rand.nextBoolean() ) ? 1 : -1;
700                        double extra = sign * ( rand.nextDouble() );
701                        double newKey = key.doubleValue() + extra;
702                        seam.put( new Double( newKey ), GeometryFactory.createPoint( newKey,
703                                                                                     boundingBox.getMax().getY() + extra
704                                                                                                             * 10,
705                                                                                     minimalHeightlevel,
706                                                                                     boundingBox.getCoordinateSystem() ) );
707                    }
708                }
709            }
710            // add points on the right envelope bound
711            keys = neighbourApproximatePoints.keySet();
712            for ( Double key : keys ) {
713                Point p = neighbourApproximatePoints.get( key );
714                if ( p != null ) {
715                    double zValue = ( p.getZ() < minimalHeightlevel ) ? minimalHeightlevel : p.getZ();
716                    seam.put( key, GeometryFactory.createPoint( key.doubleValue(),
717                                                                neighbourBBox.getMin().getY(),
718                                                                zValue,
719                                                                neighbourBBox.getCoordinateSystem() ) );
720                    if ( Math.abs( zValue - minimalHeightlevel ) > 0.0001 ) {
721                        int sign = ( rand.nextBoolean() ) ? 1 : -1;
722                        double extra = sign * ( rand.nextDouble() );
723                        double newKey = key.doubleValue() + extra;
724                        seam.put( new Double( newKey ), GeometryFactory.createPoint( newKey,
725                                                                                     neighbourBBox.getMin().getY() + extra
726                                                                                                             * 10,
727                                                                                     minimalHeightlevel,
728                                                                                     boundingBox.getCoordinateSystem() ) );
729                    }
730                }
731            }
732    
733            Point neighbourLowerLeft = neighbour.lowerLeft;
734            Point neighbourLowerRight = neighbour.lowerRight;
735    
736            if ( Math.abs( upperLeft.getX() - neighbourLowerLeft.getX() ) <= 0.0001 && Math.abs( upperLeft.getY() - neighbourLowerLeft.getY() ) <= 0.0001 ) {
737                if ( upperLeft.getZ() < neighbourLowerLeft.getZ() ) {
738                    setUpperLeft( neighbourLowerLeft );
739                } else {
740                    neighbour.setLowerLeft( upperLeft );
741                }
742            } else {
743                if ( Math.abs( upperLeft.getX() - leftBorder ) <= 0.0001 ) {
744                    seam.put( new Double( upperLeft.getX() ), upperLeft );
745                } else {
746                    seam.put( new Double( neighbourLowerLeft.getX() ), neighbourLowerLeft );
747                }
748            }
749    
750            if ( Math.abs( upperRight.getX() - neighbourLowerRight.getX() ) <= 0.0001 && Math.abs( upperRight.getY() - neighbourLowerRight.getY() ) <= 0.0001 ) {
751                if ( upperRight.getZ() < neighbourLowerRight.getZ() ) {
752                    setUpperRight( neighbourLowerRight );
753                } else {
754                    neighbour.setLowerRight( upperRight );
755                }
756            } else {
757                if ( Math.abs( upperRight.getX() - rightBorder ) <= 0.0001 ) {
758                    seam.put( new Double( upperRight.getX() ), upperRight );
759                    removeUpperRightPoint();
760                } else {
761                    seam.put( new Double( neighbourLowerRight.getX() ), neighbourLowerRight );
762                    neighbour.removeLowerRightPoint();
763                }
764            }
765            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
766                LOG.logDebug( "horizontally sowing: \n" + WKTAdapter.export( boundingBox )
767                              + "\n"
768                              + WKTAdapter.export( neighbour.boundingBox ) );
769                for ( Point p : seam.values() ) {
770                    try {
771                        LOG.logDebug( WKTAdapter.export( p ).toString() );
772                    } catch ( GeometryException e ) {
773                        e.printStackTrace();
774                    }
775                }
776            }
777            measurePoints.addAll( seam.values() );
778            neighboursMeasurePoints.addAll( seam.values() );
779        }
780    
781        /**
782         * @param newPoint
783         *            the lowerLeft Point of this request
784         */
785        private void setLowerLeft( Point newPoint ) {
786            removeLowerLeftPoint();
787            lowerLeft = newPoint;
788            measurePoints.add( lowerLeft );
789        }
790    
791        /**
792         * @param newPoint
793         *            the lowerLeft Point of this request
794         */
795        private void setLowerRight( Point newPoint ) {
796            removeLowerRightPoint();
797            lowerRight = newPoint;
798            measurePoints.add( lowerRight );
799        }
800    
801        /**
802         * @param newPoint
803         *            the lowerLeft Point of this request
804         */
805        private void setUpperRight( Point newPoint ) {
806            removeUpperRightPoint();
807            upperRight = newPoint;
808            measurePoints.add( upperRight );
809        }
810    
811        /**
812         * @param newPoint
813         *            the lowerLeft Point of this request
814         */
815        private void setUpperLeft( Point newPoint ) {
816            removeUpperLeftPoint();
817            upperLeft = newPoint;
818            measurePoints.add( upperLeft );
819        }
820    
821        /**
822         * removes the one of the four points of this terrain
823         */
824        private void removeLowerLeftPoint() {
825            if ( lowerLeft != null ) {
826                measurePoints.remove( lowerLeft );
827            }
828        }
829    
830        private void removeLowerRightPoint() {
831            if ( lowerRight != null ) {
832                measurePoints.remove( lowerRight );
833            }
834        }
835    
836        private void removeUpperRightPoint() {
837            if ( upperRight != null ) {
838                measurePoints.remove( upperRight );
839            }
840        }
841    
842        private void removeUpperLeftPoint() {
843            if ( upperLeft != null ) {
844                measurePoints.remove( upperLeft );
845            }
846        }
847    
848    }