001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wpvs/utils/StripeFactory.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/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    
044    package org.deegree.ogcwebservices.wpvs.utils;
045    
046    import java.awt.Color;
047    import java.awt.Graphics2D;
048    import java.util.ArrayList;
049    
050    import javax.media.j3d.Transform3D;
051    import javax.vecmath.Point3d;
052    import javax.vecmath.Vector3d;
053    
054    import org.deegree.framework.log.ILogger;
055    import org.deegree.framework.log.LoggerFactory;
056    import org.deegree.framework.util.MapUtils;
057    import org.deegree.model.spatialschema.Envelope;
058    import org.deegree.model.spatialschema.GeometryException;
059    import org.deegree.model.spatialschema.GeometryFactory;
060    import org.deegree.model.spatialschema.Position;
061    import org.deegree.model.spatialschema.Surface;
062    import org.deegree.ogcwebservices.wpvs.j3d.ViewPoint;
063    
064    /**
065     * This class divides a visible area into stripes by calculating a resolution for the near clipping
066     * plane and the far clipping plane. The near resolution (the one closest to the viewer) is doubled
067     * until it is <= far clippingplane resolution. If the Viewer has a pitch which is greater than the
068     * angle of view (in other words: looking steeply down) the resolutionstripes are concentric quads,
069     * which are adapted to the form of the footprint (aka. the perspective ViewFrustum's intersection
070     * with the ground).
071     * 
072     * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
073     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
074     *  
075     * @author last edited by: $Author: bezema $
076     * 
077     * $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $
078     * 
079     */
080    
081    public class StripeFactory {
082        private final ILogger LOG = LoggerFactory.getLogger( StripeFactory.class );
083    
084        private final ViewPoint viewPoint;
085    
086        private double minScaleResolution;
087    
088        /**
089         * @param vp
090         *            the capsulation of the location and viewing direction of the viewer.
091         * @param minScaleDenominator
092         *            the best possible resolution the datasets can show.
093         */
094        public StripeFactory( ViewPoint vp, double minScaleDenominator ) {
095            this.viewPoint = vp;
096            if ( minScaleDenominator < 0.0001 )
097                minScaleDenominator = 0.0001;
098            this.minScaleResolution = minScaleDenominator * MapUtils.DEFAULT_PIXEL_SIZE;
099        }
100    
101        /**
102         * Calculates the number of resolutionstripes, their size and their position in
103         * worldcoordinates.
104         * 
105         * @param imageWidth
106         *            of the request
107         * @param minimalHeight
108         *            the minimalHeight of the terrain
109         * @param g2d
110         *            if !null the stripes are drawn on the graphics object (handy for debugging
111         *            purposes
112         * @param scale
113         *            of the heightmap.
114         * @return the resolutionsstripes for the footprint
115         */
116        public ArrayList<ResolutionStripe> createResolutionStripes( int imageWidth,
117                                                                    double minimalHeight,
118                                                                    Graphics2D g2d, double scale ) {
119    
120            // Transformations used to calculate the footprint
121            Transform3D invertTransform = viewPoint.getSimpleTransform();
122            invertTransform.invert();
123            Transform3D transform = viewPoint.getSimpleTransform();
124    
125            Point3d[] footprint = this.viewPoint.getFootprint();
126            Vector3d farLeft = new Vector3d( footprint[0] );
127            Vector3d farRight = new Vector3d( footprint[1] );
128            Vector3d nearLeft = new Vector3d( footprint[2] );
129            Vector3d nearRight = new Vector3d( footprint[3] );
130            
131            if( g2d != null ){
132                g2d.drawString( "nearLeft", (int)nearLeft.x, (int)nearLeft.y );
133                g2d.drawString( "farLeft", (int)farLeft.x, (int)farLeft.y );
134                g2d.drawString( "nearRight", (int)nearRight.x, (int)nearRight.y );
135                g2d.drawString( "farRight", (int)farRight.x, (int)farRight.y );
136                
137                
138            }
139    
140            invertTransform.transform( nearRight );
141            invertTransform.transform( farRight );
142            Vector3d length = new Vector3d( farRight );
143            length.sub( nearRight );
144    
145            // double halfAngleOfView = viewPoint.getAngleOfView() * 0.5;
146            double footprintSideGradient = Math.acos( Math.abs( farRight.y - nearRight.y ) / length.length() );
147            LOG.logDebug( "halfAngleOfView : " + Math.toDegrees( footprintSideGradient ) );
148            transform.transform( nearRight );
149            transform.transform( farRight );
150    
151            // We use a tangens therefore checking if we are close to 0 - 180
152            if ( Math.abs( ( footprintSideGradient + Math.toRadians( 90 ) ) % Math.toRadians( 180 ) ) <= 0.00001 )
153                return null;
154    
155            // !!!!For now the footprint dimensions are in meters
156            double footprintNearResolution = StripeFactory.calcScaleOfVector( nearRight, nearLeft,
157                                                                              imageWidth );
158    
159            LOG.logDebug( "nearRight: " + nearRight );
160            LOG.logDebug( "nearRight: " + nearLeft );
161            length = new Vector3d( nearLeft );
162            length.sub( nearRight );
163    
164            LOG.logDebug( "length: " + length.length() );
165            LOG.logDebug( "nearResolution: " + footprintNearResolution );
166            LOG.logDebug( "(nearResolution*imageWidth)/sqrt2): "
167                          + ( ( footprintNearResolution * imageWidth ) / MapUtils.SQRT2 ) );
168            double footprintFarResolution = StripeFactory.calcScaleOfVector( farRight, farLeft,
169                                                                             imageWidth );
170    
171            // if the near clipping plane is behind the viewer, which means pitch > angleOfView the
172            // resolutionStripes should be centered around the viewpoint
173            if ( viewPoint.isNearClippingplaneBehindViewPoint() ) {
174                return createStripesForHighPitch( invertTransform, transform, farRight, farLeft,
175                                                  nearRight, nearLeft, imageWidth, minimalHeight,
176                                                  footprintSideGradient, footprintNearResolution,
177                                                  footprintFarResolution, g2d, scale );
178            }
179            return createStripesForFrontalPerspective( invertTransform, transform, farRight, farLeft,
180                                                       nearRight, nearLeft, imageWidth, minimalHeight,
181                                                       footprintSideGradient, footprintNearResolution,
182                                                       footprintFarResolution, g2d, scale );
183        }
184    
185        private ArrayList<ResolutionStripe> createStripesForFrontalPerspective(
186                                                                                Transform3D invertTransform,
187                                                                                Transform3D transform,
188                                                                                Vector3d farRight,
189                                                                                Vector3d farLeft,
190                                                                                Vector3d nearestRight,
191                                                                                Vector3d nearestLeft,
192                                                                                double imageWidth,
193                                                                                double minimalHeight,
194                                                                                double footprintSideGradientAngle,
195                                                                                double footprintNearResolution,
196                                                                                double footprintFarResolution,
197                                                                                Graphics2D g2d,
198                                                                                double scale ) {
199            ArrayList<ResolutionStripe> resultStripes = new ArrayList<ResolutionStripe>( 10 );
200    
201            if ( minScaleResolution > footprintNearResolution ){
202                footprintNearResolution = minScaleResolution;
203                LOG.logWarning( "the footprintnearResolution is smaller than the defined minScaleResolution, replacing footprints resolution accordingly" );
204            }
205            double resolutionQuotient = Math.floor( footprintFarResolution / footprintNearResolution );
206            int numberOfStripes = nearestPowerOfTwo( resolutionQuotient );
207            
208            if( numberOfStripes == 0 ){//can happen if the aov is close to the pitch
209                LOG.logDebug("number of stripes in frontal view are null, what to do?" );
210            }
211    
212            // Find the distances to switch levelofdetail
213            for ( int stripesCounter = 0; stripesCounter < numberOfStripes; ++stripesCounter ) {
214                // go back to null point
215                invertTransform.transform( nearestRight );
216                invertTransform.transform( nearestLeft );
217    
218                double xLength = ( nearestRight.x - nearestLeft.x ) * 0.5;
219                double yLength = xLength / Math.tan( footprintSideGradientAngle );
220    
221                // create the new slice by adding the lengt in x and y direction
222                Vector3d tmpFarRight = new Vector3d( nearestRight.x + xLength,
223                                                     nearestRight.y - yLength, nearestRight.z );
224                Vector3d tmpFarLeft = new Vector3d( nearestLeft.x - xLength, nearestLeft.y - yLength,
225                                                    nearestLeft.z );
226    
227                // transform the resulting shape back to the original position.
228                transform.transform( tmpFarRight );
229                transform.transform( tmpFarLeft );
230                transform.transform( nearestRight );
231                transform.transform( nearestLeft );
232    
233                ResolutionStripe rs = createResolutionStripe( nearestLeft, nearestRight, tmpFarRight,
234                                                              tmpFarLeft, imageWidth, minimalHeight,
235                                                              scale );
236                if ( rs != null )
237                    resultStripes.add( rs );
238    
239                /**
240                 * For debugging purposes
241                 */
242                if ( g2d != null ) {
243                    g2d.setColor( Color.ORANGE );
244                    g2d.drawLine( (int) nearestRight.x, (int) nearestRight.y, (int) nearestLeft.x,
245                                  (int) nearestLeft.y );
246                    g2d.drawString( "nearestRight", (int)nearestRight.x, (int)nearestRight.y );
247                    g2d.drawString( "nearestLeft", (int)nearestLeft.x, (int)nearestLeft.y );
248                }
249    
250                nearestRight = new Vector3d( tmpFarRight );
251                nearestLeft = new Vector3d( tmpFarLeft );
252            }
253    
254            ResolutionStripe rs = createResolutionStripe( nearestLeft, nearestRight, farRight, farLeft,
255                                                          imageWidth, minimalHeight, scale );
256            if ( rs != null )
257                resultStripes.add( rs );
258    
259            if ( g2d != null ) {
260                g2d.setColor( Color.ORANGE );
261                g2d.drawLine( (int) nearestRight.x, (int) nearestRight.y, (int) nearestLeft.x,
262                              (int) nearestLeft.y );
263                g2d.drawLine( (int) farRight.x, (int) farRight.y, (int) farLeft.x, (int) farLeft.y );
264                g2d.drawString( "nearestRight", (int)nearestRight.x, (int)nearestRight.y );
265                g2d.drawString( "nearestLeft", (int)nearestLeft.x, (int)nearestLeft.y );
266    
267            }
268    
269            return resultStripes;
270        }
271    
272        /**
273         * This method finds the resolutionstripes around the Viewpoint if the nearClippingplane is
274         * behind the viewer (which happens if the pitch is higher as the angleOfView).
275         * 
276         * @param invertTransform
277         * @param transform
278         * @param farRight
279         * @param farLeft
280         * @param nearRight
281         * @param nearLeft
282         * @param imageWidth
283         * @param minimalHeight
284         * @param halfAngleOfView
285         * @param footprintNearResolution
286         * @param footprintFarResolution
287         * @param g2d
288         * @return the ResolutionStripes centered around the viewpoint.
289         */
290        private ArrayList<ResolutionStripe> createStripesForHighPitch( Transform3D invertTransform,
291                                                                       Transform3D transform,
292                                                                       Vector3d farRight,
293                                                                       Vector3d farLeft,
294                                                                       Vector3d nearRight,
295                                                                       Vector3d nearLeft,
296                                                                       double imageWidth,
297                                                                       double minimalHeight,
298                                                                       double halfAngleOfView,
299                                                                       double footprintNearResolution,
300                                                                       double footprintFarResolution,
301                                                                       Graphics2D g2d, double scale ) {
302            ArrayList<ResolutionStripe> resultStripes = new ArrayList<ResolutionStripe>( 40 );
303    
304            // double halfAngleOfView = viewPoint.getAngleOfView() * 0.5;
305    
306            Vector3d origin = new Vector3d( viewPoint.getObserverPosition() );
307            invertTransform.transform( origin );
308    
309            invertTransform.transform( nearRight );
310            Vector3d length = new Vector3d( nearRight );
311            length.sub( origin );
312    
313            halfAngleOfView = Math.acos( Math.abs( origin.y - nearRight.y ) / length.length() );
314            LOG.logDebug( "halfAngleOfView: " + Math.toDegrees( halfAngleOfView ) );
315    
316            transform.transform( nearRight );
317    
318            if ( minScaleResolution > footprintNearResolution )
319                footprintNearResolution = minScaleResolution;
320    
321            double resolutionStripeLength = imageWidth * ( minScaleResolution / MapUtils.SQRT2 );
322    
323            int numberOfStripesNear = nearestPowerOfTwo( Math.floor( footprintNearResolution
324                                                                     / minScaleResolution ) );
325    
326            double tangensOfHalfAngleOfView = Math.tan( halfAngleOfView );
327    
328            double yDistance = ( resolutionStripeLength * 0.5 ) / tangensOfHalfAngleOfView;
329    
330            LOG.logDebug( "yDistance: " + yDistance );
331    
332            Vector3d backNearestLeft = new Vector3d( origin.x + resolutionStripeLength * 0.5,
333                                                     origin.y - yDistance, nearLeft.z );
334            Vector3d backNearestRight = new Vector3d( origin.x - resolutionStripeLength * 0.5,
335                                                      origin.y - yDistance, nearLeft.z );
336    
337            Vector3d frontalNearestLeft = new Vector3d( origin.x - resolutionStripeLength * 0.5,
338                                                        origin.y + yDistance, nearLeft.z );
339            Vector3d frontalNearestRight = new Vector3d( origin.x + resolutionStripeLength * 0.5,
340                                                         origin.y + yDistance, nearLeft.z );
341    
342            transform.transform( backNearestLeft );
343            transform.transform( backNearestRight );
344            transform.transform( frontalNearestLeft );
345            transform.transform( frontalNearestRight );
346            ResolutionStripe middleStripe = createResolutionStripe( backNearestLeft, backNearestRight,
347                                                                    frontalNearestRight,
348                                                                    frontalNearestLeft, minimalHeight,
349                                                                    minScaleResolution,
350                                                                    minScaleResolution, scale );
351            if ( middleStripe != null ) {
352                resultStripes.add( middleStripe );
353            }
354            /**
355             * For debugging purpaces
356             */
357    //        if ( g2d != null ) {
358    //            g2d.setColor( Color.GREEN );
359    //            g2d.drawLine( (int) backNearestRight.x, (int) backNearestRight.y,
360    //                          (int) backNearestLeft.x, (int) backNearestLeft.y );
361    //            g2d.setColor( Color.RED );
362    //            g2d.drawLine( (int) frontalNearestRight.x, (int) frontalNearestRight.y,
363    //                          (int) frontalNearestLeft.x, (int) frontalNearestLeft.y );
364    //        }
365    
366            // Find the distances to switch levelofdetail
367            /**
368             * If the near clipping plane is behind the viewer the resolutionstripes are centered around
369             * the viewpoint. <code>
370             * FL = farLeft  = UpperLeft
371             * FR = FarRight = UpperRight
372             * NL = nearLeft = LowerLeft
373             * NR = nearRight = LowerRight
374             * VP = viewPoint
375             * R = Right
376             * L = Left
377             * F = front
378             * B = Back
379             * FL__________________FR    .
380             *   \                /     /|\
381             *    \     _F_      /       |
382             *     \  L |VP| R  /     viewDir
383             *      \   -B-    /         |
384             *       \        /          |
385             *       NL------NR
386             * </code>
387             */
388            for ( int stripesCounter = 0; stripesCounter < numberOfStripesNear; ++stripesCounter ) {
389    
390                // go back to null point
391                invertTransform.transform( backNearestRight );
392                invertTransform.transform( backNearestLeft );
393                // find the distance from the center of the far from the center of the near
394                // clipping plane
395                double xLength = Math.abs( ( backNearestRight.x - backNearestLeft.x ) * 0.5 );
396                double yLength = xLength / tangensOfHalfAngleOfView;
397    
398                // For the frontal perspective ResolutionStripes we need to know the distance from the
399                // origin
400                double nearFrontalYDistance = origin.y + Math.abs( origin.y - backNearestLeft.y );
401                double farFrontalYDistance = nearFrontalYDistance + yLength;
402    
403                // create the new Back-ResolutionStripe by adding the length in x and y direction
404                // according to the angleOfView. From this resolutionStripe the left, front and right
405                // stripes are calculated
406                Vector3d tmpBackLowerLeft = new Vector3d( backNearestLeft.x + xLength,
407                                                          backNearestLeft.y - yLength,
408                                                          backNearestLeft.z );
409                Vector3d tmpBackLowerRight = new Vector3d( backNearestRight.x - xLength,
410                                                           backNearestRight.y - yLength,
411                                                           backNearestRight.z );
412                Vector3d tmpBackUpperRight = new Vector3d( backNearestRight.x - xLength,
413                                                           backNearestRight.y, backNearestRight.z );
414                Vector3d tmpBackUpperLeft = new Vector3d( backNearestLeft.x + xLength,
415                                                          backNearestLeft.y, backNearestLeft.z );
416    
417                // left
418                Vector3d tmpLeftLowerLeft = new Vector3d( tmpBackUpperLeft );
419                Vector3d tmpLeftLowerRight = new Vector3d( backNearestLeft );
420                Vector3d tmpLeftUpperRight = new Vector3d( backNearestLeft.x, nearFrontalYDistance,
421                                                           backNearestLeft.z );
422                Vector3d tmpLeftUpperLeft = new Vector3d( tmpBackUpperLeft.x, nearFrontalYDistance,
423                                                          backNearestLeft.z );
424    
425                // front
426                Vector3d tmpFrontLowerLeft = new Vector3d( tmpLeftUpperLeft );
427                Vector3d tmpFrontLowerRight = new Vector3d( tmpBackLowerRight.x, nearFrontalYDistance,
428                                                            backNearestLeft.z );
429                Vector3d tmpFrontUpperRight = new Vector3d( tmpBackLowerRight.x, farFrontalYDistance,
430                                                            backNearestLeft.z );
431                Vector3d tmpFrontUpperLeft = new Vector3d( tmpLeftUpperLeft.x, farFrontalYDistance,
432                                                           backNearestLeft.z );
433    
434                // right
435                Vector3d tmpRightLowerLeft = new Vector3d( backNearestRight );
436                Vector3d tmpRightLowerRight = new Vector3d( tmpBackUpperRight );
437                Vector3d tmpRightUpperRight = new Vector3d( tmpFrontLowerRight );
438                Vector3d tmpRightUpperLeft = new Vector3d( backNearestRight.x, nearFrontalYDistance,
439                                                           backNearestLeft.z );
440    
441                // transform the resulting shape back to scene space
442                transform.transform( tmpBackLowerLeft );
443                transform.transform( tmpBackLowerRight );
444                transform.transform( tmpBackUpperRight );
445                transform.transform( tmpBackUpperLeft );
446    
447                transform.transform( tmpLeftLowerLeft );
448                transform.transform( tmpLeftLowerRight );
449                transform.transform( tmpLeftUpperRight );
450                transform.transform( tmpLeftUpperLeft );
451    
452                transform.transform( tmpFrontLowerLeft );
453                transform.transform( tmpFrontLowerRight );
454                transform.transform( tmpFrontUpperRight );
455                transform.transform( tmpFrontUpperLeft );
456    
457                transform.transform( tmpRightLowerLeft );
458                transform.transform( tmpRightLowerRight );
459                transform.transform( tmpRightUpperRight );
460                transform.transform( tmpRightUpperLeft );
461    
462                double resolution = StripeFactory.calcScaleOfVector( tmpBackLowerLeft,
463                                                                     tmpBackLowerRight, imageWidth );
464    
465                ResolutionStripe rs = createResolutionStripe( tmpBackLowerLeft, tmpBackLowerRight,
466                                                              tmpBackUpperRight, tmpBackUpperLeft,
467                                                              minimalHeight, resolution, resolution,
468                                                              scale );
469                if ( rs != null ) {
470                    resultStripes.add( rs );
471                }
472    
473                rs = createResolutionStripe( tmpLeftLowerLeft, tmpLeftLowerRight, tmpLeftUpperRight,
474                                             tmpLeftUpperLeft, minimalHeight, resolution, resolution,
475                                             scale );
476                if ( rs != null ) {
477                    resultStripes.add( rs );
478                }
479    
480                rs = createResolutionStripe( tmpFrontLowerLeft, tmpFrontLowerRight, tmpFrontUpperRight,
481                                             tmpFrontUpperLeft, minimalHeight, resolution, resolution,
482                                             scale );
483                if ( rs != null ) {
484                    resultStripes.add( rs );
485                }
486    
487                rs = createResolutionStripe( tmpRightLowerLeft, tmpRightLowerRight, tmpRightUpperRight,
488                                             tmpRightUpperLeft, minimalHeight, resolution, resolution,
489                                             scale );
490                if ( rs != null ) {
491                    resultStripes.add( rs );
492                }
493                /**
494                 * For debugging purpaces
495                 */
496                if ( g2d != null ) {
497                    g2d.setColor( Color.GREEN );
498                    g2d.drawLine( (int) tmpBackLowerLeft.x, (int) tmpBackLowerLeft.y,
499                                  (int) tmpBackLowerRight.x, (int) tmpBackLowerRight.y );
500                    g2d.drawLine( (int) tmpBackLowerRight.x, (int) tmpBackLowerRight.y,
501                                  (int) tmpBackUpperRight.x, (int) tmpBackUpperRight.y );
502                    g2d.drawLine( (int) tmpBackUpperRight.x, (int) tmpBackUpperRight.y,
503                                  (int) tmpBackUpperLeft.x, (int) tmpBackUpperLeft.y );
504                    g2d.drawLine( (int) tmpBackUpperLeft.x, (int) tmpBackUpperLeft.y,
505                                  (int) tmpBackLowerLeft.x, (int) tmpBackLowerLeft.y );
506    
507                    g2d.setColor( Color.RED );
508                    g2d.drawLine( (int) tmpLeftLowerLeft.x, (int) tmpLeftLowerLeft.y,
509                                  (int) tmpLeftLowerRight.x, (int) tmpLeftLowerRight.y );
510                    g2d.drawLine( (int) tmpLeftLowerRight.x, (int) tmpLeftLowerRight.y,
511                                  (int) tmpLeftUpperRight.x, (int) tmpLeftUpperRight.y );
512                    g2d.drawLine( (int) tmpLeftUpperRight.x, (int) tmpLeftUpperRight.y,
513                                  (int) tmpLeftUpperLeft.x, (int) tmpLeftUpperLeft.y );
514                    g2d.drawLine( (int) tmpLeftUpperLeft.x, (int) tmpLeftUpperLeft.y,
515                                  (int) tmpLeftLowerLeft.x, (int) tmpLeftLowerLeft.y );
516    
517                    g2d.setColor( Color.BLUE );
518                    g2d.drawLine( (int) tmpFrontLowerLeft.x, (int) tmpFrontLowerLeft.y,
519                                  (int) tmpFrontLowerRight.x, (int) tmpFrontLowerRight.y );
520                    g2d.drawLine( (int) tmpFrontLowerRight.x, (int) tmpFrontLowerRight.y,
521                                  (int) tmpFrontUpperRight.x, (int) tmpFrontUpperRight.y );
522                    g2d.drawLine( (int) tmpFrontUpperRight.x, (int) tmpFrontUpperRight.y,
523                                  (int) tmpFrontUpperLeft.x, (int) tmpFrontUpperLeft.y );
524                    g2d.drawLine( (int) tmpFrontUpperLeft.x, (int) tmpFrontUpperLeft.y,
525                                  (int) tmpFrontLowerLeft.x, (int) tmpFrontLowerLeft.y );
526    
527                    g2d.setColor( Color.YELLOW );
528                    g2d.drawLine( (int) tmpRightLowerLeft.x, (int) tmpRightLowerLeft.y,
529                                  (int) tmpRightLowerRight.x, (int) tmpRightLowerRight.y );
530                    g2d.drawLine( (int) tmpRightLowerRight.x, (int) tmpRightLowerRight.y,
531                                  (int) tmpRightUpperRight.x, (int) tmpRightUpperRight.y );
532                    g2d.drawLine( (int) tmpRightUpperRight.x, (int) tmpRightUpperRight.y,
533                                  (int) tmpRightUpperLeft.x, (int) tmpRightUpperLeft.y );
534                    g2d.drawLine( (int) tmpRightUpperLeft.x, (int) tmpRightUpperLeft.y,
535                                  (int) tmpRightLowerLeft.x, (int) tmpRightLowerLeft.y );
536                }
537                backNearestRight = new Vector3d( tmpBackLowerRight );
538                backNearestLeft = new Vector3d( tmpBackLowerLeft );
539            }
540            
541            // Find intersection of the line connecting lowerLeft and upperleft of the footprint
542            invertTransform.transform( nearLeft );
543            invertTransform.transform( farLeft );
544            invertTransform.transform( nearRight );
545            invertTransform.transform( farRight );
546            invertTransform.transform( backNearestLeft );
547            invertTransform.transform( backNearestRight );
548    
549            double nearFrontalYDistance = origin.y + Math.abs( origin.y - backNearestLeft.y );
550    
551            /*
552             * We have the circular resolutionstripes, now the last back, left and right stripes (which
553             * are bounded by the footprint) have to be calculated, which can be done by Finding
554             * intersection between a horizontalLine (constant gradient) and the footprint.
555             * 
556             * A line is defined as yValue = gradient*xValue + offset therefor intersection between
557             * those two lines as follows: offsetTwo = gradientOne*xValueOne + offsetOne => xValueOne =
558             * (offsetOne - offsetTwo) / gradientOne;
559             */
560            double gradientLeft = ( nearLeft.y - farLeft.y ) / ( nearLeft.x - farLeft.x );
561            double offsetLeft = nearLeft.y - (gradientLeft * nearLeft.x);
562    
563            double gradientRight = ( nearRight.y - farRight.y ) / ( nearRight.x - farRight.x );
564            double offsetRight = nearRight.y - gradientRight * nearRight.x;
565    
566            double xIntersectionLeft = ( backNearestLeft.y - offsetLeft ) / gradientLeft;
567            double xIntersectionRight = ( backNearestRight.y - offsetRight ) / gradientRight;
568    
569            // Back
570            Vector3d tmpBackLowerLeft = new Vector3d( nearLeft );
571            Vector3d tmpBackLowerRight = new Vector3d( nearRight );
572            Vector3d tmpBackUpperRight = new Vector3d( xIntersectionRight, backNearestRight.y,
573                                                       backNearestRight.z );
574            Vector3d tmpBackUpperLeft = new Vector3d( xIntersectionLeft, backNearestLeft.y,
575                                                      backNearestLeft.z );
576    
577            // left
578            xIntersectionLeft = ( nearFrontalYDistance - offsetLeft ) / gradientLeft;
579            Vector3d tmpLeftLowerLeft = new Vector3d( tmpBackUpperLeft );
580            Vector3d tmpLeftLowerRight = new Vector3d( backNearestLeft );
581            Vector3d tmpLeftUpperRight = new Vector3d( backNearestLeft.x, nearFrontalYDistance,
582                                                       backNearestLeft.z );
583            Vector3d tmpLeftUpperLeft = new Vector3d( xIntersectionLeft, nearFrontalYDistance,
584                                                      backNearestLeft.z );
585    
586            // right
587            xIntersectionRight = ( nearFrontalYDistance - offsetRight ) / gradientRight;
588            Vector3d tmpRightLowerLeft = new Vector3d( backNearestRight );
589            Vector3d tmpRightLowerRight = new Vector3d( tmpBackUpperRight );
590            Vector3d tmpRightUpperRight = new Vector3d( xIntersectionRight, nearFrontalYDistance,
591                                                        backNearestLeft.z );
592            Vector3d tmpRightUpperLeft = new Vector3d( backNearestRight.x, nearFrontalYDistance,
593                                                       backNearestLeft.z );
594    
595            // back
596            transform.transform( tmpBackLowerLeft );
597            transform.transform( tmpBackLowerRight );
598            transform.transform( tmpBackUpperRight );
599            transform.transform( tmpBackUpperLeft );
600    
601            // left
602            transform.transform( tmpLeftLowerLeft );
603            transform.transform( tmpLeftLowerRight );
604            transform.transform( tmpLeftUpperRight );
605            transform.transform( tmpLeftUpperLeft );
606    
607            // right
608            transform.transform( tmpRightLowerLeft );
609            transform.transform( tmpRightLowerRight );
610            transform.transform( tmpRightUpperRight );
611            transform.transform( tmpRightUpperLeft );
612    
613            double minResolution = StripeFactory.calcScaleOfVector( tmpBackUpperLeft,
614                                                                    tmpBackUpperRight, imageWidth );
615            double maxResolution = StripeFactory.calcScaleOfVector( tmpBackLowerLeft,
616                                                                    tmpBackLowerRight, imageWidth );
617            resultStripes.add( createResolutionStripe( tmpBackLowerLeft, tmpBackLowerRight,
618                                                       tmpBackUpperRight, tmpBackUpperLeft,
619                                                       minimalHeight, maxResolution, minResolution,
620                                                       scale ) );
621            resultStripes.add( createResolutionStripe( tmpLeftLowerLeft, tmpLeftLowerRight,
622                                                       tmpLeftUpperRight, tmpLeftUpperLeft,
623                                                       minimalHeight, maxResolution, minResolution,
624                                                       scale ) );
625            resultStripes.add( createResolutionStripe( tmpRightLowerLeft, tmpRightLowerRight,
626                                                       tmpRightUpperRight, tmpRightUpperLeft,
627                                                       minimalHeight, maxResolution, minResolution,
628                                                       scale ) );
629    
630            /**
631             * For debugging purpaces
632             */
633            if ( g2d != null ) {
634                g2d.setColor( Color.GREEN );
635                g2d.drawLine( (int) tmpBackLowerLeft.x, (int) tmpBackLowerLeft.y,
636                              (int) tmpBackLowerRight.x, (int) tmpBackLowerRight.y );
637                g2d.drawLine( (int) tmpBackLowerRight.x, (int) tmpBackLowerRight.y,
638                              (int) tmpBackUpperRight.x, (int) tmpBackUpperRight.y );
639                g2d.drawLine( (int) tmpBackUpperRight.x, (int) tmpBackUpperRight.y,
640                              (int) tmpBackUpperLeft.x, (int) tmpBackUpperLeft.y );
641                g2d.drawLine( (int) tmpBackUpperLeft.x, (int) tmpBackUpperLeft.y,
642                              (int) tmpBackLowerLeft.x, (int) tmpBackLowerLeft.y );
643    
644                g2d.setColor( Color.BLUE );
645                g2d.drawLine( (int) tmpLeftLowerLeft.x, (int) tmpLeftLowerLeft.y,
646                              (int) tmpLeftLowerRight.x, (int) tmpLeftLowerRight.y );
647                g2d.drawString("lll", (int)tmpLeftLowerLeft.x, (int) tmpLeftLowerLeft.y );
648                g2d.drawLine( (int) tmpLeftLowerRight.x, (int) tmpLeftLowerRight.y,
649                              (int) tmpLeftUpperRight.x, (int) tmpLeftUpperRight.y );
650                g2d.drawString("llr", (int)tmpLeftLowerRight.x, (int) tmpLeftLowerRight.y );
651                g2d.drawLine( (int) tmpLeftUpperRight.x, (int) tmpLeftUpperRight.y,
652                              (int) tmpLeftUpperLeft.x, (int) tmpLeftUpperLeft.y );
653                g2d.drawString("lur", (int)tmpLeftUpperRight.x, (int) tmpLeftUpperRight.y );
654                g2d.drawLine( (int) tmpLeftUpperLeft.x, (int) tmpLeftUpperLeft.y,
655                              (int) tmpLeftLowerLeft.x, (int) tmpLeftLowerLeft.y );
656    
657                g2d.setColor( Color.YELLOW );
658                g2d.drawLine( (int) tmpRightLowerLeft.x, (int) tmpRightLowerLeft.y,
659                              (int) tmpRightLowerRight.x, (int) tmpRightLowerRight.y );
660                g2d.drawLine( (int) tmpRightLowerRight.x, (int) tmpRightLowerRight.y,
661                              (int) tmpRightUpperRight.x, (int) tmpRightUpperRight.y );
662                g2d.drawLine( (int) tmpRightUpperRight.x, (int) tmpRightUpperRight.y,
663                              (int) tmpRightUpperLeft.x, (int) tmpRightUpperLeft.y );
664                g2d.drawLine( (int) tmpRightUpperLeft.x, (int) tmpRightUpperLeft.y,
665                              (int) tmpRightLowerLeft.x, (int) tmpRightLowerLeft.y );
666            }
667    
668            // What is left, are the frontal Stripes which can be more than one (whith steep pitch).
669            Vector3d tmpFrontLowerLeft = new Vector3d( tmpLeftUpperLeft );
670            Vector3d tmpFrontLowerRight = new Vector3d( tmpRightUpperRight );
671            transform.transform( nearLeft );
672            transform.transform( farLeft );
673            transform.transform( nearRight );
674            transform.transform( farRight );
675            double frontalScale = StripeFactory.calcScaleOfVector( tmpFrontLowerLeft,
676                                                                   tmpFrontLowerRight, imageWidth );
677            if ( Math.abs( ( ( Math.abs( 1.0 / gradientLeft ) ) + Math.toRadians( 90 ) )
678                           % Math.toRadians( 180 ) ) > 0.0001 ) {
679                double footPrintAngle = Math.atan( 1.0 / gradientLeft );
680                resultStripes.addAll( createStripesForFrontalPerspective( invertTransform, transform,
681                                                                          farRight, farLeft,
682                                                                          tmpFrontLowerRight,
683                                                                          tmpFrontLowerLeft,
684                                                                          imageWidth, minimalHeight,
685                                                                          footPrintAngle, frontalScale,
686                                                                          footprintFarResolution, g2d,
687                                                                          scale ) );
688            } else {
689                // Just one remaining ResolutionStripe, because only a square is left over (could be
690                // rounding error).
691                resultStripes.add( createResolutionStripe( tmpFrontLowerLeft, tmpFrontLowerRight,
692                                                           farRight, farLeft, imageWidth,
693                                                           minimalHeight, scale ) );
694            }
695            return resultStripes;
696        }
697    
698        /**
699         * Creates a ResolutionStripe fromt the given Vectors. The variable names are just indicators
700         * for the order in which the Position are added to the polygon which is min, (min+width), max,
701         * (min+height).
702         * 
703         * @param lowerLeft
704         *            the min-value of the surface
705         * @param lowerRight
706         *            the (min + width)-value of the surface
707         * @param upperRight
708         *            the max-value of the surface
709         * @param upperLeft
710         *            the (min + height)-value of the surface
711         * @param imageWidth
712         *            needed to calculate the resolution
713         * @param minimalHeight
714         *            to put the z-value to if it does not exist
715         * @return a brand new ResolutionStripe or <code>null</code> if an exception occurred.
716         */
717        private ResolutionStripe createResolutionStripe( Vector3d lowerLeft, Vector3d lowerRight,
718                                                         Vector3d upperRight, Vector3d upperLeft,
719                                                         double imageWidth, double minimalHeight,
720                                                         double scale ) {
721            double maxResolution = StripeFactory.calcScaleOfVector( upperRight, upperLeft, imageWidth );
722            double minResolution = StripeFactory.calcScaleOfVector( lowerRight, lowerLeft, imageWidth );
723    
724            return createResolutionStripe( lowerLeft, lowerRight, upperRight, upperLeft, minimalHeight,
725                                           maxResolution, minResolution, scale );
726        }
727    
728        private ResolutionStripe createResolutionStripe( Vector3d lowerLeft, Vector3d lowerRight,
729                                                         Vector3d upperRight, Vector3d upperLeft,
730                                                         double minimalHeight, double maxResolution,
731                                                         double minResolution, double scale ) {
732            Position[] pos = new Position[5];
733            pos[0] = GeometryFactory.createPosition( lowerLeft.x, lowerLeft.y, lowerLeft.z );
734            pos[1] = GeometryFactory.createPosition( lowerRight.x, lowerRight.y, lowerLeft.z );
735            pos[2] = GeometryFactory.createPosition( upperRight.x, upperRight.y, lowerLeft.z );
736            pos[3] = GeometryFactory.createPosition( upperLeft.x, upperLeft.y, lowerLeft.z );
737            pos[4] = GeometryFactory.createPosition( lowerLeft.x, lowerLeft.y, lowerLeft.z );
738    
739            try {
740                Surface surf = GeometryFactory.createSurface( pos, null, null, viewPoint.getCrs() );
741                return new ResolutionStripe( surf, maxResolution, minResolution, minimalHeight, scale );
742    
743            } catch ( GeometryException e ) {
744                e.printStackTrace();
745            }
746            return null;
747        }
748    
749        /**
750         * @param bbox
751         * @param imageWidth
752         * @param minimalHeight
753         * @param scale
754         *            of the height in the elevationmodel
755         * @return a BoundingBox request ResolutionStripe
756         */
757        public ArrayList<ResolutionStripe> createBBoxResolutionStripe( Envelope bbox, int imageWidth,
758                                                                       double minimalHeight,
759                                                                       double scale ) {
760    
761            Surface resultSurface = null;
762            try {
763                resultSurface = GeometryFactory.createSurface( bbox, viewPoint.getCrs() );
764            } catch ( GeometryException e ) {
765                e.printStackTrace();
766            }
767            Position min = bbox.getMin();
768            double zValue = min.getZ();
769            if ( min.getCoordinateDimension() == 2 )
770                zValue = minimalHeight;
771    
772            Vector3d lowerLeftPoint = new Vector3d( min.getX(), min.getY(), zValue );
773            Vector3d lowerRightPoint = new Vector3d( min.getX() + ( bbox.getWidth() ), min.getY(),
774                                                     zValue );
775            double resolution = StripeFactory.calcScaleOfVector( lowerLeftPoint, lowerRightPoint,
776                                                                 imageWidth );
777    
778            ArrayList<ResolutionStripe> resultStripe = new ArrayList<ResolutionStripe>();
779            resultStripe.add( new ResolutionStripe( resultSurface, resolution, resolution,
780                                                    minimalHeight, scale ) );
781    
782            return resultStripe;
783        }
784    
785        /**
786         * Calculates the Scale ( = Resolution* sqrt( 2 ) (== diagonal of pixel)) of a Vector between
787         * two points on the Screen given an imagewidth. That is, how much meter is the Scale of one
788         * Pixel in an Image given a certain vector.
789         * 
790         * @param a
791         *            "from" point
792         * @param b
793         *            "to" point
794         * @param imageWidth
795         *            the target imagewidth
796         * @return the scale on the screen.
797         */
798        public static double calcScaleOfVector( Vector3d a, Vector3d b, double imageWidth ) {
799            Vector3d line = new Vector3d( a.x - b.x, a.y - b.y, a.z - b.z );
800            // how much meter is one pixel
801            // scale = the diagonal of one pixel
802            return ( line.length() / imageWidth ) * MapUtils.SQRT2;
803        }
804    
805        /**
806         * @param value
807         * @return the nearestPowerOfTwo of the given value
808         */
809        private int nearestPowerOfTwo( double value ) {
810            int result = 0;
811            int power = 2;
812            while ( power <= value ) {
813                power = power << 1;
814                result++;
815            }
816            return result;
817        }
818    
819    }