    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }