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