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 }