001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/wpvs/utils/ResolutionStripe.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.BasicStroke; 040 import java.awt.Color; 041 import java.awt.Font; 042 import java.awt.Graphics2D; 043 import java.awt.Stroke; 044 import java.awt.font.TextLayout; 045 import java.awt.geom.Rectangle2D; 046 import java.awt.image.BufferedImage; 047 import java.io.File; 048 import java.io.IOException; 049 import java.util.ArrayList; 050 import java.util.Collection; 051 import java.util.HashMap; 052 import java.util.Iterator; 053 import java.util.List; 054 import java.util.Random; 055 import java.util.Set; 056 import java.util.concurrent.Callable; 057 058 import javax.imageio.ImageIO; 059 import javax.media.j3d.OrderedGroup; 060 import javax.vecmath.Point3d; 061 import javax.vecmath.Vector3f; 062 063 import org.deegree.framework.log.ILogger; 064 import org.deegree.framework.log.LoggerFactory; 065 import org.deegree.framework.util.MapUtils; 066 import org.deegree.framework.util.StringTools; 067 import org.deegree.i18n.Messages; 068 import org.deegree.model.crs.CoordinateSystem; 069 import org.deegree.model.spatialschema.Envelope; 070 import org.deegree.model.spatialschema.GeometryException; 071 import org.deegree.model.spatialschema.Position; 072 import org.deegree.model.spatialschema.Surface; 073 import org.deegree.model.spatialschema.WKTAdapter; 074 import org.deegree.ogcwebservices.OGCWebServiceException; 075 import org.deegree.ogcwebservices.wpvs.GetViewServiceInvoker; 076 import org.deegree.ogcwebservices.wpvs.WCSInvoker; 077 import org.deegree.ogcwebservices.wpvs.WFSInvoker; 078 import org.deegree.ogcwebservices.wpvs.WMSInvoker; 079 import org.deegree.ogcwebservices.wpvs.configuration.AbstractDataSource; 080 import org.deegree.ogcwebservices.wpvs.configuration.WPVSConfiguration; 081 import org.deegree.ogcwebservices.wpvs.j3d.DefaultSurface; 082 import org.deegree.ogcwebservices.wpvs.j3d.TerrainModel; 083 import org.deegree.ogcwebservices.wpvs.j3d.TexturedHeightMapTerrain; 084 import org.deegree.ogcwebservices.wpvs.j3d.TriangleTerrain; 085 import org.deegree.processing.raster.converter.Image2RawData; 086 import org.deegree.processing.raster.filter.Convolve; 087 import org.deegree.processing.raster.filter.RasterFilterException; 088 import org.j3d.geom.GeometryData; 089 090 /** 091 * The <code>ResolutionStripe</code> class encapsulates a Surface with a maximum Resolution, which is convenient for the 092 * creation of a quadtree. 093 * 094 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 095 * 096 * @author last edited by: $Author: rbezema $ 097 * 098 * @version $Revision: 20595 $, $Date: 2009-11-05 13:26:40 +0100 (Do, 05 Nov 2009) $ 099 * 100 */ 101 102 public class ResolutionStripe implements Callable<ResolutionStripe>, Comparable<ResolutionStripe> { 103 private final static ILogger LOG = LoggerFactory.getLogger( ResolutionStripe.class ); 104 105 /** 106 * No information about the elevationmodel is known (e.g. no elevationModel is set) 107 */ 108 public final static int ELEVATION_MODEL_UNKNOWN = 0; 109 110 /** 111 * The elevationmodel uses a grid data serving datasource (e.g. CSW) 112 */ 113 public final static int ELEVATION_MODEL_GRID = 1; 114 115 /** 116 * The elevationmodel uses a point data serving datasource (e.g WFS) 117 */ 118 public final static int ELEVATION_MODEL_POINTS = 2; 119 120 private final double maxResolution; 121 122 private final double minResolution; 123 124 private Surface surface; 125 126 private TerrainModel elevationModel = null; 127 128 private AbstractDataSource elevationModelDataSource = null; 129 130 private HashMap<String, BufferedImage> textures; 131 132 private HashMap<String, OGCWebServiceException> textureExceptions; 133 134 private ArrayList<AbstractDataSource> texturesDataSources; 135 136 private HashMap<String, DefaultSurface> features; 137 138 private ArrayList<AbstractDataSource> featureCollectionDataSources; 139 140 private double minimalHeightlevel; 141 142 private String outputFormat = null; 143 144 private OrderedGroup resultingJ3DScene; 145 146 private double scale; 147 148 private int dgmType; 149 150 private BufferedImage resultTexture = null; 151 152 private List<Point3d> pointList; 153 154 // a string for debugging containing the old resolution, if the resolution was fixed because of the maxRequestSize. 155 private String debugString = null; 156 157 /** 158 * @param surface 159 * of this resolution stripe, after the stripeFactory it is a trapezium, after the quad-splitter an 160 * axis-alligned bbox, which will be a request. 161 * @param maximumResolution 162 * the largest resolution value, resulting in the smallest map Resolution, e.g. farthest away from the 163 * viewer. 164 * @param minimumResolution 165 * the smallest resolution value, resulting in the highest map Resolution, e.g. nearest to the viewer. 166 * @param minimalHeight 167 * the terrain height which will be taken if an error occurred while retrieving height data. 168 * @param scale 169 * of the heights ( 1.0 means no scaling ) 170 */ 171 public ResolutionStripe( Surface surface, double maximumResolution, double minimumResolution, double minimalHeight, 172 double scale ) { 173 this.surface = surface; 174 // calculate the requestWidth and requestHeight. 175 int rH = (int) Math.round( surface.getEnvelope().getHeight() / Math.abs( minimumResolution ) ); 176 int rW = (int) Math.round( surface.getEnvelope().getWidth() / Math.abs( minimumResolution ) ); 177 178 if ( rH > WPVSConfiguration.MAX_REQUEST_SIZE || rH < 0 || rW > WPVSConfiguration.MAX_REQUEST_SIZE || rW < 0 ) { 179 minResolution = ( ( rH > rW ) ? surface.getEnvelope().getHeight() : surface.getEnvelope().getWidth() ) 180 / WPVSConfiguration.MAX_REQUEST_SIZE; 181 maxResolution = minResolution; 182 debugString = "fixed resolution: " + minimumResolution; 183 LOG.logDebug( "Setting maxResolution to " + minResolution + " instead of " + minimumResolution 184 + " because the request width or height would be larger as + " 185 + WPVSConfiguration.MAX_REQUEST_SIZE + " pixel." ); 186 } else { 187 this.minResolution = minimumResolution; 188 this.maxResolution = maximumResolution; 189 } 190 this.minimalHeightlevel = minimalHeight; 191 this.scale = scale; 192 featureCollectionDataSources = new ArrayList<AbstractDataSource>( 5 ); 193 texturesDataSources = new ArrayList<AbstractDataSource>( 5 ); 194 textures = new HashMap<String, BufferedImage>( 10 ); 195 textureExceptions = new HashMap<String, OGCWebServiceException>( 10 ); 196 features = new HashMap<String, DefaultSurface>( 1000 ); 197 // resultingJ3DScene = new BranchGroup(); 198 resultingJ3DScene = null; 199 dgmType = ResolutionStripe.ELEVATION_MODEL_UNKNOWN; 200 } 201 202 /** 203 * @param surface 204 * of this resolution stripe, after the stripeFactory it is a trapezium, after the quad-splitter an 205 * axis-alligned bbox, which will be a request. 206 * @param maximumResolution 207 * the largest resolution value, resulting in the smallest map Resolution, e.g. farthest away from the 208 * viewer. 209 * @param minimumResolution 210 * the smallest resolution value, resulting in the highest map Resolution, e.g. nearest to the viewer. 211 * @param minimalHeight 212 * the terrain height which will be taken if an error occurred while retrieving height data. 213 * @param outputFormat 214 * of the requests. 215 * @param scale 216 * of the heights ( 1.0 means no scaling ) 217 */ 218 public ResolutionStripe( Surface surface, double maximumResolution, double minimumResolution, double minimalHeight, 219 String outputFormat, double scale ) { 220 this( surface, maximumResolution, minimumResolution, minimalHeight, scale ); 221 this.outputFormat = outputFormat; 222 } 223 224 /** 225 * @return the CRS of this ResolutionStripe 226 */ 227 public CoordinateSystem getCRSName() { 228 return surface.getCoordinateSystem(); 229 } 230 231 /** 232 * @return the largest resolution value, resulting in the smallest map Resolution, e.g. farthest away from the 233 * viewer. 234 */ 235 public double getMaxResolution() { 236 return maxResolution; 237 } 238 239 /** 240 * @return the (always possitive) resolution of the largest (away from the viewer) side of the surface as scale 241 * denominator, which means divide by {@link MapUtils#DEFAULT_PIXEL_SIZE}. 242 */ 243 public double getMaxResolutionAsScaleDenominator() { 244 return Math.abs( maxResolution ) / MapUtils.DEFAULT_PIXEL_SIZE; 245 } 246 247 /** 248 * @return the (always possitive) resolution of the smallest (towards the viewer) side of the surface as scale 249 * denominator, which means divide by {@link MapUtils#DEFAULT_PIXEL_SIZE}. 250 */ 251 public double getMinResolutionAsScaleDenominator() { 252 return Math.abs( minResolution ) / MapUtils.DEFAULT_PIXEL_SIZE; 253 } 254 255 /** 256 * @return the smallest resolution value, resulting in the highest map Resolution, e.g. nearest to the viewer. 257 */ 258 public double getMinResolution() { 259 return minResolution; 260 } 261 262 /** 263 * @return the geometric surface which is defines this resolutionStripe. 264 */ 265 public Surface getSurface() { 266 return surface; 267 } 268 269 /** 270 * @return the minimalTerrainHeight. 271 */ 272 public double getMinimalTerrainHeight() { 273 return minimalHeightlevel; 274 } 275 276 /** 277 * @return the requestwidth (in pixels) for the bbox containing this resolutionsstripe or -1 if the resolution is to 278 * high (e.g. int overload) 279 */ 280 public int getRequestWidthForBBox() { 281 int result = (int) Math.round( surface.getEnvelope().getWidth() / Math.abs( minResolution ) ); 282 if ( result > 8000 ) { 283 LOG.logDebug( "Returning -1 for the requestImageWidth, the maxResolution: " + maxResolution 284 + " the env.getHeight(): " + surface.getEnvelope().getHeight() ); 285 return -1; 286 } 287 return result; 288 } 289 290 /** 291 * @return the height (in pixels) of the request envelope or -1 if the resolution is to high 292 */ 293 public int getRequestHeightForBBox() { 294 int result = (int) Math.round( surface.getEnvelope().getHeight() / Math.abs( minResolution ) ); 295 if ( result > 80000 ) { 296 LOG.logDebug( "Returning -1 for the requestImageHeight, the maxResolution: " + maxResolution 297 + " the env.getHeight(): " + surface.getEnvelope().getHeight() ); 298 return -1; 299 } 300 return result; 301 } 302 303 /** 304 * @return the elevationModel if no elevationModel is created jet, an {@link TriangleTerrain} elevation model of the 305 * bbox of this stripe (with heightvalues set to minimalheigt) is returned. 306 */ 307 public TerrainModel getElevationModel() { 308 if ( elevationModel == null ) { 309 elevationModel = createTriangleTerrainFromBBox(); 310 } 311 return elevationModel; 312 } 313 314 /** 315 * @param elevationModel 316 * An other elevationModel. 317 */ 318 public void setElevationModel( TerrainModel elevationModel ) { 319 this.elevationModel = elevationModel; 320 } 321 322 /** 323 * @param pointList 324 * containing Points which represents the heights of measures points (normally aquired from a wfs). 325 */ 326 public void setElevationModelFromMeassurePoints( List<Point3d> pointList ) { 327 // LOG.logDebug( "Found following meassure points from a wfs: " + pointList ); 328 this.pointList = pointList; 329 // A little hack. 330 // add the heightvalues of the nearest meassurepoint to the corner points of the bbox. 331 Point3d ll = new Point3d( Double.MAX_VALUE, Double.MAX_VALUE, 0 ); 332 Point3d lr = new Point3d( Double.MIN_VALUE, Double.MAX_VALUE, 0 ); 333 Point3d ur = new Point3d( Double.MIN_VALUE, Double.MIN_VALUE, 0 ); 334 Point3d ul = new Point3d( Double.MAX_VALUE, Double.MIN_VALUE, 0 ); 335 for ( Point3d p : pointList ) { 336 if ( p.x < ll.x && p.y < ll.y ) { 337 ll.x = p.x; 338 ll.y = p.y; 339 ll.z = p.z; 340 } 341 if ( p.x > lr.x && p.y < lr.y ) { 342 lr.x = p.x; 343 lr.y = p.y; 344 lr.z = p.z; 345 } 346 if ( p.x > ur.x && p.y > ur.y ) { 347 ur.x = p.x; 348 ur.y = p.y; 349 ur.z = p.z; 350 } 351 if ( p.x < ul.x && p.y > ul.y ) { 352 ul.x = p.x; 353 ul.y = p.y; 354 ul.z = p.z; 355 } 356 } 357 Position min = surface.getEnvelope().getMin(); 358 Position max = surface.getEnvelope().getMax(); 359 this.pointList.add( new Point3d( min.getX(), min.getY(), ll.z ) ); 360 this.pointList.add( new Point3d( max.getX(), min.getY(), lr.z ) ); 361 this.pointList.add( new Point3d( max.getX(), max.getY(), ur.z ) ); 362 this.pointList.add( new Point3d( min.getX(), max.getY(), ul.z ) ); 363 } 364 365 /** 366 * @param heightMap 367 * a BufferedImage which contains height values, normally aquired from a wcs. 368 */ 369 public void setElevationModelFromHeightMap( BufferedImage heightMap ) { 370 Image2RawData i2rd = new Image2RawData( heightMap, (float) scale ); 371 float[][] heights = i2rd.parse(); 372 373 // smooth the outcome of the rasterdata, by applying a lowpass filter. 374 float[][] kernel = new float[5][5]; 375 for ( int i = 0; i < kernel.length; i++ ) { 376 for ( int j = 0; j < kernel[i].length; j++ ) { 377 kernel[i][j] = 1; 378 } 379 } 380 381 try { 382 heights = Convolve.perform( heights, kernel ); 383 } catch ( RasterFilterException e ) { 384 e.printStackTrace(); 385 } 386 387 Envelope env = surface.getEnvelope(); 388 389 Position lowerLeft = surface.getEnvelope().getMin(); 390 Vector3f lLeft = new Vector3f( (float) lowerLeft.getX(), (float) lowerLeft.getY(), 0 ); 391 392 // Triangles won't work -> an error in org.j3d.geom.terrain.ElevationGridGenerator therefor 393 // using TRIANGLE_STRIPS 394 LOG.logDebug( "Trying to create elevationmodel from the points received from a wcs" ); 395 elevationModel = new TexturedHeightMapTerrain( (float) env.getWidth(), (float) env.getHeight(), heights, lLeft, 396 GeometryData.TRIANGLE_STRIPS, false ); 397 LOG.logDebug( "From the point list of the wcs created following elevationModel: " + elevationModel ); 398 } 399 400 /** 401 * @return the features of this resolutionstripe 402 */ 403 public HashMap<String, DefaultSurface> getFeatures() { 404 return features; 405 } 406 407 /** 408 * @param key 409 * the name of the feature to be added. 410 * @param feature 411 * (e.g a building, tree etc.) as a DefaultSurface (derived frome Shape3D) to be added to the hashmap. 412 * @return true if the feature wasn't allready defined in the hashmap and could therefore be inserted, or if the key 413 * or feature are null. 414 */ 415 public boolean addFeature( String key, DefaultSurface feature ) { 416 if ( feature != null && key != null ) { 417 DefaultSurface tmp = features.get( key ); 418 if ( tmp == null && !features.containsKey( key ) ) { 419 features.put( key, feature ); 420 return true; 421 } 422 } 423 return false; 424 } 425 426 /** 427 * @return the textures value. 428 */ 429 public HashMap<String, BufferedImage> getTextures() { 430 return textures; 431 } 432 433 /** 434 * @param key 435 * the name of the texture to be added. 436 * @param texture 437 * to be added to the hashmap. 438 * @return true if the texture wasn't allready defined in the hashmap and could therefore be inserted, or if the key 439 * or texture are null. 440 */ 441 public boolean addTexture( String key, BufferedImage texture ) { 442 if ( texture != null && key != null ) { 443 BufferedImage tmp = textures.get( key ); 444 if ( tmp == null && !textures.containsKey( key ) ) { 445 textures.put( key, texture ); 446 return true; 447 } 448 } 449 return false; 450 } 451 452 /** 453 * @return the elevationModelDataSource value. 454 */ 455 public AbstractDataSource getElevationModelDataSource() { 456 return elevationModelDataSource; 457 } 458 459 /** 460 * @param elevationModelDataSource 461 * An other elevationModelDataSource value. 462 */ 463 public void setElevationModelDataSource( AbstractDataSource elevationModelDataSource ) { 464 this.elevationModelDataSource = elevationModelDataSource; 465 if ( elevationModelDataSource.getServiceType() == AbstractDataSource.LOCAL_WFS 466 || elevationModelDataSource.getServiceType() == AbstractDataSource.REMOTE_WFS ) { 467 dgmType = ResolutionStripe.ELEVATION_MODEL_POINTS; 468 } else if ( elevationModelDataSource.getServiceType() == AbstractDataSource.LOCAL_WCS 469 || elevationModelDataSource.getServiceType() == AbstractDataSource.REMOTE_WCS ) { 470 dgmType = ResolutionStripe.ELEVATION_MODEL_GRID; 471 } 472 } 473 474 /** 475 * @return the featureCollectionDataSources value. 476 */ 477 public ArrayList<AbstractDataSource> getFeatureCollectionDataSources() { 478 return featureCollectionDataSources; 479 } 480 481 /** 482 * @param featureCollectionDataSource 483 * a DataSources for a specific featureCollection. 484 */ 485 public void addFeatureCollectionDataSource( AbstractDataSource featureCollectionDataSource ) { 486 if ( featureCollectionDataSource != null ) { 487 if ( !featureCollectionDataSources.contains( featureCollectionDataSource ) ) { 488 featureCollectionDataSources.add( featureCollectionDataSource ); 489 } 490 } 491 } 492 493 /** 494 * @return the texturesDataSources value. 495 */ 496 public ArrayList<AbstractDataSource> getTexturesDataSources() { 497 return texturesDataSources; 498 } 499 500 /** 501 * @param textureDataSource 502 * An other texturesDataSources value. 503 */ 504 public void addTextureDataSource( AbstractDataSource textureDataSource ) { 505 if ( textureDataSource != null ) { 506 if ( !texturesDataSources.contains( textureDataSource ) ) { 507 texturesDataSources.add( textureDataSource ); 508 } 509 } 510 } 511 512 /** 513 * 514 * @return the OutputFormat of the resultImage 515 */ 516 public String getOutputFormat() { 517 return outputFormat; 518 } 519 520 /** 521 * @param outputFormat 522 * the mime type of the resultimage 523 */ 524 public void setOutputFormat( String outputFormat ) { 525 this.outputFormat = outputFormat; 526 } 527 528 /** 529 * After a call to this class call method, it is possible to get a Java3D representation --in form of a 530 * BranchGroup-- of this resolutionStripe. In this BranchGroup all the textures and requested features are added to 531 * the ElevationModel. 532 * 533 * @return a Java3D representation of this ResolutionStripe. 534 */ 535 public OrderedGroup getJava3DRepresentation() { 536 if ( resultingJ3DScene == null ) { 537 createJava3DRepresentation(); 538 } 539 return resultingJ3DScene; 540 } 541 542 @Override 543 public String toString() { 544 StringBuilder sb = new StringBuilder( 512 ); 545 sb.append( "Resolution: " ).append( maxResolution ).append( "\n" ); 546 547 try { 548 sb.append( "Surface: " ).append( WKTAdapter.export( this.surface ) ).append( "\n" ); 549 } catch ( GeometryException e ) { 550 e.printStackTrace(); 551 } 552 sb.append( "FeatureCollectionDataSources:\n " ); 553 if ( featureCollectionDataSources.size() == 0 ) { 554 sb.append( " - No feature collection datasources defined.\n" ); 555 } else { 556 for ( int i = 0; i < featureCollectionDataSources.size(); ++i ) { 557 sb.append( " - " ).append( i ).append( ") " ).append( featureCollectionDataSources.get( i ) ).append( 558 "\n" ); 559 } 560 } 561 sb.append( "TexturesDataSources:\n" ); 562 if ( texturesDataSources.size() == 0 ) { 563 sb.append( " - No texture datasources defined.\n" ); 564 } else { 565 for ( int i = 0; i < texturesDataSources.size(); ++i ) { 566 sb.append( " - " ).append( i ).append( ") " ).append( texturesDataSources.get( i ) ).append( "\n" ); 567 } 568 } 569 570 sb.append( "ElevationDataSource: \n" ); 571 if ( elevationModelDataSource == null ) { 572 sb.append( " - No elevation model datasources defined.\n" ); 573 } else { 574 sb.append( " - " ).append( elevationModelDataSource ).append( "\n" ); 575 } 576 return sb.toString(); 577 } 578 579 /** 580 * @return a well known representation of the geometry of this Resolutionstripe 581 */ 582 public String toWKT() { 583 try { 584 return new StringBuffer( WKTAdapter.export( this.surface ) ).toString(); 585 } catch ( GeometryException e ) { 586 e.printStackTrace(); 587 return new String( "" ); 588 } 589 } 590 591 /** 592 * Outputs the textures to the tmp directory with following format: 593 * <code>key_response:___res:_maxresolution__random_id.jpg</code> this file will be deleted at jvm termination. 594 */ 595 public void outputTextures() { 596 597 Set<String> keys = textures.keySet(); 598 Random rand = new Random( System.currentTimeMillis() ); 599 for ( String key : keys ) { 600 try { 601 // System.out.println( "saving image" ); 602 603 File f = new File( key + "_response_" + rand.nextInt() + "__res_" + maxResolution + "___.jpg" ); 604 f.deleteOnExit(); 605 LOG.logDebug( "Saving result texture ( in file: " + f.getAbsolutePath() + " for resolution stripe\n" 606 + this ); 607 // System.out.println( f ); 608 // ImageUtils.saveImage( responseImage, f, 1 ); 609 ImageIO.write( textures.get( key ), "jpg", f ); 610 } catch ( IOException e ) { 611 e.printStackTrace(); 612 } catch ( Exception e ) { 613 e.printStackTrace(); 614 } 615 } 616 } 617 618 /** 619 * @return the scale of the heights (1.0 means no scaling) 620 */ 621 public double getScale() { 622 return scale; 623 } 624 625 /** 626 * @return one of the possible values {@link ResolutionStripe#ELEVATION_MODEL_UNKNOWN} 627 * {@link ResolutionStripe#ELEVATION_MODEL_GRID} {@link ResolutionStripe#ELEVATION_MODEL_POINTS} 628 */ 629 public int getDGMType() { 630 return dgmType; 631 } 632 633 /** 634 * @return the resultTexture, which is build from all requested (wms/wcs) textures. 635 */ 636 public BufferedImage getResultTexture() { 637 return resultTexture; 638 } 639 640 /** 641 * This call method is part of the Deegree Concurrent framework ({@link org.deegree.framework.concurrent.Executor}) 642 * . In this case it requests all the Data for a <code>ResolutionStripe</code> by invoking the necessary 643 * webservices. 644 * 645 * @see java.util.concurrent.Callable#call() 646 */ 647 public ResolutionStripe call() 648 throws OGCWebServiceException { 649 int invokeCounter = 0; 650 // Strictly the different datasources must not be separated into two different 651 // DataSourceList, it might be handy (for caching) to do so though. 652 for ( AbstractDataSource textureDS : texturesDataSources ) { 653 invokeDataSource( textureDS, invokeCounter++ ); 654 } 655 // create one texture from all textures 656 createTexture(); 657 658 // Create the buildings etc. 659 for ( AbstractDataSource featureDS : featureCollectionDataSources ) { 660 invokeDataSource( featureDS, invokeCounter++ ); 661 } 662 663 // create the terrain, if no terrain was requested, just create a flat square. 664 if ( elevationModelDataSource != null ) { 665 LOG.logDebug( "Invoking terrain datasource, because an elevation model was given." ); 666 invokeDataSource( elevationModelDataSource, -1 ); 667 } else { 668 LOG.logDebug( "Create flat triangle terrain, because no elevation model was given." ); 669 elevationModel = createTriangleTerrainFromBBox(); 670 } 671 // let this thread create the model in advance. 672 // if ( dgmType == ELEVATION_MODEL_GRID || dgmType == ELEVATION_MODEL_UNKNOWN) { 673 createJava3DRepresentation(); 674 // } 675 return this; 676 } 677 678 private void invokeDataSource( AbstractDataSource ads, int id ) { 679 try { 680 GetViewServiceInvoker invoker = null; 681 if ( ads.getServiceType() == AbstractDataSource.LOCAL_WMS 682 || ads.getServiceType() == AbstractDataSource.REMOTE_WMS ) { 683 invoker = new WMSInvoker( this, id ); 684 } else if ( ads.getServiceType() == AbstractDataSource.LOCAL_WCS 685 || ads.getServiceType() == AbstractDataSource.REMOTE_WCS ) { 686 invoker = new WCSInvoker( this, id, outputFormat, ( ads == elevationModelDataSource ) ); 687 } else { // WFS -> was checked in DefaultGetViewHandler 688 invoker = new WFSInvoker( this, id, ( ads == elevationModelDataSource ) ); 689 } 690 invoker.invokeService( ads ); 691 } catch ( Throwable e ) { 692 if ( !Thread.currentThread().isInterrupted() ) { 693 LOG.logError( "WPVS: error while invoking a datasource: " + e.getMessage(), e ); 694 } 695 } 696 } 697 698 private TriangleTerrain createTriangleTerrainFromBBox() { 699 List<Point3d> measurePoints = new ArrayList<Point3d>( 0 ); 700 return new TriangleTerrain( measurePoints, surface.getEnvelope(), minimalHeightlevel, scale ); 701 } 702 703 /** 704 * Creates a java3d representation of the data wihtin this resolutionstripe. If the DGM is defined from 705 * measurepoints, these points are triangualuated before createing the OrderedGroup. 706 */ 707 private void createJava3DRepresentation() { 708 resultingJ3DScene = new OrderedGroup(); 709 710 if ( resultTexture != null && elevationModel != null ) { 711 LOG.logDebug( "Creating an elevationmodel for the resolutionstripe" ); 712 elevationModel.setTexture( resultTexture ); 713 elevationModel.createTerrain(); 714 resultingJ3DScene.addChild( elevationModel ); 715 } 716 717 // add the features (e.g. buildings) to the j3d representation of this resolutionstripe. 718 Collection<DefaultSurface> featureSurfaces = features.values(); 719 if ( featureSurfaces != null ) { 720 for ( DefaultSurface ds : featureSurfaces ) { 721 resultingJ3DScene.addChild( ds ); 722 } 723 } 724 // if( LOG.getLevel() == ILogger.LOG_DEBUG ){ 725 // 726 // 727 // try { 728 // File f = File.createTempFile( "features", ".xml" ); 729 // J3DToCityGMLExporter exporter = new J3DToCityGMLExporter( "test", surface.getCoordinateSystem().getName(), 730 // "bla", f.getParent()+File.separator+"textures", 0, 0, 0, false ); 731 // StringBuilder sb = new StringBuilder( 200000 ); 732 // exporter.export( sb, resultingJ3DScene ); 733 // LOG.logDebug( "Writing citygml file to: " + f.getAbsoluteFile() ); 734 // BufferedWriter out = new BufferedWriter( new FileWriter( f ) ); 735 // out.write( sb.toString() ); 736 // out.flush(); 737 // out.close(); 738 // } catch ( IOException e ) { 739 // // TODO Auto-generated catch block 740 // e.printStackTrace(); 741 // } 742 // } 743 } 744 745 /** 746 * Paints all textures on the given bufferedImage 747 */ 748 private void createTexture() { 749 Collection<BufferedImage> textureImages = textures.values(); 750 Graphics2D g2d = null; 751 if ( textureImages != null && !textureImages.isEmpty() ) { 752 // create texture as BufferedImage 753 if ( textureImages.size() > 0 ) { 754 Iterator<BufferedImage> it = textureImages.iterator(); 755 resultTexture = it.next(); 756 // get the g2d from the resulttexture in advance. 757 if ( resultTexture != null ) { 758 g2d = (Graphics2D) resultTexture.getGraphics(); 759 } 760 while ( it.hasNext() ) { 761 if ( resultTexture == null ) { 762 resultTexture = it.next(); 763 } else { 764 if ( g2d == null ) { 765 g2d = (Graphics2D) resultTexture.getGraphics(); 766 } 767 // draw the next image on the g2d 768 g2d.drawImage( it.next(), 0, 0, null ); 769 } 770 } 771 } 772 } 773 if ( resultTexture == null ) { 774 // no images were found, so output the error messages or the default error message. 775 LOG.logDebug( "No images were found (or all were null), therefore outputing the errormessages" ); 776 resultTexture = new BufferedImage( getRequestWidthForBBox(), getRequestHeightForBBox(), 777 BufferedImage.TYPE_INT_ARGB ); 778 g2d = (Graphics2D) resultTexture.getGraphics(); 779 780 if ( texturesDataSources.size() > 0 ) { 781 Collection<OGCWebServiceException> exceptions = textureExceptions.values(); 782 String[] exceptionStrings = new String[exceptions.size()]; 783 int count = 0; 784 for ( OGCWebServiceException ogcwse : exceptions ) { 785 786 String message = StringTools.concat( 100, "error (", Integer.valueOf( count + 1 ), "): ", 787 ogcwse.getMessage() ); 788 exceptionStrings[count++] = message; 789 } 790 paintString( g2d, exceptionStrings ); 791 } else { 792 g2d.setColor( Color.WHITE ); 793 g2d.drawRect( 0, 0, resultTexture.getWidth(), resultTexture.getHeight() ); 794 } 795 } 796 if ( g2d != null ) { 797 798 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 799 Stroke s = g2d.getStroke(); 800 Color c = g2d.getColor(); 801 g2d.setColor( Color.RED ); 802 g2d.setStroke( new BasicStroke( 3 ) ); 803 g2d.drawRect( 2, 2, resultTexture.getWidth() - 2, resultTexture.getHeight() - 2 ); 804 g2d.setColor( c ); 805 g2d.setStroke( s ); 806 807 paintString( g2d, new String[] { Double.toString( minResolution ), 808 ( ( debugString != null ) ? debugString : "" ), 809 Double.toString( maxResolution ) } ); 810 } 811 g2d.dispose(); 812 } 813 814 } 815 816 private void paintString( Graphics2D g2d, String[] stringsToPaint ) { 817 Font originalFont = g2d.getFont(); 818 float originalFontSize = originalFont.getSize(); 819 if ( stringsToPaint == null || stringsToPaint.length == 0 ) { 820 LOG.logError( Messages.getMessage( "WPVS_NO_STRINGS" ) ); 821 return; 822 } 823 // find the largest string 824 String testString = new String(); 825 for ( int i = 0; i < stringsToPaint.length; ++i ) { 826 if ( stringsToPaint[i].length() > testString.length() ) { 827 testString = stringsToPaint[i]; 828 } 829 } 830 // calculate the maximal height (with respect to the strings) of the font. 831 float requestWidth = getRequestWidthForBBox(); 832 float requestHeight = getRequestHeightForBBox(); 833 float maxFontHeight = requestHeight / stringsToPaint.length; 834 TextLayout tl = new TextLayout( testString, originalFont, g2d.getFontRenderContext() ); 835 Rectangle2D r2d = tl.getBounds(); 836 float width = (float) r2d.getWidth(); 837 float height = (float) r2d.getHeight(); 838 839 // little widther than the requestwidth ensures total readabillity 840 float approx = requestWidth / ( width * 1.2f ); 841 if ( ( originalFontSize * approx ) < maxFontHeight ) { 842 originalFont = originalFont.deriveFont( originalFontSize * approx ); 843 } else { 844 originalFont = originalFont.deriveFont( maxFontHeight ); 845 } 846 tl = new TextLayout( testString, originalFont, g2d.getFontRenderContext() ); 847 r2d = tl.getBounds(); 848 width = (float) r2d.getWidth(); 849 height = (float) r2d.getHeight(); 850 851 int x = (int) Math.round( ( requestWidth * 0.5 ) - ( width * 0.5 ) ); 852 int stringOffset = (int) Math.round( ( requestHeight * ( 1d / ( stringsToPaint.length + 1 ) ) ) 853 + ( height * 0.5 ) ); 854 g2d.setColor( Color.GRAY ); 855 // g2d.drawRect( 0, 0, (int) requestWidth, (int) requestHeight ); 856 g2d.setColor( Color.RED ); 857 g2d.setFont( originalFont ); 858 for ( int i = 0; i < stringsToPaint.length; ++i ) { 859 int y = ( i + 1 ) * stringOffset; 860 g2d.drawString( stringsToPaint[i], x, y ); 861 } 862 } 863 864 /** 865 * @param datasourceID 866 * the id of the datasources which received an exception while retrieving a texture. 867 * @param exception 868 * the exception that occured while invoking the datasource. 869 */ 870 public void setTextureRetrievalException( String datasourceID, OGCWebServiceException exception ) { 871 if ( datasourceID != null && exception != null ) { 872 if ( !textureExceptions.containsKey( datasourceID ) ) { 873 textureExceptions.put( datasourceID, exception ); 874 } 875 } 876 } 877 878 /** 879 * @return the pointList 880 */ 881 public final List<Point3d> getMeassurepointsAsList() { 882 return pointList; 883 } 884 885 /* 886 * (non-Javadoc) 887 * 888 * @see java.lang.Comparable#compareTo(java.lang.Object) 889 */ 890 public int compareTo( ResolutionStripe other ) { 891 if ( Math.abs( other.maxResolution - this.maxResolution ) < 0.0001 ) { 892 return 0; 893 } 894 return ( ( this.maxResolution - other.maxResolution ) < 0 ) ? -1 : 1; 895 896 } 897 898 }