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