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