    001    //$$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/ogcwebservices/wpvs/configuration/WPVSConfiguration.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.configuration;
    038
    039    import java.awt.GraphicsConfigTemplate;
    040    import java.awt.GraphicsConfiguration;
    041    import java.awt.GraphicsDevice;
    042    import java.awt.GraphicsEnvironment;
    043    import java.awt.image.BufferedImage;
    044    import java.io.File;
    045    import java.io.IOException;
    046    import java.io.StringReader;
    047    import java.util.ArrayList;
    048    import java.util.Arrays;
    049    import java.util.HashMap;
    050    import java.util.List;
    051    import java.util.Map;
    052    import java.util.Set;
    053    import java.util.Vector;
    054
    055    import javax.imageio.ImageIO;
    056    import javax.media.j3d.Canvas3D;
    057    import javax.media.j3d.GraphicsConfigTemplate3D;
    058    import javax.media.j3d.View;
    059    import javax.vecmath.Point3d;
    060
    061    import org.deegree.datatypes.Code;
    062    import org.deegree.datatypes.QualifiedName;
    063    import org.deegree.framework.log.ILogger;
    064    import org.deegree.framework.log.LoggerFactory;
    065    import org.deegree.framework.util.CharsetUtils;
    066    import org.deegree.framework.util.IDGenerator;
    067    import org.deegree.framework.xml.XMLTools;
    068    import org.deegree.i18n.Messages;
    069    import org.deegree.model.coverage.grid.ImageGridCoverage;
    070    import org.deegree.model.crs.CRSTransformationException;
    071    import org.deegree.model.crs.CoordinateSystem;
    072    import org.deegree.model.crs.GeoTransformer;
    073    import org.deegree.model.crs.UnknownCRSException;
    074    import org.deegree.model.feature.FeatureCollection;
    075    import org.deegree.model.filterencoding.ComplexFilter;
    076    import org.deegree.model.filterencoding.FeatureFilter;
    077    import org.deegree.model.filterencoding.FeatureId;
    078    import org.deegree.model.filterencoding.Filter;
    079    import org.deegree.model.spatialschema.Envelope;
    080    import org.deegree.model.spatialschema.GMLGeometryAdapter;
    081    import org.deegree.model.spatialschema.Position;
    082    import org.deegree.ogcbase.PropertyPath;
    083    import org.deegree.ogcwebservices.OGCWebServiceException;
    084    import org.deegree.ogcwebservices.getcapabilities.Contents;
    085    import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
    086    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
    087    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
    088    import org.deegree.ogcwebservices.wcs.WCSException;
    089    import org.deegree.ogcwebservices.wcs.WCService;
    090    import org.deegree.ogcwebservices.wcs.getcoverage.DomainSubset;
    091    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
    092    import org.deegree.ogcwebservices.wcs.getcoverage.Output;
    093    import org.deegree.ogcwebservices.wcs.getcoverage.ResultCoverage;
    094    import org.deegree.ogcwebservices.wcs.getcoverage.SpatialSubset;
    095    import org.deegree.ogcwebservices.wfs.WFService;
    096    import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
    097    import org.deegree.ogcwebservices.wfs.operation.GetFeature;
    098    import org.deegree.ogcwebservices.wpvs.capabilities.Dataset;
    099    import org.deegree.ogcwebservices.wpvs.capabilities.ElevationModel;
    100    import org.deegree.ogcwebservices.wpvs.capabilities.WPVSCapabilities;
    101    import org.deegree.ogcwebservices.wpvs.j3d.PointsToPointListFactory;
    102    import org.deegree.processing.raster.converter.Image2RawData;
    103    import org.w3c.dom.Document;
    104
    105    /**
    106     * This class represents a <code>WPVSConfiguration</code> object.
    107     *
    108     * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
    109     * @author last edited by: $Author: rbezema $
    110     *
    111     *         $Revision: 20603 $, $Date: 2009-11-05 16:26:23 +0100 (Do, 05. Nov 2009) $
    112     *
    113     */
    114    public class WPVSConfiguration extends WPVSCapabilities {
    115
    116        private static ILogger LOG = LoggerFactory.getLogger( WPVSConfiguration.class );
    117
    118        /**
    119         *
    120         */
    121        private static final long serialVersionUID = 3699085834869705611L;
    122
    123        private WPVSDeegreeParams deegreeParams;
    124
    125        private double smallestMinimalScaleDenomiator;
    126
    127        // set if the configured datasets were searched.
    128        private static boolean searchedForElevModel = false;
    129
    130        // pool for the canvasses
    131        private static List<Canvas3D> canvasPool = new ArrayList<Canvas3D>();
    132
    133        private static Vector<Canvas3D> inUseCanvases = new Vector<Canvas3D>();
    134
    135        // just some strings for getting the gpu-properties
    136        private final static String tusm = "textureUnitStateMax";
    137
    138        private final static String twm = "textureWidthMax";
    139
    140        /**
    141         * Is set to the maximum number of texture units accessable to the wpvs.
    142         */
    143        public static int availableTextureUnitStates = 1;
    144
    145        /**
    146         * Is set to the maximum size of a texture supported by the canvas3d.
    147         */
    148        public static int texture2DMaxSize = 1024;
    149
    150        /**
    151         * The size of a wfs/wms/wcs request if larger as the available texture size it will be the texture size (read from
    152         * the gpu).
    153         */
    154        public static int MAX_REQUEST_SIZE;
    155
    156        /**
    157         * The elevation model to check for the height above the terrain.
    158         */
    159        public static AbstractDataSource largestElevModel = null;
    160
    161        // holding the z values with heightMap[y][x]
    162        private static float[][] heightMap;
    163
    164        // scale mapping the width of the configured bbox to the width of the heightmap
    165        private static double scaleMapWidth;
    166
    167        // scale mapping the height of the configured bbox to the height of the heightmap
    168        private static double scaleMapHeight;
    169
    170        // actual width of the heightMap == heightmap[0].length
    171        private static int mapWidth;
    172
    173        // actual height of the heightMap == heightmap.length
    174        private static int mapHeight;
    175
    176        // the found minimalHeight for the terrain or the configuredMinTerrainHeight.
    177        private static double globalMinimalHeight;
    178
    179        // the configured bbox of the top dataset.
    180        private static Envelope configuredBBox;
    181
    182        static {
    183            canvasPool.add( createOffscreenCanvas3D() );
    184        }
    185
    186        /**
    187         * @param version
    188         *            the Version of this wpvs
    189         * @param updateSequence
    190         *            optional needed for clients who want to do caching (ogc-spec)
    191         * @param serviceIdentification
    192         * @param serviceProvider
    193         * @param operationsMetadata
    194         * @param contents
    195         * @param dataset
    196         * @param wpvsParams
    197         *            deegree specific parameters.
    198         * @param smallestMinimalScaleDenomiator
    199         *            of all datasources, it is needed to calculate the smallest resolutionstripe possible.
    200         */
    201        public WPVSConfiguration( String version, String updateSequence, ServiceIdentification serviceIdentification,
    202                                  ServiceProvider serviceProvider, OperationsMetadata operationsMetadata,
    203                                  Contents contents, Dataset dataset, WPVSDeegreeParams wpvsParams,
    204                                  double smallestMinimalScaleDenomiator ) {
    205
    206            super( version, updateSequence, serviceIdentification, serviceProvider, operationsMetadata, contents, dataset );
    207            this.deegreeParams = wpvsParams;
    208            // int size = Integer.MAX_VALUE;// deegreeParams.getMaxRequestSize();
    209            int size = deegreeParams.getMaxTextureDimension(); // deegreeParams.getMaxRequestSize();
    210            if ( size == Integer.MAX_VALUE ) {
    211                MAX_REQUEST_SIZE = texture2DMaxSize;
    212            } else {
    213                MAX_REQUEST_SIZE = size;
    214                if ( MAX_REQUEST_SIZE > texture2DMaxSize ) {
    215                    LOG.logWarning( "The specified max request size value (of the deeegree params section) is larger then the possible texture size of your graphics-card, therefore setting it to: "
    216                                    + texture2DMaxSize );
    217                    MAX_REQUEST_SIZE = texture2DMaxSize;
    218                }
    219            }
    220            // set the minmalScaleDenominator according to the request-size
    221            this.smallestMinimalScaleDenomiator = ( (double) deegreeParams.getMaxViewWidth() ) / MAX_REQUEST_SIZE;
    222            LOG.logDebug( "Smallest denomi: " + this.smallestMinimalScaleDenomiator );
    223            if ( Double.isInfinite( smallestMinimalScaleDenomiator ) || smallestMinimalScaleDenomiator < 0
    224                 || smallestMinimalScaleDenomiator > 1 ) {
    225                this.smallestMinimalScaleDenomiator = 1;
    226            }
    227
    228            // create the global height map if it does not exist already
    229            if ( !searchedForElevModel && largestElevModel == null ) {
    230                synchronized ( canvasPool ) {
    231                    if ( !searchedForElevModel ) {
    232                        largestElevModel = findLargestElevModel();
    233                        searchedForElevModel = true;
    234                        if ( largestElevModel != null ) {
    235                            Dataset topSet = getDataset();
    236                            Envelope env = topSet.getWgs84BoundingBox();
    237                            CoordinateSystem[] definedCRSs = topSet.getCrs();
    238                            CoordinateSystem defaultCRS = null;
    239                            if ( definedCRSs != null && definedCRSs.length > 0 ) {
    240                                defaultCRS = definedCRSs[0];
    241                                if ( definedCRSs.length > 1 ) {
    242                                    LOG.logInfo( "Using first defined crs: " + defaultCRS
    243                                                 + " to convert latlon (wgs84) coordinates to." );
    244                                }
    245                                try {
    246                                    GeoTransformer gt = new GeoTransformer( defaultCRS );
    247                                    configuredBBox = gt.transform( env, "EPSG:4326" );
    248                                } catch ( UnknownCRSException e ) {
    249                                    LOG.logError( e.getMessage(), e );
    250                                } catch ( CRSTransformationException e ) {
    251                                    LOG.logError( e.getMessage(), e );
    252                                }
    253                                // check if the admin has configured a minimal dgm resolution, if so the request and
    254                                // response resolution for the dgm must be set.
    255                                int requestWidth = MAX_REQUEST_SIZE;
    256                                int requestHeight = MAX_REQUEST_SIZE;
    257                                if ( configuredBBox.getWidth() >= configuredBBox.getHeight() ) {
    258                                    requestHeight = (int) Math.floor( ( configuredBBox.getHeight() / configuredBBox.getWidth() )
    259                                                                      * MAX_REQUEST_SIZE );
    260                                } else {
    261                                    requestWidth = (int) Math.floor( ( configuredBBox.getWidth() / configuredBBox.getHeight() )
    262                                                                     * MAX_REQUEST_SIZE );
    263                                }
    264                                LOG.logDebug( "Setting globalHeightmap requestWidth: " + requestWidth );
    265                                LOG.logDebug( "Setting globalHeightmap requestHeight: " + requestHeight );
    266
    267                                // Set the static members for easy global access
    268                                mapHeight = requestHeight;
    269                                mapWidth = requestWidth;
    270
    271                                scaleMapWidth = mapWidth / configuredBBox.getWidth();
    272                                scaleMapHeight = mapHeight / configuredBBox.getHeight();
    273
    274                                if ( largestElevModel instanceof LocalWCSDataSource ) {
    275                                    heightMap = invokeWCS( (LocalWCSDataSource) largestElevModel, configuredBBox,
    276                                                           requestWidth, requestHeight );
    277                                } else if ( largestElevModel instanceof LocalWFSDataSource ) {
    278                                    heightMap = invokeWFS( (LocalWFSDataSource) largestElevModel, configuredBBox,
    279                                                           requestWidth, requestHeight );
    280                                }
    281
    282                                if ( heightMap.length == 0 ) {
    283                                    LOG.logWarning( "The creation of the global heightmap has failed." );
    284                                    globalMinimalHeight = getDeegreeParams().getMinimalTerrainHeight();
    285                                } else {
    286                                    float max = Float.MIN_VALUE;
    287                                    globalMinimalHeight = Double.MAX_VALUE;
    288                                    for ( int y = 0; y < requestHeight; ++y ) {
    289                                        for ( int x = 0; x < requestWidth; ++x ) {
    290                                            float height = heightMap[y][x];
    291                                            globalMinimalHeight = Math.min( globalMinimalHeight, height );
    292                                            max = Math.max( max, height );
    293                                        }
    294                                    }
    295                                    if ( globalMinimalHeight < getDeegreeParams().getMinimalTerrainHeight() ) {
    296                                        LOG.logDebug( "Setting found globalMinimalHeight: " + globalMinimalHeight
    297                                                      + " to configured minmalTerrainHeight of: "
    298                                                      + getDeegreeParams().getMinimalTerrainHeight() );
    299                                        globalMinimalHeight = getDeegreeParams().getMinimalTerrainHeight();
    300                                    }
    301                                    if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    302                                        BufferedImage img = new BufferedImage( requestWidth, requestHeight,
    303                                                                               BufferedImage.TYPE_INT_RGB );
    304                                        LOG.logDebug( "global maximumHeight: " + max );
    305                                        LOG.logDebug( "global minimalHeight: " + globalMinimalHeight );
    306
    307                                        double scale = ( 1 / ( max - globalMinimalHeight ) ) * 255;
    308                                        for ( int y = 0; y < requestHeight; ++y ) {
    309                                            for ( int x = 0; x < requestWidth; ++x ) {
    310                                                float height = heightMap[y][x];
    311                                                byte first = (byte) Math.floor( height * scale );
    312                                                int color = first;
    313                                                color |= ( color << 8 );
    314                                                color |= ( color << 16 );
    315
    316                                                img.setRGB( x, y, color );
    317                                            }
    318
    319                                        }
    320                                        try {
    321                                            File f = File.createTempFile( "global_heightmap_response", ".png" );
    322                                            LOG.logDebug( "creating tmpfile for global heightmap with name: "
    323                                                          + f.toString() );
    324                                            f.deleteOnExit();
    325                                            ImageIO.write( img, "png", f );
    326                                        } catch ( IOException e ) {
    327                                            LOG.logError( e.getMessage(), e );
    328                                        }
    329                                    }
    330
    331                                }
    332
    333                            }
    334                        }
    335                    }
    336                    canvasPool.notifyAll();
    337                }
    338                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    339                    if ( largestElevModel != null ) {
    340                        LOG.logDebug( "found elev-model: " + largestElevModel );
    341                    }
    342                }
    343            }
    344        }
    345
    346        /**
    347         * @param dataSource
    348         * @param env
    349         * @param requestHeight
    350         * @param requestWidth
    351         * @return the wfs-feature-points mapped to the heightmap, or an empty map if no features were found.
    352         */
    353        private float[][] invokeWFS( LocalWFSDataSource dataSource, Envelope env, int requestWidth, int requestHeight ) {
    354            float[][] result = new float[0][0];
    355            WFService service = null;
    356            try {
    357                service = (WFService) dataSource.getOGCWebService();
    358            } catch ( OGCWebServiceException ogcwe ) {
    359                LOG.logError( ogcwe.getMessage() );
    360                // throw new RuntimeException( ogcwe );
    361            }
    362            if ( service == null ) {
    363                LOG.logError( "No Web Feature Service instance available for creation of the Global height map." );
    364            } else {
    365                Object response = null;
    366                try {
    367
    368                    // create the GetFeature request.
    369                    QualifiedName qn = dataSource.getName();
    370
    371                    StringBuilder sb = new StringBuilder( 5000 );
    372                    sb.append( "<?xml version='1.0' encoding='" + CharsetUtils.getSystemCharset() + "'?>" );
    373                    sb.append( "<wfs:GetFeature xmlns:wfs='http://www.opengis.net/wfs' " );
    374                    sb.append( "xmlns:ogc='http://www.opengis.net/ogc' " );
    375                    sb.append( "xmlns:gml='http://www.opengis.net/gml' " );
    376                    sb.append( "xmlns:" ).append( qn.getPrefix() ).append( '=' );
    377                    sb.append( "'" ).append( qn.getNamespace() ).append( "' " );
    378
    379                    if ( dataSource.getServiceType() == AbstractDataSource.LOCAL_WFS ) {
    380                        sb.append( "outputFormat='FEATURECOLLECTION'>" );
    381                    } else {
    382                        sb.append( "outputFormat='text/xml; subtype=gml/3.1.1'>" );
    383                    }
    384
    385                    /**
    386                     * To make things a little clearer compare with this SQL-Statement: SELECT ( !texture )? geoProperty : *
    387                     * FROM qn.getLocalName() WHERE geoPoperty intersects with resolutionStripe.getSurface() AND
    388                     * FilterConditions.
    389                     */
    390                    PropertyPath geoProperty = dataSource.getGeometryProperty();
    391
    392                    // FROM
    393                    sb.append( "<wfs:Query typeName='" ).append( qn.getPrefix() ).append( ":" );
    394                    sb.append( qn.getLocalName() ).append( "'>" );
    395
    396                    // SELECT
    397                    StringBuffer sbArea = GMLGeometryAdapter.exportAsEnvelope( env );
    398
    399                    // WHERE
    400                    sb.append( "<ogc:Filter>" );
    401
    402                    // AND
    403                    Filter filter = dataSource.getFilter();
    404                    if ( filter != null ) {
    405                        if ( filter instanceof ComplexFilter ) {
    406                            sb.append( "<ogc:And>" );
    407                            sb.append( "<ogc:Intersects>" );
    408                            sb.append( "<wfs:PropertyName>" );
    409                            sb.append( geoProperty.getAsString() );
    410                            sb.append( "</wfs:PropertyName>" );
    411                            sb.append( sbArea );
    412                            sb.append( "</ogc:Intersects>" );
    413                            // add filter as defined in the layers datasource description
    414                            // to the filter expression
    415                            org.deegree.model.filterencoding.Operation op = ( (ComplexFilter) filter ).getOperation();
    416                            sb.append( op.toXML() ).append( "</ogc:And>" );
    417                        } else {
    418                            if ( filter instanceof FeatureFilter ) {
    419                                ArrayList<FeatureId> featureIds = ( (FeatureFilter) filter ).getFeatureIds();
    420                                if ( featureIds.size() != 0 )
    421                                    sb.append( "<ogc:And>" );
    422                                for ( FeatureId fid : featureIds ) {
    423                                    sb.append( fid.toXML() );
    424                                }
    425                                if ( featureIds.size() != 0 )
    426                                    sb.append( "</ogc:And>" );
    427                            }
    428                        }
    429                    } else {
    430                        sb.append( "<ogc:Intersects>" );
    431                        sb.append( "<wfs:PropertyName>" );
    432                        sb.append( geoProperty.getAsString() );
    433                        sb.append( "</wfs:PropertyName>" );
    434                        sb.append( sbArea );
    435                        sb.append( "</ogc:Intersects>" );
    436                    }
    437
    438                    sb.append( "</ogc:Filter></wfs:Query></wfs:GetFeature>" );
    439
    440                    Document doc = null;
    441                    try {
    442                        doc = XMLTools.parse( new StringReader( sb.toString() ) );
    443                    } catch ( Exception e ) {
    444                        LOG.logError( e.getMessage(), e );
    445                        // throw new OGCWebServiceException( e.getMessage() );
    446                    }
    447                    if ( doc != null ) {
    448                        IDGenerator idg = IDGenerator.getInstance();
    449                        GetFeature getFeature = GetFeature.create( String.valueOf( idg.generateUniqueID() ),
    450                                                                   doc.getDocumentElement() );
    451                        LOG.logDebug( "WFS request: " + getFeature );
    452
    453                        // send the request
    454                        response = service.doService( getFeature );
    455                    }
    456                } catch ( OGCWebServiceException ogcwse ) {
    457                    if ( !Thread.currentThread().isInterrupted() ) {
    458                        LOG.logError( "Exception while performing WFS-GetFeature: ", ogcwse );
    459                    }
    460                }
    461
    462                if ( response != null && response instanceof FeatureResult ) {
    463                    FeatureCollection fc = (FeatureCollection) ( (FeatureResult) response ).getResponse();
    464                    if ( fc != null ) {
    465                        PointsToPointListFactory ptpFac = new PointsToPointListFactory();
    466                        List<Point3d> heights = ptpFac.createFromFeatureCollection( fc );
    467
    468                        // find the min value.
    469                        float min = Float.MAX_VALUE;
    470                        for ( Point3d p : heights ) {
    471                            min = Math.min( min, (float) p.z );
    472                        }
    473
    474                        result = new float[requestHeight][requestWidth];
    475                        for ( float[] t : result ) {
    476                            Arrays.fill( t, min );
    477                        }
    478
    479                        double scaleX = requestWidth / env.getWidth();
    480                        double scaleY = requestHeight / env.getHeight();
    481                        for ( Point3d height : heights ) {
    482                            int x = (int) Math.round( ( height.x - env.getMin().getX() ) * scaleX );
    483                            int y = (int) Math.round( requestHeight - ( ( height.y - env.getMin().getY() ) * scaleY ) );
    484                            float savedHeight = result[y][x];
    485                            if ( Math.abs( savedHeight - min ) > 1E-10 ) {
    486                                savedHeight += height.z;
    487                                result[y][x] = savedHeight * 0.5f;
    488                            } else {
    489                                result[y][x] = (float) height.z;
    490                            }
    491                        }
    492                    }
    493                } else {
    494                    LOG.logError( "ERROR creating a global heightmap while invoking wfs-datasource : "
    495                                  + dataSource.getName() + " the result was no WFS-response or no FeatureResult instance" );
    496                }
    497            }
    498            return result;
    499        }
    500
    501        /**
    502         * @param pos
    503         *            the position to get the height value
    504         * @return the height value of the given position or the globalMinimalHeight value if the position was outside the
    505         *         heightmap.
    506         */
    507        public static double getHeightForPosition( Point3d pos ) {
    508            int posX = (int) Math.floor( ( pos.x - configuredBBox.getMin().getX() ) * scaleMapWidth );
    509            int posY = (int) Math.floor( mapHeight - ( ( pos.y - configuredBBox.getMin().getY() ) * scaleMapHeight ) );
    510            if ( posY < 0 || posY > heightMap.length || posX < 0 || posX > heightMap[0].length ) {
    511                LOG.logDebug( "Given position " + pos + " is outside the global height lookup, returning minimal value: "
    512                              + globalMinimalHeight );
    513                return globalMinimalHeight;
    514            }
    515            LOG.logDebug( "The looked up value for postion: " + pos + " (mapped to: " + posX + ", " + posY + ") is: "
    516                          + heightMap[posY][posX] );
    517            return heightMap[posY][posX];
    518        }
    519
    520        /**
    521         * @param dataSource
    522         * @param env
    523         * @return the heightmap created from the wcs elevation model or an empty array if no such heightmap could be
    524         *         created.
    525         */
    526        private float[][] invokeWCS( LocalWCSDataSource dataSource, Envelope env, int requestWidth, int requestHeight ) {
    527            float[][] result = new float[0][0];
    528            WCService service = null;
    529            try {
    530                service = (WCService) dataSource.getOGCWebService();
    531            } catch ( OGCWebServiceException e1 ) {
    532                e1.printStackTrace();
    533            }
    534
    535            if ( service == null ) {
    536                LOG.logError( "No Web Coverage Service instance available for creation of the Global height map." );
    537            } else {
    538
    539                Object coverageResponse = null;
    540
    541                try {
    542                    String crsString = env.getCoordinateSystem().getFormattedString();
    543                    Output output = GetCoverage.createOutput( crsString, null, dataSource.getDefaultFormat(), null );
    544
    545                    // put missing parts in this map:
    546                    Map<String, String> map = new HashMap<String, String>( 5 );
    547                    StringBuffer sb = new StringBuffer( 1000 );
    548                    Position p = env.getMin();
    549                    sb.append( p.getX() ).append( "," ).append( p.getY() ).append( "," );
    550                    p = env.getMax();
    551                    sb.append( p.getX() ).append( "," ).append( p.getY() );
    552                    map.put( "BBOX", sb.toString() );
    553
    554                    map.put( "WIDTH", String.valueOf( requestWidth ) );
    555                    map.put( "HEIGHT", String.valueOf( requestHeight ) );
    556
    557                    SpatialSubset sps = GetCoverage.createSpatialSubset( map, crsString );
    558
    559                    GetCoverage filterCondition = dataSource.getCoverageFilterCondition();
    560
    561                    Code code = filterCondition.getDomainSubset().getRequestSRS();
    562                    DomainSubset domainSubset = new DomainSubset( code, sps, null );
    563
    564                    IDGenerator idg = IDGenerator.getInstance();
    565
    566                    GetCoverage getCoverageRequest = new GetCoverage( String.valueOf( idg.generateUniqueID() ),
    567                                                                      filterCondition.getVersion(),
    568                                                                      filterCondition.getSourceCoverage(), domainSubset,
    569                                                                      null, filterCondition.getInterpolationMethod(),
    570                                                                      output );
    571                    LOG.logDebug( "Sending wcs request:" + dataSource.getName() );
    572                    coverageResponse = service.doService( getCoverageRequest );
    573                } catch ( WCSException wcse ) {
    574                    if ( !Thread.currentThread().isInterrupted() ) {
    575                        LOG.logError( Messages.getMessage( "WPVS_WCS_REQUEST_ERROR", "WCSException", dataSource.getName(),
    576                                                           wcse.getMessage() ) );
    577                    }
    578                } catch ( OGCWebServiceException ogcwse ) {
    579                    if ( !Thread.currentThread().isInterrupted() ) {
    580                        LOG.logError( Messages.getMessage( "WPVS_WCS_REQUEST_ERROR", "OGCWebServiceException",
    581                                                           dataSource.getName(), ogcwse.getMessage() ) );
    582                    }
    583                } catch ( Throwable t ) {
    584                    if ( !Thread.currentThread().isInterrupted() ) {
    585                        t.printStackTrace();
    586                    }
    587                }
    588                if ( coverageResponse != null && coverageResponse instanceof ResultCoverage ) {
    589
    590                    LOG.logDebug( "\t -> a valid response\n" );
    591                    ResultCoverage response = (ResultCoverage) coverageResponse;
    592                    if ( response.getCoverage() != null && response.getCoverage() instanceof ImageGridCoverage ) {
    593                        ImageGridCoverage igc = (ImageGridCoverage) response.getCoverage();
    594                        BufferedImage image = igc.getAsImage( requestWidth, requestHeight );
    595                        // the heightdata is in x and -y coordinates, they must be flipped before using
    596                        // PlanarImage im2 = JAI.create( "transpose", image, TransposeDescriptor.FLIP_VERTICAL );
    597                        if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    598                            try {
    599                                File f = File.createTempFile( "global_wcs_dgm_response", ".jpg" );
    600                                LOG.logDebug( "creating tmpfile for global wcs elevationmodel response with name: "
    601                                              + f.toString() );
    602                                f.deleteOnExit();
    603                                ImageIO.write( image, "jpg", f );
    604                            } catch ( Exception e ) {
    605                                LOG.logError( e.getMessage(), e );
    606                            }
    607                        }
    608                        // BufferedImage heightMap = image.get;
    609                        Image2RawData i2rd = new Image2RawData( image, 1 );
    610                        result = i2rd.parse();
    611                    }
    612                } else {
    613                    LOG.logWarning( Messages.getMessage( "WPVS_IVALID_WCS_RESPONSE", dataSource.getName(),
    614                                                         "an ImageGridCoverage" ) );
    615                }
    616            }
    617            return result;
    618        }
    619
    620        /**
    621         * @return an elevation datasource with the largest min scale denominator or <code>null</code> if no elevation model
    622         *         datasource was found.
    623         */
    624        private AbstractDataSource findLargestElevModel() {
    625            return findLargestElevModel( super.getDataset() );
    626        }
    627
    628        /**
    629         * @param dataset
    630         *            to look for the elevationmodel datasources.
    631         * @return an elevation datasource with the largest min scale denominator or <code>null</code> if no elevation model
    632         *         datasource was found.
    633         */
    634        private AbstractDataSource findLargestElevModel( Dataset dataset ) {
    635            Dataset[] children = dataset.getDatasets();
    636            ElevationModel model = dataset.getElevationModel();
    637            AbstractDataSource elevSource = null;
    638            if ( model != null ) {
    639                AbstractDataSource[] sources = model.getDataSources();
    640                if ( sources != null ) {
    641                    for ( AbstractDataSource source : sources ) {
    642                        if ( source != null
    643                             && ( elevSource == null || source.getMinScaleDenominator() > elevSource.getMinScaleDenominator() ) ) {
    644                            elevSource = source;
    645                        }
    646                    }
    647                }
    648            }
    649            for ( Dataset set : children ) {
    650                AbstractDataSource tmpSource = findLargestElevModel( set );
    651                if ( tmpSource != null
    652                     && ( elevSource == null || tmpSource.getMinScaleDenominator() > elevSource.getMinScaleDenominator() ) ) {
    653                    elevSource = tmpSource;
    654                }
    655            }
    656            return elevSource;
    657        }
    658
    659        /**
    660         * @return an Offscreen canvas3D from a simple pool if it has no view and the renderer is not running.
    661         */
    662        public synchronized static Canvas3D getCanvas3D() {
    663            LOG.logDebug( "The pool now contains: " + canvasPool.size() + " canvasses" );
    664            LOG.logDebug( "The inuse pool now contains: " + inUseCanvases.size() + " canvasses" );
    665            for ( Canvas3D c : canvasPool ) {
    666                if ( !inUseCanvases.contains( c ) ) {
    667                    if ( c != null ) {
    668                        View v = c.getView();
    669                        LOG.logDebug( "Canvas has view attached: " + v );
    670                        if ( v != null ) {
    671                            LOG.logDebug( "Removing the view from the pooled Canvas3D because it is not in use anymore." );
    672                            v.removeAllCanvas3Ds();
    673                        }
    674                        LOG.logDebug( "Using a pooled Canvas3D." );
    675                        inUseCanvases.add( c );
    676                        return c;
    677                    }
    678                }
    679            }
    680            LOG.logDebug( "Creating a new Canvas3D, because all canvasses are in use." );
    681            Canvas3D tmp = createOffscreenCanvas3D();
    682            canvasPool.add( tmp );
    683            inUseCanvases.add( tmp );
    684            return tmp;
    685        }
    686
    687        /**
    688         * @param canvas
    689         *            to be released.
    690         * @return true if the removal operation was successful, false if the given canvas3D was not in the list of used
    691         *         canvasses.
    692         */
    693        public synchronized static boolean releaseCanvas3D( Canvas3D canvas ) {
    694            if ( canvas != null ) {
    695                View v = canvas.getView();
    696                if ( v != null ) {
    697                    LOG.logDebug( "Removing the view from the Canvas3D because it is not used anymore." );
    698                    v.removeAllCanvas3Ds();
    699                }
    700                if ( inUseCanvases.contains( canvas ) ) {
    701                    LOG.logDebug( "Removing the given Canvas3D from the list." );
    702
    703                    return inUseCanvases.remove( canvas );
    704                }
    705                LOG.logInfo( "The given canvas3D was not held by the configuration." );
    706            }
    707            LOG.logDebug( "The pool now contains: " + canvasPool.size() + " canvasses" );
    708            LOG.logDebug( "The inuse pool now contains: " + inUseCanvases.size() + " canvasses" );
    709            return false;
    710        }
    711
    712        /**
    713         * creates and returns a canvas for offscreen rendering
    714         *
    715         * @return a offscreen Canvas3D on which the the scene will be rendered.
    716         */
    717        @SuppressWarnings("unchecked")
    718        protected static synchronized Canvas3D createOffscreenCanvas3D() {
    719            GraphicsDevice[] gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
    720            GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D();
    721            gc3D.setSceneAntialiasing( GraphicsConfigTemplate.PREFERRED );
    722            gc3D.setDoubleBuffer( GraphicsConfigTemplate.REQUIRED );
    723
    724            if ( gd != null && gd.length > 0 ) {
    725                GraphicsConfiguration gc = gd[0].getBestConfiguration( gc3D );
    726                if ( gc != null ) {
    727                    Canvas3D offScreenCanvas3D = new Canvas3D( gc, true );
    728                    Map<String, ?> props = offScreenCanvas3D.queryProperties();
    729                    if ( props.containsKey( tusm ) ) {
    730                        Integer tus = (Integer) props.get( tusm );
    731                        if ( tus != null ) {
    732                            availableTextureUnitStates = tus.intValue();
    733                        }
    734                    }
    735
    736                    if ( props.containsKey( twm ) ) {
    737                        Integer tw = (Integer) props.get( twm );
    738                        if ( tw != null ) {
    739                            texture2DMaxSize = tw.intValue();
    740                        }
    741                    }
    742
    743                    if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    744                        Set<String> keys = props.keySet();
    745                        StringBuilder sb = new StringBuilder( "Canvas3D has following properties:\n" );
    746                        for ( String key : keys ) {
    747                            sb.append( key ).append( " : " ).append( props.get( key ) ).append( "\n" );
    748                        }
    749                        LOG.logDebug( sb.toString() );
    750                    }
    751                    return offScreenCanvas3D;
    752                }
    753                LOG.logError( "Could not get a GraphicsConfiguration from the graphics environment, cannot create a canvas3d." );
    754            } else {
    755                LOG.logError( "Could not get a graphicsdevice to create a canvas3d." );
    756            }
    757            return null;
    758        }
    759
    760        /**
    761         * @return Returns the deegreeParams.
    762         */
    763        public WPVSDeegreeParams getDeegreeParams() {
    764            return deegreeParams;
    765        }
    766
    767        /**
    768         * @param deegreeParams
    769         *            The deegreeParams to set.
    770         */
    771        public void setDeegreeParams( WPVSDeegreeParams deegreeParams ) {
    772            this.deegreeParams = deegreeParams;
    773        }
    774
    775        /**
    776         * @return the smallestMinimalScaleDenomiator value.
    777         */
    778        public double getSmallestMinimalScaleDenomiator() {
    779            return smallestMinimalScaleDenomiator;
    780        }
    781    }