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