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